diff --git a/wire/core/Pagefile.php b/wire/core/Pagefile.php index bb51cd48..b1a7bf04 100644 --- a/wire/core/Pagefile.php +++ b/wire/core/Pagefile.php @@ -45,6 +45,7 @@ * @property User|NullPage $createdUser User that added/uploaded the file or NullPage if not known (3.0.154)+. #pw-group-other * @property User|NullPage $modifiedUser User that last modified the file or NullPage if not known (3.0.154)+. #pw-group-other * @property bool $formatted True when value has had Textformatters applied. #pw-internal + * @property string $uploadName Original unsanitized filename at upload, see notes for uploadName() method (3.0.212+). #pw-group-other * * @method void install($filename) * @method string httpUrl() @@ -702,6 +703,9 @@ class Pagefile extends WireData implements WireArrayItem { case 'fieldValues': $value = $this->fieldValues; break; + case 'uploadName': + $value = $this->uploadName(); + break; default: $value = $this->getFieldValue($key); } @@ -933,6 +937,24 @@ class Pagefile extends WireData implements WireArrayItem { return $basename; } + /** + * Original and unsanitized filename at the time it was uploaded + * + * Returned value is also entity encoded if $page’s output formatting state is ON. + * For files uploaded in ProcessWire 3.0.212 or newer. Falls back to current file + * basename for files that were uploaded prior to 3.0.212. + * + * @return string + * @since 3.0.212 + * + */ + public function uploadName() { + $uploadName = (string) $this->filedata('uploadName'); + if(!strlen($uploadName)) $uploadName = $this->basename(); + if($this->page && $this->page->of()) $uploadName = $this->wire()->sanitizer->entities($uploadName); + return $uploadName; + } + /** * Get or set the "tags" property, when in use. * diff --git a/wire/core/WireUpload.php b/wire/core/WireUpload.php index 82f918e2..1734d9c6 100644 --- a/wire/core/WireUpload.php +++ b/wire/core/WireUpload.php @@ -53,6 +53,14 @@ class WireUpload extends Wire { */ protected $completedFilenames = array(); + /** + * Original unsanitized file basenames indexed by completed basenames + * + * @var array + * + */ + protected $originalFilenames = array(); + /** * Allow files to be overwritten? * @@ -474,8 +482,8 @@ class WireUpload extends Wire { /** * Save the uploaded file * - * @param string $tmp_name Temporary filename - * @param string $filename Actual filename + * @param string $tmp_name Temporary filename (full path and filename) + * @param string $filename Actual filename (basename) * @param bool $ajax Is this an AJAX upload? * @return array|bool|string Boolean false on fail, array of multiple filenames, or string of filename if maxFiles=1 * @@ -486,6 +494,7 @@ class WireUpload extends Wire { $success = false; $error = ''; + $originalFilename = basename($filename); $filename = $this->getTargetFilename($filename); $_filename = $filename; $filename = $this->validateFilename($filename, $this->validExtensions); @@ -552,7 +561,7 @@ class WireUpload extends Wire { return $this->completedFilenames; } else { - $this->completedFilenames[] = $filename; + $this->addUploadedFilename($filename, $originalFilename); return $filename; } } @@ -602,6 +611,7 @@ class WireUpload extends Wire { } $basename = $file; + $originalFilename = $basename; $basename = $this->validateFilename($basename, $this->validExtensions); if($basename) { @@ -624,7 +634,7 @@ class WireUpload extends Wire { } if($destination && rename($pathname, $destination)) { - $this->completedFilenames[] = basename($destination); + $this->addUploadedFilename($destination, $originalFilename); $cnt++; } else { $fileTools->unlink($pathname, $tmpDir); @@ -649,6 +659,34 @@ class WireUpload extends Wire { return $this->completedFilenames; } + /** + * Add a completed upload file name and its original name + * + * @param string $completedFilename Sanitized filename or basename that was used for saved file + * @param string $originalFilename Unsanitized filename as uploaded + * + */ + protected function addUploadedFilename($completedFilename, $originalFilename) { + $completedFilename = basename($completedFilename); + $originalFilename = basename($originalFilename); + $this->completedFilenames[] = $completedFilename; + if($this->wire()->sanitizer->getTextTools()->strlen($originalFilename) > 255) { + $originalFilename = $completedFilename; + } + $this->originalFilenames[$completedFilename] = $originalFilename; + } + + /** + * Get unsanitized array of original filenames (basenames) indexed by completed basename + * + * @return array + * @since 3.0.212 + * + */ + public function getOriginalFilenames() { + return $this->originalFilenames; + } + /** * Set the target filename, only useful for single uploads * diff --git a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module index 8ac88ec2..a5053a79 100644 --- a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module +++ b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module @@ -31,7 +31,7 @@ * @method string renderUpload($value) * @method void fileAdded(Pagefile $pagefile) * @method array extractMetadata(Pagefile $pagefile, array $metadata = array()) - * @method void processInputAddFile($filename) + * @method Pagefile|null processInputAddFile($filename) * @method void processInputDeleteFile(Pagefile $pagefile) * @method bool processInputFile(WireInputData $input, Pagefile $pagefile, $n) * @method bool processItemInputfields(Pagefile $pagefile, InputfieldWrapper $inputfields, $id, WireInputData $input) @@ -519,11 +519,20 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $displayName = $this->getDisplayBasename($pagefile); $deleteLabel = $this->labels['delete']; + $uploadName = $pagefile->uploadName(); + $icon = wireIconMarkupFile($pagefile->basename, "fa-fw HideIfEmpty"); + + $tooltip = $this->wire()->sanitizer->entities($pagefile->basename); + + if($uploadName && $uploadName != $pagefile->basename) { + $uploadName = $this->wire()->sanitizer->entities($uploadName); + $icon = "$icon"; + } $out = "

