From 787db88750d6268fe9e1416f962d26159f1fe74a Mon Sep 17 00:00:00 2001 From: 12b Date: Thu, 25 Apr 2024 12:01:52 +0200 Subject: [PATCH] fix(bazar): fix issue 884 with grace and plentyness --- docs/fr/bazar.md | 3 ++- includes/YesWiki.php | 27 +++++++------------ includes/constants.php | 3 +++ tools/attach/lang/attach_fr.inc.php | 2 +- tools/attach/libs/attach.lib.php | 24 +---------------- tools/bazar/config.yaml | 2 -- tools/bazar/fields/FileField.php | 26 ++++++++++++++---- tools/bazar/fields/ImageField.php | 18 +++++-------- tools/bazar/lang/bazar_fr.inc.php | 1 + tools/bazar/lang/bazarjs_fr.inc.php | 4 ++- .../presentation/javascripts/file-field.js | 20 ++++++++++++++ .../form-edit-template/fields/file.js | 2 +- .../form-edit-template/fields/image.js | 6 +++-- .../presentation/javascripts/image-field.js | 14 +++++----- tools/bazar/templates/inputs/file.twig | 3 ++- tools/bazar/templates/inputs/image.twig | 3 ++- 16 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 tools/bazar/presentation/javascripts/file-field.js diff --git a/docs/fr/bazar.md b/docs/fr/bazar.md index 5a33428e2..041b0ca64 100644 --- a/docs/fr/bazar.md +++ b/docs/fr/bazar.md @@ -163,6 +163,7 @@ Un champ de type image permet d'importer un fichier image qui sera ensuite visua - **Hauteur re-dimension** : YesWiki peut harmoniser la taille des images importées pour ce formulaire. Ce paramètre permet de préciser la hauteur de cette image redimensionnée. - **Largeur re-dimension** : YesWiki peut harmoniser la taille des images importées pour ce formulaire. Ce paramètre permet de préciser la largeur de cette image redimensionnée. - **Alignement** : C'est là que l'on paramètre le comportement d'affichage de l'image. Son fonctionnement est similaire à ce qui se passe dans l'édition de pages (quand on joint une image avec le bouton Fichier). +- **Taille max** Ce paramètre permet de limiter la taille du fichier. Il s'agit d'un nombre d'octets mais qui peut être écrit avec des préfixe d'unités : k pour kilo, m pour mega (par ex.: 2097152, 2048k, 2m). Si la valeur donnée dépasse la valeur configurée sur le serveur, la valeur du serveur sera prise. ### URL Permet de saisir un lien web qui sera cliquable dans la fiche @@ -170,7 +171,7 @@ Permet de saisir un lien web qui sera cliquable dans la fiche ### Upload de fichier Ce type de champ permet d'uploader un fichier (par exemple au format PDF). Ce fichier est ensuite téléchargeable par les personnes qui visualisent la fiche. #### Paramètres spécifiques au type de champs « upload de fichier » : - - **Taille max** Ce paramètre permet de limiter la taille du fichier. Il s'agit d'un nombre d'octets (par ex. 2000000 équivalent à 2 Mo). + - **Taille max** Ce paramètre permet de limiter la taille du fichier. Il s'agit d'un nombre d'octets mais qui peut être écrit avec des préfixe d'unités : k pour kilo, m pour mega (par ex.: 2097152, 2048k, 2m). Si la valeur donnée dépasse la valeur configurée sur le serveur, la valeur du serveur sera prise. ### Email Ce type de champs permet de saisir une adresse électronique. YesWiki effectue automatiquement des contrôles sur la syntaxe de l'adresse et propose également de paramétrer des comportements spécifiquement liés à ce type de données. diff --git a/includes/YesWiki.php b/includes/YesWiki.php index 0a8f7895d..131c590c3 100755 --- a/includes/YesWiki.php +++ b/includes/YesWiki.php @@ -1386,31 +1386,24 @@ public function parse_size($size) $size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size. if ($unit) { // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by. - return round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); + return intval(round($size * pow(1024, stripos('bkmgtpezy', $unit[0])))); } else { - return round($size); + return intval(round($size)); } } - // Drupal code under GPL2 cf. http://stackoverflow.com/questions/13076480/php-get-actual-maximum-upload-size#25370978 - // Returns a file size limit in bytes based on the PHP upload_max_filesize - // and post_max_size + // Returns a file size limit in bytes based on the PHP upload_max_filesize, + // post_max_size and wakka config max_file_size public function file_upload_max_size() { - static $max_size = -1; + $conf_max_file_size = $this->GetConfigValue("max_file_size") ? $this->parse_size($this->GetConfigValue("max_file_size")) : 0; - if ($max_size < 0) { - // Start with post_max_size. - $max_size = $this->parse_size(ini_get('post_max_size')); + $post_max_size = $this->parse_size(ini_get('post_max_size')); - // If upload_max_size is less, then reduce. Except if upload_max_size is - // zero, which indicates no limit. - $upload_max = $this->parse_size(ini_get('upload_max_filesize')); - if ($upload_max > 0 && $upload_max < $max_size) { - $max_size = $upload_max; - } - } - return $max_size; + $upload_max = $this->parse_size(ini_get('upload_max_filesize')); + + // return the min size limit, excluding 0 values that mean no limit + return min(array_filter([$conf_max_file_size, $post_max_size, $upload_max]) ?? DEFAULT_MAX_UPLOAD_SIZE); } /** diff --git a/includes/constants.php b/includes/constants.php index 4e53ded6e..ab4a6c54e 100755 --- a/includes/constants.php +++ b/includes/constants.php @@ -55,3 +55,6 @@ // for package updates define('SEMVER', '(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?'); + +// default max size for upload +define('DEFAULT_MAX_UPLOAD_SIZE', 2000*1024); \ No newline at end of file diff --git a/tools/attach/lang/attach_fr.inc.php b/tools/attach/lang/attach_fr.inc.php index 9faeddf0d..24211598c 100755 --- a/tools/attach/lang/attach_fr.inc.php +++ b/tools/attach/lang/attach_fr.inc.php @@ -118,6 +118,6 @@ // edit config action 'EDIT_CONFIG_HINT_ATTACH-VIDEO-CONFIG[DEFAULT_VIDEO_SERVICE]' => 'Service de vidéo par défaut (peertube, youtube ou vimeo)', 'EDIT_CONFIG_HINT_ATTACH-VIDEO-CONFIG[DEFAULT_PEERTUBE_INSTANCE]' => 'Adresse du serveur peertube par défaut', - 'EDIT_CONFIG_HINT_MAX_FILE_SIZE' => 'Taille maximum des fichiers téléversés - octets (ex: Taille maximum des fichiers téléversés - octets (ex: 2097152 pour 2Mo)', + 'EDIT_CONFIG_HINT_MAX_FILE_SIZE' => 'Taille maximum des fichiers téléversés - octets (ex: Taille maximum des fichiers téléversés (ex: 2097152, 2048k, 2m)', 'EDIT_CONFIG_GROUP_ATTACH' => 'Insertion de médias (images, vidéos)', ]; diff --git a/tools/attach/libs/attach.lib.php b/tools/attach/libs/attach.lib.php index ddb0817f7..422e40304 100644 --- a/tools/attach/libs/attach.lib.php +++ b/tools/attach/libs/attach.lib.php @@ -49,7 +49,7 @@ public function __construct(&$wiki) } if (empty($this->attachConfig['max_file_size'])) { - $this->attachConfig['max_file_size'] = $this->wiki->GetConfigValue("max_file_size") ? $this->wiki->GetConfigValue("max_file_size") : $this->file_upload_max_size(); + $this->attachConfig['max_file_size'] = $this->params->get("max-upload-size"); } $safemode = $this->wiki->GetConfigValue("no_safe_mode"); @@ -67,28 +67,6 @@ public function __construct(&$wiki) /****************************************************************************** * FONCTIONS UTILES *******************************************************************************/ - // Returns a file size limit in bytes based on the PHP upload_max_filesize - // and post_max_size - public function file_upload_max_size() - { - static $max_size = -1; - - if ($max_size < 0) { - // Start with post_max_size. - $post_max_size = $this->parse_size(ini_get('post_max_size')); - if ($post_max_size > 0) { - $max_size = $post_max_size; - } - - // If upload_max_size is less, then reduce. Except if upload_max_size is - // zero, which indicates no limit. - $upload_max = $this->parse_size(ini_get('upload_max_filesize')); - if ($upload_max > 0 && $upload_max < $max_size) { - $max_size = $upload_max; - } - } - return $max_size; - } /** * transforme des valeurs en mega / kilo / giga octets en entier * diff --git a/tools/bazar/config.yaml b/tools/bazar/config.yaml index c6de0ea45..2d3a01b5a 100644 --- a/tools/bazar/config.yaml +++ b/tools/bazar/config.yaml @@ -41,8 +41,6 @@ parameters: # Valeur par defaut d'etat de la fiche annonce apres saisie # 0 pour 'en attente de validation', 1 pour 'directement validee' BAZ_ETAT_VALIDATION: '1' - # Valeur maximale en octets pour la taille d'un fichier joint a telecharger - BAZ_TAILLE_MAX_FICHIER: '%max-upload-size%' # Type d'affichage des dates dans la liste # Mettre jma pour jour mois annee, ou jm, ou jmah BAZ_TYPE_AFFICHAGE_LISTE: 'jma' diff --git a/tools/bazar/fields/FileField.php b/tools/bazar/fields/FileField.php index 0380c608c..6454d7478 100644 --- a/tools/bazar/fields/FileField.php +++ b/tools/bazar/fields/FileField.php @@ -8,6 +8,7 @@ use YesWiki\Bazar\Service\EntryManager; use YesWiki\Bazar\Service\Guard; use YesWiki\Core\Service\EventDispatcher; +use YesWiki\Core\Service\AssetsManager; use YesWiki\Security\Controller\SecurityController; /** @@ -16,10 +17,12 @@ class FileField extends BazarField { protected $readLabel; + protected const FIELD_MAX_SIZE = 14; protected const FIELD_READ_LABEL = 6; protected const FIELD_AUTHORIZED_EXTS_LABEL = 7; protected $attach; + protected $maxSize; protected $authorizedExts; public function __construct(array $values, ContainerInterface $services) @@ -37,14 +40,24 @@ public function __construct(array $values, ContainerInterface $services) $this->authorizedExts = array_filter($exts, function ($ext) { return preg_match('/^\.[a-z0-9]{1,4}+$/', $ext); }); + $maxFieldSize = $values[self::FIELD_MAX_SIZE] ? + $this->getWiki()->parse_size($values[self::FIELD_MAX_SIZE]) : + 0; + + // take the min size limit, excluding 0 values that mean no limit + $this->maxSize = min(array_filter( + [ + $maxFieldSize, + $this->getService(ParameterBagInterface::class)->get('max-upload-size')] + )); } protected function renderInput($entry) { + $wiki = $this->getWiki(); $value = $this->getValue($entry); - $deletedFile = false; - // ToDo: Faire comme dans ImageField pour vérifier que la taille du fichier n'est pas trop grosse + $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/file-field.js'); if (!empty($value)) { if (!empty($entry) && isset($_GET['delete_file']) && $_GET['delete_file'] === $value) { @@ -63,10 +76,11 @@ protected function renderInput($entry) } } } - return ($alertMessage ?? '') . $this->render('@bazar/inputs/file.twig', ( empty($value) || !file_exists($this->getBasePath(). $value) || $deletedFile - ? [] + ? [ + 'maxSize' => $this->maxSize + ] : [ 'value' => $value, 'shortFileName' => $this->getShortFileName($value), @@ -79,7 +93,6 @@ protected function renderInput($entry) public function formatValuesBeforeSave($entry) { - // ToDo: Vérifier que le fichier à bien été téléchargé et que sa taille n'est pas trop grosse $value = $this->getValue($entry); $params = $this->getService(ParameterBagInterface::class); @@ -95,6 +108,9 @@ public function formatValuesBeforeSave($entry) $extension = preg_replace("/_$/", "", $extension); if ($extension != '' && in_array($extension, array_keys($params->get('authorized-extensions')))) { if (!file_exists($filePath)) { + if ($_FILES[$this->propertyName]['size'] > $this->maxSize) { + throw new \Exception(_t('BAZ_FILEFIELD_TOO_LARGE_FILE', ['fileMaxSize' => $this->maxSize])); + } move_uploaded_file($_FILES[$this->propertyName]['tmp_name'], $filePath); chmod($filePath, 0755); } else { diff --git a/tools/bazar/fields/ImageField.php b/tools/bazar/fields/ImageField.php index 825248807..19642b67e 100644 --- a/tools/bazar/fields/ImageField.php +++ b/tools/bazar/fields/ImageField.php @@ -4,7 +4,7 @@ use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; -use YesWiki\Core\Service\AclService; +use YesWiki\Core\Service\AssetsManager; use YesWiki\Security\Controller\SecurityController; /** @@ -43,16 +43,9 @@ protected function renderInput($entry) { $wiki = $this->getWiki(); $value = $this->getValue($entry); - $maxSize = $wiki->config['BAZ_TAILLE_MAX_FICHIER'] ; - // javascript pour gerer la previsualisation - // si une taille maximale est indiquée, on teste - // ToDo: améliorer pour prendre la taille max définie au niveau du champ si elle existe et faire pareil dans FileField - if (!empty($maxSize)) { - $wiki->addJavascript("var imageMaxSize = {$maxSize};"); - } - $wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/image-field.js'); + $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/image-field.js'); if (isset($value) && $value != '') { if (isset($_GET['suppr_image']) && $_GET['suppr_image'] === $value) { @@ -98,12 +91,11 @@ protected function renderInput($entry) ]); } } - return ($alertMessage ?? '') .$this->render('@bazar/inputs/image.twig'); + return ($alertMessage ?? '') .$this->render('@bazar/inputs/image.twig', ['maxSize' => $this->maxSize]); } public function formatValuesBeforeSave($entry) { - // ToDo: Vérifier que l'image à bien été téléchargée et que sa taille n'est pas trop grosse $params = $this->getService(ParameterBagInterface::class); $value = $this->getValue($entry); if (!empty($_FILES[$this->propertyName]['name']) && !empty($entry['id_fiche'])) { @@ -115,6 +107,10 @@ public function formatValuesBeforeSave($entry) if ($this->isImage($rawFileName) && !$this->getService(SecurityController::class)->isWikiHibernated()) { if (!file_exists($filePath)) { + if ($_FILES[$this->propertyName]['size'] > $this->maxSize) { + throw new \Exception(_t('BAZ_FILEFIELD_TOO_LARGE_FILE', ['fileMaxSize' => $this->maxSize])); + } + move_uploaded_file($_FILES[$this->propertyName]['tmp_name'], $filePath); chmod($filePath, 0755); diff --git a/tools/bazar/lang/bazar_fr.inc.php b/tools/bazar/lang/bazar_fr.inc.php index 3fb19b854..90b321b20 100755 --- a/tools/bazar/lang/bazar_fr.inc.php +++ b/tools/bazar/lang/bazar_fr.inc.php @@ -296,6 +296,7 @@ 'BAZ_FILE_ALREADY_EXISTING' => 'fichier déja existant', 'BAZ_NOT_AUTHORIZED_FILE' => 'fichier non autorise', 'BAZ_FILEFIELD_FILE' => 'Fichier : {filename}', + 'BAZ_FILEFIELD_TOO_LARGE_FILE' => 'Le fichier est trop volumineux, maximum %{fileMaxSize} octets', // fields/ImageField.php 'BAZ_IMAGE_ALREADY_EXISTING' => 'L\'image {fileName} existait déja, elle n\'a pas été remplacée.', diff --git a/tools/bazar/lang/bazarjs_fr.inc.php b/tools/bazar/lang/bazarjs_fr.inc.php index 5d9e0337f..acce56803 100644 --- a/tools/bazar/lang/bazarjs_fr.inc.php +++ b/tools/bazar/lang/bazarjs_fr.inc.php @@ -16,6 +16,8 @@ // fields/FileField.php 'BAZ_FILEFIELD_FILE' => 'Fichier : {filename}', 'BAZ_FORM_EDIT_FILE_READLABEL_LABEL' => 'Label à l\'affichage', + 'FILEFIELD_TOO_LARGE_FILE' => 'Le fichier est trop volumineux, maximum {fileMaxSize} octets', + // fields/ImageField.php 'IMAGEFIELD_TOO_LARGE_IMAGE' => 'L\'image est trop volumineuse, maximum {imageMaxSize} octets', @@ -203,7 +205,7 @@ 'BAZ_FORM_EDIT_TEXTAREA_SIZE_LABEL' => 'Largeur champ de saisie', 'BAZ_FORM_EDIT_TEXTAREA_ROWS_LABEL' => 'Nombre de lignes', 'BAZ_FORM_EDIT_TEXTAREA_ROWS_PLACEHOLDER' => 'Défaut vide = 3 lignes', - 'BAZ_FORM_EDIT_FILE_MAXSIZE_LABEL' => 'Taille max (en octets)', + 'BAZ_FORM_EDIT_FILE_MAXSIZE_LABEL' => 'Taille max (ex: 2097152, 2048k, 2m)', 'BAZ_FORM_EDIT_INSCRIPTIONLISTE_EMAIL_LABEL' => 'Email pour s\'inscrire', 'BAZ_FORM_EDIT_INSCRIPTIONLISTE_EMAIL_FIELDID' => 'Champ du formulaire fournissant l\'email à inscire', 'BAZ_FORM_EDIT_INSCRIPTIONLISTE_MAILINGLIST' => 'Type de service de diffusion', diff --git a/tools/bazar/presentation/javascripts/file-field.js b/tools/bazar/presentation/javascripts/file-field.js new file mode 100644 index 000000000..70bef009d --- /dev/null +++ b/tools/bazar/presentation/javascripts/file-field.js @@ -0,0 +1,20 @@ +function handleFileSelect(evt) { + const target = evt.target || evt.srcElement + const { id } = target + const { files } = target // FileList object + + for (var i = 0, f; f = files[i]; i++) { + let fileMaxSize = document.getElementById(id).dataset.maxSize + if (f.size > fileMaxSize) { + alert(_t('FILEFIELD_TOO_LARGE_FILE', { fileMaxSize })) + document.getElementById(id).type = '' + document.getElementById(id).type = 'file' + continue + } + } +} + +const fileinputs = document.getElementsByClassName('yw-file-upload') +for (let i = 0; i < fileinputs.length; i++) { + fileinputs.item(i).addEventListener('change', handleFileSelect, false) +} diff --git a/tools/bazar/presentation/javascripts/form-edit-template/fields/file.js b/tools/bazar/presentation/javascripts/form-edit-template/fields/file.js index 6334eb644..bf05d8b46 100644 --- a/tools/bazar/presentation/javascripts/form-edit-template/fields/file.js +++ b/tools/bazar/presentation/javascripts/form-edit-template/fields/file.js @@ -21,7 +21,7 @@ export default { }, advancedAttributes: ['read', 'write', 'semantic', 'maxsize','authorizedExts'], // disabledAttributes: [], - attributesMapping: { ...defaultMapping, ...{ 3: 'maxsize', 6: 'readlabel', 7: 'authorizedExts' } }, + attributesMapping: { ...defaultMapping, ...{ 14: 'maxsize', 6: 'readlabel', 7: 'authorizedExts' } }, // renderInput(field) { // return { // field: ``, diff --git a/tools/bazar/presentation/javascripts/form-edit-template/fields/image.js b/tools/bazar/presentation/javascripts/form-edit-template/fields/image.js index 13dc3881e..b1484e5c4 100644 --- a/tools/bazar/presentation/javascripts/form-edit-template/fields/image.js +++ b/tools/bazar/presentation/javascripts/form-edit-template/fields/image.js @@ -19,11 +19,12 @@ export default { value: 'right', options: { left: _t('LEFT'), right: _t('RIGHT') } }, + maxsize: { label: _t('BAZ_FORM_EDIT_FILE_MAXSIZE_LABEL'), value: '' }, read: readConf, write: writeconf, semantic: semanticConf }, - advancedAttributes: ['read', 'write', 'semantic', 'thumb_height','thumb_width','resize_height','resize_width'], + advancedAttributes: ['read', 'write', 'semantic', 'thumb_height','thumb_width','resize_height','resize_width','maxsize'], // disabledAttributes: [], attributesMapping: { ...defaultMapping, @@ -33,7 +34,8 @@ export default { 4: 'thumb_width', 5: 'resize_height', 6: 'resize_width', - 7: 'align' + 7: 'align', + 14: 'maxsize' } }, renderInput(fieldData) { diff --git a/tools/bazar/presentation/javascripts/image-field.js b/tools/bazar/presentation/javascripts/image-field.js index 15885cb09..3329c1932 100644 --- a/tools/bazar/presentation/javascripts/image-field.js +++ b/tools/bazar/presentation/javascripts/image-field.js @@ -34,14 +34,12 @@ function handleFileSelect(evt) { if (!f.type.match('image.*')) { continue } - // ToDo: Faire pareil pour file-field - if (typeof imageMaxSize !== 'undefined') { - if (f.size > imageMaxSize) { - alert(_t('IMAGEFIELD_TOO_LARGE_IMAGE', { imageMaxSize })) - document.getElementById(id).type = '' - document.getElementById(id).type = 'file' - continue - } + let imageMaxSize = document.getElementById(id).dataset.maxSize + if (f.size > imageMaxSize) { + alert(_t('IMAGEFIELD_TOO_LARGE_IMAGE', { imageMaxSize })) + document.getElementById(id).type = '' + document.getElementById(id).type = 'file' + continue } const reader = new FileReader() // Closure to capture the file information. diff --git a/tools/bazar/templates/inputs/file.twig b/tools/bazar/templates/inputs/file.twig index da1e42795..eab13d0dc 100644 --- a/tools/bazar/templates/inputs/file.twig +++ b/tools/bazar/templates/inputs/file.twig @@ -33,10 +33,11 @@ {% block inputblock %} diff --git a/tools/bazar/templates/inputs/image.twig b/tools/bazar/templates/inputs/image.twig index 37ebc42be..df2919936 100644 --- a/tools/bazar/templates/inputs/image.twig +++ b/tools/bazar/templates/inputs/image.twig @@ -34,11 +34,12 @@ {% else %}