bootstrap = true; $this->display = 'view'; // Force custom view parent::__construct(); } public function initContent() { // Не викликаємо parent::initContent(), бо нам не потрібен стандартний список // Але нам потрібен header і footer адмінки $this->context->smarty->assign([ 'content' => $this->renderView(), // Це вставить наш tpl ]); // Викликаємо батьківський метод для відображення структури адмінки parent::initContent(); } public function renderView() { $ajax_url = $this->context->link->getAdminLink( 'AdminAddLivePhoto', true, [], ['ajax' => 1] ); $this->context->smarty->assign([ 'ajax_url' => $ajax_url, 'module_dir' => _MODULE_DIR_ . $this->module->name . '/', ]); return $this->context->smarty->fetch($this->module->getLocalPath() . 'views/templates/admin/uploader.tpl'); } public function ajaxProcess() { $action = Tools::getValue('action'); try { switch ($action) { case 'searchProduct': $this->processSearchProduct(); break; case 'uploadImage': $this->processUploadImage(); break; case 'deleteImage': $this->processDeleteFreshImage(); break; default: throw new Exception('Unknown action'); } } catch (Exception $e) { $this->jsonResponse(['success' => false, 'message' => $e->getMessage()]); } exit; } protected function processSearchProduct() { $identifier = trim(Tools::getValue('identifier')); if (empty($identifier)) { throw new Exception($this->trans('Please enter a barcode or ID.', [], 'Modules.Addlivephoto.Admin')); } $id_product = 0; // 1. Спробуємо знайти по EAN13 $sql = 'SELECT id_product FROM `' . _DB_PREFIX_ . 'product` WHERE ean13 = "'.pSQL($identifier).'"'; $id_by_ean = Db::getInstance()->getValue($sql); if ($id_by_ean) { $id_product = (int)$id_by_ean; } elseif (is_numeric($identifier)) { // 2. Якщо це число, пробуємо як ID $id_product = (int)$identifier; } $product = new Product($id_product, false, $this->context->language->id); if (!Validate::isLoadedObject($product)) { throw new Exception($this->trans('Product not found.', [], 'Modules.Addlivephoto.Admin')); } // Отримуємо існуючі фото $existing_photos = $this->getLivePhotos($product->id); $this->jsonResponse([ 'success' => true, 'data' => [ 'id_product' => $product->id, 'name' => $product->name, 'reference' => $product->reference, 'ean13' => $product->ean13, 'existing_photos' => $existing_photos ] ]); } protected function processUploadImage() { $id_product = (int)Tools::getValue('id_product'); $rawImage = Tools::getValue('imageData'); // base64 string $imageType = Tools::getValue('image_type'); // expiry або packaging if (!$id_product || !$rawImage) { throw new Exception('Missing ID or Image Data'); } if (!in_array($imageType, ['expiry', 'packaging'])) { $imageType = 'expiry'; // Fallback } // Clean Base64 if (preg_match('/^data:image\/(\w+);base64,/', $rawImage, $type)) { $rawImage = substr($rawImage, strpos($rawImage, ',') + 1); $type = strtolower($type[1]); // jpg, png, webp if (!in_array($type, ['jpg', 'jpeg', 'png', 'webp'])) { throw new Exception('Invalid image type'); } $rawImage = base64_decode($rawImage); if ($rawImage === false) { throw new Exception('Base64 decode failed'); } } else { throw new Exception('Did not match data URI with image data'); } // Generate Filename $filename = uniqid() . '.webp'; // Save as WebP usually $path = $this->module->getProductImageServerPath($id_product); if (!$path) { throw new Exception('Could not create directory'); } // Save File if (!file_put_contents($path . $filename, $rawImage)) { throw new Exception('Failed to write file to disk'); } // Save to DB $res = Db::getInstance()->insert('add_live_photo', [ 'id_product' => $id_product, 'image_name' => pSQL($filename), 'image_type' => pSQL($imageType), 'date_add' => date('Y-m-d H:i:s'), ]); if (!$res) { @unlink($path . $filename); // Cleanup throw new Exception('Database insert error'); } $photoUrl = $this->module->getProductImageUri($id_product, $filename); $this->jsonResponse([ 'success' => true, 'message' => 'Saved successfully!', 'photo' => [ 'name' => $filename, 'url' => $photoUrl, 'type' => $imageType ] ]); } protected function processDeleteFreshImage() { $id_product = (int)Tools::getValue('id_product'); $image_name = Tools::getValue('image_name'); if ($this->module->deleteProductImage($id_product, $image_name)) { $this->jsonResponse(['success' => true, 'message' => 'Deleted']); } else { throw new Exception('Delete failed'); } } private function getLivePhotos($id_product) { $sql = new DbQuery(); $sql->select('*'); $sql->from('add_live_photo'); $sql->where('id_product = ' . (int)$id_product); $sql->orderBy('date_add DESC'); $res = Db::getInstance()->executeS($sql); $photos = []; if ($res) { foreach($res as $row) { $photos[] = [ 'name' => $row['image_name'], 'type' => isset($row['image_type']) ? $row['image_type'] : 'expiry', 'url' => $this->module->getProductImageUri($id_product, $row['image_name']) ]; } } return $photos; } private function jsonResponse($data) { header('Content-Type: application/json'); echo json_encode($data); exit; } }