- Added formObjectOptions hook for document pages - CSS/JS injected via DOMContentLoaded for proper timing - Toggle between list and tile view (localStorage persisted) - Lightbox for image gallery with keyboard navigation - Fixed hook contexts (productdocuments, invoicedocuments, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
289 lines
8.4 KiB
PHP
289 lines
8.4 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* Get related documents for document browser
|
|
*/
|
|
|
|
if (!defined('NOTOKENRENEWAL')) {
|
|
define('NOTOKENRENEWAL', '1');
|
|
}
|
|
if (!defined('NOREQUIREMENU')) {
|
|
define('NOREQUIREMENU', '1');
|
|
}
|
|
if (!defined('NOREQUIREAJAX')) {
|
|
define('NOREQUIREAJAX', '1');
|
|
}
|
|
|
|
// Load Dolibarr environment
|
|
$res = 0;
|
|
// Path from /custom/filearchiv/ajax/ to main.inc.php
|
|
if (!$res && file_exists("../../../main.inc.php")) {
|
|
$res = @include "../../../main.inc.php";
|
|
}
|
|
// Fallback for different directory structures
|
|
if (!$res && file_exists("../../../../main.inc.php")) {
|
|
$res = @include "../../../../main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../main.inc.php")) {
|
|
$res = @include "../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
|
|
|
// Debug logging
|
|
$debugFile = '/tmp/filearchiv_getdocs_debug.log';
|
|
@file_put_contents($debugFile, date('Y-m-d H:i:s') . " === getdocuments.php called ===\n", FILE_APPEND);
|
|
|
|
// Security check
|
|
if (!isModEnabled('filearchiv')) {
|
|
http_response_code(403);
|
|
echo json_encode(array('success' => false, 'error' => 'Module not enabled'));
|
|
exit;
|
|
}
|
|
|
|
$element = GETPOST('element', 'aZ09');
|
|
$id = GETPOST('id', 'int');
|
|
|
|
if (empty($element) || empty($id)) {
|
|
echo json_encode(array('success' => false, 'error' => 'Missing parameters'));
|
|
exit;
|
|
}
|
|
|
|
$langs->load('filearchiv@filearchiv');
|
|
|
|
// Get the main object
|
|
$object = null;
|
|
$thirdparty = null;
|
|
|
|
switch ($element) {
|
|
case 'propal':
|
|
require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
|
|
$object = new Propal($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
case 'commande':
|
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
|
$object = new Commande($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
case 'facture':
|
|
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
|
|
$object = new Facture($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
case 'expedition':
|
|
require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
|
|
$object = new Expedition($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
case 'order_supplier':
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
|
|
$object = new CommandeFournisseur($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
case 'invoice_supplier':
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
|
|
$object = new FactureFournisseur($db);
|
|
$object->fetch($id);
|
|
$object->fetch_thirdparty();
|
|
$thirdparty = $object->thirdparty;
|
|
break;
|
|
default:
|
|
echo json_encode(array('success' => false, 'error' => 'Unknown element type'));
|
|
exit;
|
|
}
|
|
|
|
if (!$object || !$object->id) {
|
|
echo json_encode(array('success' => false, 'error' => 'Object not found'));
|
|
exit;
|
|
}
|
|
|
|
@file_put_contents($debugFile, "Element: $element, ID: $id\n", FILE_APPEND);
|
|
@file_put_contents($debugFile, "Object ref: " . $object->ref . ", entity: " . $object->entity . "\n", FILE_APPEND);
|
|
@file_put_contents($debugFile, "Thirdparty: " . ($thirdparty ? $thirdparty->id . " - " . $thirdparty->name : "none") . "\n", FILE_APPEND);
|
|
|
|
$categories = array();
|
|
|
|
// 1. Customer/Supplier documents
|
|
if ($thirdparty && $thirdparty->id) {
|
|
$socdir = $conf->societe->multidir_output[$thirdparty->entity] . '/' . dol_sanitizeFileName($thirdparty->id);
|
|
@file_put_contents($debugFile, "Societe dir: $socdir (exists: " . (is_dir($socdir) ? "yes" : "no") . ")\n", FILE_APPEND);
|
|
$files = getDocumentsForPath($socdir, $debugFile);
|
|
if (!empty($files)) {
|
|
$categories[] = array(
|
|
'title' => $langs->trans('CustomerDocuments'),
|
|
'icon' => 'fa-building',
|
|
'files' => $files
|
|
);
|
|
}
|
|
}
|
|
|
|
// 2. Current object documents
|
|
$objdir = '';
|
|
switch ($element) {
|
|
case 'propal':
|
|
$objdir = $conf->propal->multidir_output[$object->entity] . '/' . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
case 'commande':
|
|
$objdir = $conf->commande->multidir_output[$object->entity] . '/' . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
case 'facture':
|
|
$objdir = $conf->facture->multidir_output[$object->entity] . '/' . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
case 'expedition':
|
|
$objdir = $conf->expedition->dir_output . '/sending/' . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
case 'order_supplier':
|
|
$objdir = $conf->fournisseur->commande->multidir_output[$object->entity] . '/' . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
case 'invoice_supplier':
|
|
$objdir = $conf->fournisseur->facture->multidir_output[$object->entity] . '/' . get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier') . dol_sanitizeFileName($object->ref);
|
|
break;
|
|
}
|
|
|
|
if ($objdir) {
|
|
@file_put_contents($debugFile, "Object dir: $objdir (exists: " . (is_dir($objdir) ? "yes" : "no") . ")\n", FILE_APPEND);
|
|
$files = getDocumentsForPath($objdir, $debugFile);
|
|
@file_put_contents($debugFile, "Files found: " . count($files) . "\n", FILE_APPEND);
|
|
if (!empty($files)) {
|
|
$titleKey = ucfirst($element) . 'Documents';
|
|
$categories[] = array(
|
|
'title' => $langs->trans($titleKey),
|
|
'icon' => getIconForElement($element),
|
|
'files' => $files
|
|
);
|
|
}
|
|
}
|
|
|
|
// 3. Product documents from object lines
|
|
$productFiles = array();
|
|
@file_put_contents($debugFile, "Lines count: " . (isset($object->lines) ? count($object->lines) : "not set") . "\n", FILE_APPEND);
|
|
if (!empty($object->lines)) {
|
|
foreach ($object->lines as $line) {
|
|
if (!empty($line->fk_product) && $line->fk_product > 0) {
|
|
$proddir = $conf->product->multidir_output[$conf->entity] . '/' . dol_sanitizeFileName($line->fk_product);
|
|
@file_put_contents($debugFile, "Product dir: $proddir\n", FILE_APPEND);
|
|
$files = getDocumentsForPath($proddir, $debugFile);
|
|
foreach ($files as $file) {
|
|
$file['product_ref'] = $line->ref;
|
|
$file['product_label'] = $line->product_label;
|
|
$productFiles[] = $file;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!empty($productFiles)) {
|
|
$categories[] = array(
|
|
'title' => $langs->trans('ProductDocuments'),
|
|
'icon' => 'fa-cube',
|
|
'files' => $productFiles
|
|
);
|
|
}
|
|
|
|
header('Content-Type: application/json; charset=UTF-8');
|
|
echo json_encode(array('success' => true, 'categories' => $categories));
|
|
|
|
/**
|
|
* Get documents from a directory path
|
|
*
|
|
* @param string $path Directory path
|
|
* @param string $debugFile Debug file path (optional)
|
|
* @return array Array of file info
|
|
*/
|
|
function getDocumentsForPath($path, $debugFile = null)
|
|
{
|
|
global $langs;
|
|
|
|
$files = array();
|
|
|
|
if (!is_dir($path)) {
|
|
if ($debugFile) {
|
|
@file_put_contents($debugFile, " Path not a dir: $path\n", FILE_APPEND);
|
|
}
|
|
return $files;
|
|
}
|
|
|
|
if ($debugFile) {
|
|
@file_put_contents($debugFile, " Scanning dir: $path\n", FILE_APPEND);
|
|
}
|
|
|
|
$filearray = dol_dir_list($path, 'files', 0, '', '(\.meta|_preview.*\.png)$');
|
|
|
|
foreach ($filearray as $file) {
|
|
$files[] = array(
|
|
'name' => $file['name'],
|
|
'fullpath' => $file['fullname'],
|
|
'relativepath' => $file['relativename'],
|
|
'size' => $file['size'],
|
|
'size_formatted' => dol_print_size($file['size']),
|
|
'date' => dol_print_date($file['date'], 'day'),
|
|
'icon' => getIconForFile($file['name'])
|
|
);
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Get Font Awesome icon for file type
|
|
*
|
|
* @param string $filename Filename
|
|
* @return string Icon class
|
|
*/
|
|
function getIconForFile($filename)
|
|
{
|
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
|
|
|
$icons = array(
|
|
'pdf' => 'fa-file-pdf',
|
|
'doc' => 'fa-file-word',
|
|
'docx' => 'fa-file-word',
|
|
'xls' => 'fa-file-excel',
|
|
'xlsx' => 'fa-file-excel',
|
|
'ppt' => 'fa-file-powerpoint',
|
|
'pptx' => 'fa-file-powerpoint',
|
|
'jpg' => 'fa-file-image',
|
|
'jpeg' => 'fa-file-image',
|
|
'png' => 'fa-file-image',
|
|
'gif' => 'fa-file-image',
|
|
'zip' => 'fa-file-archive',
|
|
'rar' => 'fa-file-archive',
|
|
'txt' => 'fa-file-alt',
|
|
'csv' => 'fa-file-csv',
|
|
);
|
|
|
|
return isset($icons[$ext]) ? $icons[$ext] : 'fa-file';
|
|
}
|
|
|
|
/**
|
|
* Get icon for element type
|
|
*
|
|
* @param string $element Element type
|
|
* @return string Icon class
|
|
*/
|
|
function getIconForElement($element)
|
|
{
|
|
$icons = array(
|
|
'propal' => 'fa-file-signature',
|
|
'commande' => 'fa-file-invoice',
|
|
'facture' => 'fa-file-invoice-dollar',
|
|
'expedition' => 'fa-truck',
|
|
'order_supplier' => 'fa-shopping-cart',
|
|
'invoice_supplier' => 'fa-file-invoice',
|
|
);
|
|
|
|
return isset($icons[$element]) ? $icons[$element] : 'fa-file';
|
|
}
|