417 lines
11 KiB
PHP
Executable file
417 lines
11 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/**
|
|
* Class AnlageFile
|
|
* Manages file attachments for installation elements
|
|
*/
|
|
class AnlageFile extends CommonObject
|
|
{
|
|
public $element = 'anlagefile';
|
|
public $table_element = 'kundenkarte_anlage_files';
|
|
|
|
public $fk_anlage;
|
|
public $filename;
|
|
public $filepath;
|
|
public $filesize;
|
|
public $mimetype;
|
|
public $file_type;
|
|
public $label;
|
|
public $description;
|
|
public $is_cover;
|
|
public $position;
|
|
public $share;
|
|
|
|
public $date_creation;
|
|
public $fk_user_creat;
|
|
public $fk_user_modif;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param DoliDB $db Database handler
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Create object in database
|
|
*
|
|
* @param User $user User that creates
|
|
* @return int Return integer <0 if KO, Id of created object if OK
|
|
*/
|
|
public function create($user)
|
|
{
|
|
global $conf;
|
|
|
|
$error = 0;
|
|
$now = dol_now();
|
|
|
|
if (empty($this->fk_anlage) || empty($this->filename) || empty($this->filepath)) {
|
|
$this->error = 'ErrorMissingParameters';
|
|
return -1;
|
|
}
|
|
|
|
// Determine file type
|
|
if (empty($this->file_type)) {
|
|
$this->file_type = $this->determineFileType($this->mimetype, $this->filename);
|
|
}
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
|
$sql .= "entity, fk_anlage, filename, filepath, filesize, mimetype,";
|
|
$sql .= " file_type, label, description, is_cover, position, share,";
|
|
$sql .= " date_creation, fk_user_creat";
|
|
$sql .= ") VALUES (";
|
|
$sql .= ((int) $conf->entity);
|
|
$sql .= ", ".((int) $this->fk_anlage);
|
|
$sql .= ", '".$this->db->escape($this->filename)."'";
|
|
$sql .= ", '".$this->db->escape($this->filepath)."'";
|
|
$sql .= ", ".((int) $this->filesize);
|
|
$sql .= ", ".($this->mimetype ? "'".$this->db->escape($this->mimetype)."'" : "NULL");
|
|
$sql .= ", '".$this->db->escape($this->file_type)."'";
|
|
$sql .= ", ".($this->label ? "'".$this->db->escape($this->label)."'" : "NULL");
|
|
$sql .= ", ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
|
$sql .= ", ".((int) $this->is_cover);
|
|
$sql .= ", ".((int) $this->position);
|
|
$sql .= ", ".($this->share ? "'".$this->db->escape($this->share)."'" : "NULL");
|
|
$sql .= ", '".$this->db->idate($now)."'";
|
|
$sql .= ", ".((int) $user->id);
|
|
$sql .= ")";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
$error++;
|
|
$this->errors[] = "Error ".$this->db->lasterror();
|
|
}
|
|
|
|
if (!$error) {
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
|
|
}
|
|
|
|
if ($error) {
|
|
$this->db->rollback();
|
|
return -1 * $error;
|
|
} else {
|
|
$this->db->commit();
|
|
return $this->id;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load object from database
|
|
*
|
|
* @param int $id ID of record
|
|
* @return int Return integer <0 if KO, 0 if not found, >0 if OK
|
|
*/
|
|
public function fetch($id)
|
|
{
|
|
global $conf;
|
|
|
|
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE rowid = ".((int) $id);
|
|
$sql .= " AND entity = ".((int) $conf->entity);
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
if ($this->db->num_rows($resql)) {
|
|
$obj = $this->db->fetch_object($resql);
|
|
|
|
$this->id = $obj->rowid;
|
|
$this->entity = $obj->entity;
|
|
$this->fk_anlage = $obj->fk_anlage;
|
|
$this->filename = $obj->filename;
|
|
$this->filepath = $obj->filepath;
|
|
$this->filesize = $obj->filesize;
|
|
$this->mimetype = $obj->mimetype;
|
|
$this->file_type = $obj->file_type;
|
|
$this->label = $obj->label;
|
|
$this->description = $obj->description;
|
|
$this->is_cover = $obj->is_cover;
|
|
$this->position = $obj->position;
|
|
$this->share = $obj->share;
|
|
$this->date_creation = $this->db->jdate($obj->date_creation);
|
|
$this->fk_user_creat = $obj->fk_user_creat;
|
|
$this->fk_user_modif = $obj->fk_user_modif;
|
|
|
|
$this->db->free($resql);
|
|
return 1;
|
|
} else {
|
|
$this->db->free($resql);
|
|
return 0;
|
|
}
|
|
} else {
|
|
$this->error = $this->db->lasterror();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete object in database and file from disk
|
|
*
|
|
* @param User $user User that deletes
|
|
* @return int Return integer <0 if KO, >0 if OK
|
|
*/
|
|
public function delete($user)
|
|
{
|
|
global $conf;
|
|
|
|
$error = 0;
|
|
|
|
// Delete physical file (use clean filepath)
|
|
$fullpath = $this->getFullPath();
|
|
if (file_exists($fullpath)) {
|
|
dol_delete_file($fullpath);
|
|
|
|
// Delete thumbnail if exists
|
|
$thumbpath = dirname($fullpath).'/thumbs/'.pathinfo($this->filename, PATHINFO_FILENAME).'_small.'.pathinfo($this->filename, PATHINFO_EXTENSION);
|
|
if (file_exists($thumbpath)) {
|
|
dol_delete_file($thumbpath);
|
|
}
|
|
}
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
$error++;
|
|
$this->errors[] = "Error ".$this->db->lasterror();
|
|
}
|
|
|
|
if ($error) {
|
|
$this->db->rollback();
|
|
return -1 * $error;
|
|
} else {
|
|
$this->db->commit();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch all files for an installation element
|
|
*
|
|
* @param int $anlageId Installation element ID
|
|
* @param string $fileType Filter by file type (image, pdf, document, other)
|
|
* @return array Array of AnlageFile objects
|
|
*/
|
|
public function fetchAllByAnlage($anlageId, $fileType = '')
|
|
{
|
|
global $conf;
|
|
|
|
$results = array();
|
|
|
|
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE fk_anlage = ".((int) $anlageId);
|
|
$sql .= " AND entity = ".((int) $conf->entity);
|
|
if ($fileType) {
|
|
$sql .= " AND file_type = '".$this->db->escape($fileType)."'";
|
|
}
|
|
$sql .= " ORDER BY is_cover DESC, position ASC, filename ASC";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $this->db->fetch_object($resql)) {
|
|
$file = new AnlageFile($this->db);
|
|
$file->id = $obj->rowid;
|
|
$file->fk_anlage = $obj->fk_anlage;
|
|
$file->filename = $obj->filename;
|
|
$file->filepath = $obj->filepath;
|
|
$file->filesize = $obj->filesize;
|
|
$file->mimetype = $obj->mimetype;
|
|
$file->file_type = $obj->file_type;
|
|
$file->label = $obj->label;
|
|
$file->description = $obj->description;
|
|
$file->is_cover = $obj->is_cover;
|
|
$file->position = $obj->position;
|
|
$file->share = $obj->share;
|
|
$file->date_creation = $this->db->jdate($obj->date_creation);
|
|
|
|
$results[] = $file;
|
|
}
|
|
$this->db->free($resql);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Determine file type from mimetype and filename
|
|
*
|
|
* @param string $mimetype MIME type
|
|
* @param string $filename Filename
|
|
* @return string File type (image, pdf, document, other)
|
|
*/
|
|
public function determineFileType($mimetype, $filename)
|
|
{
|
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
|
|
|
if (in_array($ext, array('jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp')) || strpos($mimetype, 'image/') === 0) {
|
|
return 'image';
|
|
}
|
|
if ($ext == 'pdf' || $mimetype == 'application/pdf') {
|
|
return 'pdf';
|
|
}
|
|
if (in_array($ext, array('doc', 'docx', 'xls', 'xlsx', 'odt', 'ods', 'txt', 'rtf'))) {
|
|
return 'document';
|
|
}
|
|
|
|
return 'other';
|
|
}
|
|
|
|
/**
|
|
* Get full file path
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getFullPath()
|
|
{
|
|
global $conf;
|
|
return $conf->kundenkarte->dir_output.'/'.$this->getCleanFilepath();
|
|
}
|
|
|
|
/**
|
|
* Get URL to view/download the file
|
|
*
|
|
* @param bool $forceDownload If true, force download instead of inline display
|
|
* @return string
|
|
*/
|
|
public function getUrl($forceDownload = false)
|
|
{
|
|
// Sanitize filepath - ensure it's relative, not absolute
|
|
$filepath = $this->getCleanFilepath();
|
|
$url = DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file='.urlencode($filepath);
|
|
|
|
// Add attachment=0 for inline display (especially for PDFs)
|
|
// If forceDownload is true, don't add this parameter (default Dolibarr behavior is download)
|
|
if (!$forceDownload) {
|
|
$url .= '&attachment=0';
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Get clean relative filepath (fixes bad data)
|
|
*
|
|
* @return string Clean relative filepath
|
|
*/
|
|
public function getCleanFilepath()
|
|
{
|
|
$filepath = $this->filepath;
|
|
|
|
// If filepath contains "anlagen/" extract from there
|
|
if (strpos($filepath, 'anlagen/') !== false) {
|
|
$filepath = substr($filepath, strpos($filepath, 'anlagen/'));
|
|
}
|
|
// If it's still an absolute path (starts with / or file:// or Windows drive), just use filename
|
|
elseif (preg_match('/^(\/|file:\/\/|[A-Za-z]:)/', $filepath)) {
|
|
// Try to get just the filename and rebuild proper path
|
|
$filename = basename($filepath);
|
|
// Use fk_anlage to build correct path if available
|
|
if ($this->fk_anlage > 0) {
|
|
global $db;
|
|
$sql = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."kundenkarte_anlage WHERE rowid = ".((int) $this->fk_anlage);
|
|
$resql = $db->query($sql);
|
|
if ($resql && $db->num_rows($resql)) {
|
|
$obj = $db->fetch_object($resql);
|
|
$filepath = 'anlagen/'.$obj->fk_soc.'/'.$this->fk_anlage.'/'.$filename;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $filepath;
|
|
}
|
|
|
|
/**
|
|
* Get thumbnail URL for images
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getThumbUrl()
|
|
{
|
|
if ($this->file_type != 'image') {
|
|
return '';
|
|
}
|
|
|
|
$filepath = $this->getCleanFilepath();
|
|
$dir = dirname($filepath);
|
|
$filename = pathinfo($this->filename, PATHINFO_FILENAME);
|
|
$ext = pathinfo($this->filename, PATHINFO_EXTENSION);
|
|
|
|
$thumbpath = $dir.'/thumbs/'.$filename.'_small.'.$ext;
|
|
|
|
// Use attachment=0 for inline display
|
|
return DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file='.urlencode($thumbpath).'&attachment=0';
|
|
}
|
|
|
|
/**
|
|
* Set this file as cover image
|
|
*
|
|
* @param User $user User making the change
|
|
* @return int <0 if KO, >0 if OK
|
|
*/
|
|
public function setAsCover($user)
|
|
{
|
|
$this->db->begin();
|
|
|
|
// Unset other covers for this anlage
|
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET is_cover = 0";
|
|
$sql .= " WHERE fk_anlage = ".((int) $this->fk_anlage);
|
|
$this->db->query($sql);
|
|
|
|
// Set this as cover
|
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET is_cover = 1, fk_user_modif = ".((int) $user->id);
|
|
$sql .= " WHERE rowid = ".((int) $this->id);
|
|
$resql = $this->db->query($sql);
|
|
|
|
if ($resql) {
|
|
$this->is_cover = 1;
|
|
$this->db->commit();
|
|
return 1;
|
|
} else {
|
|
$this->db->rollback();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate thumbnail for image
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function generateThumbnail()
|
|
{
|
|
global $conf;
|
|
|
|
if ($this->file_type != 'image') {
|
|
return false;
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
|
|
|
|
$fullpath = $this->getFullPath();
|
|
if (!file_exists($fullpath)) {
|
|
return false;
|
|
}
|
|
|
|
$thumbdir = dirname($fullpath).'/thumbs';
|
|
if (!is_dir($thumbdir)) {
|
|
dol_mkdir($thumbdir);
|
|
}
|
|
|
|
// Generate small thumbnail
|
|
$result = vignette($fullpath, 200, 160, '_small', 80, 'thumbs');
|
|
|
|
return ($result !== false);
|
|
}
|
|
}
|