" . - wireIconMarkupFile($pagefile->basename, "fa-fw HideIfEmpty") . ' ' . - "$displayName " . + "$icon " . + "$displayName " . "" . str_replace(' ', ' ', $pagefile->filesizeStr) . " "; if(!$this->renderValueMode) $out .= @@ -882,6 +891,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * Process input to add a file * * @param string $filename + * @return Pagefile|null Returns Pagefile (added 3.0.212+) * @throws WireException * */ @@ -890,7 +900,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $total = count($this->value); $metadata = array(); - if($this->maxFiles > 1 && $total >= $this->maxFiles) return; + if($this->maxFiles > 1 && $total >= $this->maxFiles) return null; // allow replacement of file if maxFiles is 1 if($this->maxFiles == 1 && $total) { @@ -930,7 +940,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $ul->error(sprintf($this->_('Refused file %s because it is already on the file system and owned by a different field.'), $filename)); $err = true; } - if($err) return; + if($err) return null; } } @@ -955,6 +965,8 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $this->value->remove($item); throw new WireException($e->getMessage()); } + + return $item; } /** @@ -1204,9 +1216,15 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } $ul->setValidExtensions($this->getAllowedExtensions(true)); + + $filenames = $ul->execute(); + $originalFilenames = $ul->getOriginalFilenames(); - foreach($ul->execute() as $filename) { - $this->processInputAddFile($filename); + foreach($filenames as $filename) { + $pagefile = $this->processInputAddFile($filename); + if($pagefile && isset($originalFilenames[$filename]) && $originalFilenames[$filename] != $filename) { + $pagefile->filedata('uploadName', $originalFilenames[$filename]); + } $changed = true; } diff --git a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module index 3204d043..3b78c0ea 100755 --- a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module +++ b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.module @@ -716,10 +716,18 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu $inputfields = $this->getItemInputfields($pagefile); if($inputfields) $additional .= $inputfields->render(); + + $uploadName = $pagefile->uploadName(); + if($uploadName != "$basename.$ext") { + $tooltip = $uploadName; + } else { + $tooltip = $this->_('Click to rename'); + } + $tooltip = $sanitizer->entities($tooltip); $out .= "

-

$basename.$ext

+

$basename.$ext

$fileStats
$error
$buttons $actions