diff --git a/api/v1/post/application.php b/api/v1/post/application.php index dd567bf..0c397e1 100644 --- a/api/v1/post/application.php +++ b/api/v1/post/application.php @@ -15,7 +15,6 @@ if ($action !=''){ //Connect to DB //------------------------------------------ $pdo = dbConnect($dbname); -$pdo2 = dbConnect($dbname); //------------------------------------------ //CONTENT FROM API (POST) @@ -99,224 +98,108 @@ switch ($action) { ]; $description = json_encode($history_description, JSON_UNESCAPED_UNICODE); + // -------------------------------------------- // Check if multiple serialnumbers are provided // -------------------------------------------- - if(is_array($post_content['sn'])){ - foreach ($post_content['sn'] as $sn){ - //Get equipmentid based on rowID - $rowID = getrowID($dbname,'rowID','equipment','serialnumber="'.$sn.'"'); - if ($rowID){ - //check if under warranty - $warranty = getrowID($dbname,'rowID','equipment_history','equipmentid="'.$rowID['rowID'].'" && (type="'.$type9.'" || type="'.$type10.'" || type="'.$type11.'" || type="'.$type12.'")'); - if ($warranty){ - // -------------------------------------------- - // Already under contract - // -------------------------------------------- - //Serialnumber under warranty - $message_box[] = $sn.' - '.$register_message_2; - $communication_check = 1; - } else - { - // -------------------------------------------- - // Not under warranty - // -------------------------------------------- - //Send user firmware account - $firmware_account_send = 1; - //create history - // Prepare queries - $sql = 'INSERT INTO equipment_history (equipmentid, type, description, created, createdby,updatedby) VALUES (?,?,?,?,?,?)'; - $stmt = $pdo->prepare($sql); - $stmt->execute([$rowID['rowID'],$type9,$description,$timestamp,$post_content['email'],$post_content['email']]); + // Normalize input to always be an array + $serial_numbers = is_array($post_content['sn']) ? $post_content['sn'] : [$post_content['sn']]; - //GET PARTNER DETAILS OF EQUIPMENT - $partner_equipment = getrowID($dbname,'accounthierarchy','equipment','rowID="'.$rowID['rowID'].'"'); - $partner_equipment = json_decode($partner_equipment['accounthierarchy']); - - //Setup partnerhierarchy (salesID) - $partnerhierarchy =[ - "salesid"=>$partner_equipment->salesid, - "soldto"=>$partner_equipment->soldto - ]; - - //Setup variables for partner - $partnername = $post_content['organization']; - $partnernotes = 'created based on user registration'; - $salesID = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); - $createdby = 'system'; - - //Check if shipto is empty and if empty search partner or create - if ($partner_equipment->shipto == ''){ - $partner_shipto = getrowID($dbname,'partnerID','partner','partnername = "'.$partnername.'" && partnertype="'.$partnertype3.'"'); - if ($partner_shipto){ - //Partner exists - Use it - $partnerhierarchy['shipto'] = $partner_shipto['partnerID'].'-'.$partnername; - } else { - //Partner does not exist create - $sql = 'INSERT INTO partner (partnertype,partnername,salesID,createdby,status) VALUES (?,?,?,?,?)'; - $stmt = $pdo2->prepare($sql); - $stmt->execute([$partnertype3,$partnername,$salesID,$createdby,'1']); - - //Get rowID of created partner and use it - $partner_rowid = $pdo2->lastInsertId(); - $partnerhierarchy['shipto'] = $partner_rowid.'-'.$partnername; - } - } else { - // Shipto exist use it - $partnerhierarchy['shipto'] = $partner_equipment->shipto; - } - //Check if location is empty and if empty search partner or create - if ($partner_equipment->location == ''){ - $partner_location = getrowID($dbname,'partnerID','partner','partnername = "'.$partnername.'" && partnertype="'.$partnertype4.'"'); - if ($partner_location){ - //Partner exists - Use it - $partnerhierarchy['location'] = $partner_location['partnerID'].'-'.$partnername; - - } else { - //Partner does not exist create - $sql = 'INSERT INTO partner (partnertype,partnername,salesID,createdby,status) VALUES (?,?,?,?,?)'; - $stmt = $pdo2->prepare($sql); - $stmt->execute([$partnertype4,$partnername,$salesID,$createdby,'1']); - - //Get rowID of created partner and use it - $partner_rowid = $pdo2->lastInsertId(); - $partnerhierarchy['location'] = $partner_rowid.'-'.$partnername; - } - - } else { - // Location exist use it - $partnerhierarchy['location'] = $partner_equipment->location; - } - - $shipto = $partnerhierarchy['shipto'] ?? ''; - $partnerhierarchy = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); - // -------------------------------------------- - // Update equipment record warranty_date, partnerhierarchy, status equipment - // -------------------------------------------- - $sql = 'UPDATE equipment SET status = ?, warranty_date = ?, accounthierarchy = ?,updatedby = ? WHERE rowID = ?'; - $stmt = $pdo->prepare($sql); - $stmt->execute(['4',$warranty_extended,$partnerhierarchy,$username,$rowID['rowID']]); - - //Add warranty to changelog - $warranty_user = $post_content['email'] ?? 'system'; - changelog($dbname,'equipment',$rowID['rowID'],'Warranty',$warranty_extended,$warranty_user); - - //Serialnumber recognized - $message_box[] = $sn.' - '.$register_message_3; - $communication_check = 1; - } - } else { - //Serialnumber not recognized - $message_box[] = $sn.' - '.$register_message_1; - } + foreach ($serial_numbers as $sn) { + // Get equipment ID based on serial number + $rowID = getrowID($dbname, 'rowID', 'equipment', 'serialnumber="' . $sn . '"'); + + if (!$rowID) { + // Serial number not recognized + $message_box[] = $sn . ' - ' . $register_message_1; + continue; } - } - else { - // -------------------------------------------- - //Get equipmentid based on rowID - // -------------------------------------------- - $rowID = getrowID($dbname,'rowID','equipment','serialnumber="'.$post_content['sn'].'"'); - if ($rowID){ - //check if under warranty - $warranty = getrowID($dbname,'rowID','equipment_history','equipmentid="'.$rowID['rowID'].'" && (type="'.$type9.'" || type="'.$type10.'" || type="'.$type11.'" || type="'.$type12.'")'); - if ($warranty){ - // -------------------------------------------- + + // Check if under warranty + $warranty_types = [$type9, $type10, $type11, $type12]; + $warranty_condition = 'equipmentid="' . $rowID['rowID'] . '" && (type="' . implode('" || type="', $warranty_types) . '")'; + $warranty = getrowID($dbname, 'rowID', 'equipment_history', $warranty_condition); + + if ($warranty) { // Already under contract - // -------------------------------------------- - //Serialnumber not recognized - $message_box[] = $post_content['sn'].' - '.$register_message_2; - } else - { - // -------------------------------------------- - // Not under warranty - // -------------------------------------------- - $firmware_account_send = 1; - //create history - $sql = 'INSERT INTO equipment_history (equipmentid, type, description, created, createdby, updatedby) VALUES (?,?,?,?,?,?)'; - $stmt = $pdo->prepare($sql); - $stmt->execute([$rowID['rowID'],$type9,$description,$timestamp,$post_content['email'],$post_content['email']]); - - //GET PARTNER DETAILS OF EQUIPMENT - $partner_equipment = getrowID($dbname,'accounthierarchy','equipment','rowID="'.$rowID['rowID'].'"'); - $partner_equipment = json_decode($partner_equipment['accounthierarchy']); - - //Setup partnerhierarchy (salesID) - $partnerhierarchy =[ - "salesid"=>$partner_equipment->salesid, - "soldto"=>$partner_equipment->soldto - ]; - - //Setup variables for partner - $partnername = $post_content['organization']; - $partnernotes = 'created based on user registration'; - $salesID = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); - $createdby = 'system'; - - //Check if shipto is empty and if empty search partner or create - if ($partner_equipment->shipto == ''){ - $partner_shipto = getrowID($dbname,'partnerID','partner','partnername = "'.$partnername.'" && partnertype="'.$partnertype3.'"'); - if ($partner_shipto){ - //Partner exists - Use it - $partnerhierarchy['shipto'] = $partner_shipto['partnerID'].'-'.$partnername; - } else { - //Partner does not exist create - $sql = 'INSERT INTO partner (partnertype, partnername,salesID,createdby,status) VALUES (?,?,?,?,?)'; - $stmt = $pdo2->prepare($sql); - $stmt->execute([$partnertype3,$partnername,$salesID,$createdby,'1']); - - //Get rowID of created partner and use it - $partner_rowid = $pdo2->lastInsertId(); - $partnerhierarchy['shipto'] = $partner_rowid.'-'.$partnername; - } - } else { - // Shipto exist use it - $partnerhierarchy['shipto'] = $partner_equipment->shipto; - } - //Check if location is empty and if empty search partner or create - if ($partner_equipment->location == ''){ - $partner_location = getrowID($dbname,'partnerID','partner','partnername = "'.$partnername.'" && partnertype="'.$partnertype4.'"'); - if ($partner_location){ - //Partner exists - Use it - $partnerhierarchy['location'] = $partner_location['partnerID'].'-'.$partnername; - - } else { - //Partner does not exist create - $sql = 'INSERT INTO partner (partnertype,partnername,salesID,createdby,status) VALUES (?,?,?,?,?)'; - $stmt = $pdo2->prepare($sql); - $stmt->execute([$partnertype4,$partnername,$salesID,$createdby,'1']); - - //Get rowID of created partner and use it - $partner_rowid = $pdo2->lastInsertId(); - $partnerhierarchy['location'] = $partner_rowid.'-'.$partnername; - } - } else { - // Location exist use it - $partnerhierarchy['location'] = $partner_equipment->location; - } - - $partnerhierarchy = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); - - // -------------------------------------------- - // Update equipment record warranty_date, partnerhierarchy, status equipment - // -------------------------------------------- - $sql = 'UPDATE equipment SET status = ?, warranty_date = ?, accounthierarchy = ?, updatedby = ? WHERE rowID = ?'; - $stmt = $pdo->prepare($sql); - $stmt->execute(['4',$warranty_extended,$partnerhierarchy,$username,$rowID['rowID']]); - - //Add warranty to changelog - $warranty_user = $post_content['email'] ?? 'system'; - changelog($dbname,'equipment',$rowID['rowID'],'Warranty',$warranty_extended,$warranty_user); - - //Serialnumber recognized - $message_box[] = $post_content['sn'].' - '.$register_message_3; - } + $message_box[] = $sn . ' - ' . $register_message_2; + $communication_check = 1; + continue; } - else { - //Serialnumber not recognized - $message_box[] = $post_content['sn'].' - '.$register_message_1; - } - } + + // Not under warranty - process registration + $firmware_account_send = 1; + + // Create history entry + $sql = 'INSERT INTO equipment_history (equipmentid, type, description, created, createdby, updatedby) VALUES (?,?,?,?,?,?)'; + $stmt = $pdo->prepare($sql); + $stmt->execute([ + $rowID['rowID'], + $type9, + $description, + $timestamp, + $post_content['email'], + $post_content['email'] + ]); + + // Get partner details of equipment + $partner_equipment = getrowID($dbname, 'accounthierarchy', 'equipment', 'rowID="' . $rowID['rowID'] . '"'); + $partner_equipment = json_decode($partner_equipment['accounthierarchy']); + + // Setup partner hierarchy + $partnerhierarchy = [ + "salesid" => $partner_equipment->salesid, + "soldto" => $partner_equipment->soldto + ]; + + // Setup variables for partner + $partnername = $post_content['organization']; + $salesID = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); + $createdby = 'system'; + + // Helper function to get or create partner + $getOrCreatePartner = function($partnertype) use ($dbname, $partnername, $salesID, $createdby, $pdo) { + $partner = getrowID($dbname, 'partnerID', 'partner', 'partnername = "' . $partnername . '" && partnertype="' . $partnertype . '"'); + + if ($partner) { + return $partner['partnerID'] . '-' . $partnername; + } + + // Partner does not exist - create + $sql = 'INSERT INTO partner (partnertype, partnername, salesID, createdby, status) VALUES (?,?,?,?,?)'; + $stmt = $pdo->prepare($sql); + $stmt->execute([$partnertype, $partnername, $salesID, $createdby, '1']); + + $partner_rowid = $pdo->lastInsertId(); + return $partner_rowid . '-' . $partnername; + }; + + // Handle shipto + $partnerhierarchy['shipto'] = empty($partner_equipment->shipto) + ? $getOrCreatePartner($partnertype3) + : $partner_equipment->shipto; + + // Handle location + $partnerhierarchy['location'] = empty($partner_equipment->location) + ? $getOrCreatePartner($partnertype4) + : $partner_equipment->location; + + $partnerhierarchy_json = json_encode($partnerhierarchy, JSON_UNESCAPED_UNICODE); + + // Update equipment record + $sql = 'UPDATE equipment SET status = ?, warranty_date = ?, accounthierarchy = ?, updatedby = ? WHERE rowID = ?'; + $stmt = $pdo->prepare($sql); + $stmt->execute(['4', $warranty_extended, $partnerhierarchy_json, $username, $rowID['rowID']]); + + // Add warranty to changelog + $warranty_user = $post_content['email'] ?? 'system'; + changelog($dbname, 'equipment', $rowID['rowID'], 'Warranty', $warranty_extended, $warranty_user); + + // Serial number recognized + $message_box[] = $sn . ' - ' . $register_message_3; + $communication_check = 1; + } // -------------------------------------------- // Send generic account to user for software updates diff --git a/api/v2/post/marketing_update.php b/api/v2/post/marketing_update.php index 1062147..8ede166 100644 --- a/api/v2/post/marketing_update.php +++ b/api/v2/post/marketing_update.php @@ -19,9 +19,6 @@ if (isAllowed('marketing',$profile,$permission,'U') === 1){ $input = json_decode(file_get_contents('php://input'), true); $file_id = $input['file_id'] ?? ''; - $folder_id = $input['folder_id'] ?? ''; - $tags = $input['tags'] ?? []; - $title = $input['title'] ?? ''; if (empty($file_id)) { echo json_encode(['success' => false, 'error' => 'File ID is required']); @@ -29,35 +26,61 @@ if (isAllowed('marketing',$profile,$permission,'U') === 1){ } try { - // Update file - $update_sql = 'UPDATE `marketing_files` SET `title` = ?, `folder_id` = ? WHERE `id` = ? AND `accounthierarchy` LIKE ?'; - $stmt = $pdo->prepare($update_sql); - $stmt->execute([ - $title, - $folder_id ?: null, - $file_id, - $condition - ]); - - if ($stmt->rowCount() === 0) { + // First verify the file exists and user has access + $check_sql = 'SELECT id FROM `marketing_files` WHERE `id` = ?'; + $check_stmt = $pdo->prepare($check_sql); + $check_stmt->execute([$file_id]); + + if ($check_stmt->rowCount() === 0) { echo json_encode(['success' => false, 'error' => 'File not found or access denied']); exit; } - // Update tags - first remove existing - $pdo->prepare('DELETE FROM `marketing_file_tags` WHERE `file_id` = ?')->execute([$file_id]); + // Build dynamic UPDATE query for only changed fields + $update_fields = []; + $update_params = []; + + if (isset($input['title'])) { + $update_fields[] = '`title` = ?'; + $update_params[] = $input['title']; + } + + if (isset($input['folder_id'])) { + $update_fields[] = '`folder_id` = ?'; + $update_params[] = $input['folder_id'] ?: null; + } + + // Always update updatedby if there are changes + if (!empty($update_fields)) { + $update_fields[] = '`updatedby` = ?'; + $update_params[] = $username; + $update_params[] = $file_id; + + $update_sql = 'UPDATE `marketing_files` SET ' . implode(', ', $update_fields) . ' WHERE `id` = ?'; + $stmt = $pdo->prepare($update_sql); + $stmt->execute($update_params); + } - // Insert new tags - if (!empty($tags)) { - $tag_sql = 'INSERT IGNORE INTO `marketing_tags` (`tag_name`) VALUES (?)'; - $tag_stmt = $pdo->prepare($tag_sql); + // Update tags only if provided + if (isset($input['tags'])) { + // Remove existing tags + $pdo->prepare('DELETE FROM `marketing_file_tags` WHERE `file_id` = ?')->execute([$file_id]); + + // Parse and insert new tags + $tags_string = $input['tags']; + $tags_array = array_filter(array_map('trim', explode(',', $tags_string))); - $file_tag_sql = 'INSERT INTO `marketing_file_tags` (`file_id`, `tag_id`) SELECT ?, id FROM marketing_tags WHERE tag_name = ?'; - $file_tag_stmt = $pdo->prepare($file_tag_sql); - - foreach ($tags as $tag) { - $tag_stmt->execute([trim($tag)]); - $file_tag_stmt->execute([$file_id, trim($tag)]); + if (!empty($tags_array)) { + $tag_sql = 'INSERT IGNORE INTO `marketing_tags` (`tag_name`) VALUES (?)'; + $tag_stmt = $pdo->prepare($tag_sql); + + $file_tag_sql = 'INSERT INTO `marketing_file_tags` (`file_id`, `tag_id`) SELECT ?, id FROM marketing_tags WHERE tag_name = ?'; + $file_tag_stmt = $pdo->prepare($file_tag_sql); + + foreach ($tags_array as $tag) { + $tag_stmt->execute([$tag]); + $file_tag_stmt->execute([$file_id, $tag]); + } } } diff --git a/api/v2/post/marketing_upload.php b/api/v2/post/marketing_upload.php index a84a066..5055f52 100644 --- a/api/v2/post/marketing_upload.php +++ b/api/v2/post/marketing_upload.php @@ -53,8 +53,8 @@ if (isAllowed('marketing',$profile,$permission,'C') === 1){ } // Non-images must be under 10MB - if (!$isImage && $file['size'] > 10000000) { - echo json_encode(['success' => false, 'error' => 'File too large. Maximum size is 10MB.']); + if (!$isImage && $file['size'] > 25000000) { + echo json_encode(['success' => false, 'error' => 'File too large. Maximum size is 25MB.']); exit; } @@ -70,19 +70,28 @@ if (isAllowed('marketing',$profile,$permission,'C') === 1){ } if (move_uploaded_file($file['tmp_name'], $target_file)) { - // Generate thumbnail for images + // Generate thumbnail $thumbnail_path = null; + $thumb_dir = $target_dir . "thumbs/"; + if (!file_exists($thumb_dir)) { + mkdir($thumb_dir, 0755, true); + } + + // Generate thumbnail for images if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) { - $thumb_dir = $target_dir . "thumbs/"; - if (!file_exists($thumb_dir)) { - mkdir($thumb_dir, 0755, true); - } - $thumbnail_file = $thumb_dir . $unique_filename; if (generateThumbnail($target_file, $thumbnail_file, 200, 200)) { $thumbnail_path = "marketing/uploads/thumbs/" . $unique_filename; } } + // Generate thumbnail for videos + elseif (in_array($ext, ['mp4', 'mov', 'avi'])) { + $thumbnail_filename = pathinfo($unique_filename, PATHINFO_FILENAME) . '.jpg'; + $thumbnail_file = $thumb_dir . $thumbnail_filename; + if (generateVideoThumbnail($target_file, $thumbnail_file)) { + $thumbnail_path = "marketing/uploads/thumbs/" . $thumbnail_filename; + } + } // Insert into database $insert_sql = 'INSERT INTO `marketing_files` (`title`, `original_filename`, `file_path`, `thumbnail_path`, `file_type`, `file_size`, `folder_id`, `tags`, `createdby`, `accounthierarchy`) VALUES (?,?,?,?,?,?,?,?,?,?)'; @@ -299,4 +308,29 @@ function generateThumbnail($source, $destination, $width, $height) { return $result; } +// Function to generate video thumbnail +function generateVideoThumbnail($source, $destination) { + // Check if ffmpeg is available + $ffmpeg = trim(shell_exec('which ffmpeg 2>/dev/null')); + if (empty($ffmpeg)) { + return false; + } + + // Generate thumbnail from video at 1 second mark + // -i: input file + // -ss: seek to 1 second + // -vframes 1: extract one frame + // -vf: scale to 200x200 maintaining aspect ratio + $command = sprintf( + '%s -i %s -ss 00:00:01 -vframes 1 -vf "scale=200:200:force_original_aspect_ratio=decrease" %s 2>&1', + escapeshellarg($ffmpeg), + escapeshellarg($source), + escapeshellarg($destination) + ); + + exec($command, $output, $return_code); + + return $return_code === 0 && file_exists($destination); +} + ?> \ No newline at end of file diff --git a/assets/functions.php b/assets/functions.php index b9ed30c..ed8aff8 100644 --- a/assets/functions.php +++ b/assets/functions.php @@ -1771,12 +1771,12 @@ function warrantyStatus($input){ //INCLUDE TRANSLATION FILE if(isset($_SESSION['country_code'])){ $api_file_language = dirname(__FILE__,2).'/settings/translations/translations_'.strtoupper($_SESSION['country_code']).'.php'; - if (file_exists($api_file_language)){ - include $api_file_language; //Include the code - } - else { - include dirname(__FILE__,2).'/settings/translations/translations_US.php'; - } + if (file_exists($api_file_language)){ + include $api_file_language; //Include the code + } + else { + include dirname(__FILE__,2).'/settings/translations/translations_US.php'; + } } else { include dirname(__FILE__,2).'/settings/translations/translations_US.php'; @@ -1787,7 +1787,7 @@ function warrantyStatus($input){ if (!empty($input) && $input < $warrantydate){ $warranty_date_due = ''.$warranty_outdated_text.''; } else { - $warranty_date_due =''.$warranty_recent.' ('.date('Y-m-d', strtotime($input. ' + 365 days')).')'; + $warranty_date_due = ''.$warranty_recent.' ('.date('Y-m-d', strtotime($input. ' + 365 days')).')'; } return $warranty_date_due; @@ -5526,312 +5526,12 @@ function generateSoftwareInvoice($invoice_data, $order_id, $language = 'US') { } } - // Build HTML invoice - $html = ' - - - - - ' . htmlspecialchars($lbl_invoice) . ' - Total Safety Solutions - - - - - - -
' . htmlspecialchars($lbl_invoice) . '
- -
-
-

Total Safety Solutions B.V.

-

Laarakkerweg 8

-

5061 JR OISTERWIJK

-

Nederland

-
-
-
-
- -
-
-
-
Invoice Date
-
: ' . htmlspecialchars(date('d-m-Y', strtotime($invoice_date))) . '
-
-
-
Invoice Number
-
: ' . htmlspecialchars($order_id) . '
-
-
-
-
-
Reference
-
: Online order
-
-
-
Order number
-
: ' . htmlspecialchars($order_id) . '
-
-
-
- - - - - - - - - - - - '; - - foreach ($items as $item) { - $line_total = $item['price'] * $item['quantity']; - $html .= ' - - - - - - '; - } - - $html .= ' -
Item codeDescriptionQuantityPriceTotal
SOFTWARE' . htmlspecialchars($item['name']); - - if ($item['serial_number'] !== 'N/A') { - $html .= '
Serial: ' . htmlspecialchars($item['serial_number']) . ''; - } - if ($item['license_key'] !== 'Pending') { - $html .= '
License: ' . htmlspecialchars($item['license_key']) . ''; - } - - $html .= '
' . htmlspecialchars($item['quantity']) . ' € ' . number_format($item['price'], 2) . '€ ' . number_format($line_total, 2) . '
- -
-
-
' . htmlspecialchars($lbl_subtotal) . '
-
€ ' . number_format($subtotal, 2) . '
-
'; - - if ($tax_amount > 0) { - $html .= '
-
' . htmlspecialchars($lbl_tax) . '
-
€ ' . number_format($tax_amount, 2) . '
-
'; - } else { - $html .= '
-
VAT
-
included
-
'; - } - - $html .= '
-
' . htmlspecialchars($lbl_total) . '
-
€ ' . number_format($payment_amount, 2) . '
-
-
- - -'; - - return [$html, $customer_email, $order_id]; + return [$message,$pdf,$customer_email, $order_id]; } /** diff --git a/assets/mail/email_template_invoice.php b/assets/mail/email_template_invoice.php new file mode 100644 index 0000000..f45d166 --- /dev/null +++ b/assets/mail/email_template_invoice.php @@ -0,0 +1,138 @@ + + + + + + ' . htmlspecialchars($lbl_invoice) . ' - Total Safety Solutions + + + + + + +
+ + + + + + + +
+ + +

' . htmlspecialchars($lbl_invoice) . '

+ + + + + + + +
+ Total Safety Solutions B.V.
+ Laarakkerweg 8
+ 5061 JR OISTERWIJK
+ Nederland +
+ +
+ + + + + + + +
+ + + + + + + + + +
Invoice Date:' . htmlspecialchars(date('d-m-Y', strtotime($invoice_date))) . '
Invoice Number:' . htmlspecialchars($order_id) . '
+
+ + + + + + + + + +
Reference:Online order
Order number:' . htmlspecialchars($order_id) . '
+
+ + + + + + + + + + + + + '; + +foreach ($items as $item) { + $line_total = $item['price'] * $item['quantity']; + $message .= ' + + + + + + '; +} + +$message .= ' +
Item codeDescriptionQuantityPriceTotal
SOFTWARE' . htmlspecialchars($item['name']); + + if ($item['serial_number'] !== 'N/A') { + $message .= '
Serial: ' . htmlspecialchars($item['serial_number']) . ''; + } + if ($item['license_key'] !== 'Pending') { + $message .= '
License: ' . htmlspecialchars($item['license_key']) . ''; + } + + $message .= '
' . htmlspecialchars($item['quantity']) . '€ ' . number_format($item['price'], 2) . '€ ' . number_format($line_total, 2) . '
+ + + + + + + '; + +if ($tax_amount > 0) { + $message .= ' + + + '; +} else { + $message .= ' + + + '; +} + +$message .= ' + + + +
' . htmlspecialchars($lbl_subtotal) . '€ ' . number_format($subtotal, 2) . '
' . htmlspecialchars($lbl_tax) . '€ ' . number_format($tax_amount, 2) . '
VATincluded
' . htmlspecialchars($lbl_total) . '€ ' . number_format($payment_amount, 2) . '
+ +
+
+ +'; \ No newline at end of file diff --git a/assets/mail/pdf_template_invoice.php b/assets/mail/pdf_template_invoice.php new file mode 100644 index 0000000..f30e7e9 --- /dev/null +++ b/assets/mail/pdf_template_invoice.php @@ -0,0 +1,306 @@ + + + + + + ' . htmlspecialchars($lbl_invoice) . ' - Total Safety Solutions + + + + + + +
' . htmlspecialchars($lbl_invoice) . '
+ +
+
+

Total Safety Solutions B.V.

+

Laarakkerweg 8

+

5061 JR OISTERWIJK

+

Nederland

+
+
+
+
+ +
+
+
+
Invoice Date
+
: ' . htmlspecialchars(date('d-m-Y', strtotime($invoice_date))) . '
+
+
+
Invoice Number
+
: ' . htmlspecialchars($order_id) . '
+
+
+
+
+
Reference
+
: Online order
+
+
+
Order number
+
: ' . htmlspecialchars($order_id) . '
+
+
+
+ + + + + + + + + + + + '; + + foreach ($items as $item) { + $line_total = $item['price'] * $item['quantity']; + $pdf .= ' + + + + + + '; + } + +$pdf .= ' +
Item codeDescriptionQuantityPriceTotal
SOFTWARE' . htmlspecialchars($item['name']); + + if ($item['serial_number'] !== 'N/A') { + $pdf .= '
Serial: ' . htmlspecialchars($item['serial_number']) . ''; + } + if ($item['license_key'] !== 'Pending') { + $pdf .= '
License: ' . htmlspecialchars($item['license_key']) . ''; + } + + $pdf .= '
' . htmlspecialchars($item['quantity']) . ' € ' . number_format($item['price'], 2) . '€ ' . number_format($line_total, 2) . '
+ +
+
+
' . htmlspecialchars($lbl_subtotal) . '
+
€ ' . number_format($subtotal, 2) . '
+
'; + + if ($tax_amount > 0) { + $pdf .= '
+
' . htmlspecialchars($lbl_tax) . '
+
€ ' . number_format($tax_amount, 2) . '
+
'; + } else { + $pdf .= '
+
VAT
+
included
+
'; + } + + $pdf .= '
+
' . htmlspecialchars($lbl_total) . '
+
€ ' . number_format($payment_amount, 2) . '
+
+
+ + +'; \ No newline at end of file diff --git a/assets/marketing.js b/assets/marketing.js index d5581f7..faf20a2 100644 --- a/assets/marketing.js +++ b/assets/marketing.js @@ -17,6 +17,13 @@ class MarketingFileManager { this.folders = []; // Store folders data this.loadRequestId = 0; // Track the latest load request + // Get permissions from PHP + this.permissions = window.marketingPermissions || { + canCreate: 0, + canUpdate: 0, + canDelete: 0 + }; + this.init(); } @@ -105,6 +112,18 @@ class MarketingFileManager { document.getElementById('saveEdit')?.addEventListener('click', () => { this.saveEdit(); }); + + // Edit folder + document.getElementById('saveEditFolder')?.addEventListener('click', () => { + this.saveEditFolder(); + }); + + // Delete folder + document.getElementById('deleteFolder')?.addEventListener('click', () => { + if (this.selectedFolder) { + this.deleteFolder(this.selectedFolder); + } + }); } bindUploadEvents() { @@ -317,15 +336,29 @@ class MarketingFileManager { const hasChildren = folder.children && folder.children.length > 0; const expandIcon = hasChildren ? '' : ''; + // Only show edit button if user has update permission + const editButton = this.permissions.canUpdate === 1 + ? `` + : ''; + folderItem.innerHTML = ` ${expandIcon} ${this.escapeHtml(folder.folder_name)} (${folder.file_count}) + ${editButton} `; - folderItem.addEventListener('click', () => { - this.selectFolder(folder.id); + folderItem.addEventListener('click', (e) => { + // Don't select folder if edit button was clicked + if (e.target.closest('.folder-edit-btn')) { + e.stopPropagation(); + this.editFolder(folder); + } else { + this.selectFolder(folder.id); + } }); container.appendChild(folderItem); @@ -929,13 +962,20 @@ class MarketingFileManager { const folderId = document.getElementById('editFolder').value; const tags = document.getElementById('editTags').value.trim(); - // Prepare update data - const updateData = { - file_id: this.selectedFile.id, - title: title || null, - folder_id: folderId || null, - tags: tags ? tags.split(',').map(tag => tag.trim()).filter(tag => tag) : [] - }; + // Compare with original values to detect changes + const originalTitle = this.selectedFile.title || ''; + const originalFolderId = this.selectedFile.folder_id || ''; + const originalTags = (this.selectedFile.tags || []).join(', '); + + const hasChanges = title !== originalTitle || + folderId !== originalFolderId || + tags !== originalTags; + + if (!hasChanges) { + this.showToast('No changes detected', 'info'); + this.closeModal(document.getElementById('editModal')); + return; + } // Show loading state const saveBtn = document.getElementById('saveEdit'); @@ -943,22 +983,35 @@ class MarketingFileManager { saveBtn.innerHTML = ' Saving...'; saveBtn.disabled = true; + // Prepare FormData with only changed fields + const formData = new FormData(); + formData.append('file_id', this.selectedFile.id); + + if (title !== originalTitle) { + formData.append('title', title); + } + if (folderId !== originalFolderId) { + formData.append('folder_id', folderId); + } + if (tags !== originalTags) { + formData.append('tags', tags); + } + // Send update request - fetch('./marketing.php?action=update_file', { + fetch('index.php?page=marketing&action=marketing_update', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(updateData) + body: formData }) .then(response => response.json()) .then(data => { if (data.success) { this.showToast('File updated successfully!', 'success'); - this.closeModal(document.getElementById('editModal')); - this.loadFiles(); // Reload to show changes + // Reload window to reflect changes + setTimeout(() => { + window.location.reload(); + }, 500); } else { - throw new Error(data.message || 'Failed to update file'); + throw new Error(data.error || data.message || 'Failed to update file'); } }) .catch(error => { @@ -994,6 +1047,193 @@ class MarketingFileManager { this.renderUploadQueue(); document.getElementById('startUpload').disabled = this.uploadQueue.length === 0; } + + // Edit folder functionality + editFolder(folder) { + this.selectedFolder = folder; + this.showEditFolderModal(); + this.populateEditFolderModal(folder); + } + + showEditFolderModal() { + const modal = document.getElementById('editFolderModal'); + if (modal) { + this.showModal(modal); + } + } + + populateEditFolderModal(folder) { + // Populate folder name + const nameInput = document.getElementById('editFolderName'); + if (nameInput) { + nameInput.value = folder.folder_name || ''; + } + + // Populate parent folder select + const parentSelect = document.getElementById('editParentFolder'); + if (parentSelect) { + parentSelect.innerHTML = ''; + this.addFolderOptionsExcluding(parentSelect, this.folders, folder.id); + + // Select current parent + if (folder.parent_id) { + parentSelect.value = folder.parent_id; + } + } + + // Populate description + const descInput = document.getElementById('editFolderDescription'); + if (descInput) { + descInput.value = folder.description || ''; + } + } + + addFolderOptionsExcluding(select, folders, excludeId, level = 0) { + // Add folders but exclude the current folder and its children + folders.forEach(folder => { + if (folder.id !== excludeId && !this.isFolderDescendant(folder, excludeId)) { + const option = document.createElement('option'); + option.value = folder.id; + option.textContent = ' '.repeat(level) + folder.folder_name; + select.appendChild(option); + + if (folder.children && folder.children.length > 0) { + this.addFolderOptionsExcluding(select, folder.children, excludeId, level + 1); + } + } + }); + } + + isFolderDescendant(folder, ancestorId) { + // Check if folder is a descendant of ancestorId + if (folder.id === ancestorId) return true; + if (folder.children) { + for (let child of folder.children) { + if (this.isFolderDescendant(child, ancestorId)) { + return true; + } + } + } + return false; + } + + saveEditFolder() { + if (!this.selectedFolder) return; + + const folderName = document.getElementById('editFolderName').value.trim(); + const parentId = document.getElementById('editParentFolder').value; + const description = document.getElementById('editFolderDescription').value.trim(); + + // Compare with original values + const originalName = this.selectedFolder.folder_name || ''; + const originalParentId = this.selectedFolder.parent_id || ''; + const originalDescription = this.selectedFolder.description || ''; + + const hasChanges = folderName !== originalName || + parentId !== originalParentId || + description !== originalDescription; + + if (!hasChanges) { + this.showToast('No changes detected', 'info'); + this.closeModal(document.getElementById('editFolderModal')); + return; + } + + if (!folderName) { + this.showToast('Folder name is required', 'error'); + return; + } + + // Show loading state + const saveBtn = document.getElementById('saveEditFolder'); + const originalText = saveBtn.innerHTML; + saveBtn.innerHTML = ' Saving...'; + saveBtn.disabled = true; + + // Prepare FormData + const formData = new FormData(); + formData.append('id', this.selectedFolder.id); + + if (folderName !== originalName) { + formData.append('folder_name', folderName); + } + if (parentId !== originalParentId) { + formData.append('parent_id', parentId); + } + if (description !== originalDescription) { + formData.append('description', description); + } + + // Send update request + fetch('index.php?page=marketing&action=marketing_folders', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + this.showToast('Folder updated successfully!', 'success'); + // Reload window to reflect changes + setTimeout(() => { + window.location.reload(); + }, 500); + } else { + throw new Error(data.error || data.message || 'Failed to update folder'); + } + }) + .catch(error => { + console.error('Update folder error:', error); + this.showToast('Error updating folder: ' + error.message, 'error'); + }) + .finally(() => { + // Restore button state + saveBtn.innerHTML = originalText; + saveBtn.disabled = false; + }); + } + + async deleteFolder(folder) { + if (!confirm(`Are you sure you want to delete the folder "${folder.folder_name}"? This action cannot be undone.`)) { + return; + } + + try { + const formData = new FormData(); + formData.append('id', folder.id); + formData.append('delete', 'true'); + + const response = await fetch('index.php?page=marketing&action=marketing_folders', { + method: 'POST', + body: formData + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const text = await response.text(); + if (!text || text.trim() === '') { + throw new Error('Empty response from server'); + } + + const data = JSON.parse(text); + + if (data && (data.success || !data.error)) { + this.closeModal(document.getElementById('editFolderModal')); + this.showToast('Folder deleted successfully!', 'success'); + // Reload window to reflect changes + setTimeout(() => { + window.location.reload(); + }, 500); + } else if (data.error) { + throw new Error(data.error); + } else { + throw new Error('Unexpected response format'); + } + } catch (error) { + this.showToast(error.message || 'Error deleting folder', 'error'); + } + } } // Initialize when DOM is ready diff --git a/assets/softwaretool.js b/assets/softwaretool.js index 280feaf..a5db168 100644 --- a/assets/softwaretool.js +++ b/assets/softwaretool.js @@ -15,6 +15,71 @@ let reader; let readableStreamClosed; let keepReading = true; +// Browser compatibility check +let isSerialSupported = false; + +// Check browser compatibility on page load +function checkBrowserCompatibility() { + isSerialSupported = 'serial' in navigator; + + if (!isSerialSupported) { + // Show warning banner + showBrowserWarningBanner(); + // Disable connect button + disableSerialFunctionality(); + } + + return isSerialSupported; +} + +function showBrowserWarningBanner() { + const connectDevice = document.getElementById("connectdevice"); + if (!connectDevice) return; + + const warningBanner = document.createElement("div"); + warningBanner.id = "browserWarningBanner"; + warningBanner.style.cssText = ` + background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); + color: white; + padding: 15px 20px; + border-radius: 8px; + margin-bottom: 15px; + display: flex; + align-items: center; + gap: 15px; + box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3); + `; + + warningBanner.innerHTML = ` + +
+ Browser Not Supported +

+ Please use Chrome, Edge, or Opera to access device connectivity features. +

+
+ `; + + connectDevice.parentNode.insertBefore(warningBanner, connectDevice); +} + +function disableSerialFunctionality() { + const connectButton = document.getElementById("connectButton"); + if (connectButton) { + connectButton.disabled = true; + connectButton.style.opacity = "0.5"; + connectButton.style.cursor = "not-allowed"; + connectButton.title = "Browser is not supported. Please use Chrome, Edge, or Opera."; + } +} + +// Call on page load +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', checkBrowserCompatibility); +} else { + checkBrowserCompatibility(); +} + // Function to log communication to API (reused from scripts.js) async function logCommunication(data, direction) { // Only log if debug mode is enabled @@ -69,6 +134,13 @@ function progressBar(percentage, message, color){ // Connect device for software tool async function connectDeviceForSoftware() { + // Browser compatibility check + if (!isSerialSupported) { + progressBar("100", "Browser not supported - Please use Chrome, Edge, or Opera", "#dc3545"); + await logCommunication('Connection attempt failed: Web Serial API not supported in browser', 'error'); + return; + } + //clear input readBar.innerHTML = ''; serialResultsDiv.innerHTML = ''; @@ -162,11 +234,18 @@ async function connectDeviceForSoftware() { } catch (error) { await logCommunication(`Connection error: ${error.message}`, 'error'); - // Check for specific "No port selected" error and show user-friendly message - if (error.message && error.message.includes('No port selected by the user')) { - progressBar("100", "No device selected, please try again", "#ff6666"); + // Improved error messages with specific cases + if (error.name === 'NotSupportedError' || !navigator.serial) { + progressBar("100", "Browser not supported - Please use Chrome, Edge, or Opera", "#dc3545"); + } else if (error.message && error.message.includes('No port selected by the user')) { + progressBar("100", "No device selected - Please try again", "#ff6666"); + } else if (error.name === 'NetworkError') { + progressBar("100", "Connection failed - Please check device connection", "#ff6666"); + } else if (error.name === 'InvalidStateError') { + progressBar("100", "Port already in use - Refreshing page...", "#ff9800"); + setTimeout(() => location.reload(), 2000); } else { - progressBar("100", "Error: " + error.message, "#ff6666"); + progressBar("100", "Connection error: " + error.message, "#ff6666"); } } } diff --git a/equipment.php b/equipment.php index b0e2cf1..e800dd3 100644 --- a/equipment.php +++ b/equipment.php @@ -54,11 +54,11 @@ $history = ioServer($api_url,''); if (!empty($history)){$history = json_decode($history);}else{$history = null;} //CALL TO API FOR EQUIPMENT DATA -$api_url = '/v1/equipment_data/equipmentid='.$responses->equipmentID; +$api_url = '/v2/equipment_data/equipmentid='.$responses->equipmentID; $equipment_data = ioServer($api_url,''); //Decode Payload -if (!empty($equipment_data )){$equipment_data = decode_payload($equipment_data );}else{$equipment_data = null;} +if (!empty($equipment_data )){$equipment_data = json_decode($equipment_data );}else{$equipment_data = null;} //CALL TO API FOR CUSTOMER AND WARRANTY DATA (type = customer,Warranty in history) $api_url = '/v2/history/equipmentID='.$responses->equipmentID.'&type=customer,Warranty'; @@ -83,10 +83,10 @@ if (is_array($registration_data) && count($registration_data) > 0) { } //GET PRODUCTS_SOFTWARE -$api_url = '/v1/products_software/productrowid='.$responses->productrowid.'&status=1'; +$api_url = '/v2/products_software_versions/hw_version='.$responses->hw_version.'&status=1'; $products_software = ioServer($api_url,''); //Decode Payload -if (!empty($products_software)){$products_software = decode_payload($products_software);}else{$products_software = null;} +if (!empty($products_software)){$products_software = json_decode($products_software);}else{$products_software = null;} //------------------------------ //Variables @@ -96,6 +96,17 @@ $warrantydate = warrantyStatus($responses->warranty_date); $service_date_due = serviceStatus($responses->service_date); $firmware_status = availableFirmware($responses->sw_version, $responses->sw_version_latest); +// Get upgrade version text if exists +$sw_version_upgrade_text = ''; +if (!empty($responses->sw_version_upgrade) && isset($products_software) && $products_software != '') { + foreach ($products_software as $products_soft) { + if ($products_soft->rowID == $responses->sw_version_upgrade) { + $sw_version_upgrade_text = $products_soft->version; + break; + } + } +} + //Calculate Healthindex based on last test $total_score = assetHealthIndex($_SESSION['profile'],$_SESSION['permission'],$equipment_data,0); @@ -117,15 +128,12 @@ if ($update_allowed === 1){ //GET ALL POST DATA $data = json_encode($_POST, JSON_UNESCAPED_UNICODE); - //Secure data - $payload = generate_payload($data); - //API call - $responses = ioServer('/v1/history', $payload); + $responses = ioServer('/v2/history', $data); if ($responses === 'NOK'){ } else { - header('Location: index.php?page=equipment&equipmentID='.$_POST['equipmentid'].'&success_msg=2'); + header('Location: index.php?page=equipment&equipmentID='.$_POST['equipmentid'].'&success_msg=2'); exit; } } @@ -261,7 +269,7 @@ $view .= '
'.$software_status.' - '.$firmware_status.' + '.$firmware_status.((!empty($responses->sw_version_upgrade)) ? ' ' : '').' '.$equipment_label5.' diff --git a/equipment_manage.php b/equipment_manage.php index 3c179a8..c0f8adc 100644 --- a/equipment_manage.php +++ b/equipment_manage.php @@ -85,10 +85,8 @@ if (isset($_GET['equipmentID'])) { $_POST['geolocation'] = json_encode($_POST['geolocation'],JSON_UNESCAPED_UNICODE); //GET ALL POST DATA $data = json_encode($_POST, JSON_UNESCAPED_UNICODE); - //Secure data - $payload = generate_payload($data); //API call - $responses = ioServer('/v1/equipments', $payload); + $responses = ioServer('/v2/equipments', $data); if ($responses === 'NOK'){ } else { @@ -102,10 +100,8 @@ if (isset($_GET['equipmentID'])) { if (isset($_POST['delete'])) { //GET ALL POST DATA $data = json_encode($_POST , JSON_UNESCAPED_UNICODE); - //Secure data - $payload = generate_payload($data); //API call - $responses = ioServer('/v1/equipments', $payload); + $responses = ioServer('/v2/equipments', $data); // Redirect and delete equipment if ($responses === 'NOK'){ @@ -123,10 +119,8 @@ if (isset($_GET['equipmentID'])) { $_POST['geolocation'] = json_encode($_POST['geolocation'],JSON_UNESCAPED_UNICODE); //GET ALL POST DATA $data = json_encode($_POST, JSON_UNESCAPED_UNICODE); - //Secure data - $payload = generate_payload($data); //API call - $responses = ioServer('/v1/equipments', $payload); + $responses = ioServer('/v2/equipments', $data); if ($responses === 'NOK'){ } else { diff --git a/equipments_mass_update.php b/equipments_mass_update.php index 9f37425..5225e6c 100644 --- a/equipments_mass_update.php +++ b/equipments_mass_update.php @@ -54,11 +54,11 @@ if ($update_allowed === 1){ foreach ($excel_data_1 as $key => $val){ //GET ROW ID - $api_url = '/v1/application/serialnumber='.$val.'/get_rowID'; + $api_url = '/v2/application/serialnumber='.$val.'/get_rowID'; $responses = ioServer($api_url,''); //Decode Payload - if (!empty($responses)){$responses = decode_payload($responses);}else{$responses = null;} + if (!empty($responses)){$responses = json_decode($responses);}else{$responses = null;} //IF rowID is found add to array if (isset ($responses->rowID)){ @@ -92,10 +92,8 @@ if ($update_allowed === 1){ foreach ($output_excel as $data_to_update){ $data = json_encode($data_to_update, JSON_UNESCAPED_UNICODE); - //Secure data - $payload = generate_payload($data); //API call - $responses = ioServer('/v1/equipments', $payload); + $responses = ioServer('/v2/equipments', $data); if ($responses === 'NOK'){ } diff --git a/factuur.php b/factuur.php index 713af31..5145079 100644 --- a/factuur.php +++ b/factuur.php @@ -59,7 +59,7 @@ if (!empty($invoice_data['customer']['language'])) { // Generate invoice HTML //+++++++++++++++++++++++++++++++++++++++++++++++++++++ -list($data, $customer_email, $order_id) = generateSoftwareInvoice($invoice_data, $order_number, $invoice_language); +list($message,$pdf, $customer_email, $order_id) = generateSoftwareInvoice($invoice_data, $order_number, $invoice_language); //+++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -67,7 +67,7 @@ list($data, $customer_email, $order_id) = generateSoftwareInvoice($invoice_data, //+++++++++++++++++++++++++++++++++++++++++++++++++++++ if (isset($_GET['output']) && $_GET['output'] === 'html') { // Output HTML directly to browser - echo $data; + echo $message; exit; } @@ -82,7 +82,7 @@ $options->set('isRemoteEnabled', true); $dompdf = new Dompdf($options); // Load HTML content -$dompdf->loadHtml($data); +$dompdf->loadHtml($pdf); // Setup paper size and orientation $dompdf->setPaper('A4', 'portrait'); @@ -101,7 +101,6 @@ $file_name = 'Factuur - ' . $order_id; if (isset($_POST['email_invoice'])) { $to = $customer_email; $subject = 'Factuur - ' . $order_id; - $message = $data; $attachment = $dompdf->output(); $attachment_name = $file_name . '.pdf'; @@ -118,7 +117,6 @@ if (isset($_POST['email_invoice'])) { if (isset($_POST['email_invoice_to_admin'])) { $to = $customer_email; $subject = 'Factuur - ' . $order_id; - $message = $data; $attachment = $dompdf->output(); $attachment_name = $file_name . '.pdf'; diff --git a/licenses.php b/licenses.php index 30e1860..892dcdd 100644 --- a/licenses.php +++ b/licenses.php @@ -269,10 +269,10 @@ $view .= ' '.$response->license_key.' '.$response->version_name.' '.$status_text.' - '.($response->transaction_id ?? '-').' + '.(isset($response->transaction_id) && strpos($response->transaction_id, 'SC') === 0 ? ''.$response->transaction_id.' ' : ($response->transaction_id ?? '-')).' '.$starts_display.' '.$expires_display.' - '.($response->assigned_serial ?? '-').' + '.(isset($response->assigned_serial) ? ''.$response->assigned_serial.' ' : ($response->assigned_serial ?? '-')).' '; } diff --git a/marketing.php b/marketing.php index abdee3c..98a6ee9 100644 --- a/marketing.php +++ b/marketing.php @@ -40,13 +40,30 @@ if (isset($_GET['action'])) { // Marketing folders if ($action === 'marketing_folders') { if ($_SERVER['REQUEST_METHOD'] === 'POST') { - // Create folder - use standard format expected by POST API - $payload = [ - 'folder_name' => $_POST['folder_name'] ?? '', - 'parent_id' => $_POST['parent_id'] ?? '', - 'description' => $_POST['description'] ?? '' - // rowID is empty = insert (standard pattern) - ]; + // Pass through all POST data for create/update/delete + $payload = []; + + // Always include id if present (for update/delete) + if (isset($_POST['id'])) { + $payload['id'] = $_POST['id']; + } + + // Include delete flag if present + if (isset($_POST['delete'])) { + $payload['delete'] = $_POST['delete']; + } + + // Only include other fields if they were sent + if (isset($_POST['folder_name'])) { + $payload['folder_name'] = $_POST['folder_name']; + } + if (isset($_POST['parent_id'])) { + $payload['parent_id'] = $_POST['parent_id']; + } + if (isset($_POST['description'])) { + $payload['description'] = $_POST['description']; + } + $response = ioServer('/v2/marketing_folders', json_encode($payload)); } else { // Get folders @@ -128,7 +145,19 @@ if (isset($_GET['action'])) { // Marketing update if ($action === 'marketing_update' && $_SERVER['REQUEST_METHOD'] === 'POST') { - $payload = $_POST; + $payload = ['file_id' => $_POST['file_id'] ?? '']; + + // Only include fields that were actually sent + if (isset($_POST['title'])) { + $payload['title'] = $_POST['title']; + } + if (isset($_POST['folder_id'])) { + $payload['folder_id'] = $_POST['folder_id']; + } + if (isset($_POST['tags'])) { + $payload['tags'] = $_POST['tags']; + } + $response = ioServer('/v2/marketing_update', json_encode($payload)); header('Content-Type: application/json'); echo $response; @@ -401,15 +430,64 @@ template_header('Marketing', 'marketing');
+ + + + + + + + + +
@@ -276,8 +283,11 @@ echo ' var MOLLIE_ENABLED = '.(mollie_enabled ? 'true' : 'false').'; var PAYPAL_ENABLED = '.(paypal_enabled ? 'true' : 'false').'; var PAY_ON_DELIVERY_ENABLED = '.(pay_on_delivery_enabled ? 'true' : 'false').'; - - // Translation variables + // Early browser compatibility check + if (!("serial" in navigator)) { + console.warn("Web Serial API is not supported in this browser"); + } + // Translation variables var TRANS_NAME = "'.($account_name ?? 'Name').'"; var TRANS_EMAIL = "'.($account_email ?? 'Email').'"; var TRANS_ADDRESS = "'.($shipping_address ?? 'Address').'"; diff --git a/style/marketing.css b/style/marketing.css index c1b2cce..8668595 100644 --- a/style/marketing.css +++ b/style/marketing.css @@ -156,6 +156,7 @@ border-radius: 4px; transition: all 0.2s; font-size: 0.9rem; + position: relative; } .folder-item:hover { @@ -180,6 +181,26 @@ color: var(--color-text-light, #6c757d); } +.folder-edit-btn { + display: none; + background: none; + border: none; + padding: 0.25rem 0.5rem; + cursor: pointer; + color: var(--color-text-light, #6c757d); + border-radius: 4px; + transition: all 0.2s; +} + +.folder-item:hover .folder-edit-btn { + display: inline-block; +} + +.folder-edit-btn:hover { + background-color: var(--color-primary-light, #cce7f0); + color: var(--color-primary, #005655); +} + .expand-icon { cursor: pointer; transition: transform 0.2s; diff --git a/webhook_mollie.php b/webhook_mollie.php index 43f5951..d668b3e 100644 --- a/webhook_mollie.php +++ b/webhook_mollie.php @@ -250,12 +250,12 @@ try { } // Generate invoice HTML (using custom template for software upgrades) - list($data,$customer_email,$order_id) = generateSoftwareInvoice($invoice_cust,$orderId,$invoice_language); + list($message,$pdf,$customer_email,$order_id) = generateSoftwareInvoice($invoice_cust,$orderId,$invoice_language); //+++++++++++++++++++++++++++++++++++++++++++++++++++++ //CREATE PDF using DomPDF //+++++++++++++++++++++++++++++++++++++++++++++++++++++ - $dompdf->loadHtml($data); + $dompdf->loadHtml($pdf); $dompdf->setPaper('A4', 'portrait'); $dompdf->render(); $subject = 'Software Upgrade - Invoice: '.$order_id; @@ -265,12 +265,12 @@ try { //Send email via PHPMailer //+++++++++++++++++++++++++++++++++++++++++++++++++++++ // The send_mail function will exit on error and debuglog the error - $mail_result = send_mail($customer_email, $subject, $data, $attachment, $subject); + $mail_result = send_mail($customer_email, $subject, $message, $attachment, $subject); // Send to bookkeeping if configured if(invoice_bookkeeping){ debuglog("WEBHOOK: Sending to bookkeeping: " . email_bookkeeping); - send_mail(email_bookkeeping, $subject, $data, $attachment, $subject); + send_mail(email_bookkeeping, $subject, $message, $attachment, $subject); } } else { debuglog("WEBHOOK: No invoice data found for invoice_id: $invoice_id"); diff --git a/webhook_paypal.php b/webhook_paypal.php index 2691d85..084e955 100644 --- a/webhook_paypal.php +++ b/webhook_paypal.php @@ -294,12 +294,12 @@ try { } // Generate invoice HTML (using custom template for software upgrades) - list($data,$customer_email,$order_id) = generateSoftwareInvoice($invoice_cust,$orderId,$invoice_language); + list($message,$pdf,$customer_email,$order_id) = generateSoftwareInvoice($invoice_cust,$orderId,$invoice_language); //+++++++++++++++++++++++++++++++++++++++++++++++++++++ //CREATE PDF using DomPDF //+++++++++++++++++++++++++++++++++++++++++++++++++++++ - $dompdf->loadHtml($data); + $dompdf->loadHtml($pdf); $dompdf->setPaper('A4', 'portrait'); $dompdf->render(); $subject = 'Software Upgrade - Invoice: '.$order_id; @@ -308,12 +308,12 @@ try { //+++++++++++++++++++++++++++++++++++++++++++++++++++++ //Send email via PHPMailer //+++++++++++++++++++++++++++++++++++++++++++++++++++++ - $mail_result = send_mail($customer_email, $subject, $data, $attachment, $subject); + $mail_result = send_mail($customer_email, $subject, $message, $attachment, $subject); // Send to bookkeeping if configured if(invoice_bookkeeping){ debuglog("PAYPAL WEBHOOK: Sending to bookkeeping: " . email_bookkeeping); - send_mail(email_bookkeeping, $subject, $data, $attachment, $subject); + send_mail(email_bookkeeping, $subject, $message, $attachment, $subject); } } else { debuglog("PAYPAL WEBHOOK: No invoice data found for invoice_id: $invoice_id");