first commit
This commit is contained in:
324
addlivephoto.php
Normal file
324
addlivephoto.php
Normal file
@@ -0,0 +1,324 @@
|
||||
<?php
|
||||
/**
|
||||
* 2007-2023 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License (AFL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/afl-3.0.php
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author Panariga
|
||||
* @copyright 2025
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*/
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class AddLivePhoto extends Module
|
||||
{
|
||||
const TABLE_NAME = 'add_live_photo';
|
||||
const IMG_DIR_VAR_PATH = _PS_ROOT_DIR_ . '/modules/addlivephoto/photo/';
|
||||
const IMG_DIR_MODULE_PATH = _PS_MODULE_DIR_ . 'addlivephoto/photo/'; // Kept for URI generation context
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'addlivephoto';
|
||||
$this->tab = 'front_office_features';
|
||||
$this->version = '1.0.0';
|
||||
$this->author = 'Panariga';
|
||||
$this->need_instance = 0;
|
||||
$this->bootstrap = true;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->trans('Add Live Product Photos',[], 'Modules.Addlivephoto.Admin');
|
||||
$this->description = $this->trans('Allows admin to add live photos of product details like expiry dates directly from their phone. Displays fresh images on the product page.',[], 'Modules.Addlivephoto.Admin');
|
||||
|
||||
$this->ps_versions_compliancy = array('min' => '8.0.0', 'max' => _PS_VERSION_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module installation process.
|
||||
* @return bool
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
if (
|
||||
!parent::install() ||
|
||||
!$this->registerHook('displayProductPriceBlock') ||
|
||||
!$this->registerHook('actionAdminControllerSetMedia') ||
|
||||
!$this->installDb() ||
|
||||
!$this->installAdminTab() ||
|
||||
!$this->createImageDirectories()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module uninstallation process.
|
||||
* @return bool
|
||||
*/
|
||||
public function uninstall()
|
||||
{
|
||||
// Note: For safety, we are not deleting the /var/modules/addlivephoto directory
|
||||
// with user-uploaded images by default. You can add a configuration option for this.
|
||||
return parent::uninstall() &&
|
||||
$this->uninstallDb() &&
|
||||
$this->uninstallAdminTab();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the database table for storing image information.
|
||||
* @return bool
|
||||
*/
|
||||
protected function installDb()
|
||||
{
|
||||
$sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . self::TABLE_NAME . '` (
|
||||
`id_add_live_photo` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`id_product` INT(11) UNSIGNED NOT NULL,
|
||||
`image_name` VARCHAR(255) NOT NULL,
|
||||
`date_add` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id_add_live_photo`),
|
||||
INDEX `id_product_idx` (`id_product`)
|
||||
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';
|
||||
|
||||
return Db::getInstance()->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the database table.
|
||||
* @return bool
|
||||
*/
|
||||
protected function uninstallDb()
|
||||
{
|
||||
// return Db::getInstance()->execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . self::TABLE_NAME . '`');
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the link to our Admin Controller in the Quick Access menu.
|
||||
* @return bool
|
||||
*/
|
||||
protected function installAdminTab()
|
||||
{
|
||||
$tab = new Tab();
|
||||
$tab->active = 1;
|
||||
$tab->class_name = 'AdminAddLivePhoto';
|
||||
$tab->name = array();
|
||||
foreach (Language::getLanguages(true) as $lang) {
|
||||
$tab->name[$lang['id_lang']] = $this->trans('Live Photo Uploader',[], 'Modules.Addlivephoto.Admin');
|
||||
}
|
||||
$tab->id_parent = (int) Tab::getIdFromClassName('IMPROVE');
|
||||
$tab->module = $this->name;
|
||||
|
||||
return $tab->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the Admin Controller link.
|
||||
* @return bool
|
||||
*/
|
||||
protected function uninstallAdminTab()
|
||||
{
|
||||
$id_tab = (int) Tab::getIdFromClassName('AdminAddLivePhoto');
|
||||
if ($id_tab) {
|
||||
$tab = new Tab($id_tab);
|
||||
return $tab->delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create directories for storing images.
|
||||
* @return bool
|
||||
*/
|
||||
protected function createImageDirectories()
|
||||
{
|
||||
if (!is_dir(self::IMG_DIR_VAR_PATH)) {
|
||||
// Create directory recursively with write permissions
|
||||
if (!mkdir(self::IMG_DIR_VAR_PATH, 0775, true)) {
|
||||
$this->_errors[] = $this->trans('Could not create image directory: ',[], 'Modules.Addlivephoto.Admin') . self::IMG_DIR_VAR_PATH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Add an index.php file for security
|
||||
if (!file_exists(self::IMG_DIR_VAR_PATH . 'index.php')) {
|
||||
@copy(_PS_MODULE_DIR_.$this->name.'/views/index.php', self::IMG_DIR_VAR_PATH . 'index.php');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to display content on the product page.
|
||||
* @param array $params
|
||||
* @return string|void
|
||||
*/
|
||||
public function hookDisplayProductPriceBlock($params)
|
||||
{
|
||||
if (!isset($params['type']) || $params['type'] !== 'after_price') {
|
||||
return;
|
||||
}
|
||||
|
||||
$id_product = (int) Tools::getValue('id_product');
|
||||
if (!$id_product) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch images from the last 4 months
|
||||
$sql = new DbQuery();
|
||||
$sql->select('`image_name`');
|
||||
$sql->from(self::TABLE_NAME);
|
||||
$sql->where('`id_product` = ' . $id_product);
|
||||
$sql->where('`date_add` >= DATE_SUB(NOW(), INTERVAL 4 MONTH)');
|
||||
$sql->orderBy('`date_add` DESC');
|
||||
|
||||
$results = Db::getInstance()->executeS($sql);
|
||||
|
||||
if (!$results) {
|
||||
return;
|
||||
}
|
||||
|
||||
$live_photos = [];
|
||||
foreach ($results as $row) {
|
||||
$image_uri = $this->getProductImageUri($id_product, $row['image_name']);
|
||||
if ($image_uri) {
|
||||
$live_photos[] = [
|
||||
'url' => $image_uri,
|
||||
// This alt text is crucial for SEO
|
||||
'alt' => sprintf(
|
||||
$this->trans('Freshness photo for %s, taken on %s',[], 'Modules.Addlivephoto.Shop'),
|
||||
$this->context->smarty->tpl_vars['product']->value['name'],
|
||||
date('Y-m-d') // You can store the date_add and format it here
|
||||
),
|
||||
'title' => $this->trans('Click to see the expiry date photo',[], 'Modules.Addlivephoto.Shop'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($live_photos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->context->smarty->assign([
|
||||
'live_photos' => $live_photos,
|
||||
'module_name' => $this->name,
|
||||
]);
|
||||
|
||||
return $this->display(__FILE__, 'views/templates/hook/displayProductPriceBlock.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to add CSS/JS to the admin controller page.
|
||||
*/
|
||||
public function hookActionAdminControllerSetMedia()
|
||||
{
|
||||
// We only want to load these assets on our specific controller page
|
||||
if (Tools::getValue('controller') == 'AdminAddLivePhoto') {
|
||||
$this->context->controller->addJS($this->_path . 'views/js/admin.js');
|
||||
$this->context->controller->addCSS($this->_path . 'views/css/admin.css');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full server path to a product's image directory, creating it if necessary.
|
||||
* Follows the PrestaShop pattern (e.g., /1/2/3/ for ID 123).
|
||||
*
|
||||
* @param int $id_product
|
||||
* @return string|false The path to the directory or false on failure.
|
||||
*/
|
||||
public function getProductImageServerPath($id_product)
|
||||
{
|
||||
if (!is_numeric($id_product)) {
|
||||
return false;
|
||||
}
|
||||
$path = self::IMG_DIR_VAR_PATH . implode('/', str_split((string)$id_product)) . '/';
|
||||
|
||||
if (!is_dir($path)) {
|
||||
if (!mkdir($path, 0775, true)) {
|
||||
return false;
|
||||
}
|
||||
// Add an index.php file for security
|
||||
if (!file_exists($path . 'index.php')) {
|
||||
@copy(_PS_MODULE_DIR_.$this->name.'/views/index.php', $path . 'index.php');
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public URI for a specific product image.
|
||||
*
|
||||
* @param int $id_product
|
||||
* @param string $image_name
|
||||
* @return string|false The public URI or false if file does not exist.
|
||||
*/
|
||||
public function getProductImageUri($id_product, $image_name)
|
||||
{
|
||||
if (!is_numeric($id_product) || empty($image_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path_parts = str_split((string)$id_product);
|
||||
$image_path = implode('/', $path_parts) . '/' . $image_name;
|
||||
|
||||
$server_path_check = self::IMG_DIR_VAR_PATH . $image_path;
|
||||
|
||||
// We check if the file actually exists before returning a URI
|
||||
if (!file_exists($server_path_check)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->context->link->getBaseLink() . 'modules/addlivephoto/photo/' . $image_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a live photo record and its corresponding file.
|
||||
* @param int $id_product
|
||||
* @param string $image_name
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteProductImage($id_product, $image_name)
|
||||
{
|
||||
if (!is_numeric($id_product) || empty($image_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
$deleted_from_db = Db::getInstance()->delete(
|
||||
self::TABLE_NAME,
|
||||
'`id_product` = ' . (int)$id_product . ' AND `image_name` = \'' . pSQL($image_name) . '\''
|
||||
);
|
||||
|
||||
// Delete file from server
|
||||
$file_path = $this->getProductImageServerPath($id_product) . $image_name;
|
||||
$deleted_from_disk = false;
|
||||
if (file_exists($file_path) && is_writable($file_path)) {
|
||||
$deleted_from_disk = unlink($file_path);
|
||||
}
|
||||
|
||||
// Return true if both operations were successful or if the file was already gone but DB entry was removed
|
||||
return $deleted_from_db && ($deleted_from_disk || !file_exists($file_path));
|
||||
}
|
||||
public function isUsingNewTranslationSystem()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user