Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions controllers/front/download.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

class Pixel_Product_FilesDownloadModuleFrontController extends ModuleFrontController
{
public $ssl = true;

public function initContent()
{
$idFile = (int)Tools::getValue('id_file');

if (!$idFile) {
$this->errors[] = $this->module->l('File ID is missing', 'download');
$this->redirectWithNotifications($this->context->link->getPageLink('404'));
return;
}

try {
$productFile = Db::getInstance()->executeS("
SELECT file, title
FROM " . _DB_PREFIX_ . "product_file
WHERE id = " . $idFile . " AND id_lang = " . $this->context->language->id . " AND id_shop = " . $this->context->shop->id
);

Db::getInstance()->execute("
UPDATE " . _DB_PREFIX_ . "product_file
SET nb_download = nb_download + 1
WHERE id = " . $idFile . " AND id_lang = " . $this->context->language->id . " AND id_shop = " . $this->context->shop->id
);

$this->downloadFile($productFile[0]);
} catch (\Exception $e) {
PrestaShopLogger::addLog(
'Error downloading file: ' . $e->getMessage(),
3,
null,
'ProductFile',
$idFile
);
Tools::redirect('index.php?controller=404');
}
}

protected function downloadFile(array $productFile)
{
$filePath = Pixel_product_files::FILE_BASE_DIR . $productFile['file'];

if (!file_exists($filePath)) {
$this->errors[] = $this->module->l('File does not exist on server', 'download');
$this->redirectWithNotifications($this->context->link->getPageLink('404'));
return;
}

// Déterminer le type MIME
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);

// Obtenir le nom du fichier
$fileName = $productFile['title']
? $this->sanitizeFileName($productFile['title'])
: basename($productFile['file']);

// Ajouter l'extension si nécessaire
$extension = pathinfo($productFile['file'], PATHINFO_EXTENSION);
if (!preg_match('/\.' . preg_quote($extension, '/') . '$/', $fileName)) {
$fileName .= '.' . $extension;
}

// Headers pour le téléchargement
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Length: ' . filesize($filePath));
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');

// Nettoyage du buffer de sortie
if (ob_get_level()) {
ob_end_clean();
}

// Lecture et envoi du fichier
readfile($filePath);
exit;
}

protected function sanitizeFileName($fileName)
{
// Remplacer les caractères spéciaux
$fileName = str_replace(['"', "'", '/', '\\', '?', '*', ':', '|', '<', '>'], '-', $fileName);
// Supprimer les espaces multiples
$fileName = preg_replace('/\s+/', '_', $fileName);

return trim($fileName);
}
}
51 changes: 49 additions & 2 deletions pixel_product_files.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class Pixel_product_files extends Module implements WidgetInterface
public function __construct()
{
$this->name = 'pixel_product_files';
$this->version = '1.3.1';
$this->version = '1.4.0';
$this->author = 'Pixel Open';
$this->tab = 'content_management';
$this->need_instance = 0;
Expand Down Expand Up @@ -117,7 +117,8 @@ public function install(): bool
$this->registerHook('displayBackOfficeHeader') &&
$this->registerHook('actionAdminProductsControllerSaveBefore') &&
$this->registerHook('actionBeforeUpdateProductFormHandler') &&
$this->registerHook('actionFrontControllerSetMedia');
$this->registerHook('actionFrontControllerSetMedia') &&
$this->registerHook("displayAdminStatsModules");
}

