diff --git a/README.md b/README.md index d272ae7..6dc4db4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +#Fork of unclecheese dropzone just added SS6 requirement + # Dropzone for SilverStripe #### Upload with sanity. diff --git a/composer.json b/composer.json index 3f03ab8..9072cce 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "unclecheese/dropzone", + "name": "chromos33/dropzone", "description": "An HTML5 upload field for the CMS and frontend forms.", "type": "silverstripe-vendormodule", "keywords": ["silverstripe", "upload", "uploader", "files", "forms", "cms"], @@ -9,7 +9,7 @@ "email": "unclecheese@leftandmain.com" }], "require": { - "silverstripe/framework": "4.*" + "silverstripe/framework": "^6" }, "extra": { "installer-name": "dropzone", @@ -24,4 +24,4 @@ "UncleCheese\\Dropzone\\": "src/" } } -} \ No newline at end of file +} diff --git a/lang/de.yml b/lang/de.yml new file mode 100644 index 0000000..1e59b7b --- /dev/null +++ b/lang/de.yml @@ -0,0 +1,13 @@ +de: + Dropzone: + ADDEDNOW: 'Soeben hinzugefügt' + ADDEDON: 'Hinzugefügt am {date}' + ATTACHFILEHERE_OR: 'Hängen Sie eine Datei an, indem Sie sie hier ablegen.' + ATTACHFILESHERE_OR: 'Hängen Sie Dateien an, indem Sie sie hier ablegen.' + BROWSEYOURCOMPUTER: 'Ihren Computer durchsuchen' + CHANGEAFTERSAVE: 'Die Änderung wird nach dem Speichern wirksam.' + CHOOSEEXISTING: 'aus vorhandenen Dateien auswählen' + DELETED: gelöscht + DETACHFILE: entfernen + ERROR: 'Oh nein!' + YOUCANALSO: "Sie können auch" diff --git a/src/DropzoneFile.php b/src/DropzoneFile.php index 8eb98fd..f89f433 100644 --- a/src/DropzoneFile.php +++ b/src/DropzoneFile.php @@ -2,12 +2,12 @@ namespace UncleCheese\Dropzone; +use SilverStripe\Core\Extension; use SilverStripe\Assets\Folder; use SilverStripe\Assets\Image; use SilverStripe\Control\Director; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Manifest\ModuleResourceLoader; -use SilverStripe\ORM\DataExtension; /** * Adds helper methods to the core {@link File} object @@ -15,7 +15,7 @@ * @package unclecheese/dropzone * @author Uncle Cheese */ -class DropzoneFile extends DataExtension +class DropzoneFile extends Extension { @@ -84,9 +84,9 @@ public function getPreviewThumbnail($w = null, $h = null) protected function getFilenameForType($ext, $size) { return ModuleResourceLoader::singleton()->resolveURL(sprintf( - 'unclecheese/dropzone:images/file-icons/%spx/%s.png', + 'chromos33/dropzone:images/file-icons/%spx/%s.png', $size, strtolower($ext) )); } -} \ No newline at end of file +} diff --git a/src/FileAttachmentField.php b/src/FileAttachmentField.php index ddea293..e982e7c 100644 --- a/src/FileAttachmentField.php +++ b/src/FileAttachmentField.php @@ -2,9 +2,11 @@ namespace UncleCheese\Dropzone; +use SilverStripe\Core\Convert; use SilverStripe\Core\Manifest\ModuleLoader; use SilverStripe\Core\Manifest\ModuleManifest; use SilverStripe\Core\Manifest\ModuleResourceLoader; +use SilverStripe\Core\Validation\ValidationResult; use SilverStripe\Forms\FileField; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; @@ -18,13 +20,13 @@ use SilverStripe\Assets\Image; use SilverStripe\Core\Injector\Injector; use SilverStripe\Admin\LeftAndMain; -use SilverStripe\Core\Convert; use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\SS_List; -use SilverStripe\ORM\ArrayList; +use SilverStripe\Model\List\ArrayList; use SilverStripe\ORM\RelationList; use SilverStripe\ORM\UnsavedRelationList; + /** * Defines the FileAttachementField form field type * @@ -131,8 +133,8 @@ public static function camelise($str) { return preg_replace_callback( '/_([a-z])/', function ($c) { - return strtoupper($c[1]); - }, $str + return strtoupper($c[1]); + }, $str ); } @@ -148,8 +150,8 @@ public static function underscorise($str) return preg_replace_callback( '/([A-Z])/', function ($c) { - return "_" . strtolower($c[1]); - }, $str + return "_" . strtolower($c[1]); + }, $str ); } @@ -163,8 +165,8 @@ public static function get_filesize_from_ini() { $bytes = min( array( - File::ini2bytes(ini_get('post_max_size') ?: '8M'), - File::ini2bytes(ini_get('upload_max_filesize') ?: '2M') + Convert::memstring2bytes(ini_get('post_max_size') ?: '8M'), + Convert::memstring2bytes(ini_get('upload_max_filesize') ?: '2M') ) ); @@ -229,12 +231,12 @@ public function SmallFieldHolder($attributes = array ()) */ protected function defineFieldHolderRequirements() { - Requirements::javascript('unclecheese/dropzone:javascript/dropzone.js'); - Requirements::javascript('unclecheese/dropzone:javascript/file_attachment_field.js'); + Requirements::javascript('chromos33/dropzone:javascript/dropzone.js'); + Requirements::javascript('chromos33/dropzone:javascript/file_attachment_field.js'); if($this->isCMS()) { - Requirements::javascript('unclecheese/dropzone:javascript/file_attachment_field_backend.js'); + Requirements::javascript('chromos33/dropzone:javascript/file_attachment_field_backend.js'); } - Requirements::css('unclecheese/dropzone:css/file_attachment_field.css'); + Requirements::css('chromos33/dropzone:css/file_attachment_field.css'); if(!$this->getSetting('url')) { $this->settings['url'] = $this->Link('upload'); @@ -487,54 +489,48 @@ public function getValidFileIDs() /** * Check that the user is submitting the file IDs that they uploaded. - * - * @return boolean */ - public function validate($validator) - { - $result = true; - - // Detect if files have been removed between AJAX uploads and form submission - $value = $this->dataValue(); - - if ($this->hasInvalidFileID) { - // If detected invalid file during 'Form::loadDataFrom' - // (Below validation isn't triggered as setValue() removes the invalid ID - // to prevent the CMS from loading something it shouldn't, also stops the - // validator from realizing there's an invalid ID.) - $validator->validationError( - $this->name, - _t( - 'FileAttachmentField.VALIDATION', - 'Invalid file ID sent.' - ), - "validation" - ); - $result = false; - } else if ($value && is_array($value)) { - // Prevent a malicious user from inspecting element and changing - // one of the fields to use an invalid File ID. - $validIDs = $this->getValidFileIDs(); + public function validate(): ValidationResult + { + $this->beforeExtending('updateValidate', function (ValidationResult $result) { + // Detect if files have been removed between AJAX uploads and form submission + $value = $this->dataValue(); + + if ($this->hasInvalidFileID) { + // Detected invalid file during 'Form::loadDataFrom'. setValue() may remove the invalid ID + // to prevent the CMS from loading something it shouldn't; add an error to the result. + $result->addFieldError( + $this->name, + _t( + 'FileAttachmentField.VALIDATION', + 'Invalid file ID sent.' + ), + "validation" + ); + return; // No need to double-handle below + } + + if ($value && is_array($value)) { + // Prevent a malicious user from changing one of the hidden inputs to an invalid File ID. + $validIDs = $this->getValidFileIDs(); - foreach ($value as $id) { - if (!isset($validIDs[$id])) { - if ($validator) { - $validator->validationError( + foreach ($value as $id) { + if (!isset($validIDs[$id])) { + $result->addFieldError( $this->name, _t( 'FileAttachmentField.VALIDATION', - 'Invalid file ID sent %s.', - array('id' => $id) + 'Invalid file ID sent {id}.', + ['id' => $id] ), "validation" ); } - $result = false; } } - } + }); - return $result; + return parent::validate(); } /** @@ -552,7 +548,7 @@ public function setValue($val, $data = array()) if ($data->getSchema()->hasOneComponent(get_class($data), $fieldName)) { $id = $data->{$fieldName.'ID'}; if ($id) { - $ids[] = $id; + $ids[] = $id; } } else if ($data->getSchema()->hasManyComponent(get_class($data), $fieldName) || $data->getSchema()->manyManyComponent(get_class($data), $fieldName)) { $files = $data->{$fieldName}(); @@ -561,7 +557,7 @@ public function setValue($val, $data = array()) if (!$file->exists()) { continue; } - $ids[] = $file->ID; + $ids[] = $file->ID; } } } @@ -831,7 +827,7 @@ public function setPermission($perm, $val) { return $this->setPermissions( array( - $perm => $val + $perm => $val ) ); } @@ -862,7 +858,7 @@ public function isCMS() { return Controller::curr() instanceof LeftAndMain; } - + /** * @note these are user-friendlier versions of internal PHP errors reported back in the ['error'] value of an upload * @return string @@ -871,29 +867,29 @@ private function getUploadUserError($code) { $error_message = ""; switch($code) { - case UPLOAD_ERR_OK: - // no error - 0 - return ""; - break; - case UPLOAD_ERR_INI_SIZE: - case UPLOAD_ERR_FORM_SIZE: - $error_message = _t('FileAttachmentField.ERRFILESIZE', 'The file is too large, please try again with a smaller version of the file.'); - break; - case UPLOAD_ERR_PARTIAL: - $error_message = _t('FileAttachmentField.ERRPARTIALUPLOAD', 'The file was only partially uploaded, did you cancel the upload? Please try again.'); - break; - case UPLOAD_ERR_NO_FILE: - $error_message = _t('FileAttachmentField.ERRNOFILE', 'No file upload was detected.'); - break; - case UPLOAD_ERR_NO_TMP_DIR: - case UPLOAD_ERR_CANT_WRITE: - case UPLOAD_ERR_EXTENSION: - $error_message = _t('FileAttachmentField.ERRSYSTEMFAIL', 'Sorry, the system is not allowing file uploads at this time.'); - break; - default: - // handles if an extra error value is added at some point as a general error - $error_message = _t('FileAttachmentField.ERRUNKNOWNCODE', 'Sorry, an unknown error has occured. Please try again later.'); - break; + case UPLOAD_ERR_OK: + // no error - 0 + return ""; + break; + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + $error_message = _t('FileAttachmentField.ERRFILESIZE', 'The file is too large, please try again with a smaller version of the file.'); + break; + case UPLOAD_ERR_PARTIAL: + $error_message = _t('FileAttachmentField.ERRPARTIALUPLOAD', 'The file was only partially uploaded, did you cancel the upload? Please try again.'); + break; + case UPLOAD_ERR_NO_FILE: + $error_message = _t('FileAttachmentField.ERRNOFILE', 'No file upload was detected.'); + break; + case UPLOAD_ERR_NO_TMP_DIR: + case UPLOAD_ERR_CANT_WRITE: + case UPLOAD_ERR_EXTENSION: + $error_message = _t('FileAttachmentField.ERRSYSTEMFAIL', 'Sorry, the system is not allowing file uploads at this time.'); + break; + default: + // handles if an extra error value is added at some point as a general error + $error_message = _t('FileAttachmentField.ERRUNKNOWNCODE', 'Sorry, an unknown error has occured. Please try again later.'); + break; } return $error_message; } @@ -914,7 +910,7 @@ private function getUploadUserError($code) */ public function upload(HTTPRequest $request) { - + $name = $this->getSetting('paramName'); $files = (!empty($_FILES[$name]) ? $_FILES[$name] : array()); $tmpFiles = array(); @@ -924,14 +920,14 @@ public function upload(HTTPRequest $request) $error_message = _t('FileAttachmentField.UPLOADFORBIDDEN', 'Files cannot be uploaded via this form at the current time.'); return $this->httpError(403, $error_message); } - + // No files detected in the upload, this can occur if post_max_size is < the upload size $value = $request->postVar($name); if(empty($files) || empty($value)) { $error_message = _t('FileAttachmentField.NOFILESUPLOADED', 'No files were detected in your upload. Please try again later.'); return $this->httpError(400, $error_message); } - + // Security token check, must go after above check as a low post_max_size can scrub the Security Token name from the request $form = $this->getForm(); if($form) { @@ -981,7 +977,7 @@ public function upload(HTTPRequest $request) } if ($this->getTrackFiles()) { - $controller = Controller::has_curr() ? Controller::curr() : null; + $controller = Controller::curr(); $formClass = ($form) ? get_class($form) : ''; $trackFile = FileAttachmentFieldTrack::create(); @@ -1139,7 +1135,7 @@ public function AttachedFiles() public function RootThumbnailsDir() { return $this->getSetting('thumbnailsDir') ?: - ModuleResourceLoader::singleton()->resolveURL('unclecheese/dropzone:images/file-icons'); + ModuleResourceLoader::singleton()->resolveURL('chromos33/dropzone:images/file-icons'); } /** @@ -1172,7 +1168,7 @@ public function CSSSize() */ public function DropzoneDir() { - return ModuleLoader::inst()->getManifest()->getModule('unclecheese/dropzone') + return ModuleLoader::inst()->getManifest()->getModule('chromos33/dropzone') ->getResourcesDir(); } @@ -1312,9 +1308,9 @@ public function getFileClass($filename = null) } if($filename) { - if($defaultClass == "Image" - && $this->config()->upgrade_images - && !Injector::inst()->get($class) instanceof Image + if($defaultClass == \SilverStripe\Assets\Image::class + && $this->config()->upgrade_images + && !Injector::inst()->get($class) instanceof \SilverStripe\Assets\Image ) { $class = Image::class; } @@ -1404,14 +1400,13 @@ protected function getSetting($setting) */ protected function getDefaults() { - $file_path = ModuleLoader::inst()->getManifest()->getModule('unclecheese/dropzone') + $file_path = ModuleLoader::inst()->getManifest()->getModule('chromos33/dropzone') ->getResource($this->config()->default_config_path) ->getPath(); if(!file_exists($file_path)) { throw new Exception("FileAttachmentField::getDefaults() - There is no config json file at $file_path"); } - - return Convert::json2array(file_get_contents($file_path)); + return json_decode(file_get_contents($file_path),true); } /** @@ -1486,7 +1481,7 @@ public function getConfigJSON() } } - return Convert::array2json($data); + return json_encode($data); } public function performReadonlyTransformation() @@ -1494,10 +1489,10 @@ public function performReadonlyTransformation() $readonly = clone $this; $readonly->setPermissions( [ - 'attach' => false, - 'detach' => false, - 'upload' => false, - 'delete' => false + 'attach' => false, + 'detach' => false, + 'upload' => false, + 'delete' => false ] ); diff --git a/src/FileAttachmentFieldCleanTask.php b/src/FileAttachmentFieldCleanTask.php index 801e450..1a4d2ad 100644 --- a/src/FileAttachmentFieldCleanTask.php +++ b/src/FileAttachmentFieldCleanTask.php @@ -4,6 +4,9 @@ use SilverStripe\Dev\BuildTask; use SilverStripe\ORM\DB; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use SilverStripe\PolyExecution\PolyOutput; /** * Delete all files being tracked that weren't saved against anything. * @@ -16,27 +19,34 @@ class FileAttachmentFieldCleanTask extends BuildTask { private static $segment = 'dropzone-clean'; - protected $title = "File Attachment Field - Clear all tracked files that are older than 1 hour"; - - protected $description = 'Delete files uploaded via FileAttachmentField that aren\'t attached to anything.'; + protected string $title = 'File Attachment Field - Clear all tracked files that are older than 1 hour'; - public function run($request) + protected static string $description = 'Delete files uploaded via FileAttachmentField that aren\'t attached to anything.'; + + protected function execute(InputInterface $input, PolyOutput $output): int { - $files = FileAttachmentFieldTrack::get()->filter(array('Created:LessThanOrEqual' => date('Y-m-d H:i:s', time()-3600))); + $files = FileAttachmentFieldTrack::get()->filter(['Created:LessThanOrEqual' => date('Y-m-d H:i:s', time() - 3600)]); $files = $files->toArray(); if ($files) { foreach ($files as $trackRecord) { $file = $trackRecord->File(); - if ($file->exists()) { - DB::alteration_message('Remove File #'.$file->ID.' from "'.$trackRecord->ControllerClass.'" on '.$trackRecord->RecordClass.' #'.$trackRecord->RecordID, 'error'); + if ($file && $file->exists()) { + $msg = 'Remove File #' . $file->ID . ' from "' . $trackRecord->ControllerClass . '" on ' . $trackRecord->RecordClass . ' #' . $trackRecord->RecordID; + $output->writeln($msg); + DB::alteration_message($msg, 'error'); $file->delete(); } else { - DB::alteration_message('Untrack missing File #'.$file->ID.' from "'.$trackRecord->ControllerClass.'" on '.$trackRecord->RecordClass.' #'.$trackRecord->RecordID, 'error'); + $msg = 'Untrack missing File #' . $trackRecord->FileID . ' from "' . $trackRecord->ControllerClass . '" on ' . $trackRecord->RecordClass . ' #' . $trackRecord->RecordID; + $output->writeln($msg); + DB::alteration_message($msg, 'error'); } $trackRecord->delete(); } } else { - DB::alteration_message('No tracked files to remove.'); + $msg = 'No tracked files to remove.'; + $output->writeln($msg); + DB::alteration_message($msg); } + return Command::SUCCESS; } } diff --git a/src/FileAttachmentFieldTrack.php b/src/FileAttachmentFieldTrack.php index c032e9e..0a2561f 100644 --- a/src/FileAttachmentFieldTrack.php +++ b/src/FileAttachmentFieldTrack.php @@ -43,7 +43,8 @@ public function onBeforeWrite() parent::onBeforeWrite(); if (!$this->exists()) { // Store record this file was tracked on. - if (!$this->RecordID && Controller::has_curr()) { + $controller = Controller::curr(); + if (!$this->RecordID && $controller) { $controller = Controller::curr(); $pageRecord = null; if ($controller->hasMethod('data')) { diff --git a/templates/UncleCheese/Dropzone/FileAttachmentField_preview.ss b/templates/UncleCheese/Dropzone/FileAttachmentField_preview.ss index 8e9ef31..4aa200f 100644 --- a/templates/UncleCheese/Dropzone/FileAttachmentField_preview.ss +++ b/templates/UncleCheese/Dropzone/FileAttachmentField_preview.ss @@ -1,7 +1,7 @@
  • - + @@ -20,13 +20,13 @@ - +
    <%t Dropzone.ERROR 'Oh no!' %>
    - +
  • diff --git a/templates/UncleCheese/Dropzone/Includes/FileAttachmentField_attachments.ss b/templates/UncleCheese/Dropzone/Includes/FileAttachmentField_attachments.ss index 79616eb..0bf8c66 100644 --- a/templates/UncleCheese/Dropzone/Includes/FileAttachmentField_attachments.ss +++ b/templates/UncleCheese/Dropzone/Includes/FileAttachmentField_attachments.ss @@ -28,13 +28,13 @@ <% if $Scope.CanDetach %> <%t Dropzone.DETACHFILE 'remove' %> - + <% end_if %> <% if $Scope.CanDelete %> <%t Dropzone.MARKFORDELETION 'delete' %> - + <% end_if %> @@ -44,7 +44,7 @@
    <%t Dropzone.REMOVED 'removed' %>
    <%t Dropzone.CHANGEAFTERSAVE 'The change will take effect after you save.' %> - +
    <% end_if %> @@ -53,7 +53,7 @@
    <%t Dropzone.DELETED 'deleted' %>
    <%t Dropzone.CHANGEAFTERSAVE 'The change will take effect after you save.' %> - +
    <% end_if %>