From eb5ad5778d345bcec29e6140fcdad49caf9b4654 Mon Sep 17 00:00:00 2001 From: Lee Bradley Date: Thu, 20 Jun 2019 12:55:01 +0100 Subject: [PATCH] Updates to work with SS 4.4.* including AssetStoreRouter Also allows uploads via the drag and drop field --- src/Forms/UploadFileField.php | 129 +++++++++++++++++++++++++++++- src/Storage/CloudinaryStorage.php | 20 ++++- 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/src/Forms/UploadFileField.php b/src/Forms/UploadFileField.php index 18d970e..c078d5c 100644 --- a/src/Forms/UploadFileField.php +++ b/src/Forms/UploadFileField.php @@ -2,34 +2,61 @@ namespace MadeHQ\Cloudinary\Forms; +use MadeHQ\Cloudinary\Model\FileLink; + +use SilverStripe\AssetAdmin\Controller\AssetAdmin; use SilverStripe\AssetAdmin\Forms\UploadField; use SilverStripe\Assets\Upload_Validator; -// use SilverStripe\ORM\ArrayList; +use SilverStripe\Core\Injector\Injector; + use SilverStripe\ORM\SS_List; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\FileUploadReceiver; use SilverStripe\Forms\FormField; use SilverStripe\Forms\TextField; use SilverStripe\Forms\TextareaField; use SilverStripe\View\Requirements; +use SilverStripe\Control\{ + HTTPRequest, + HTTPResponse +}; + class UploadFileField extends FormField { + use FileUploadReceiver; + private $fields; + private $uploadField; + + /** + * @config + * @var array + */ + private static $allowed_actions = [ + 'upload' + ]; + /** * {@inheritdoc} */ public function __construct($name, $title = null, $value = null) { - $uploadField = new UploadField(sprintf('%s[File]', $name), 'File'); + $this->constructFileUploadReceiver(); + + // When creating new files, rename on conflict + $this->getUpload()->setReplaceFile(false); + + $this->uploadField = new CustomUploadField(sprintf('%s[File]', $name), 'File'); $this->fields = [ - $uploadField->setAllowedMaxFileNumber(1), + $this->uploadField->setAllowedMaxFileNumber(1), TextField::create(sprintf('%s[Title]', $name), 'Title'), TextareaField::create(sprintf('%s[Description]', $name), 'Description'), ]; @@ -53,6 +80,60 @@ public function addField(FormField $field) return $this; } + /** + * Creates a single file based on a form-urlencoded upload. + * + * @param HTTPRequest $request + * @return HTTPResponse + */ + public function upload(HTTPRequest $request) + { + if ($this->isDisabled() || $this->isReadonly()) { + return $this->httpError(403); + } + + // CSRF check + $token = $this->getForm()->getSecurityToken(); + if (!$token->checkRequest($request)) { + return $this->httpError(400); + } + + $tmpFile = $request->postVar('Upload'); + /** @var File $file */ + $file = $this->saveTemporaryFile($tmpFile, $error); + + // Prepare result + if ($error) { + $result = [ + 'message' => [ + 'type' => 'error', + 'value' => $error, + ] + ]; + $this->getUpload()->clearErrors(); + return (new HTTPResponse(json_encode($result), 400)) + ->addHeader('Content-Type', 'application/json'); + } + + // We need an ID for getObjectFromData + if (!$file->isInDB()) { + $file->write(); + } + + // Return success response + $result = [ + AssetAdmin::singleton()->getObjectFromData($file) + ]; + + // Don't discard pre-generated client side canvas thumbnail + if ($result[0]['category'] === 'image') { + unset($result[0]['thumbnail']); + } + $this->getUpload()->clearErrors(); + return (new HTTPResponse(json_encode($result))) + ->addHeader('Content-Type', 'application/json'); + } + /** * Removes the field with the given name from the list of fields * to show in the CMS @@ -184,4 +265,46 @@ public function getValidator() { return Upload_Validator::create(); } + + /** + * Gets the foreign class that needs to be created, or 'File' as default if there + * is no relationship, or it cannot be determined. + * + * @param string $default Default value to return if no value could be calculated + * @return string Foreign class name. + */ + public function getRelationAutosetClass($default = File::class) + { + // Don't autodetermine relation if no relationship between parent record + if (!$this->getRelationAutoSetting()) { + return $default; + } + + // Check record and name + $name = $this->getName(); + $record = $this->getRecord(); + if (empty($name) || empty($record)) { + return $default; + } else { + $class = $record->getRelationClass($name); + if (is_subclass_of($class, FileLink::class)) { + $class = Injector::inst()->create($class)->getRelationClass('File'); + } + + return empty($class) ? $default : $class; + } + } +} + +/** + * This is a nasty hack so that the child Upload Field ends up pointing to this field + */ +class CustomUploadField extends UploadField +{ + public function Link($action = null) + { + $link = parent::Link($action); + $link = str_replace('[File]', '', $link); + return $link; + } } diff --git a/src/Storage/CloudinaryStorage.php b/src/Storage/CloudinaryStorage.php index cddbf66..cc0c167 100644 --- a/src/Storage/CloudinaryStorage.php +++ b/src/Storage/CloudinaryStorage.php @@ -2,12 +2,13 @@ namespace MadeHQ\Cloudinary\Storage; -use SilverStripe\Assets\Storage\AssetStoreRouter; +use MadeHQ\Cloudinary\Model\File; +use SilverStripe\Assets\Storage; use Cloudinary; use Cloudinary\Uploader; use SilverStripe\Control\Director; -class CloudinaryStorage implements AssetStoreRouter +class CloudinaryStorage implements Storage\AssetStore, Storage\AssetStoreRouter { /** * @inheritdoc @@ -35,6 +36,21 @@ public function setFromString($data, $filename, $hash = null, $variant = null, $ // intentionally empty } + /** + * This is called from the URL `/assets/{$Filename}` + * @inheritdoc + */ + public function getResponseFor($asset) + { + $file = File::getOneByPublicId($asset); + $controller = \SilverStripe\Control\Controller::curr(); + if ($file) { + return $controller->redirect($file->AbsoluteURL, 301); + } + + return $controller->httpError(404, sprintf('Unable to find [%s]', $asset)); + } + /** * @inheritdoc */