diff --git a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module index 0cff175f..60e34f7b 100644 --- a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module +++ b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module @@ -2,6 +2,9 @@ /** * An Inputfield for handling file uploads + * + * ProcessWire 3.x, Copyright 2022 by Ryan Cramer + * https://processwire.com * * @property string $extensions Allowed file extensions, space separated * @property array $okExtensions File extensions that are whitelisted if any in $extensions are problematic. (3.0.167+) @@ -40,9 +43,9 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel return array( 'title' => __('Files', __FILE__), // Module Title 'summary' => __('One or more file uploads (sortable)', __FILE__), // Module Summary - 'version' => 126, + 'version' => 127, 'permanent' => true, - ); + ); } /** @@ -166,19 +169,21 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel 'choose-files' => $this->_('Choose Files'), ); - $this->isAjax = $this->wire('input')->get('InputfieldFileAjax') - || $this->wire('input')->get('reloadInputfieldAjax') - || $this->wire('input')->get('renderInputfieldAjax'); + $input = $this->wire()->input; + + $this->isAjax = $input->get('InputfieldFileAjax') + || $input->get('reloadInputfieldAjax') + || $input->get('renderInputfieldAjax'); $this->setMaxFilesize(trim(ini_get('post_max_size'))); - $this->uploadOnlyMode = (int) $this->wire('input')->get('uploadOnlyMode'); + $this->uploadOnlyMode = (int) $input->get('uploadOnlyMode'); $this->addClass('InputfieldItemList', 'wrapClass'); $this->addClass('InputfieldHasFileList', 'wrapClass'); $themeDefaults = array( 'error' => "{out}", ); - $themeSettings = $this->wire('config')->InputfieldFile; + $themeSettings = $this->wire()->config->InputfieldFile; $this->themeSettings = is_array($themeSettings) ? array_merge($themeDefaults, $themeSettings) : $themeDefaults; } @@ -322,40 +327,47 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ protected function renderItemDescriptionField(Pagefile $pagefile, $id, $n) { + + $sanitizer = $this->wire()->sanitizer; + $languages = $this->noLang ? null : $this->wire()->languages; + $user = $this->wire()->user; if($n) {} $out = ''; $tabs = ''; + static $hasLangTabs = null; static $langTabSettings = array(); if($this->renderValueMode) { - if($this->wire('languages')) { - $description = $pagefile->description($this->wire('user')->language); + if($languages) { + $description = $pagefile->description($user->language); } else { $description = $pagefile->description; } if(strlen($description)) $description = - "
" . $this->wire('sanitizer')->entities1($description) . "
"; + "
" . + $sanitizer->entities1($description) . + "
"; return $description; } if($this->descriptionRows > 0) { - $userLanguage = $this->wire('user')->language; - $languages = $this->noLang ? null : $this->wire('languages'); - $defaultDescriptionFieldLabel = $this->wire('sanitizer')->entities1($this->labels['description']); - - if(!$userLanguage || !$languages || $languages->count() < 2) { + $userLanguage = $languages ? $user->language : null; + $defaultDescriptionFieldLabel = $sanitizer->entities1($this->labels['description']); + + if(!$userLanguage || $languages->count() < 2) { $numLanguages = 0; $languages = array(null); } else { $numLanguages = $languages->count(); if(is_null($hasLangTabs)) { - $hasLangTabs = $this->wire('modules')->isInstalled('LanguageTabs'); + $modules = $this->wire()->modules; + $hasLangTabs = $modules->isInstalled('LanguageTabs'); if($hasLangTabs) { /** @var LanguageTabs $languageTabs */ - $languageTabs = $this->wire('modules')->getModule('LanguageTabs'); + $languageTabs = $modules->getModule('LanguageTabs'); $langTabSettings = $languageTabs->getSettings(); } } @@ -369,10 +381,11 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $attrStr = ''; if($language) { + /** @var Language $language */ $tabField = empty($langTabSettings['tabField']) ? 'title' : $langTabSettings['tabField']; $descriptionFieldLabel = (string) $language->getUnformatted($tabField); if(empty($descriptionFieldLabel)) $descriptionFieldLabel = $language->get('name'); - $descriptionFieldLabel = $this->wire('sanitizer')->entities($descriptionFieldLabel); + $descriptionFieldLabel = $sanitizer->entities($descriptionFieldLabel); if(!$language->isDefault()) $descriptionFieldName = "description{$language->id}_$id"; $labelClass .= ' LanguageSupportLabel'; if(!$languages->editable($language)) { @@ -394,7 +407,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $out .= ""; - $description = $this->wire('sanitizer')->entities($pagefile->description($language)); + $description = $sanitizer->entities($pagefile->description($language)); if($this->descriptionRows > 1) { $out .= ""; @@ -408,8 +421,19 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel if($numLanguages && $hasLangTabs) { $ulClass = empty($langTabSettings['ulClass']) ? '' : " class='$langTabSettings[ulClass]'"; $ulAttr = empty($langTabSettings['ulAttrs']) ? '' : " $langTabSettings[ulAttrs]"; - $out = "
$out
"; - if($this->isAjax) $out .= ""; + + $out = + "
" . + "
" . + "" . + $out . + "
" . + "
"; + + if($this->isAjax) { + $js = 'script'; + $out .= "<$js>setupLanguageTabs($('#wrap_" . $this->attr('id') . "'));"; + } } } @@ -429,10 +453,12 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ protected function renderItemTagsField(Pagefile $pagefile, $id, $n) { + + $sanitizer = $this->wire()->sanitizer; if($n) {} - $tagsLabel = $this->wire('sanitizer')->entities($this->labels['tags']) . '…'; - $tagsStr = $this->wire('sanitizer')->entities($pagefile->tags); + $tagsLabel = $sanitizer->entities($this->labels['tags']) . '…'; + $tagsStr = $sanitizer->entities($pagefile->tags); $tagsAttr = ''; if($this->useTags >= FieldtypeFile::useTagsPredefined) { @@ -547,8 +573,9 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel if(!$this->renderValueMode) { // if just rendering the files list (as opposed to saving it), delete any temp files that may have accumulated if(!$this->overwrite && !count($_POST) && !$this->isAjax && !$this->uploadOnlyMode) { + $input = $this->wire()->input; // don't delete files when in render single field or fields mode - if(!$this->wire('input')->get('field') && !$this->wire('input')->get('fields')) { + if(!$input->get('field') && !$input->get('fields')) { if($value instanceof Pagefiles) $value->deleteAllTemp(); } } @@ -571,7 +598,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $this->renderListReady($value); if(!$this->uploadOnlyMode && WireArray::iterable($value)) { - foreach($value as $k => $pagefile) { + foreach($value as $pagefile) { $id = $this->pagefileId($pagefile); $this->currentItem = $pagefile; $out .= $this->renderItemWrap($this->renderItem($pagefile, $id, $n++)); @@ -659,14 +686,14 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel */ public function renderReady(Inputfield $parent = null, $renderValueMode = false) { - /** @var Config $config */ - $config = $this->wire('config'); + $config = $this->wire()->config; $this->addClass('InputfieldNoFocus', 'wrapClass'); if(!$renderValueMode) $this->addClass('InputfieldHasUpload', 'wrapClass'); if($this->useTags) { - $this->wire('modules')->get('JqueryUI')->use('selectize'); + $jQueryUI = $this->wire()->modules->get('JqueryUI'); /** @var JqueryUI $jQueryUI */ + $jQueryUI->use('selectize'); $this->addClass('InputfieldFileHasTags', 'wrapClass'); if($this->useTags >= FieldtypeFile::useTagsPredefined && $this->hasField) { // predefined tags @@ -720,16 +747,25 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ public function ___render() { - if(!$this->extensions) $this->error($this->_('No file extensions are defined for this field.')); - $numItems = wireCount($this->value); - if($this->allowCollapsedItems()) $this->addClass('InputfieldItemListCollapse', 'wrapClass'); - if($numItems == 0) { + + if(!$this->extensions) { + $this->error($this->_('No file extensions are defined for this field.')); + } + + if($this->allowCollapsedItems()) { + $this->addClass('InputfieldItemListCollapse', 'wrapClass'); + } + + $numItems = (int) wireCount($this->value); + + if($numItems === 0) { $this->addClass('InputfieldFileEmpty', 'wrapClass'); - } else if($numItems == 1) { + } else if($numItems === 1) { $this->addClass('InputfieldFileSingle', 'wrapClass'); } else { $this->addClass('InputfieldFileMultiple', 'wrapClass'); } + return $this->renderList($this->value) . $this->renderUpload($this->value); } @@ -754,7 +790,9 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ protected function ___fileAdded(Pagefile $pagefile) { + if($this->noUpload) return; + $sanitizer = $this->wire()->sanitizer; $isValid = $sanitizer->validateFile($pagefile->filename(), array( @@ -782,9 +820,10 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } else { $this->message($message); } - - $pagefile->createdUser = $this->wire('user'); - $pagefile->modifiedUser = $this->wire('user'); + + $user = $this->wire()->user; + $pagefile->createdUser = $user; + $pagefile->modifiedUser = $user; } /** @@ -811,10 +850,11 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $metadata['description'] = $pagefile->description; - /** @var Languages $languages */ - $languages = $this->wire('languages'); - if($languages && !$this->noLang) { + $languages = $this->noLang ? null : $this->wire()->languages; + + if($languages) { foreach($languages as $language) { + /** @var Language $language */ if($language->isDefault()) continue; $metadata["description$language->id"] = $pagefile->description($language); } @@ -822,7 +862,10 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $metadata['tags'] = $pagefile->tags; $filedata = $pagefile->filedata(); - if(count($filedata)) $metadata['filedata'] = $filedata; + + if(count($filedata)) { + $metadata['filedata'] = $filedata; + } return $metadata; } @@ -838,12 +881,12 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $total = count($this->value); $metadata = array(); - $rm = null; if($this->maxFiles > 1 && $total >= $this->maxFiles) return; // allow replacement of file if maxFiles is 1 if($this->maxFiles == 1 && $total) { + /** @var Pagefile $pagefile */ $pagefile = $this->value->first(); $metadata = $this->extractMetadata($pagefile, $metadata); $rm = true; @@ -871,10 +914,11 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel // see if any files were overwritten that weren't part of our field // if so, we need to restore them and issue an error $err = false; + $files = $this->wire()->files; foreach($ul->getOverwrittenFiles() as $bakFile => $newFile) { if(basename($newFile) != $filename) continue; - $this->wire('files')->unlink($newFile); - $this->wire('files')->rename($bakFile, $newFile); // restore + $files->unlink($newFile); + $files->rename($bakFile, $newFile); // restore $ul->error(sprintf($this->_('Refused file %s because it is already on the file system and owned by a different field.'), $filename)); $err = true; } @@ -883,6 +927,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } $this->value->add($filename); + /** @var Pagefile $item */ $item = $this->value->last(); @@ -892,7 +937,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } // items saved in ajax or uploadOnly mode are temporary till saved in non-ajax/non-uploadOnly if($this->isAjax && !$this->overwrite) { - if($this->wire('input')->get('InputfieldFileAjax') !== 'noTemp') { + if($this->wire()->input->get('InputfieldFileAjax') !== 'noTemp') { $item->isTemp(true); } } @@ -911,7 +956,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ protected function ___processInputDeleteFile(Pagefile $pagefile) { - $fileLabel = $this->wire('config')->debug ? $pagefile->url() : $pagefile->name; + $fileLabel = $this->wire()->config->debug ? $pagefile->url() : $pagefile->name; $this->message($this->_("Deleted file:") . " $fileLabel"); // Label that precedes a deleted filename $this->value->delete($pagefile); $this->trackChange('value'); @@ -947,7 +992,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel if($unused) {} } $replaceFile = $this->value->getFile($replace); - if($replaceFile && $replaceFile instanceof Pagefile) { + if($replaceFile instanceof Pagefile) { $this->processInputDeleteFile($replaceFile); if(strtolower($pagefile->ext()) == strtolower($replaceFile->ext())) { $this->value->rename($pagefile, $replaceFile->name); @@ -976,7 +1021,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } // description and tags - $languages = $this->noLang ? null : $this->wire('languages'); + $languages = $this->noLang ? null : $this->wire()->languages; $keys = $languages ? array('tags') : array('description', 'tags'); foreach($keys as $key) { @@ -992,14 +1037,17 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } // multi-language descriptions - if($languages) foreach($languages as $language) { - if(!$languages->editable($language)) continue; - $key = $language->isDefault() ? "description_$id" : "description{$language->id}_$id"; - if(!isset($input[$key])) continue; - $value = trim($input[$key]); - if($value != $pagefile->description($language)) { - $pagefile->description($language, $value); - $changed = true; + if($languages) { + foreach($languages as $language) { + /** @var Language $language */ + if(!$languages->editable($language)) continue; + $key = $language->isDefault() ? "description_$id" : "description{$language->id}_$id"; + if(!isset($input[$key])) continue; + $value = trim($input[$key]); + if($value != $pagefile->description($language)) { + $pagefile->description($language, $value); + $changed = true; + } } } @@ -1037,9 +1085,11 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $pagefile->isTemp(false); // @todo should the next statement instead be this below? // if($this->maxFiles > 0) while(count($this->value) > $this->>maxFiles) { ... } ? - if($this->maxFiles == 1) while(count($this->value) > 1) { - $item = $this->value->first(); - $this->value->remove($item); + if(((int) $this->maxFiles) === 1) { + while(count($this->value) > 1) { + $item = $this->value->first(); + $this->value->remove($item); + } } $changed = true; } @@ -1097,8 +1147,13 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel */ public function ___processInput(WireInputData $input) { - if(is_null($this->value)) $this->value = $this->wire(new Pagefiles($this->wire('page'))); - if(!$this->destinationPath) $this->destinationPath = $this->value->path(); + if(is_null($this->value)) { + $this->value = $this->wire(new Pagefiles($this->wire()->page)); + } + + if(!$this->destinationPath) { + $this->destinationPath = $this->value->path(); + } if(!$this->destinationPath || !is_dir($this->destinationPath)) { return $this->error($this->_("destinationPath is empty or does not exist")); @@ -1175,7 +1230,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ protected function renderAjaxResponse() { - if($this->wire('input')->get('ckeupload')) { + if($this->wire()->input->get('ckeupload')) { // https://docs.ckeditor.com/ckeditor4/docs/#!/guide/dev_file_upload $a = $this->ajaxResponses[0]; $response = array( @@ -1214,7 +1269,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel 'markup' => $markup, 'replace' => $this->singleFileReplacement, 'overwrite' => $this->overwrite - ); + ); $this->ajaxResponses[] = $response; } @@ -1226,7 +1281,9 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel * */ public function getWireUpload() { - if(is_null($this->wireUpload)) $this->wireUpload = $this->wire(new WireUpload($this->attr('name'))); + if(is_null($this->wireUpload)) { + $this->wireUpload = $this->wire(new WireUpload($this->attr('name'))); + } return $this->wireUpload; } @@ -1354,11 +1411,23 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel $inputfields = $this->itemFieldgroup->getPageInputfields($page, $id, '', false); if(!$inputfields) return false; - /** @var Languages|null $languages */ - $languages = $this->wire('languages'); + $languages = $this->wire()->languages; foreach($inputfields->getAll() as $f) { + /** @var Inputfield $f */ + if(wireInstanceOf($f, 'InputfieldCKEditor')) { + /** @var InputfieldCKEditor $f */ + $ckeField = $f->hasField; + if($ckeField) { + $f->configName = $f->className() . "_$ckeField->name"; + $imagesField = $this->hasField; + if($imagesField && $this->itemFieldgroup && $this->itemFieldgroup->hasFieldContext($ckeField)) { + $f->configName .= "_$imagesField->name"; + } + } + } + if(!$item) { // prepare inputfields for render rather than populating them $f->renderReady(); @@ -1367,11 +1436,12 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel /** @var Inputfield $f */ $name = str_replace($id, '', $f->name); - $value = $item ? $item->getFieldValue($name) : null; + $value = $item->getFieldValue($name); if($value === null) continue; if($languages && $f->getSetting('useLanguages') && $value instanceof LanguagesValueInterface) { foreach($languages as $language) { + /** @var Language $language */ $v = $value->getLanguageValue($language->id); if($language->isDefault()) $f->val($v); $f->set("value$language->id", $v); @@ -1384,7 +1454,8 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } else { $f->val($value); } - + + /* if($f->className() === 'InputfieldCKEditor') { // CKE does not like being placed in file/image fields. // I'm sure it's possible, but needs more work and debugging, so it's disabled for now. @@ -1401,6 +1472,7 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel '

'; $f->getParent()->remove($f); } + */ } return $inputfields;