/**
Expand Down Expand Up @@ -380,6 +381,52 @@ protected function saveProductFileData($files): void
}
}

public function hookDisplayAdminStatsModules()
{
$html = "";

$productFiles = Db::getInstance()->executeS("
SELECT pf.id, pf.title, pl.name, pf.nb_download
FROM " . _DB_PREFIX_ . "product_file AS pf
INNER JOIN " . _DB_PREFIX_ . "product_lang AS pl ON (pf.id_product = pl.id_product AND pl.id_lang = pf.id_lang)
ORDER BY pf.nb_download DESC
");

if (count($productFiles) > 0) {
$html = '
<script type="text/javascript">$(\'#calendar\').slideToggle();</script>
<div class="panel-heading">' . $this->trans("Product file download", [], "Modules.PixelproductFiles.Admin") . '</div>
<table class="table">
<thead>
<tr>
<th><span class="title_box_active">' . $this->trans("Id", [], "Modules.PixelproductFiles.Admin") . '</span></th>
<th><span class="title_box_active">' . $this->trans("File name", [], "Modules.PixelproductFiles.Admin") . '</span></th>
<th><span class="title_box_active">' . $this->trans("Product", [], "Modules.PixelproductFiles.Admin") . '</span></th>
<th><span class="title_box_active">' . $this->trans("Number of downloads", [], "Modules.PixelproductFiles.Admin") . '</span></th>
</tr>
</thead>
<tbody>';

foreach ($productFiles as $productFile) {
$html .= '<tr>
<td>' . $productFile['id'] . '</td>
<td>' . $productFile['title'] . '</td>
<td>' . $productFile['name'] . '</td>
<td>' . $productFile['nb_download'] . '</td>
</tr>';
}

$html .= '
</tbody>
</table>
';
} else {
$html .= "<p>" . $this->trans("There are no files associated with product", [], "Modules.Pixelproductfiles.Admin") . "</p>";
}

return $html;
}

/**************/
/** USEFULLY **/
/**************/
Expand Down
17 changes: 17 additions & 0 deletions src/Entity/ProductFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class ProductFile
*/
private $position;

/**
* @var int
*
* @ORM\Column(name="nb_download", type="integer", nullable=false)
*/
private $nbDownload;

/**
* @return int
*/
Expand Down Expand Up @@ -230,6 +237,16 @@ public function setPosition(?int $position): ProductFile
return $this;
}

public function getNbDownload(): int
{
return $this->nbDownload;
}

public function setNbDownload(int $nbDownload): void
{
$this->nbDownload = $nbDownload;
}

/**
* @return mixed[]
*/
Expand Down
9 changes: 9 additions & 0 deletions upgrade/upgrade-1.4.0.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

function upgrade_module_1_4_0($module)
{
return (Db::getInstance()->execute('
ALTER TABLE `' . _DB_PREFIX_ . 'product_file`
ADD `nb_download` INT(10) NULL DEFAULT 0')
) && $module->registerHook("displayAdminStatsModules");
}
6 changes: 5 additions & 1 deletion views/templates/admin/files.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
<label for="form_additional_position_{{ file.id }}_{{ file.idLang }}">{{ 'Position'|trans({}, 'Modules.Pixelproductfiles.Admin') }}</label>
<input type="text" class="form-control" id="form_additional_position_{{ file.id }}_{{ file.idLang }}" name="file[{{ file.id }}][position]" value="{{ file.position }}" />
</div>
<div class="mt-2">
<label for="form_additional_nb_download_{{ file.id }}_{{ file.idLang }}">{{ "Number of downloads"|trans({}, "Modules.Pixelproductfiles.Admin") }}</label>
<input type="text" class="form-control" id="form_additional_nb_download_{{ file.id }}_{{ file.idLang }}" value="{{ file.nbDownload }}" readonly />
</div>
</div>
<div class="product-files-actions">
<a href="{{ delete_url }}&id_file={{ file.id }}" onclick="return confirm('{{ 'Are you sure you want to delete this file?'|trans({}, 'Modules.Pixelproductfiles.Admin')|e }}');">{{ 'Delete'|trans({}, 'Modules.Pixelproductfiles.Admin') }}</a>
Expand All @@ -69,4 +73,4 @@
$('#product_files_add_file_modal').modal('show');
});
</script>
</div>
</div>
2 changes: 1 addition & 1 deletion views/templates/widget/files.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{foreach from=$files item=file}
{assign var="extension" value=$file->getFile()|pathinfo:$smarty.const.PATHINFO_EXTENSION}
<li class="product-file">
<a href="{$path.docs}{$file->getFile()}" target="_blank" class="product-file-link">
<a href="{$link->getModuleLink("pixel_product_files", "download", ['id_file' => $file->getId()])}" class="product-file-link">
{if isset($icons[$extension])}
<img src="{$path.icons}{$icons[$extension]}" alt="{$file->getTitle()}" />
{/if}
Expand Down