dolibarr/htdocs/core/class/fileupload.class.php

666 lines
25 KiB
PHP
Raw Normal View History

<?php
fix ignored phpstan (most of expects string, int given) (#30649) * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan
2024-09-05 14:05:37 +00:00
/* Copyright (C) 2011-2022 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2011-2023 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
2012-06-09 11:54:05 +00:00
*
* 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.
2012-06-09 11:54:05 +00:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
2012-06-09 11:54:05 +00:00
*
* You should have received a copy of the GNU General Public License
2019-09-23 19:55:30 +00:00
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
2023-12-21 10:48:00 +00:00
* \file htdocs/core/class/fileupload.class.php
2024-02-07 21:18:45 +00:00
* \brief File to return the ajax response of core/ajax/fileupload.php for common file upload.
* Security is check by the ajax component.
2024-07-31 11:44:48 +00:00
* For large files upload, see flowjs-server.php
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
/**
2012-06-09 11:54:05 +00:00
* This class is used to manage file upload using ajax
*/
class FileUpload
{
/**
* @var array{script_url:string,upload_dir:string,upload_url:string,param_name:string,delete_type:string,max_file_size:?int,min_file_size:int,accept_file_types:string,max_number_of_files:?int,max_width:?int,max_height:?int,min_width:int,min_height:int,discard_aborted_uploads:bool,image_versions:array<string,array{upload_dir:string,upload_url:string,max_width:int,max_height:int,jpeg_quality?:int}>}
*/
public $options;
/**
* @var int
*/
protected $fk_element;
fix ignored phpstan (most of expects string, int given) (#30649) * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan
2024-09-05 14:05:37 +00:00
/**
* @var string object element
*/
protected $element;
/**
* Constructor.
* This set ->$options
*
* @param ?array{script_url?:string,upload_dir?:string,upload_url?:string,param_name?:string,delete_type?:string,max_file_size?:?int,min_file_size?:int,accept_file_types?:string,max_number_of_files?:?int,max_width?:?int,max_height?:?int,min_width?:int,min_height?:int,discard_aborted_uploads?:bool,image_versions?:array<string,array{upload_dir?:string,upload_url?:string,max_width?:int,max_height?:int,jpeg_quality?:int}>} $options Options array
* @param int $fk_element ID of element
* @param string $element Code of element
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
* @throws Exception If the object was not found, if the element does not support file
* uploading, or if the upload directory is missing or not writable
*/
2019-02-27 22:55:18 +00:00
public function __construct($options = null, $fk_element = null, $element = null)
{
global $hookmanager;
2023-03-01 20:37:18 +00:00
$hookmanager->initHooks(array('fileupload'));
$element_prop = getElementProperties($element);
//var_dump($element_prop);
$this->fk_element = $fk_element;
$this->element = $element;
$pathname = str_replace('/class', '', $element_prop['classpath']);
2023-12-21 10:43:30 +00:00
$filename = dol_sanitizeFileName($element_prop['classfile']);
$dir_output = dol_sanitizePathName($element_prop['dir_output']);
$savingDocMask = '';
2023-06-10 23:07:21 +00:00
//print 'fileupload.class.php: element='.$element.' pathname='.$pathname.' filename='.$filename.' dir_output='.$dir_output."\n";
$object_ref = 'UndefinedReference';
$object = null;
// If pathname and filename are null then we can still upload files if we have specified upload_dir on $options
if ($pathname !== null && $filename !== null) {
// Get object from its id and type
$object = fetchObjectByElement($fk_element, $element);
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
// fetchObjectByElement() also returns an object when the record was not found (fetch() returning 0),
// so we must check the object was really loaded. Without this, files would be stored at the root of
// the module directory, out of any object and out of any permission check on the object.
if (!is_object($object) || empty($object->id)) {
dol_syslog(get_class($this)."::__construct object ".$element." with id ".((int) $fk_element)." was not found", LOG_WARNING);
throw new Exception('objectnotfound');
}
// Directory of the module, including the sub directory used by some elements (/sending for a shipment,
// /commande for a supplier order, /<project ref> for a task, ...). We must use the same directory than
// the one used by the "Attached files" tab of the object, otherwise the uploaded file is stored but
// never shown to the user.
// Note: getMultidirOutput() only knows the elements of its own switch, that is a minority of them. For
// all the others it does not return an empty string but the string
// 'error-diroutput-not-defined-for-this-object=x', and keeping the directory of getElementProperties()
// is then the nominal case, not a degraded one. So we only accept an absolute path: that string is a
// relative path, and writing into it would create the files under the web root.
$tmpdir = getMultidirOutput($object, $element);
if (!empty($tmpdir) && preg_match('/^([a-z]:)?[\\\\\/]/i', $tmpdir)) {
$dir_output = dol_sanitizePathName($tmpdir);
}
// Add object reference as file name prefix if const MAIN_DISABLE_SUGGEST_REF_AS_PREFIX is not enabled
if (!getDolGlobalInt('MAIN_DISABLE_SUGGEST_REF_AS_PREFIX')) {
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
$savingDocMask = dol_sanitizeFileName($object->ref).'-__file__';
}
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
// get_exdir() forges the directory of an object the way the "Attached files" tabs do: it always
// uses the id for a thirdparty (a thirdparty ref is a company name, so it is not unique), and it
// falls back on the id when the ref is empty. Using anything else here would store the file into
// a directory the tab never reads.
// Note that a few tabs sanitize the ref themselves instead of calling this function, so they have
// no fallback: on an object whose ref is empty in database, which the interface does not produce
// but old records may hold, they read the root of the directory of the module while we store
// under the id. Storing at the root would mix the files of every object of the module, so the
// fallback is kept and those tabs are the ones that should be fixed.
$object_ref = get_exdir(0, 0, 0, 1, $object, $element);
// For the modules storing their documents on several levels, get_exdir() returned the level
// directories only, so we must append the directory of the object itself.
if (in_array($element, array('invoice_supplier', 'supplier_invoice'))) {
$object_ref .= '/'.dol_sanitizeFileName($object->ref);
}
}
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
// Tested after the call to getMultidirOutput(), because some elements have no 'dir_output' returned by
// getElementProperties() while getMultidirOutput() is still able to resolve their output directory.
if (empty($dir_output)) {
dol_syslog(get_class($this)."::__construct element ".$element." is not supported for uploading file, dir_output is unknown", LOG_WARNING);
throw new Exception('elementnotsupported');
}
// Note: 'upload_url' is not always the url of the file stored into 'upload_dir', because document.php
// forges the path of the file with its own rules for each value of modulepart. It is currently not a
// problem because the only caller of this class (the drag and drop of a file on a card) does not use
// the url returned into the json.
$this->options = array(
'script_url' => $_SERVER['PHP_SELF'],
'upload_dir' => $dir_output.'/'.$object_ref.'/',
'upload_url' => DOL_URL_ROOT.'/document.php?modulepart='.$element.'&attachment=1&file=/'.$object_ref.'/',
'saving_doc_mask' => $savingDocMask,
'param_name' => 'files',
// Set the following option to 'POST', if your server does not support
// DELETE requests. This is a parameter sent to the client:
'delete_type' => 'DELETE',
// The php.ini settings upload_max_filesize and post_max_size
// take precedence over the following max_file_size setting:
'max_file_size' => null,
'min_file_size' => 1,
'accept_file_types' => '/.+$/i',
// The maximum number of files for the upload directory:
'max_number_of_files' => null,
// Image resolution restrictions:
'max_width' => null,
'max_height' => null,
'min_width' => 1,
'min_height' => 1,
// Set the following option to false to enable resumable uploads:
'discard_aborted_uploads' => true,
'image_versions' => array(
// Uncomment the following version to restrict the size of
// uploaded images. You can also add additional versions with
// their own upload directories:
/*
'large' => array(
'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/',
'upload_url' => $this->getFullUrl().'/files/',
'max_width' => 1920,
'max_height' => 1200,
'jpeg_quality' => 95
),
*/
'thumbnail' => array(
'upload_dir' => $dir_output.'/'.$object_ref.'/thumbs/',
2023-12-21 10:43:30 +00:00
'upload_url' => DOL_URL_ROOT.'/document.php?modulepart='.urlencode($element).'&attachment=1&file='.urlencode('/'.$object_ref.'/thumbs/'),
'max_width' => 80,
'max_height' => 80
)
)
);
2023-01-03 13:06:25 +00:00
global $action;
$hookmanager->executeHooks(
'overrideUploadOptions',
array(
'options' => &$options,
'element' => $element
),
$object, // @phan-suppress-current-line PhanTypeMismatchArgumentNullable
2023-01-03 13:06:25 +00:00
$action
);
if ($options) {
$this->options = array_replace_recursive($this->options, $options);
}
2024-07-31 11:44:48 +00:00
// At this point we should have a valid upload_dir in this->options
if (empty($pathname) || empty($filename)) {
Qual: Apply automatic phan fixes (deprecations, unneeded imports) (#28154) * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports). * Qual: Apply automatic phan fixes (deprecations, unneeded imports) # Qual: Apply automatic phan fixes (deprecations, unneeded imports) This applies automatic fixes by phan for deprecated functions, unneeded imports).
2024-02-13 20:46:12 +00:00
if (!array_key_exists("upload_dir", $this->options)) {
setEventMessage('If $fk_element = null or $element = null you must specify upload_dir on $options', 'errors');
throw new Exception('If $fk_element = null or $element = null you must specify upload_dir on $options');
2023-03-01 09:11:55 +00:00
} elseif (!is_dir($this->options['upload_dir'])) {
2023-03-01 07:44:06 +00:00
setEventMessage('The directory '.$this->options['upload_dir'].' doesn\'t exists', 'errors');
throw new Exception('The directory '.$this->options['upload_dir'].' doesn\'t exists');
2023-03-01 09:11:55 +00:00
} elseif (!is_writable($this->options['upload_dir'])) {
2023-03-01 07:44:06 +00:00
setEventMessage('The directory '.$this->options['upload_dir'].' is not writable', 'errors');
throw new Exception('The directory '.$this->options['upload_dir'].' is not writable');
}
}
}
/**
2012-06-09 11:54:05 +00:00
* Return full URL
*
2012-06-09 11:54:05 +00:00
* @return string URL
*/
2012-06-09 11:54:05 +00:00
protected function getFullUrl()
{
2012-07-02 17:30:37 +00:00
$https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
return
($https ? 'https://' : 'http://').
(!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
($https && $_SERVER['SERVER_PORT'] === 443 ||
$_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
}
/**
* Set delete url
*
* @param stdClass $file File object (see getFileObject)
2012-06-09 11:54:05 +00:00
* @return void
*/
2012-06-09 11:54:05 +00:00
protected function setFileDeleteUrl($file)
2012-07-02 17:30:37 +00:00
{
$file->delete_url = $this->options['script_url'].'?file='.urlencode((string) ($file->name)).'&fk_element='.urlencode((string) ($this->fk_element)).'&element='.urlencode((string) ($this->element));
2012-07-02 17:30:37 +00:00
$file->delete_type = $this->options['delete_type'];
if ($file->delete_type !== 'DELETE') {
$file->delete_url .= '&_method=DELETE';
}
}
/**
2016-04-09 13:07:55 +00:00
* getFileObject
*
* @param string $file_name Filename
* @return ?stdClass
*/
2012-06-09 11:54:05 +00:00
protected function getFileObject($file_name)
{
2023-12-21 10:43:30 +00:00
$file_path = $this->options['upload_dir'].dol_sanitizeFileName($file_name);
2023-12-21 10:48:00 +00:00
2023-12-21 10:43:30 +00:00
if (dol_is_file($file_path) && $file_name[0] !== '.') {
$file = new stdClass();
$file->name = $file_name;
$file->mime = dol_mimetype($file_name, '', 2);
$file->size = filesize($file_path);
2023-12-21 10:43:30 +00:00
$file->url = $this->options['upload_url'].urlencode($file->name);
2024-02-07 21:18:45 +00:00
foreach ($this->options['image_versions'] as $version => $options) {
2023-12-21 10:43:30 +00:00
if (dol_is_file($options['upload_dir'].$file_name)) {
$tmp = explode('.', $file->name);
2023-07-10 20:45:15 +00:00
// We save the path of mini file into file->... (seems not used)
$keyforfile = $version.'_url';
2023-12-21 10:43:30 +00:00
$file->$keyforfile = $options['upload_url'].urlencode($tmp[0].'_mini.'.$tmp[1]);
}
}
2012-06-09 11:54:05 +00:00
$this->setFileDeleteUrl($file);
return $file;
}
return null;
}
/**
2016-04-09 13:07:55 +00:00
* getFileObjects
*
* @return array<?stdClass> Array of objects
*/
2012-06-09 11:54:05 +00:00
protected function getFileObjects()
{
2012-06-09 11:54:05 +00:00
return array_values(array_filter(array_map(array($this, 'getFileObject'), scandir($this->options['upload_dir']))));
}
/**
2023-07-10 20:45:15 +00:00
* Create thumbs of a file uploaded.
*
* @param string $file_name Filename
* @param array{upload_dir:string} $options is array('max_width', 'max_height')
* @return bool
*/
2012-06-09 11:54:05 +00:00
protected function createScaledImage($file_name, $options)
{
2023-07-10 20:45:15 +00:00
global $maxwidthmini, $maxheightmini, $maxwidthsmall, $maxheightsmall;
$file_path = $this->options['upload_dir'].$file_name;
$new_file_path = $options['upload_dir'].$file_name;
2021-02-23 21:03:23 +00:00
if (dol_mkdir($options['upload_dir']) >= 0) {
list($img_width, $img_height) = @getimagesize($file_path);
if (!$img_width || !$img_height) {
return false;
}
2023-07-10 20:45:15 +00:00
$res = vignette($file_path, $maxwidthmini, $maxheightmini, '_mini'); // We don't use ->addThumbs here because there is no object
if (preg_match('/error/i', $res)) {
return false;
}
2023-07-10 20:45:15 +00:00
$res = vignette($file_path, $maxwidthsmall, $maxheightsmall, '_small'); // We don't use ->addThumbs here because there is no object
2021-02-23 21:03:23 +00:00
if (preg_match('/error/i', $res)) {
return false;
}
2023-07-10 20:45:15 +00:00
return true;
2020-05-21 13:05:19 +00:00
} else {
return false;
}
}
/**
2023-12-21 10:43:30 +00:00
* Make validation on an uploaded file
*
* @param string $uploaded_file Upload file
2022-05-20 15:34:37 +00:00
* @param object $file File
* @param string $error Error
* @param string $index Index
2016-04-09 13:07:55 +00:00
* @return boolean True if OK, False if KO
*/
protected function validate($uploaded_file, $file, $error, $index)
{
if ($error) {
$file->error = $error;
return false;
}
2012-07-02 17:30:37 +00:00
if (!$file->name) {
$file->error = 'missingFileName';
return false;
}
if (!preg_match($this->options['accept_file_types'], $file->name)) {
$file->error = 'acceptFileTypes';
return false;
}
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
2023-12-21 10:43:30 +00:00
$file_size = dol_filesize($uploaded_file);
} else {
$file_size = $_SERVER['CONTENT_LENGTH'];
}
if ($this->options['max_file_size'] && (
2023-12-04 11:04:36 +00:00
$file_size > $this->options['max_file_size'] ||
$file->size > $this->options['max_file_size']
)
) {
$file->error = 'maxFileSize';
return false;
}
if ($this->options['min_file_size'] &&
$file_size < $this->options['min_file_size']) {
$file->error = 'minFileSize';
return false;
}
if (is_numeric($this->options['max_number_of_files']) && (
2023-12-04 11:04:36 +00:00
count($this->getFileObjects()) >= $this->options['max_number_of_files']
)
) {
$file->error = 'maxNumberOfFiles';
return false;
}
list($img_width, $img_height) = @getimagesize($uploaded_file);
if (is_numeric($img_width)) {
if ($this->options['max_width'] && $img_width > $this->options['max_width'] ||
$this->options['max_height'] && $img_height > $this->options['max_height']) {
$file->error = 'maxResolution';
return false;
}
if ($this->options['min_width'] && $img_width < $this->options['min_width'] ||
$this->options['min_height'] && $img_height < $this->options['min_height']) {
$file->error = 'minResolution';
return false;
}
}
return true;
}
/**
* Enter description here ...
*
2012-06-09 11:54:05 +00:00
* @param int $matches ???
* @return string ???
*/
2012-06-09 11:54:05 +00:00
protected function upcountNameCallback($matches)
2012-07-02 17:30:37 +00:00
{
$index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
$ext = isset($matches[2]) ? $matches[2] : '';
return ' ('.$index.')'.$ext;
}
/**
* Enter description here ...
*
2012-06-09 11:54:05 +00:00
* @param string $name ???
* @return string ???
*/
2012-06-09 11:54:05 +00:00
protected function upcountName($name)
2012-07-02 17:30:37 +00:00
{
return preg_replace_callback('/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/', array($this, 'upcountNameCallback'), $name, 1);
}
/**
* trimFileName
*
* @param string $name Filename
* @param string $type ???
* @param string $index ???
* @return string
*/
2012-06-09 11:54:05 +00:00
protected function trimFileName($name, $type, $index)
2012-07-02 17:30:37 +00:00
{
// Remove path information and dots around the filename, to prevent uploading
// into different directories or replacing hidden system files.
2023-12-21 10:43:30 +00:00
$file_name = basename(dol_sanitizeFileName($name));
$file_name = preg_replace('/ {2,}/', ' ', $file_name); // replaces multiple spaces into one space like the upload flow via input field
2012-07-02 17:30:37 +00:00
// Add missing file extension for known image types:
2023-03-01 20:37:18 +00:00
$matches = array();
if (strpos($file_name, '.') === false && preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
2012-07-02 17:30:37 +00:00
$file_name .= '.'.$matches[1];
}
2021-02-23 21:03:23 +00:00
if ($this->options['discard_aborted_uploads']) {
2023-12-21 10:43:30 +00:00
while (dol_is_file($this->options['upload_dir'].$file_name)) {
2012-07-02 17:30:37 +00:00
$file_name = $this->upcountName($file_name);
}
}
return $file_name;
}
/**
2023-07-10 20:45:15 +00:00
* handleFileUpload.
* Validate data, move the uploaded file then create the thumbs if this is an image.
*
* @param string $uploaded_file Upload file
* @param string $name Name
* @param int $size Size
* @param string $type Type
* @param string $error Error
* @param string $index Index
2023-03-19 09:19:35 +00:00
* @return stdClass|null
* @see dol_add_file_process()
*/
2012-06-09 11:54:05 +00:00
protected function handleFileUpload($uploaded_file, $name, $size, $type, $error, $index)
{
$file = new stdClass();
2012-06-09 11:54:05 +00:00
$file->name = $this->trimFileName($name, $type, $index);
$file->mime = dol_mimetype($file->name, '', 2);
$file->size = intval($size);
$file->type = $type;
2023-12-21 10:43:30 +00:00
// Sanitize to avoid stream execution when calling file_size(). Not that this is a second security because
// most streams are already disabled by stream_wrapper_unregister() in filefunc.inc.php
$uploaded_file = preg_replace('/\s*(http|ftp|sftp|)s?:/i', '', $uploaded_file);
2023-12-21 10:48:00 +00:00
$uploaded_file = realpath($uploaded_file); // A hack to be sure the file point to an existing file on disk (and is not a SSRF attack)
2023-12-21 10:43:30 +00:00
$validate = $this->validate($uploaded_file, $file, $error, $index);
if ($validate) {
if (dol_mkdir($this->options['upload_dir']) >= 0) {
// Add object reference as file name prefix if const MAIN_DISABLE_SUGGEST_REF_AS_PREFIX is not enabled
$fileNameWithoutExt = preg_replace('/\.[^\.]+$/', '', $file->name);
$savingDocMask = $this->options['saving_doc_mask'];
if ($savingDocMask && strpos($savingDocMask, $fileNameWithoutExt) !== 0) {
$fileNameWithPrefix = preg_replace('/__file__/', $file->name, $savingDocMask);
$file->name = $fileNameWithPrefix;
}
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
// trimFileName() checked the name is not already used, but it did it before the reference of the
// object was added as a prefix above, so it compared a name that is not the one we store. We must
// check it again on the final name, otherwise uploading twice the same file silently overwrites
// the first one, because dol_move_uploaded_file() is called below with $allowoverwrite = 1.
// The .noexe suffix is appended by dol_move_uploaded_file() on an executable file, so we must also
// look for the suffixed name, otherwise such a file is never seen as already existing and it is
// overwritten at each upload.
if ($this->options['discard_aborted_uploads']) {
$tmppath = dol_sanitizePathName($this->options['upload_dir']);
while (dol_is_file($tmppath.dol_sanitizeFileName($file->name)) || dol_is_file($tmppath.dol_sanitizeFileName($file->name).'.noexe')) {
$file->name = $this->upcountName($file->name);
}
}
2023-12-21 10:43:30 +00:00
$file_path = dol_sanitizePathName($this->options['upload_dir']).dol_sanitizeFileName($file->name);
$append_file = !$this->options['discard_aborted_uploads'] && dol_is_file($file_path) && $file->size > dol_filesize($file_path);
clearstatcache();
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
// multipart/formdata uploads (POST method uploads)
if ($append_file) {
file_put_contents($file_path, fopen($uploaded_file, 'r'), FILE_APPEND);
} else {
// TODO Replace this with a call of dol_add_file_process(... $mode=1)
$result = dol_move_uploaded_file($uploaded_file, $file_path, 1, 0, 0, 0, 'userfile');
NEW Drag and drop a file on the 23 remaining cards (#39473) * FIX Drag and drop of a file reports a wrong error, or none at all The error handler of dragAndDropFileUpload() had three defects that all end on the user believing the file was attached when it was not. - The first assignment of the message was dead code, there was no return after the test on the http code 403, so a refusal was reported as a generic error. - The key ErrorUploadPermissionDenied it used exists in no language file, so the raw key was shown. - JSON.parse() was called with no try/catch on an answer that is not always a json, a fatal error of the endpoint or a request over post_max_size for example. The exception left the user on a page with no message at all. - An empty list of files was treated as a success, while it means the endpoint stored nothing. The value of PHP_SELF is also escaped before it is written into the 6 generated javascript strings. It holds the path info of the request on a server that accepts it, so it is a user input. dol_escape_js() is called with the mode that escapes a double quote by a double quote, the strings being delimited by double quotes, and the sequence '</' is escaped too because the function does not do it and the path info could otherwise close the script tag and open one of its own. Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a batch failed and some did not. * FIX getMultidirOutput returns a directory the Documents tab does not read Four defects of the same function, all ending on a file stored where the user will never see it, or written outside the documents directory. - The ref of the project of a task was only passed through dol_sanitizePathName(), which keeps a slash, a colon and the accented chars, while projet/tasks/document.php sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even created an extra level of directory. Measured on real databases: 304 projects over 330 hold a slash on one of them, carrying 701 tasks over 824. - The same case calls $object->fetchProject() with no guard, while the signature of the function accepts an object that is not a CommonObject, and even a null when a module is given. Such a caller gets a fatal error where it expects the error string. No core caller is in that case today, an external module or a hook can be. - The entity of the object may have no declared directory, an object shared by another entity for example. The undefined index returned a relative path, so the caller read or wrote under the web root. The current entity is used instead, and the fallback is logged because the directory is then not the one of the entity of the object, which matters for a caller that deletes files. - When the current entity has no declared directory either, the fallback returned the sub directory alone, again a relative path. The same error than for a module that declares no directory at all is now returned. Adds the sub directory of a partnership and of a stock transfer, which their own document tabs already read. * FIX getElementProperties answers wrong properties for 9 elements A customer payment, a supplier payment, a various payment, a stock transfer and the 4 objects of the hrm module had no properties at all, or wrong ones, so any caller that resolves a class, a table or a document directory from an element failed on them. - payment, payment_supplier and payment_various had no branch. The branch of a customer payment tests $elementType and not $element, because the rule on the elements named myobject_mysubobject rewrites $element to 'payment' for 'payment_salary' too, which is stored somewhere else. - job, position, skill and evaluation answered a wrong table (hrm_job_user for a position, and so on) and no sub directory, while their document tabs read one named after the element. - stocktransfer answered an empty classname, because it is not the ucfirst() of the element, so a caller doing new $classname($db) ended on a fatal error. - The sub directory was concatenated even when the module is disabled and the directory is empty, which answered a path at the root of the file system. - A contact and a conference are stored into a sub directory their tab reads. isModEnabled('invoice') is tested for a customer payment: there is no module named 'compta', so testing it was always false, while $conf->compta->payment is set unconditionally by Conf::setValues() and could not be used as a proxy. * FIX Access refused to everyone on 11 objects of the core restrictedArea() and checkUserAccessToObject() refuse the access to objects that no permission and no rule can match, whatever the user, an administrator included. - The hrm module declares no permission at its first level, only 'all', and the stocktransfer module only 'stocktransfer'. A check on the module itself therefore tests a permission that does not exist. The mapping is the same one as into User::hasRight(). - The module of an event organization declares no permission at all, its whole permission block being commented out on purpose, and its cards check the parent project instead. The feature is mapped onto that project, with the two guards the card has: an external user is refused, and so is a conference with no parent project, whose id of 0 would otherwise grant an access with no check on the record at all. - The default rule of checkUserAccessToObject() builds its sql on the columns entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user and llx_hrm_skill have neither. The sql failed, so the access was refused to every user this rule applies to. These tables are now checked on their entity only, which is what the $check rule already does for the same class of tables, and the 3 tables of hrm can be checked on nothing at all. The rule is selected on the table and not on the element, because $object is an id and not an object for most of the callers, the cards of an asset and of a workstation included, which are broken today for any user without the permission to see all third parties. - An external user is refused explicitly on those tables: none of these objects is linked to a third party, so the default rule refused him through a link that does not exist, and the rules that replace it do not look at the third party of the user at all. Measured on a vanilla instance with 5 profiles, an administrator, an internal user with every right, one without the permission to see all third parties, one that is not a sales representative of the third party of the object, and an external user: the 11 objects go from refused to granted for the internal users and stay refused for the external one, and the 26 other elements answer exactly the same for the 5 profiles. * FIX A file dropped on a card is lost, or reported as refused when it was stored FileUpload stores the file into a directory that the "Attached files" tab of the object never reads, so the user attaches a file that no screen will ever show, and nothing is indexed in database to find it back. Measured on real databases: 216487 thirdparties over 216887 and 157852 products over 280319 are in that case on the cards that already enable the drag and drop. - The directory of the object is now forged with get_exdir(), the way the tabs do: it always uses the id for a thirdparty, whose ref is a company name and is not unique, and it falls back on the id when the ref is empty. The sub directory of the module is read with getMultidirOutput(), which knows the elements that store their documents into one. That function does not return an empty string when it fails but a string starting with 'error-', so only an absolute path is accepted: writing into that string would create the files under the web root. - fetchObjectByElement() returns an object even when fetch() returned 0. The object was then not loaded, and the file was stored at the root of the directory of the module, out of any object. The constructor now throws, and the endpoint answers the error with the same json contract than a successful call so that the caller can show it, instead of a fatal error and an http 500. - An attachment of the same name was silently overwritten, dol_move_uploaded_file() being called with $allowoverwrite = 1 while the name was checked before the ref of the object was added as a prefix. The check is done again on the final name, and on the .noexe suffixed name too, which that function appends to an executable file. - An executable file was renamed with that .noexe suffix and then reported as an error, while it was correctly stored. - The endpoint called restrictedArea() with an empty feature when the element is unknown, and the loop of that function then takes no branch at all and grants the access with no check. It refuses before, with the same http code and the same message than a refusal, so that a user cannot tell an object that exists but is not allowed from an object that does not exist. The refusal of an external user on an object of another third party answered a message of its own, which allowed the same enumeration. Comes with the tests of the path resolution, of the fallback of get_exdir(), of the rejection of the error string of getMultidirOutput(), and of the file name deduplication. * FIX Remove the drop area from 13 cards where dropping a file is harmful Two distinct groups, both of them removing a drop area that only produces a result the user does not want. 7 pages have no "Attached files" tab at all, so a file dropped on them can be reached by no screen: a fiscal year and its info page, an accounting model, a webhook trigger history, an intracomm report, a bookcal calendar and its booking list. Measured: 4 of them accepted the upload and wrote an orphan file, the accounting model wrote it at the root of the directory of the module, out of any object, and the 2 bookcal ones answered an http 500. 6 cards print their tabs inside their edit form, so the drop area covered that form: a product, an expense report, a VAT payment, a social contribution, a loan and a salary. Dropping a file there reloads the page, which discards what the user is typing. A product and an expense report have a second call for the read only view, so the drop area is only removed from the call of the edit branch; the 4 others share a single call between both views, so the parameter is now conditional. * NEW Drag and drop a file on the 23 remaining cards Every card of the core that owns an "Attached files" tab can now receive a file by drag and drop, which was the case of 32 of them only. The 23 added here are an asset, an event, a various payment, a customer payment, a contact, a donation, a conference or booth, a shipment, a supplier payment, a leave request, an evaluation, a job, a position, a skill, a knowledge record, a manufacturing order, a partnership, a lot, a stock transfer, a task, a reception, a resource and a workstation. The tab bar of a leave request and of a resource is printed inside their edit form, so the drop area is not enabled there: dropping a file reloads the page, which would discard what the user is typing. The card of a supplier payment prints its tab bar even when the object was not loaded, so the drop area is only enabled when it is. * FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd] The previous revision of this PR fell back on the directory of the current entity when the entity of the object had none, with a LOG_WARNING. On a multicompany install that made a caller read, write and above all delete files in the directory of another entity. Refuse instead: the function already answers 'error-diroutput-not-defined-for-this-object' when the module declares no directory at all, so the caller has one behaviour to handle, not two. The entity is cast to int, which is what the array index needs and what silences the four PhanTypeMismatchDimFetchNullable this function reported. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 00:46:21 +00:00
// A return of 2 means the file was stored with a .noexe suffix appended on its name.
// We must follow that renaming, otherwise the size check below is done on a file that
// does not exist, and we report an error on a file that was correctly stored.
if ($result == 2) {
$file->name .= '.noexe';
$file_path .= '.noexe';
}
}
2012-07-02 17:30:37 +00:00
} else {
// Non-multipart uploads (PUT method support)
file_put_contents($file_path, fopen('php://input', 'r'), $append_file ? FILE_APPEND : 0);
2012-07-02 17:30:37 +00:00
}
dolChmod($file_path);
2023-12-21 10:43:30 +00:00
$file_size = dol_filesize($file_path);
if ($file_size === $file->size) {
2023-12-21 10:43:30 +00:00
$file->url = $this->options['upload_url'].urlencode($file->name);
foreach ($this->options['image_versions'] as $version => $options) {
2023-07-10 20:45:15 +00:00
if ($this->createScaledImage($file->name, $options)) { // Creation of thumbs mini and small is ok
$tmp = explode('.', $file->name);
2023-07-10 20:45:15 +00:00
// We save the path of mini file into file->... (seems not used)
$keyforfile = $version.'_url';
2023-12-21 10:43:30 +00:00
$file->$keyforfile = $options['upload_url'].urlencode($tmp[0].'_mini.'.$tmp[1]);
}
2012-07-02 17:30:37 +00:00
}
} elseif ($this->options['discard_aborted_uploads']) {
unlink($file_path);
$file->error = 'abort';
2012-07-02 17:30:37 +00:00
}
$file->size = $file_size;
$this->setFileDeleteUrl($file);
} else {
$file->error = 'failedtocreatedestdir';
}
} else {
// should not happen
}
return $file;
}
/**
* Output data
*
* @return void
*/
2024-02-07 21:18:45 +00:00
/*public function get()
{
2024-02-07 21:18:45 +00:00
$file_name = isset($_REQUEST['file']) ? basename(stripslashes($_REQUEST['file'])) : null;
2021-02-23 21:03:23 +00:00
if ($file_name) {
2012-06-09 11:54:05 +00:00
$info = $this->getFileObject($file_name);
2020-05-21 13:05:19 +00:00
} else {
2012-06-09 11:54:05 +00:00
$info = $this->getFileObjects();
}
2024-02-07 21:18:45 +00:00
header('Content-type: application/json');
echo json_encode($info);
}
2024-02-07 21:18:45 +00:00
*/
/**
* Output data
*
* @return int 0 if OK, nb of error if errors
*/
public function post()
{
$error = 0;
2024-02-07 21:18:45 +00:00
$upload = isset($_FILES[$this->options['param_name']]) ? $_FILES[$this->options['param_name']] : null;
Fix #28071 - New branch to fix bad merge (#28083) * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Qual: Introduce getDataToShowPhoto to prepare generic code * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Fix missing trans * Fix langs * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Remove useless files in web templates * Clean code * Fix duplicate translation key * Fix duplicate translation key * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Fix duplicate key * Fix $object * Debug v19 * WIP SMSing * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * WIP EMAILINGS_SUPPORT_ALSO_SMS * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * WIP SMSing * Debug the "validate" feature * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Clean code * Move rights->x->y into hasRight('x', 'y') * Move rights->x->y into hasRight('x', 'y') * Move rights->x->y into hasRight('x', 'y') * Move rights->x->y into hasRight('x', 'y') * Move rights->x->y into hasRight('x', 'y') * Move rights->x->y into hasRight('x', 'y') * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Enhance rector to fix empty($user->rights->modulex->perm1) * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Fix template to use v19 dev rules * Fix use v19 dev rules * Fix phpunit * Debug v19 * Clean code * Use rector to convert user->rights into user->hasRight * Clean code * Use rector to convert user->rights into user->hasRight * Use rector to convert user->rights into user->hasRight * Clean code * Fix phpcs * add editorconfig for sql files (#27999) Co-authored-by: Laurent Destailleur <eldy@destailleur.fr> * add model_pdf field in llx_ticket-ticket.sql (#27996) * add model_pdf field in llx_ticket-ticket.sql * Update 19.0.0-20.0.0.sql * Update 19.0.0-20.0.0.sql * Improve wording in README (#27994) * fix phpstan (#27989) * fix phpstan * Update UserRightsToFunction.php --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr> * Qual: Fix spelling/working in datapolicy translations (#28006) # Qual: Fix spelling/wording in datapolicy translations Fixed some spelling and wording in datapolicy translations. * qual: phpstan for htdocs/ticket/class/ticketstats.class.php (#27986) htdocs/ticket/class/ticketstats.class.php 98 Parameter #1 $year (string) of method TicketStats::getNbByMonth() should be compatible with parameter $year (int) of method Stats::getNbByMonth() * Merge branch '19.0' of git@github.com:Dolibarr/dolibarr.git into develop * Fix user with readonly perm on email template must be able to read. * Fix doc * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Better message * Add missing fields in merge of thirdparty * Debug v19 selection of ticket printer per terminal * Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop * Use constant * NEW: Adding a recipient on emails sent, change status to sent partialy. * fix travis (#28052) * fix travis * Update partnership.class.php * fix php doc (#28047) * fix undefined array key (#28048) * Add region and departament for Cuba (#28046) * Update llx_10_c_regions.sql Add Cuba Regions (id_country=77) * Update llx_20_c_departements.sql Add Provinces Cuba (id country=77) * Find the typo (#28050) * Find the typo * clean code * add last_main_doc field to product (#28045) * add las_main_doc field to product * add field fetch * NEW Add Categorie filter for ActionComm (#28041) * New Add Categorie filter for ActionComm New Add Categorie filter for ActionComm * Fix space errors Fix space errors * Fix space errors 2 Fix space errors 2 * Update cunits.class.php (#28056) FIX: error SQL when creating a Cunit * Update codespell-lines-ignore.txt to avoid PR merge conflict --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr> Co-authored-by: Frédéric FRANCE <frederic34@users.noreply.github.com> Co-authored-by: thibdrev <thibault.drevet@gmail.com> Co-authored-by: sonikf <93765174+sonikf@users.noreply.github.com> Co-authored-by: Ikarus <44511582+LeKarSol@users.noreply.github.com> Co-authored-by: Anthony Damhet <73399671+EchoLoGeek@users.noreply.github.com> Co-authored-by: Quentin-Seekness <72733832+Quentin-Seekness@users.noreply.github.com>
2024-02-09 14:58:49 +00:00
2024-02-09 15:00:35 +00:00
$info = array();
2021-02-23 21:03:23 +00:00
if ($upload && is_array($upload['tmp_name'])) {
2012-07-02 17:30:37 +00:00
// param_name is an array identifier like "files[]",
// $_FILES is a multi-dimensional array:
foreach ($upload['tmp_name'] as $index => $value) {
$tmpres = $this->handleFileUpload(
$upload['tmp_name'][$index],
isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : $upload['name'][$index],
isset($_SERVER['HTTP_X_FILE_SIZE']) ? $_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'][$index],
isset($_SERVER['HTTP_X_FILE_TYPE']) ? $_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'][$index],
$upload['error'][$index],
(string) $index
);
if (!empty($tmpres->error)) {
$error++;
}
$info[] = $tmpres;
}
} elseif ($upload || isset($_SERVER['HTTP_X_FILE_NAME'])) {
2012-07-02 17:30:37 +00:00
// param_name is a single object identifier like "file",
// $_FILES is a one-dimensional array:
$tmpres = $this->handleFileUpload(
isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : (isset($upload['name']) ? $upload['name'] : null),
isset($_SERVER['HTTP_X_FILE_SIZE']) ? $_SERVER['HTTP_X_FILE_SIZE'] : (isset($upload['size']) ? $upload['size'] : null),
isset($_SERVER['HTTP_X_FILE_TYPE']) ? $_SERVER['HTTP_X_FILE_TYPE'] : (isset($upload['type']) ? $upload['type'] : null),
isset($upload['error']) ? $upload['error'] : null,
fix ignored phpstan (most of expects string, int given) (#30649) * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * fix ignored phpstan * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * more ignore * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan * fix phpstan
2024-09-05 14:05:37 +00:00
'0'
);
if (!empty($tmpres->error)) {
$error++;
}
$info[] = $tmpres;
}
header('Vary: Accept');
$json = json_encode($info);
/* disabled. Param redirect seems not used
$redirect = isset($_REQUEST['redirect']) ? stripslashes($_REQUEST['redirect']) : null;
2012-07-02 17:30:37 +00:00
if ($redirect) {
2023-12-21 10:43:30 +00:00
header('Location: '.sprintf($redirect, urlencode($json)));
2012-07-02 17:30:37 +00:00
return;
}
*/
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) {
header('Content-type: application/json');
} else {
header('Content-type: text/plain');
}
echo $json;
return $error;
}
/**
* Delete uploaded file
*
2024-02-02 18:50:43 +00:00
* @param string $file File
* @return int
*/
2024-02-07 21:18:45 +00:00
/*
2024-02-02 18:50:43 +00:00
public function delete($file)
{
2024-02-02 18:50:43 +00:00
$file_name = $file ? basename($file) : null;
2023-12-21 10:43:30 +00:00
$file_path = $this->options['upload_dir'].dol_sanitizeFileName($file_name);
$success = dol_is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
2021-02-23 21:03:23 +00:00
if ($success) {
foreach ($this->options['image_versions'] as $version => $options) {
$file = $options['upload_dir'].$file_name;
2023-12-21 10:43:30 +00:00
if (dol_is_file($file)) {
unlink($file);
}
}
}
2023-05-01 16:49:47 +00:00
// Return result in json format
header('Content-type: application/json');
echo json_encode($success);
2023-05-01 16:49:47 +00:00
return 0;
}
2024-02-07 21:18:45 +00:00
*/
}