", $file->Filename;
- $file->write();
- }
- echo "Done!";
- }
-
- /**
- * Return the most recent modification time of anything in the folder.
- *
- * @param string $folder The folder, relative to the site root
- * @param array $extensionList An option array of file extensions to limit the search to
- * @return string Same as filemtime() format.
- */
- public static function folderModTime($folder, $extensionList = null)
- {
- $modTime = 0;
- if (!Filesystem::isAbsolute($folder)) {
- $folder = Director::baseFolder() . '/' . $folder;
- }
-
- $items = scandir($folder);
- foreach ($items as $item) {
- if ($item[0] != '.') {
- // Recurse into folders
- if (is_dir("$folder/$item")) {
- $modTime = max($modTime, self::folderModTime("$folder/$item", $extensionList));
-
- // Check files
- } else {
- $extension = null;
- if ($extensionList) {
- $extension = strtolower(substr($item, strrpos($item, '.')+1));
- }
- if (!$extensionList || in_array($extension, $extensionList)) {
- $modTime = max($modTime, filemtime("$folder/$item"));
- }
- }
- }
- }
-
- //if(!$recursiveCall) self::$cache_folderModTime[$cacheID] = $modTime;
- return $modTime;
- }
-
- /**
- * Returns true if the given filename is an absolute file reference.
- * Works on Linux and Windows.
- *
- * @param String $filename Absolute or relative filename, with or without path.
- * @return Boolean
- */
- public static function isAbsolute($filename)
- {
- if ($_ENV['OS'] == "Windows_NT" || $_SERVER['WINDIR']) {
- return $filename[1] == ':' && $filename[2] == '/';
- } else {
- return $filename[0] == '/';
- }
- }
-}
diff --git a/src/Assets/Flysystem/AssetAdapter.php b/src/Assets/Flysystem/AssetAdapter.php
deleted file mode 100644
index d594c8d28..000000000
--- a/src/Assets/Flysystem/AssetAdapter.php
+++ /dev/null
@@ -1,153 +0,0 @@
- [
- 'public' => 0744,
- 'private' => 0700,
- ],
- 'dir' => [
- 'public' => 0755,
- 'private' => 0700,
- ]
- );
-
- public function __construct($root = null, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS)
- {
- // Get root path, and ensure that this exists and is safe
- $root = $this->findRoot($root);
- Filesystem::makeFolder($root);
- $root = realpath($root);
-
- // Override permissions with config
- $permissions = $this->config()->get('file_permissions');
- parent::__construct($root, $writeFlags, $linkHandling, $permissions);
-
- // Configure server
- $this->configureServer();
- }
-
- /**
- * Determine the root folder absolute system path
- *
- * @param string $root
- * @return string
- */
- protected function findRoot($root)
- {
- // Empty root will set the path to assets
- if (!$root) {
- throw new \InvalidArgumentException("Missing argument for root path");
- }
-
- // Substitute leading ./ with BASE_PATH
- if (strpos($root, './') === 0) {
- return BASE_PATH . substr($root, 1);
- }
-
- // Substitute leading ./ with parent of BASE_PATH, in case storage is outside of the webroot.
- if (strpos($root, '../') === 0) {
- return dirname(BASE_PATH) . substr($root, 2);
- }
-
- return $root;
- }
-
- /**
- * Force flush and regeneration of server files
- */
- public function flush()
- {
- $this->configureServer(true);
- }
-
- /**
- * Configure server files for this store
- *
- * @param bool $forceOverwrite Force regeneration even if files already exist
- * @throws \Exception
- */
- protected function configureServer($forceOverwrite = false)
- {
- // Get server type
- $type = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '*';
- list($type) = explode('/', strtolower($type));
-
- // Determine configurations to write
- $rules = $this->config()->get('server_configuration');
- if (empty($rules[$type])) {
- return;
- }
- $configurations = $rules[$type];
-
- // Apply each configuration
- $config = new FlysystemConfig();
- $config->set('visibility', 'private');
- foreach ($configurations as $file => $template) {
- if ($forceOverwrite || !$this->has($file)) {
- // Evaluate file
- $content = $this->renderTemplate($template);
- $success = $this->write($file, $content, $config);
- if (!$success) {
- throw new \Exception("Error writing server configuration file \"{$file}\"");
- }
- }
- }
- }
-
- /**
- * Render server configuration file from a template file
- *
- * @param string $template
- * @return string Rendered results
- */
- protected function renderTemplate($template)
- {
- // Build allowed extensions
- $allowedExtensions = new ArrayList();
- foreach (File::config()->allowed_extensions as $extension) {
- if ($extension) {
- $allowedExtensions->push(new ArrayData(array(
- 'Extension' => preg_quote($extension)
- )));
- }
- }
-
- $viewer = new SSViewer(array($template));
- return (string)$viewer->process(new ArrayData(array(
- 'AllowedExtensions' => $allowedExtensions
- )));
- }
-}
diff --git a/src/Assets/Flysystem/FlysystemAssetStore.php b/src/Assets/Flysystem/FlysystemAssetStore.php
deleted file mode 100644
index 62be01d09..000000000
--- a/src/Assets/Flysystem/FlysystemAssetStore.php
+++ /dev/null
@@ -1,910 +0,0 @@
- 'private'
- );
-
- /**
- * Assign new flysystem backend
- *
- * @param Filesystem $filesystem
- * @return $this
- */
- public function setPublicFilesystem(Filesystem $filesystem)
- {
- if (!$filesystem->getAdapter() instanceof PublicAdapter) {
- throw new InvalidArgumentException("Configured adapter must implement PublicAdapter");
- }
- $this->publicFilesystem = $filesystem;
- return $this;
- }
-
- /**
- * Get the currently assigned flysystem backend
- *
- * @return Filesystem
- * @throws LogicException
- */
- public function getPublicFilesystem()
- {
- if (!$this->publicFilesystem) {
- throw new LogicException("Filesystem misconfiguration error");
- }
- return $this->publicFilesystem;
- }
-
- /**
- * Assign filesystem to use for non-public files
- *
- * @param Filesystem $filesystem
- * @return $this
- */
- public function setProtectedFilesystem(Filesystem $filesystem)
- {
- if (!$filesystem->getAdapter() instanceof ProtectedAdapter) {
- throw new InvalidArgumentException("Configured adapter must implement ProtectedAdapter");
- }
- $this->protectedFilesystem = $filesystem;
- return $this;
- }
-
- /**
- * Get filesystem to use for non-public files
- *
- * @return Filesystem
- * @throws Exception
- */
- public function getProtectedFilesystem()
- {
- if (!$this->protectedFilesystem) {
- throw new Exception("Filesystem misconfiguration error");
- }
- return $this->protectedFilesystem;
- }
-
- /**
- * Return the store that contains the given fileID
- *
- * @param string $fileID Internal file identifier
- * @return Filesystem
- */
- protected function getFilesystemFor($fileID)
- {
- if ($this->getPublicFilesystem()->has($fileID)) {
- return $this->getPublicFilesystem();
- }
-
- if ($this->getProtectedFilesystem()->has($fileID)) {
- return $this->getProtectedFilesystem();
- }
-
- return null;
- }
-
- public function getCapabilities()
- {
- return array(
- 'visibility' => array(
- self::VISIBILITY_PUBLIC,
- self::VISIBILITY_PROTECTED
- ),
- 'conflict' => array(
- self::CONFLICT_EXCEPTION,
- self::CONFLICT_OVERWRITE,
- self::CONFLICT_RENAME,
- self::CONFLICT_USE_EXISTING
- )
- );
- }
-
- public function getVisibility($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- if ($this->getPublicFilesystem()->has($fileID)) {
- return self::VISIBILITY_PUBLIC;
- }
-
- if ($this->getProtectedFilesystem()->has($fileID)) {
- return self::VISIBILITY_PROTECTED;
- }
-
- return null;
- }
-
-
- public function getAsStream($filename, $hash, $variant = null)
- {
- $fileID = $this->getFileID($filename, $hash, $variant);
- return $this
- ->getFilesystemFor($fileID)
- ->readStream($fileID);
- }
-
- public function getAsString($filename, $hash, $variant = null)
- {
- $fileID = $this->getFileID($filename, $hash, $variant);
- return $this
- ->getFilesystemFor($fileID)
- ->read($fileID);
- }
-
- public function getAsURL($filename, $hash, $variant = null, $grant = true)
- {
- if ($grant) {
- $this->grant($filename, $hash);
- }
- $fileID = $this->getFileID($filename, $hash, $variant);
-
- // Check with filesystem this asset exists in
- $public = $this->getPublicFilesystem();
- $protected = $this->getProtectedFilesystem();
- if ($public->has($fileID) || !$protected->has($fileID)) {
- /** @var PublicAdapter $publicAdapter */
- $publicAdapter = $public->getAdapter();
- return $publicAdapter->getPublicUrl($fileID);
- } else {
- /** @var ProtectedAdapter $protectedAdapter */
- $protectedAdapter = $protected->getAdapter();
- return $protectedAdapter->getProtectedUrl($fileID);
- }
- }
-
- public function setFromLocalFile($path, $filename = null, $hash = null, $variant = null, $config = array())
- {
- // Validate this file exists
- if (!file_exists($path)) {
- throw new InvalidArgumentException("$path does not exist");
- }
-
- // Get filename to save to
- if (empty($filename)) {
- $filename = basename($path);
- }
-
- // Callback for saving content
- $callback = function (Filesystem $filesystem, $fileID) use ($path) {
- // Read contents as string into flysystem
- $handle = fopen($path, 'r');
- if ($handle === false) {
- throw new InvalidArgumentException("$path could not be opened for reading");
- }
- $result = $filesystem->putStream($fileID, $handle);
- fclose($handle);
- return $result;
- };
-
- // When saving original filename, generate hash
- if (!$variant) {
- $hash = sha1_file($path);
- }
-
- // Submit to conflict check
- return $this->writeWithCallback($callback, $filename, $hash, $variant, $config);
- }
-
- public function setFromString($data, $filename, $hash = null, $variant = null, $config = array())
- {
- // Callback for saving content
- $callback = function (Filesystem $filesystem, $fileID) use ($data) {
- return $filesystem->put($fileID, $data);
- };
-
- // When saving original filename, generate hash
- if (!$variant) {
- $hash = sha1($data);
- }
-
- // Submit to conflict check
- return $this->writeWithCallback($callback, $filename, $hash, $variant, $config);
- }
-
- public function setFromStream($stream, $filename, $hash = null, $variant = null, $config = array())
- {
- // If the stream isn't rewindable, write to a temporary filename
- if (!$this->isSeekableStream($stream)) {
- $path = $this->getStreamAsFile($stream);
- $result = $this->setFromLocalFile($path, $filename, $hash, $variant, $config);
- unlink($path);
- return $result;
- }
-
- // Callback for saving content
- $callback = function (Filesystem $filesystem, $fileID) use ($stream) {
- return $filesystem->putStream($fileID, $stream);
- };
-
- // When saving original filename, generate hash
- if (!$variant) {
- $hash = $this->getStreamSHA1($stream);
- }
-
- // Submit to conflict check
- return $this->writeWithCallback($callback, $filename, $hash, $variant, $config);
- }
-
- public function delete($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- $protected = $this->deleteFromFilesystem($fileID, $this->getProtectedFilesystem());
- $public = $this->deleteFromFilesystem($fileID, $this->getPublicFilesystem());
- return $protected || $public;
- }
-
- /**
- * Delete the given file (and any variants) in the given {@see Filesystem}
- *
- * @param string $fileID
- * @param Filesystem $filesystem
- * @return bool True if a file was deleted
- */
- protected function deleteFromFilesystem($fileID, Filesystem $filesystem)
- {
- $deleted = false;
- foreach ($this->findVariants($fileID, $filesystem) as $nextID) {
- $filesystem->delete($nextID);
- $deleted = true;
- }
-
- // Truncate empty dirs
- $this->truncateDirectory(dirname($fileID), $filesystem);
-
- return $deleted;
- }
-
- /**
- * Clear directory if it's empty
- *
- * @param string $dirname Name of directory
- * @param Filesystem $filesystem
- */
- protected function truncateDirectory($dirname, Filesystem $filesystem)
- {
- if ($dirname
- && ltrim(dirname($dirname), '.')
- && ! $this->config()->get('keep_empty_dirs')
- && ! $filesystem->listContents($dirname)
- ) {
- $filesystem->deleteDir($dirname);
- }
- }
-
- /**
- * Returns an iterable {@see Generator} of all files / variants for the given $fileID in the given $filesystem
- * This includes the empty (no) variant.
- *
- * @param string $fileID ID of original file to compare with.
- * @param Filesystem $filesystem
- * @return Generator
- */
- protected function findVariants($fileID, Filesystem $filesystem)
- {
- $dirname = ltrim(dirname($fileID), '.');
- foreach ($filesystem->listContents($dirname) as $next) {
- if ($next['type'] !== 'file') {
- continue;
- }
- $nextID = $next['path'];
- // Compare given file to target, omitting variant
- if ($fileID === $this->removeVariant($nextID)) {
- yield $nextID;
- }
- }
- }
-
- public function publish($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- $protected = $this->getProtectedFilesystem();
- $public = $this->getPublicFilesystem();
- $this->moveBetweenFilesystems($fileID, $protected, $public);
- }
-
- public function protect($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- $public = $this->getPublicFilesystem();
- $protected = $this->getProtectedFilesystem();
- $this->moveBetweenFilesystems($fileID, $public, $protected);
- }
-
- /**
- * Move a file (and its associative variants) between filesystems
- *
- * @param string $fileID
- * @param Filesystem $from
- * @param Filesystem $to
- */
- protected function moveBetweenFilesystems($fileID, Filesystem $from, Filesystem $to)
- {
- foreach ($this->findVariants($fileID, $from) as $nextID) {
- // Copy via stream
- $stream = $from->readStream($nextID);
- $to->putStream($nextID, $stream);
- fclose($stream);
- $from->delete($nextID);
- }
-
- // Truncate empty dirs
- $this->truncateDirectory(dirname($fileID), $from);
- }
-
- public function grant($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- $granted = Session::get(self::GRANTS_SESSION) ?: array();
- $granted[$fileID] = true;
- Session::set(self::GRANTS_SESSION, $granted);
- }
-
- public function revoke($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- $granted = Session::get(self::GRANTS_SESSION) ?: array();
- unset($granted[$fileID]);
- if ($granted) {
- Session::set(self::GRANTS_SESSION, $granted);
- } else {
- Session::clear(self::GRANTS_SESSION);
- }
- }
-
- public function canView($filename, $hash)
- {
- $fileID = $this->getFileID($filename, $hash);
- if ($this->getProtectedFilesystem()->has($fileID)) {
- return $this->isGranted($fileID);
- }
- return true;
- }
-
- /**
- * Determine if a grant exists for the given FileID
- *
- * @param string $fileID
- * @return bool
- */
- protected function isGranted($fileID)
- {
- // Since permissions are applied to the non-variant only,
- // map back to the original file before checking
- $originalID = $this->removeVariant($fileID);
- $granted = Session::get(self::GRANTS_SESSION) ?: array();
- return !empty($granted[$originalID]);
- }
-
- /**
- * get sha1 hash from stream
- *
- * @param resource $stream
- * @return string str1 hash
- */
- protected function getStreamSHA1($stream)
- {
- Util::rewindStream($stream);
- $context = hash_init('sha1');
- hash_update_stream($context, $stream);
- return hash_final($context);
- }
-
- /**
- * Get stream as a file
- *
- * @param resource $stream
- * @return string Filename of resulting stream content
- * @throws Exception
- */
- protected function getStreamAsFile($stream)
- {
- // Get temporary file and name
- $file = tempnam(sys_get_temp_dir(), 'ssflysystem');
- $buffer = fopen($file, 'w');
- if (!$buffer) {
- throw new Exception("Could not create temporary file");
- }
-
- // Transfer from given stream
- Util::rewindStream($stream);
- stream_copy_to_stream($stream, $buffer);
- if (! fclose($buffer)) {
- throw new Exception("Could not write stream to temporary file");
- }
-
- return $file;
- }
-
- /**
- * Determine if this stream is seekable
- *
- * @param resource $stream
- * @return bool True if this stream is seekable
- */
- protected function isSeekableStream($stream)
- {
- return Util::isSeekableStream($stream);
- }
-
- /**
- * Invokes the conflict resolution scheme on the given content, and invokes a callback if
- * the storage request is approved.
- *
- * @param callable $callback Will be invoked and passed a fileID if the file should be stored
- * @param string $filename Name for the resulting file
- * @param string $hash SHA1 of the original file content
- * @param string $variant Variant to write
- * @param array $config Write options. {@see AssetStore}
- * @return array Tuple associative array (Filename, Hash, Variant)
- * @throws Exception
- */
- protected function writeWithCallback($callback, $filename, $hash, $variant = null, $config = array())
- {
- // Set default conflict resolution
- if (empty($config['conflict'])) {
- $conflictResolution = $this->getDefaultConflictResolution($variant);
- } else {
- $conflictResolution = $config['conflict'];
- }
-
- // Validate parameters
- if ($variant && $conflictResolution === AssetStore::CONFLICT_RENAME) {
- // As variants must follow predictable naming rules, they should not be dynamically renamed
- throw new InvalidArgumentException("Rename cannot be used when writing variants");
- }
- if (!$filename) {
- throw new InvalidArgumentException("Filename is missing");
- }
- if (!$hash) {
- throw new InvalidArgumentException("File hash is missing");
- }
-
- $filename = $this->cleanFilename($filename);
- $fileID = $this->getFileID($filename, $hash, $variant);
-
- // Check conflict resolution scheme
- $resolvedID = $this->resolveConflicts($conflictResolution, $fileID);
- if ($resolvedID !== false) {
- // Check if source file already exists on the filesystem
- $mainID = $this->getFileID($filename, $hash);
- $filesystem = $this->getFilesystemFor($mainID);
-
- // If writing a new file use the correct visibility
- if (!$filesystem) {
- // Default to public store unless requesting protected store
- if (isset($config['visibility']) && $config['visibility'] === self::VISIBILITY_PROTECTED) {
- $filesystem = $this->getProtectedFilesystem();
- } else {
- $filesystem = $this->getPublicFilesystem();
- }
- }
-
- // Submit and validate result
- $result = $callback($filesystem, $resolvedID);
- if (!$result) {
- throw new Exception("Could not save {$filename}");
- }
-
- // in case conflict resolution renamed the file, return the renamed
- $filename = $this->getOriginalFilename($resolvedID);
- } elseif (empty($variant)) {
- // If deferring to the existing file, return the sha of the existing file,
- // unless we are writing a variant (which has the same hash value as its original file)
- $stream = $this
- ->getFilesystemFor($fileID)
- ->readStream($fileID);
- $hash = $this->getStreamSHA1($stream);
- }
-
- return array(
- 'Filename' => $filename,
- 'Hash' => $hash,
- 'Variant' => $variant
- );
- }
-
- /**
- * Choose a default conflict resolution
- *
- * @param string $variant
- * @return string
- */
- protected function getDefaultConflictResolution($variant)
- {
- // If using new naming scheme (segment by hash) it's normally safe to overwrite files.
- // Variants are also normally safe to overwrite, since lazy-generation is implemented at a higher level.
- $legacy = $this->useLegacyFilenames();
- if (!$legacy || $variant) {
- return AssetStore::CONFLICT_OVERWRITE;
- }
-
- // Legacy behaviour is to rename
- return AssetStore::CONFLICT_RENAME;
- }
-
- /**
- * Determine if legacy filenames should be used. These do not have hash path parts.
- *
- * @return bool
- */
- protected function useLegacyFilenames()
- {
- return $this->config()->get('legacy_filenames');
- }
-
- public function getMetadata($filename, $hash, $variant = null)
- {
- $fileID = $this->getFileID($filename, $hash, $variant);
- $filesystem = $this->getFilesystemFor($fileID);
- if ($filesystem) {
- return $filesystem->getMetadata($fileID);
- }
- return null;
- }
-
- public function getMimeType($filename, $hash, $variant = null)
- {
- $fileID = $this->getFileID($filename, $hash, $variant);
- $filesystem = $this->getFilesystemFor($fileID);
- if ($filesystem) {
- return $filesystem->getMimetype($fileID);
- }
- return null;
- }
-
- public function exists($filename, $hash, $variant = null)
- {
- $fileID = $this->getFileID($filename, $hash, $variant);
- $filesystem = $this->getFilesystemFor($fileID);
- return !empty($filesystem);
- }
-
- /**
- * Determine the path that should be written to, given the conflict resolution scheme
- *
- * @param string $conflictResolution
- * @param string $fileID
- * @return string|false Safe filename to write to. If false, then don't write, and use existing file.
- * @throws Exception
- */
- protected function resolveConflicts($conflictResolution, $fileID)
- {
- // If overwrite is requested, simply put
- if ($conflictResolution === AssetStore::CONFLICT_OVERWRITE) {
- return $fileID;
- }
-
- // Otherwise, check if this exists
- $exists = $this->getFilesystemFor($fileID);
- if (!$exists) {
- return $fileID;
- }
-
- // Flysystem defaults to use_existing
- switch ($conflictResolution) {
- // Throw tantrum
- case static::CONFLICT_EXCEPTION: {
- throw new InvalidArgumentException("File already exists at path {$fileID}");
- }
-
- // Rename
- case static::CONFLICT_RENAME: {
- foreach ($this->fileGeneratorFor($fileID) as $candidate) {
- if (!$this->getFilesystemFor($candidate)) {
- return $candidate;
- }
- }
-
- throw new InvalidArgumentException("File could not be renamed with path {$fileID}");
- }
-
- // Use existing file
- case static::CONFLICT_USE_EXISTING:
- default: {
- return false;
- }
- }
- }
-
- /**
- * Get an asset renamer for the given filename.
- *
- * @param string $fileID Adapter specific identifier for this file/version
- * @return AssetNameGenerator
- */
- protected function fileGeneratorFor($fileID)
- {
- return Injector::inst()->createWithArgs('AssetNameGenerator', array($fileID));
- }
-
- /**
- * Performs filename cleanup before sending it back.
- *
- * This name should not contain hash or variants.
- *
- * @param string $filename
- * @return string
- */
- protected function cleanFilename($filename)
- {
- // Since we use double underscore to delimit variants, eradicate them from filename
- return preg_replace('/_{2,}/', '_', $filename);
- }
-
- /**
- * Given a FileID, map this back to the original filename, trimming variant and hash
- *
- * @param string $fileID Adapter specific identifier for this file/version
- * @return string Filename for this file, omitting hash and variant
- */
- protected function getOriginalFilename($fileID)
- {
- // Remove variant
- $originalID = $this->removeVariant($fileID);
-
- // Remove hash (unless using legacy filenames, without hash)
- if ($this->useLegacyFilenames()) {
- return $originalID;
- } else {
- return preg_replace(
- '/(?[a-zA-Z0-9]{10}\\/)(?[^\\/]+)$/',
- '$2',
- $originalID
- );
- }
- }
-
- /**
- * Remove variant from a fileID
- *
- * @param string $fileID
- * @return string FileID without variant
- */
- protected function removeVariant($fileID)
- {
- // Check variant
- if (preg_match('/^(?((?[^\\.]+)(?.*)$/', $fileID, $matches)) {
- return $matches['before'] . $matches['after'];
- }
- // There is no variant, so return original value
- return $fileID;
- }
-
- /**
- * Map file tuple (hash, name, variant) to a filename to be used by flysystem
- *
- * The resulting file will look something like my/directory/EA775CB4D4/filename__variant.jpg
- *
- * @param string $filename Name of file
- * @param string $hash Hash of original file
- * @param string $variant (if given)
- * @return string Adapter specific identifier for this file/version
- */
- protected function getFileID($filename, $hash, $variant = null)
- {
- // Since we use double underscore to delimit variants, eradicate them from filename
- $filename = $this->cleanFilename($filename);
- $name = basename($filename);
-
- // Split extension
- $extension = null;
- if (($pos = strpos($name, '.')) !== false) {
- $extension = substr($name, $pos);
- $name = substr($name, 0, $pos);
- }
-
- // Unless in legacy mode, inject hash just prior to the filename
- if ($this->useLegacyFilenames()) {
- $fileID = $name;
- } else {
- $fileID = substr($hash, 0, 10) . '/' . $name;
- }
-
- // Add directory
- $dirname = ltrim(dirname($filename), '.');
- if ($dirname) {
- $fileID = $dirname . '/' . $fileID;
- }
-
- // Add variant
- if ($variant) {
- $fileID .= '__' . $variant;
- }
-
- // Add extension
- if ($extension) {
- $fileID .= $extension;
- }
-
- return $fileID;
- }
-
- /**
- * Ensure each adapter re-generates its own server configuration files
- */
- public static function flush()
- {
- // Ensure that this instance is constructed on flush, thus forcing
- // bootstrapping of necessary .htaccess / web.config files
- $instance = singleton('AssetStore');
- if ($instance instanceof FlysystemAssetStore) {
- $publicAdapter = $instance->getPublicFilesystem()->getAdapter();
- if ($publicAdapter instanceof AssetAdapter) {
- $publicAdapter->flush();
- }
- $protectedAdapter = $instance->getProtectedFilesystem()->getAdapter();
- if ($protectedAdapter instanceof AssetAdapter) {
- $protectedAdapter->flush();
- }
- }
- }
-
- public function getResponseFor($asset)
- {
- // Check if file exists
- $filesystem = $this->getFilesystemFor($asset);
- if (!$filesystem) {
- return $this->createMissingResponse();
- }
-
- // Block directory access
- if ($filesystem->get($asset) instanceof Directory) {
- return $this->createDeniedResponse();
- }
-
- // Deny if file is protected and denied
- if ($filesystem === $this->getProtectedFilesystem() && !$this->isGranted($asset)) {
- return $this->createDeniedResponse();
- }
-
- // Serve up file response
- return $this->createResponseFor($filesystem, $asset);
- }
-
- /**
- * Generate an {@see HTTPResponse} for the given file from the source filesystem
- * @param FilesystemInterface $flysystem
- * @param string $fileID
- * @return HTTPResponse
- */
- protected function createResponseFor(FilesystemInterface $flysystem, $fileID)
- {
- // Build response body
- // @todo: gzip / buffer response?
- $body = $flysystem->read($fileID);
- $mime = $flysystem->getMimetype($fileID);
- $response = new HTTPResponse($body, 200);
-
- // Add headers
- $response->addHeader('Content-Type', $mime);
- $headers = $this->config()->get('file_response_headers');
- foreach ($headers as $header => $value) {
- $response->addHeader($header, $value);
- }
- return $response;
- }
-
- /**
- * Generate a response for requests to a denied protected file
- *
- * @return HTTPResponse
- */
- protected function createDeniedResponse()
- {
- $code = (int)$this->config()->get('denied_response_code');
- return $this->createErrorResponse($code);
- }
-
- /**
- * Generate a response for missing file requests
- *
- * @return HTTPResponse
- */
- protected function createMissingResponse()
- {
- $code = (int)$this->config()->get('missing_response_code');
- return $this->createErrorResponse($code);
- }
-
- /**
- * Create a response with the given error code
- *
- * @param int $code
- * @return HTTPResponse
- */
- protected function createErrorResponse($code)
- {
- $response = new HTTPResponse('', $code);
-
- // Show message in dev
- if (!Director::isLive()) {
- $response->setBody($response->getStatusDescription());
- }
-
- return $response;
- }
-}
diff --git a/src/Assets/Flysystem/GeneratedAssetHandler.php b/src/Assets/Flysystem/GeneratedAssetHandler.php
deleted file mode 100644
index 9eb73e8f4..000000000
--- a/src/Assets/Flysystem/GeneratedAssetHandler.php
+++ /dev/null
@@ -1,116 +0,0 @@
-assetStore = $store;
- return $this;
- }
-
- /**
- * Get the asset backend
- *
- * @return Filesystem
- * @throws Exception
- */
- public function getFilesystem()
- {
- if (!$this->assetStore) {
- throw new Exception("Filesystem misconfiguration error");
- }
- return $this->assetStore;
- }
-
- public function getContentURL($filename, $callback = null)
- {
- $result = $this->checkOrCreate($filename, $callback);
- if (!$result) {
- return null;
- }
- /** @var PublicAdapter $adapter */
- $adapter = $this
- ->getFilesystem()
- ->getAdapter();
- return $adapter->getPublicUrl($filename);
- }
-
- public function getContent($filename, $callback = null)
- {
- $result = $this->checkOrCreate($filename, $callback);
- if (!$result) {
- return null;
- }
- return $this
- ->getFilesystem()
- ->read($filename);
- }
-
- /**
- * Check if the file exists or that the $callback provided was able to regenerate it.
- *
- * @param string $filename
- * @param callable $callback
- * @return bool Whether or not the file exists
- * @throws Exception If an error has occurred during save
- */
- protected function checkOrCreate($filename, $callback = null)
- {
- // Check if there is an existing asset
- if ($this->getFilesystem()->has($filename)) {
- return true;
- }
-
- if (!$callback) {
- return false;
- }
-
- // Invoke regeneration and save
- $content = call_user_func($callback);
- $this->setContent($filename, $content);
- return true;
- }
-
- public function setContent($filename, $content)
- {
- // Store content
- $result = $this
- ->getFilesystem()
- ->put($filename, $content);
-
- if (!$result) {
- throw new Exception("Error regenerating file \"{$filename}\"");
- }
- }
-
- public function removeContent($filename)
- {
- if ($this->getFilesystem()->has($filename)) {
- $handler = $this->getFilesystem()->get($filename);
- $handler->delete();
- }
- }
-}
diff --git a/src/Assets/Flysystem/ProtectedAdapter.php b/src/Assets/Flysystem/ProtectedAdapter.php
deleted file mode 100644
index 33f76df3d..000000000
--- a/src/Assets/Flysystem/ProtectedAdapter.php
+++ /dev/null
@@ -1,20 +0,0 @@
- array(
- '.htaccess' => "SilverStripe\\Assets\\Flysystem\\ProtectedAssetAdapter_HTAccess"
- ),
- 'microsoft-iis' => array(
- 'web.config' => "SilverStripe\\Assets\\Flysystem\\ProtectedAssetAdapter_WebConfig"
- )
- );
-
- protected function findRoot($root)
- {
- // Use explicitly defined path
- if ($root) {
- return parent::findRoot($root);
- }
-
- // Use environment defined path or default location is under assets
- if ($path = getenv('SS_PROTECTED_ASSETS_PATH')) {
- return $path;
- }
-
- // Default location
- return ASSETS_PATH . '/' . Config::inst()->get(__CLASS__, 'secure_folder');
- }
-
- /**
- * Provide secure downloadable
- *
- * @param string $path
- * @return string|null
- */
- public function getProtectedUrl($path)
- {
- // Public URLs are handled via a request handler within /assets.
- // If assets are stored locally, then asset paths of protected files should be equivalent.
- return Controller::join_links(Director::baseURL(), ASSETS_DIR, $path);
- }
-}
diff --git a/src/Assets/Flysystem/PublicAdapter.php b/src/Assets/Flysystem/PublicAdapter.php
deleted file mode 100644
index 0770e246c..000000000
--- a/src/Assets/Flysystem/PublicAdapter.php
+++ /dev/null
@@ -1,20 +0,0 @@
- array(
- '.htaccess' => "SilverStripe\\Assets\\Flysystem\\PublicAssetAdapter_HTAccess"
- ),
- 'microsoft-iis' => array(
- 'web.config' => "SilverStripe\\Assets\\Flysystem\\PublicAssetAdapter_WebConfig"
- )
- );
-
- protected function findRoot($root)
- {
- if ($root) {
- $path = parent::findRoot($root);
- } else {
- $path = ASSETS_PATH;
- }
-
- // Detect segment between root directory and assets root
- if (stripos($path, BASE_PATH) === 0) {
- $this->parentUrlPrefix = substr($path, strlen(BASE_PATH));
- } else {
- $this->parentUrlPrefix = ASSETS_DIR;
- }
- return $path;
- }
-
- /**
- * Provide downloadable url
- *
- * @param string $path
- * @return string|null
- */
- public function getPublicUrl($path)
- {
- return Controller::join_links(Director::baseURL(), $this->parentUrlPrefix, $path);
- }
-}
diff --git a/src/Assets/Folder.php b/src/Assets/Folder.php
deleted file mode 100644
index a9458f47d..000000000
--- a/src/Assets/Folder.php
+++ /dev/null
@@ -1,355 +0,0 @@
-isInDB();
- }
-
- /**
- * Find the given folder or create it as a database record
- *
- * @param string $folderPath Directory path relative to assets root
- * @return Folder|null
- */
- public static function find_or_make($folderPath)
- {
- // replace leading and trailing slashes
- $folderPath = preg_replace('/^\/?(.*)\/?$/', '$1', trim($folderPath));
- $parts = explode("/", $folderPath);
-
- $parentID = 0;
- $item = null;
- $filter = FileNameFilter::create();
- foreach ($parts as $part) {
- if (!$part) {
- continue; // happens for paths with a trailing slash
- }
-
- // Ensure search includes folders with illegal characters removed, but
- // err in favour of matching existing folders if $folderPath
- // includes illegal characters itself.
- $partSafe = $filter->filter($part);
- $item = Folder::get()->filter(array(
- 'ParentID' => $parentID,
- 'Name' => array($partSafe, $part)
- ))->first();
-
- if (!$item) {
- $item = new Folder();
- $item->ParentID = $parentID;
- $item->Name = $partSafe;
- $item->Title = $part;
- $item->write();
- }
- $parentID = $item->ID;
- }
-
- return $item;
- }
-
- public function onBeforeDelete()
- {
- foreach ($this->AllChildren() as $child) {
- $child->delete();
- }
-
- parent::onBeforeDelete();
- }
-
- /**
- * Return the relative URL of an icon for this file type
- *
- * @return string
- */
- public function getIcon()
- {
- return FRAMEWORK_DIR . "/client/images/app_icons/folder_icon_large.png";
- }
-
- /**
- * Override setting the Title of Folders to that Name and Title are always in sync.
- * Note that this is not appropriate for files, because someone might want to create a human-readable name
- * of a file that is different from its name on disk. But folders should always match their name on disk.
- *
- * @param string $title
- * @return $this
- */
- public function setTitle($title)
- {
- $this->setField('Title', $title);
- $this->setField('Name', $title);
-
- return $this;
- }
-
- /**
- * Get the folder title
- *
- * @return string
- */
- public function getTitle()
- {
- return $this->Name;
- }
-
- /**
- * A folder doesn't have a (meaningful) file size.
- *
- * @return null
- */
- public function getSize()
- {
- return null;
- }
-
- /**
- * Returns all children of this folder
- *
- * @return DataList
- */
- public function myChildren()
- {
- return File::get()->filter("ParentID", $this->ID);
- }
-
- /**
- * Returns true if this folder has children
- *
- * @return bool
- */
- public function hasChildren()
- {
- return $this->myChildren()->exists();
- }
-
- /**
- * Returns true if this folder has children
- *
- * @return bool
- */
- public function hasChildFolders()
- {
- return $this->ChildFolders()->exists();
- }
-
- /**
- * Return the FieldList used to edit this folder in the CMS.
- * You can modify this FieldList by subclassing folder, or by creating a {@link DataExtension}
- * and implemeting updateCMSFields(FieldList $fields) on that extension.
- *
- * @return FieldList
- */
- public function getCMSFields()
- {
- // Don't show readonly path until we can implement parent folder selection,
- // it's too confusing when readonly (makes sense for files only).
-
- $width = (int)Image::config()->get('asset_preview_width');
- $previewLink = Convert::raw2att($this->ScaleMaxWidth($width)->getIcon());
- $image = "";
-
- $content = Tab::create(
- 'Main',
- HeaderField::create('TitleHeader', $this->Title, 1)
- ->addExtraClass('editor__heading'),
- LiteralField::create("IconFull", $image)
- ->addExtraClass('editor__file-preview'),
- TabSet::create(
- 'Editor',
- Tab::create(
- 'Details',
- TextField::create("Name", $this->fieldLabel('Filename'))
- )
- ),
- HiddenField::create('ID', $this->ID)
- );
-
- $fields = FieldList::create(TabSet::create('Root', $content));
-
- $this->extend('updateCMSFields', $fields);
-
- return $fields;
- }
-
- /**
- * Get the children of this folder that are also folders.
- *
- * @return DataList
- */
- public function ChildFolders()
- {
- return Folder::get()->filter('ParentID', $this->ID);
- }
-
- /**
- * Get the number of children of this folder that are also folders.
- *
- * @return int
- */
- public function numChildFolders()
- {
- return $this->ChildFolders()->count();
- }
- /**
- * @return string
- */
- public function CMSTreeClasses()
- {
- $classes = sprintf('class-%s', $this->class);
-
- if (!$this->canDelete()) {
- $classes .= " nodelete";
- }
-
- if (!$this->canEdit()) {
- $classes .= " disabled";
- }
-
- $classes .= $this->markingClasses('numChildFolders');
-
- return $classes;
- }
-
- /**
- * @return string
- */
- public function getTreeTitle()
- {
- return sprintf(
- "%s",
- Convert::raw2att(preg_replace('~\R~u', ' ', $this->Title))
- );
- }
-
- public function getFilename()
- {
- return parent::generateFilename() . '/';
- }
-
- /**
- * Folders do not have public URLs
- *
- * @param bool $grant
- * @return null|string
- */
- public function getURL($grant = true)
- {
- return null;
- }
-
- /**
- * Folders do not have public URLs
- *
- * @return string
- */
- public function getAbsoluteURL()
- {
- return null;
- }
-
- public function onAfterWrite()
- {
- parent::onAfterWrite();
-
- // No publishing UX for folders, so just cascade changes live
- if (Versioned::get_stage() === Versioned::DRAFT) {
- $this->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
- }
-
- // Update draft version of all child records
- $this->updateChildFilesystem();
- }
-
- public function onAfterDelete()
- {
- parent::onAfterDelete();
-
- // Cascade deletions to live
- if (Versioned::get_stage() === Versioned::DRAFT) {
- $this->deleteFromStage(Versioned::LIVE);
- }
- }
-
- public function updateFilesystem()
- {
- // No filesystem changes to update
- }
-
- /**
- * If a write is skipped due to no changes, ensure that nested records still get asked to update
- */
- public function onAfterSkippedWrite()
- {
- $this->updateChildFilesystem();
- }
-
- /**
- * Update filesystem of all children
- */
- public function updateChildFilesystem()
- {
- // Don't synchronise on live (rely on publishing instead)
- if (Versioned::get_stage() === Versioned::LIVE) {
- return;
- }
-
- $this->flushCache();
- // Writing this record should trigger a write (and potential updateFilesystem) on each child
- foreach ($this->AllChildren() as $child) {
- $child->write();
- }
- }
-
- public function StripThumbnail()
- {
- return null;
- }
-
- public function validate()
- {
- $result = ValidationResult::create();
- $this->extend('validate', $result);
- return $result;
- }
-}
diff --git a/src/Assets/GDBackend.php b/src/Assets/GDBackend.php
deleted file mode 100644
index a530af2b4..000000000
--- a/src/Assets/GDBackend.php
+++ /dev/null
@@ -1,767 +0,0 @@
-cache = Injector::inst()->get(CacheInterface::class . '.GDBackend_Manipulations');
-
- if ($assetContainer) {
- $this->loadFromContainer($assetContainer);
- }
- }
-
- public function __destruct()
- {
- if ($resource = $this->getImageResource()) {
- imagedestroy($resource);
- }
- }
-
- public function loadFrom($path)
- {
- // If we're working with image resampling, things could take a while. Bump up the time-limit
- increase_time_limit_to(300);
- $this->resetResource();
-
- // Skip if path is unavailable
- if (!file_exists($path)) {
- return;
- }
- $mtime = filemtime($path);
-
- // Skip if load failed before
- if ($this->failedResample($path, $mtime)) {
- return;
- }
-
- // We use getimagesize instead of extension checking, because sometimes extensions are wrong.
- $meta = getimagesize($path);
- if ($meta === false || !$this->checkAvailableMemory($meta)) {
- $this->markFailed($path, $mtime);
- return;
- }
-
- $gd = null;
- switch ($meta[2]) {
- case 1:
- if (function_exists('imagecreatefromgif')) {
- $gd = imagecreatefromgif($path);
- }
- break;
- case 2:
- if (function_exists('imagecreatefromjpeg')) {
- $gd = imagecreatefromjpeg($path);
- }
- break;
- case 3:
- if (function_exists('imagecreatefrompng')) {
- $gd = imagecreatefrompng($path);
- if ($gd) {
- imagesavealpha($gd, true); // save alphablending setting (important)
- }
- }
- break;
- }
-
- // image failed
- if ($gd === false) {
- $this->markFailed($path, $mtime);
- return;
- }
-
- // Save
- $this->setImageResource($gd);
- }
-
- public function loadFromContainer(AssetContainer $assetContainer)
- {
- // If we're working with image resampling, things could take a while. Bump up the time-limit
- increase_time_limit_to(300);
- $this->resetResource();
-
- // Skip non-existant files
- if (!$assetContainer->exists()) {
- return;
- }
-
- // Skip if failed before, or image is too large
- $filename = $assetContainer->getFilename();
- $hash = $assetContainer->getHash();
- $variant = $assetContainer->getVariant();
- if ($this->failedResample($filename, $hash, $variant)) {
- return;
- }
-
- $content = $assetContainer->getString();
-
- // We use getimagesizefromstring instead of extension checking, because sometimes extensions are wrong.
- $meta = getimagesizefromstring($content);
- if ($meta === false || !$this->checkAvailableMemory($meta)) {
- $this->markFailed($filename, $hash, $variant);
- return;
- }
-
- // Mark as potentially failed prior to creation, resetting this on success
- $image = imagecreatefromstring($content);
- if ($image === false) {
- $this->markFailed($filename, $hash, $variant);
- return;
- }
-
- imagealphablending($image, false);
- imagesavealpha($image, true); // save alphablending setting (important)
- $this->setImageResource($image);
- }
-
- /**
- * Clear GD resource
- */
- protected function resetResource()
- {
- // Set defaults and clear resource
- $this->setImageResource(null);
- $this->quality = $this->config()->default_quality;
- $this->interlace = $this->config()->image_interlace;
- }
-
- /**
- * Assign or clear GD resource
- *
- * @param resource|null $resource
- */
- public function setImageResource($resource)
- {
- $this->gd = $resource;
- $this->width = $resource ? imagesx($resource) : 0;
- $this->height = $resource ? imagesy($resource) : 0;
- }
-
- /**
- * Get the currently assigned GD resource
- *
- * @return resource
- */
- public function getImageResource()
- {
- return $this->gd;
- }
-
- /**
- * Check if this image has previously crashed GD when attempting to open it - if it's opened
- * successfully, the manipulation's cache key is removed.
- *
- * @param string $arg,... Any number of args that identify this image
- * @return bool True if failed
- */
- public function failedResample($arg = null)
- {
- $key = sha1(implode('|', func_get_args()));
- return (bool)$this->cache->get($key);
- }
-
- /**
- * Check if we've got enough memory available for resampling this image. This check is rough,
- * so it will not catch all images that are too large - it also won't work accurately on large,
- * animated GIFs as bits per pixel can't be calculated for an animated GIF with a global color
- * table.
- *
- * @param array $imageInfo Value from getimagesize() or getimagesizefromstring()
- * @return boolean
- */
- protected function checkAvailableMemory($imageInfo)
- {
- $limit = translate_memstring(ini_get('memory_limit'));
- if ($limit < 0) {
- return true; // memory_limit == -1
- }
-
- // bits per channel (rounded up, default to 1)
- $bits = isset($imageInfo['bits']) ? ($imageInfo['bits'] + 7) / 8 : 1;
-
- // channels (default 4 rgba)
- $channels = isset($imageInfo['channels']) ? $imageInfo['channels'] : 4;
- $bytesPerPixel = $bits * $channels;
-
- // width * height * bytes per pixel
- $memoryRequired = $imageInfo[0] * $imageInfo[1] * $bytesPerPixel;
-
- return $memoryRequired + memory_get_usage() < $limit;
- }
-
- /**
- * Mark a file as failed
- *
- * @param string $arg,... Any number of args that identify this image
- */
- protected function markFailed($arg = null)
- {
- $key = sha1(implode('|', func_get_args()));
- $this->cache->set($key, '1');
- }
-
- /**
- * Mark a file as succeeded
- *
- * @param string $arg,... Any number of args that identify this image
- */
- protected function markSucceeded($arg = null)
- {
- $key = sha1(implode('|', func_get_args()));
- $this->cache->set($key, '0');
- }
-
-
- public function setQuality($quality)
- {
- $this->quality = $quality;
- }
-
- public function croppedResize($width, $height)
- {
- if (!$this->gd) {
- return null;
- }
-
- $width = round($width);
- $height = round($height);
-
- // Check that a resize is actually necessary.
- if ($width == $this->width && $height == $this->height) {
- return $this;
- }
-
- $newGD = imagecreatetruecolor($width, $height);
-
- // Preserves transparency between images
- imagealphablending($newGD, false);
- imagesavealpha($newGD, true);
-
- $destAR = $width / $height;
- if ($this->width > 0 && $this->height > 0) {
- // We can't divide by zero theres something wrong.
-
- $srcAR = $this->width / $this->height;
-
- // Destination narrower than the source
- if ($destAR < $srcAR) {
- $srcY = 0;
- $srcHeight = $this->height;
-
- $srcWidth = round($this->height * $destAR);
- $srcX = round(($this->width - $srcWidth) / 2);
-
- // Destination shorter than the source
- } else {
- $srcX = 0;
- $srcWidth = $this->width;
-
- $srcHeight = round($this->width / $destAR);
- $srcY = round(($this->height - $srcHeight) / 2);
- }
-
- imagecopyresampled($newGD, $this->gd, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight);
- }
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- /**
- * Resizes the image to fit within the given region.
- * Behaves similarly to paddedResize but without the padding.
- * @todo This method isn't very efficent
- *
- * @param int $width
- * @param int $height
- * @return static
- */
- public function fittedResize($width, $height)
- {
- $gd = $this->resizeByHeight($height);
- if ($gd->width > $width) {
- $gd = $gd->resizeByWidth($width);
- }
- return $gd;
- }
-
- /**
- * @param int $width
- * @param int $height
- * @return static
- */
- public function resize($width, $height)
- {
- if (!$this->gd) {
- return null;
- }
-
- if ($width < 0 || $height < 0) {
- throw new InvalidArgumentException("Image resizing dimensions cannot be negative");
- }
- if (!$width && !$height) {
- throw new InvalidArgumentException("No dimensions given when resizing image");
- }
- if (!$width) {
- throw new InvalidArgumentException("Width not given when resizing image");
- }
- if (!$height) {
- throw new InvalidArgumentException("Height not given when resizing image");
- }
-
- //use whole numbers, ensuring that size is at least 1x1
- $width = max(1, round($width));
- $height = max(1, round($height));
-
- // Check that a resize is actually necessary.
- if ($width == $this->width && $height == $this->height) {
- return $this;
- }
-
-
- $newGD = imagecreatetruecolor($width, $height);
-
- // Preserves transparency between images
- imagealphablending($newGD, false);
- imagesavealpha($newGD, true);
-
- imagecopyresampled($newGD, $this->gd, 0, 0, 0, 0, $width, $height, $this->width, $this->height);
-
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- /**
- * Rotates image by given angle.
- *
- * @param float $angle Angle in degrees
- * @return static
- */
- public function rotate($angle)
- {
- if (!$this->gd) {
- return null;
- }
-
- if (function_exists("imagerotate")) {
- $newGD = imagerotate($this->gd, $angle, 0);
- } else {
- //imagerotate is not included in PHP included in Ubuntu
- $newGD = $this->rotatePixelByPixel($angle);
- }
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- /**
- * Rotates image by given angle. It's slow because makes it pixel by pixel rather than
- * using built-in function. Used when imagerotate function is not available(i.e. Ubuntu)
- *
- * @param float $angle Angle in degrees
- * @return static
- */
- public function rotatePixelByPixel($angle)
- {
- if (!$this->gd) {
- return null;
- }
- $sourceWidth = imagesx($this->gd);
- $sourceHeight = imagesy($this->gd);
- if ($angle == 180) {
- $destWidth = $sourceWidth;
- $destHeight = $sourceHeight;
- } else {
- $destWidth = $sourceHeight;
- $destHeight = $sourceWidth;
- }
- $rotate=imagecreatetruecolor($destWidth, $destHeight);
- imagealphablending($rotate, false);
- for ($x = 0; $x < ($sourceWidth); $x++) {
- for ($y = 0; $y < ($sourceHeight); $y++) {
- $color = imagecolorat($this->gd, $x, $y);
- switch ($angle) {
- case 90:
- imagesetpixel($rotate, $y, $destHeight - $x - 1, $color);
- break;
- case 180:
- imagesetpixel($rotate, $destWidth - $x - 1, $destHeight - $y - 1, $color);
- break;
- case 270:
- imagesetpixel($rotate, $destWidth - $y - 1, $x, $color);
- break;
- default:
- $rotate = $this->gd;
- };
- }
- }
- return $rotate;
- }
-
-
- /**
- * Crop's part of image.
- *
- * @param int $top y position of left upper corner of crop rectangle
- * @param int $left x position of left upper corner of crop rectangle
- * @param int $width rectangle width
- * @param int $height rectangle height
- * @return static
- */
- public function crop($top, $left, $width, $height)
- {
- if (!$this->gd) {
- return null;
- }
-
- $newGD = imagecreatetruecolor($width, $height);
-
- // Preserve alpha channel between images
- imagealphablending($newGD, false);
- imagesavealpha($newGD, true);
-
- imagecopyresampled($newGD, $this->gd, 0, 0, $left, $top, $width, $height, $width, $height);
-
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- /**
- * Width of image.
- *
- * @return int
- */
- public function getWidth()
- {
- return $this->width;
- }
-
- /**
- * Height of image.
- *
- * @return int
- */
- public function getHeight()
- {
- return $this->height;
- }
-
- public function resizeByWidth($width)
- {
- $heightScale = $width / $this->width;
- return $this->resize($width, $heightScale * $this->height);
- }
-
- /**
- * @param int $height
- * @return static
- */
- public function resizeByHeight($height)
- {
- $scale = $height / $this->height;
- return $this->resize($scale * $this->width, $height);
- }
-
- public function resizeRatio($maxWidth, $maxHeight, $useAsMinimum = false)
- {
- $widthRatio = $maxWidth / $this->width;
- $heightRatio = $maxHeight / $this->height;
-
- if ($widthRatio < $heightRatio) {
- return $useAsMinimum
- ? $this->resizeByHeight($maxHeight)
- : $this->resizeByWidth($maxWidth);
- } else {
- return $useAsMinimum
- ? $this->resizeByWidth($maxWidth)
- : $this->resizeByHeight($maxHeight);
- }
- }
-
- public function paddedResize($width, $height, $backgroundColor = "FFFFFF")
- {
- if (!$this->gd) {
- return null;
- }
- $width = round($width);
- $height = round($height);
-
- // Check that a resize is actually necessary.
- if ($width == $this->width && $height == $this->height) {
- return $this;
- }
-
- $newGD = imagecreatetruecolor($width, $height);
-
- // Preserves transparency between images
- imagealphablending($newGD, false);
- imagesavealpha($newGD, true);
-
- $bg = $this->colourWeb2GD($newGD, $backgroundColor);
- imagefilledrectangle($newGD, 0, 0, $width, $height, $bg);
-
- $destAR = $width / $height;
- if ($this->width > 0 && $this->height > 0) {
- // We can't divide by zero theres something wrong.
-
- $srcAR = $this->width / $this->height;
-
- // Destination narrower than the source
- if ($destAR > $srcAR) {
- $destY = 0;
- $destHeight = $height;
-
- $destWidth = round($height * $srcAR);
- $destX = round(($width - $destWidth) / 2);
-
- // Destination shorter than the source
- } else {
- $destX = 0;
- $destWidth = $width;
-
- $destHeight = round($width / $srcAR);
- $destY = round(($height - $destHeight) / 2);
- }
-
- imagecopyresampled(
- $newGD,
- $this->gd,
- $destX,
- $destY,
- 0,
- 0,
- $destWidth,
- $destHeight,
- $this->width,
- $this->height
- );
- }
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- /**
- * Make the image greyscale.
- * Default color weights are based on standard BT.601 (those used in PAL, NTSC and many software packages, also see
- * https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems )
- *
- * @param int $R red weight, defaults to 299
- * @param int $G green weight, defaults to 587
- * @param int $B blue weight, defaults to 114
- * @param int $brightness brightness in percentage, defaults to 100
- * @return GDBackend
- */
- public function greyscale($R = 299, $G = 587, $B = 114, $brightness = 100)
- {
- if (!$this->gd) {
- return null;
- }
-
- $width = $this->width;
- $height = $this->height;
- $newGD = imagecreatetruecolor($this->width, $this->height);
-
- // Preserves transparency between images
- imagealphablending($newGD, false);
- imagesavealpha($newGD, true);
-
- $rt = $R + $G + $B;
- // if $rt is 0, bad parameters are provided, so result will be a black image
- $rr = $rt ? $R/$rt : 0;
- $gr = $rt ? $G/$rt : 0;
- $br = $rt ? $B/$rt : 0;
- // iterate over all pixels and make them grey
- for ($dy = 0; $dy < $height; $dy++) {
- for ($dx = 0; $dx < $width; $dx++) {
- $pxrgb = imagecolorat($this->gd, $dx, $dy);
- $heightgb = imagecolorsforindex($this->gd, $pxrgb);
- $newcol = ($rr*$heightgb['red']) + ($br*$heightgb['blue']) + ($gr*$heightgb['green']);
- $newcol = min(255, $newcol*$brightness/100);
- $setcol = imagecolorallocatealpha($newGD, $newcol, $newcol, $newcol, $heightgb['alpha']);
- imagesetpixel($newGD, $dx, $dy, $setcol);
- }
- }
-
- $output = clone $this;
- $output->setImageResource($newGD);
- return $output;
- }
-
- public function writeToStore(AssetStore $assetStore, $filename, $hash = null, $variant = null, $config = array())
- {
- // Write to temporary file, taking care to maintain the extension
- $path = tempnam(sys_get_temp_dir(), 'gd');
- if ($extension = pathinfo($filename, PATHINFO_EXTENSION)) {
- $path .= "." . $extension;
- }
-
- $writeSuccess = $this->writeTo($path);
- if (!$writeSuccess) {
- return null;
- }
-
- $result = $assetStore->setFromLocalFile($path, $filename, $hash, $variant, $config);
- unlink($path);
-
- return $result;
- }
-
- /**
- * @param string $filename
- * @return boolean
- */
- public function writeTo($filename)
- {
- if (!$filename) {
- return false;
- }
-
- // The GD resource might not exist if the image is too large to be processed, see checkAvailableMemory().
- if (!$this->gd) {
- return false;
- }
-
- // Get current image data
- if (file_exists($filename)) {
- list($width, $height, $type, $attr) = getimagesize($filename);
- unlink($filename);
- } else {
- Filesystem::makeFolder(dirname($filename));
- }
-
- // If image type isn't known, guess from extension
- $ext = strtolower(substr($filename, strrpos($filename, '.')+1));
- if (empty($type)) {
- switch ($ext) {
- case "gif":
- $type = IMAGETYPE_GIF;
- break;
- case "jpeg":
- case "jpg":
- case "jpe":
- $type = IMAGETYPE_JPEG;
- break;
- default:
- $type = IMAGETYPE_PNG;
- break;
- }
- }
-
- // If $this->interlace != 0, the output image will be interlaced.
- imageinterlace($this->gd, $this->interlace);
-
- // if the extension does not exist, the file will not be created!
- switch ($type) {
- case IMAGETYPE_GIF:
- imagegif($this->gd, $filename);
- break;
- case IMAGETYPE_JPEG:
- imagejpeg($this->gd, $filename, $this->quality);
- break;
-
- // case 3, and everything else
- default:
- // Save them as 8-bit images
- // imagetruecolortopalette($this->gd, false, 256);
- imagepng($this->gd, $filename);
- break;
- }
-
- if (!file_exists($filename)) {
- return false;
- }
-
- @chmod($filename, 0664);
-
- return true;
- }
-
- /**
- * Helper function to allocate a colour to an image
- *
- * @param resource $image
- * @param string $webColor
- * @return int
- */
- protected function colourWeb2GD($image, $webColor)
- {
- if (substr($webColor, 0, 1) == "#") {
- $webColor = substr($webColor, 1);
- }
- $r = hexdec(substr($webColor, 0, 2));
- $g = hexdec(substr($webColor, 2, 2));
- $b = hexdec(substr($webColor, 4, 2));
-
- return imagecolorallocate($image, $r, $g, $b);
- }
-
- public static function flush()
- {
- $cache = Injector::inst()->get(CacheInterface::class . '.GDBackend_Manipulations');
- $cache->clear();
- }
-}
diff --git a/src/Assets/Image.php b/src/Assets/Image.php
deleted file mode 100644
index e11be4ea5..000000000
--- a/src/Assets/Image.php
+++ /dev/null
@@ -1,255 +0,0 @@
-File->setAllowedCategories('image/supported');
- }
-
- public function getCMSFields()
- {
- $path = '/' . dirname($this->getFilename());
-
- $previewLink = Convert::raw2att($this->PreviewLink());
- $image = "";
-
- $link = $this->Link();
-
- $statusTitle = $this->getStatusTitle();
- $statusFlag = "{$statusTitle}";
-
- $content = Tab::create(
- 'Main',
- HeaderField::create('TitleHeader', $this->Title, 1)
- ->addExtraClass('editor__heading'),
- LiteralField::create("ImageFull", $image)
- ->addExtraClass('editor__file-preview'),
- TabSet::create(
- 'Editor',
- Tab::create(
- 'Details',
- TextField::create("Title", $this->fieldLabel('Title')),
- TextField::create("Name", $this->fieldLabel('Filename')),
- ReadonlyField::create(
- "Path",
- _t('AssetTableField.PATH', 'Path'),
- (($path !== '/.') ? $path : '') . '/'
- ),
- HTMLReadonlyField::create(
- 'ClickableURL',
- _t('AssetTableField.URL', 'URL'),
- sprintf(
- '%s',
- 'font-icon-link btn--icon-large form-control-static__icon',
- $link,
- $link
- )
- )
- ),
- Tab::create(
- 'Usage',
- DatetimeField::create(
- "Created",
- _t('AssetTableField.CREATED', 'First uploaded')
- )->setReadonly(true),
- DatetimeField::create(
- "LastEdited",
- _t('AssetTableField.LASTEDIT', 'Last changed')
- )->setReadonly(true)
- )
- ),
- HiddenField::create('ID', $this->ID)
- );
-
- if ($dimensions = $this->getDimensions()) {
- $content->insertAfter(
- 'TitleHeader',
- LiteralField::create(
- "DisplaySize",
- sprintf(
- '%spx, %s %s
',
- $dimensions,
- $this->getSize(),
- $statusFlag
- )
- )
- );
- } else {
- $content->insertAfter(
- 'TitleHeader',
- LiteralField::create('StatusFlag', $statusFlag)
- );
- }
-
- $fields = FieldList::create(TabSet::create('Root', $content));
-
- $this->extend('updateCMSFields', $fields);
-
- return $fields;
- }
-
- public function getIsImage()
- {
- return true;
- }
-
- /**
- * Replace"[image id=n]" shortcode with an image reference.
- * Permission checks will be enforced by the file routing itself.
- *
- * @param array $args Arguments passed to the parser
- * @param string $content Raw shortcode
- * @param ShortcodeParser $parser Parser
- * @param string $shortcode Name of shortcode used to register this handler
- * @param array $extra Extra arguments
- * @return string Result of the handled shortcode
- */
- public static function handle_shortcode($args, $content, $parser, $shortcode, $extra = array())
- {
- // Find appropriate record, with fallback for error handlers
- $record = static::find_shortcode_record($args, $errorCode);
- if ($errorCode) {
- $record = static::find_error_record($errorCode);
- }
- if (!$record) {
- return null; // There were no suitable matches at all.
- }
-
- // Check if a resize is required
- $src = $record->Link();
- if ($record instanceof Image) {
- $width = isset($args['width']) ? $args['width'] : null;
- $height = isset($args['height']) ? $args['height'] : null;
- $hasCustomDimensions = ($width && $height);
- if ($hasCustomDimensions && (($width != $record->getWidth()) || ($height != $record->getHeight()))) {
- $resized = $record->ResizedImage($width, $height);
- // Make sure that the resized image actually returns an image
- if ($resized) {
- $src = $resized->getURL();
- }
- }
- }
-
- // Build the HTML tag
- $attrs = array_merge(
- // Set overrideable defaults
- ['src' => '', 'alt' => $record->Title],
- // Use all other shortcode arguments
- $args,
- // But enforce some values
- ['id' => '', 'src' => $src]
- );
-
- // Clean out any empty attributes
- $attrs = array_filter($attrs, function ($v) {
- return (bool)$v;
- });
-
- // Condense to HTML attribute string
- $attrsStr = join(' ', array_map(function ($name) use ($attrs) {
- return Convert::raw2att($name) . '="' . Convert::raw2att($attrs[$name]) . '"';
- }, array_keys($attrs)));
-
- return '';
- }
-
- /**
- * Regenerates "[image id=n]" shortcode with new src attribute prior to being edited within the CMS.
- *
- * @param array $args Arguments passed to the parser
- * @param string $content Raw shortcode
- * @param ShortcodeParser $parser Parser
- * @param string $shortcode Name of shortcode used to register this handler
- * @param array $extra Extra arguments
- * @return string Result of the handled shortcode
- */
- public static function regenerate_shortcode($args, $content, $parser, $shortcode, $extra = array())
- {
- // Check if there is a suitable record
- $record = static::find_shortcode_record($args);
- if ($record) {
- $args['src'] = $record->getURL();
- }
-
- // Rebuild shortcode
- $parts = array();
- foreach ($args as $name => $value) {
- $htmlValue = Convert::raw2att($value ?: $name);
- $parts[] = sprintf('%s="%s"', $name, $htmlValue);
- }
- return sprintf("[%s %s]", $shortcode, implode(' ', $parts));
- }
-
- /**
- * Helper method to regenerate all shortcode links.
- *
- * @param string $value HTML value
- * @return string value with links resampled
- */
- public static function regenerate_html_links($value)
- {
- // Create a shortcode generator which only regenerates links
- $regenerator = ShortcodeParser::get('regenerator');
- return $regenerator->parse($value);
- }
-
- public function PreviewLink($action = null)
- {
- // Since AbsoluteLink can whitelist protected assets,
- // do permission check first
- if (!$this->canView()) {
- return false;
- }
-
- // Size to width / height
- $width = (int)$this->config()->get('asset_preview_width');
- $height = (int)$this->config()->get('asset_preview_height');
- $resized = $this->FitMax($width, $height);
- if ($resized && $resized->exists()) {
- $link = $resized->getAbsoluteURL();
- } else {
- $link = $this->getIcon();
- }
- $this->extend('updatePreviewLink', $link, $action);
- return $link;
- }
-}
diff --git a/src/Assets/ImageManipulation.php b/src/Assets/ImageManipulation.php
deleted file mode 100644
index 104a86be7..000000000
--- a/src/Assets/ImageManipulation.php
+++ /dev/null
@@ -1,828 +0,0 @@
-isSize($width, $height)) {
- return $this;
- }
-
- $variant = $this->variantName(__FUNCTION__, $width, $height, $backgroundColor);
- return $this->manipulateImage(
- $variant,
- function (Image_Backend $backend) use ($width, $height, $backgroundColor) {
- return $backend->paddedResize($width, $height, $backgroundColor);
- }
- );
- }
-
- /**
- * Forces the image to be resampled, if possible
- *
- * @return AssetContainer
- */
- public function Resampled()
- {
- // If image is already resampled, return self reference
- $variant = $this->getVariant();
- if ($variant) {
- return $this;
- }
-
- // Resample, but fallback to original object
- $result = $this->manipulateImage(__FUNCTION__, function (Image_Backend $backend) {
- return $backend;
- });
- if ($result) {
- return $result;
- }
- return $this;
- }
-
- /**
- * Update the url to point to a resampled version if forcing
- *
- * @param string $url
- */
- public function updateURL(&$url)
- {
- // Skip if resampling is off, or is already resampled, or is not an image
- if (!Config::inst()->get(get_class($this), 'force_resample') || $this->getVariant() || !$this->getIsImage()) {
- return;
- }
-
- // Attempt to resample
- $resampled = $this->Resampled();
- if (!$resampled) {
- return;
- }
-
- // Only update if resampled file is a smaller file size
- if ($resampled->getAbsoluteSize() < $this->getAbsoluteSize()) {
- $url = $resampled->getURL();
- }
- }
-
-
- /**
- * Generate a resized copy of this image with the given width & height.
- * This can be used in templates with $ResizedImage but should be avoided,
- * as it's the only image manipulation function which can skew an image.
- *
- * @param integer $width Width to resize to
- * @param integer $height Height to resize to
- * @return AssetContainer
- */
- public function ResizedImage($width, $height)
- {
- if ($this->isSize($width, $height)) {
- return $this;
- }
-
- $variant = $this->variantName(__FUNCTION__, $width, $height);
- return $this->manipulateImage($variant, function (Image_Backend $backend) use ($width, $height) {
- return $backend->resize($width, $height);
- });
- }
-
- /**
- * Scale image proportionally to fit within the specified bounds
- *
- * @param integer $width The width to size within
- * @param integer $height The height to size within
- * @return AssetContainer
- */
- public function Fit($width, $height)
- {
- // Prevent divide by zero on missing/blank file
- if (!$this->getWidth() || !$this->getHeight()) {
- return null;
- }
-
- // Check if image is already sized to the correct dimension
- $widthRatio = $width / $this->getWidth();
- $heightRatio = $height / $this->getHeight();
-
- if ($widthRatio < $heightRatio) {
- // Target is higher aspect ratio than image, so check width
- if ($this->isWidth($width)) {
- return $this;
- }
- } else {
- // Target is wider or same aspect ratio as image, so check height
- if ($this->isHeight($height)) {
- return $this;
- }
- }
-
- // Item must be regenerated
- $variant = $this->variantName(__FUNCTION__, $width, $height);
- return $this->manipulateImage($variant, function (Image_Backend $backend) use ($width, $height) {
- return $backend->resizeRatio($width, $height);
- });
- }
-
- /**
- * Proportionally scale down this image if it is wider or taller than the specified dimensions.
- * Similar to Fit but without up-sampling. Use in templates with $FitMax.
- *
- * @uses ScalingManipulation::Fit()
- * @param integer $width The maximum width of the output image
- * @param integer $height The maximum height of the output image
- * @return AssetContainer
- */
- public function FitMax($width, $height)
- {
- return $this->getWidth() > $width || $this->getHeight() > $height
- ? $this->Fit($width, $height)
- : $this;
- }
-
-
- /**
- * Scale image proportionally by width. Use in templates with $ScaleWidth.
- *
- * @param integer $width The width to set
- * @return AssetContainer
- */
- public function ScaleWidth($width)
- {
- if ($this->isWidth($width)) {
- return $this;
- }
-
- $variant = $this->variantName(__FUNCTION__, $width);
- return $this->manipulateImage($variant, function (Image_Backend $backend) use ($width) {
- return $backend->resizeByWidth($width);
- });
- }
-
- /**
- * Proportionally scale down this image if it is wider than the specified width.
- * Similar to ScaleWidth but without up-sampling. Use in templates with $ScaleMaxWidth.
- *
- * @uses ScalingManipulation::ScaleWidth()
- * @param integer $width The maximum width of the output image
- * @return AssetContainer
- */
- public function ScaleMaxWidth($width)
- {
- return $this->getWidth() > $width
- ? $this->ScaleWidth($width)
- : $this;
- }
-
- /**
- * Scale image proportionally by height. Use in templates with $ScaleHeight.
- *
- * @param int $height The height to set
- * @return AssetContainer
- */
- public function ScaleHeight($height)
- {
- if ($this->isHeight($height)) {
- return $this;
- }
-
- $variant = $this->variantName(__FUNCTION__, $height);
- return $this->manipulateImage($variant, function (Image_Backend $backend) use ($height) {
- return $backend->resizeByHeight($height);
- });
- }
-
- /**
- * Proportionally scale down this image if it is taller than the specified height.
- * Similar to ScaleHeight but without up-sampling. Use in templates with $ScaleMaxHeight.
- *
- * @uses ScalingManipulation::ScaleHeight()
- * @param integer $height The maximum height of the output image
- * @return AssetContainer
- */
- public function ScaleMaxHeight($height)
- {
- return $this->getHeight() > $height
- ? $this->ScaleHeight($height)
- : $this;
- }
-
-
- /**
- * Crop image on X axis if it exceeds specified width. Retain height.
- * Use in templates with $CropWidth. Example: $Image.ScaleHeight(100).$CropWidth(100)
- *
- * @uses CropManipulation::Fill()
- * @param integer $width The maximum width of the output image
- * @return AssetContainer
- */
- public function CropWidth($width)
- {
- return $this->getWidth() > $width
- ? $this->Fill($width, $this->getHeight())
- : $this;
- }
-
- /**
- * Crop image on Y axis if it exceeds specified height. Retain width.
- * Use in templates with $CropHeight. Example: $Image.ScaleWidth(100).CropHeight(100)
- *
- * @uses CropManipulation::Fill()
- * @param integer $height The maximum height of the output image
- * @return AssetContainer
- */
- public function CropHeight($height)
- {
- return $this->getHeight() > $height
- ? $this->Fill($this->getWidth(), $height)
- : $this;
- }
-
- /**
- * Crop this image to the aspect ratio defined by the specified width and height,
- * then scale down the image to those dimensions if it exceeds them.
- * Similar to Fill but without up-sampling. Use in templates with $FillMax.
- *
- * @uses ImageManipulation::Fill()
- * @param integer $width The relative (used to determine aspect ratio) and maximum width of the output image
- * @param integer $height The relative (used to determine aspect ratio) and maximum height of the output image
- * @return AssetContainer
- */
- public function FillMax($width, $height)
- {
- // Prevent divide by zero on missing/blank file
- if (!$this->getWidth() || !$this->getHeight()) {
- return null;
- }
-
- // Is the image already the correct size?
- if ($this->isSize($width, $height)) {
- return $this;
- }
-
- // If not, make sure the image isn't upsampled
- $imageRatio = $this->getWidth() / $this->getHeight();
- $cropRatio = $width / $height;
- // If cropping on the x axis compare heights
- if ($cropRatio < $imageRatio && $this->getHeight() < $height) {
- return $this->Fill($this->getHeight() * $cropRatio, $this->getHeight());
- }
-
- // Otherwise we're cropping on the y axis (or not cropping at all) so compare widths
- if ($this->getWidth() < $width) {
- return $this->Fill($this->getWidth(), $this->getWidth() / $cropRatio);
- }
-
- return $this->Fill($width, $height);
- }
-
- /**
- * Resize and crop image to fill specified dimensions.
- * Use in templates with $Fill
- *
- * @param integer $width Width to crop to
- * @param integer $height Height to crop to
- * @return AssetContainer
- */
- public function Fill($width, $height)
- {
- if ($this->isSize($width, $height)) {
- return $this;
- }
-
- // Resize
- $variant = $this->variantName(__FUNCTION__, $width, $height);
- return $this->manipulateImage($variant, function (Image_Backend $backend) use ($width, $height) {
- return $backend->croppedResize($width, $height);
- });
- }
-
- /**
- * Default CMS thumbnail
- *
- * @return DBFile|DBHTMLText Either a resized thumbnail, or html for a thumbnail icon
- */
- public function CMSThumbnail()
- {
- $width = (int)Config::inst()->get(__CLASS__, 'cms_thumbnail_width');
- $height = (int)Config::inst()->get(__CLASS__, 'cms_thumbnail_height');
- return $this->ThumbnailIcon($width, $height);
- }
-
- /**
- * Generates a thumbnail for use in the gridfield view
- *
- * @return AssetContainer|DBHTMLText Either a resized thumbnail, or html for a thumbnail icon
- */
- public function StripThumbnail()
- {
- $width = (int)Config::inst()->get(__CLASS__, 'strip_thumbnail_width');
- $height = (int)Config::inst()->get(__CLASS__, 'strip_thumbnail_height');
- return $this->ThumbnailIcon($width, $height);
- }
-
- /**
- * Get preview for this file
- *
- * @return AssetContainer|DBHTMLText Either a resized thumbnail, or html for a thumbnail icon
- */
- public function PreviewThumbnail()
- {
- $width = (int)Config::inst()->get(__CLASS__, 'asset_preview_width');
- return $this->ScaleMaxWidth($width) ?: $this->IconTag();
- }
-
- /**
- * Default thumbnail generation for Images
- *
- * @param int $width
- * @param int $height
- * @return AssetContainer
- */
- public function Thumbnail($width, $height)
- {
- return $this->Pad($width, $height);
- }
-
- /**
- * Thubnail generation for all file types.
- *
- * Resizes images, but returns an icon tag if this is not a resizable image
- *
- * @param int $width
- * @param int $height
- * @return AssetContainer|DBHTMLText
- */
- public function ThumbnailIcon($width, $height)
- {
- return $this->Thumbnail($width, $height) ?: $this->IconTag();
- }
-
- /**
- * Get HTML for img containing the icon for this file
- *
- * @return DBHTMLText
- */
- public function IconTag()
- {
- return DBField::create_field(
- 'HTMLFragment',
- ''
- );
- }
-
- /**
- * Get URL to thumbnail of the given size.
- *
- * May fallback to default icon
- *
- * @param int $width
- * @param int $height
- * @return string
- */
- public function ThumbnailURL($width, $height)
- {
- $thumbnail = $this->Thumbnail($width, $height);
- if ($thumbnail) {
- return $thumbnail->getURL();
- }
- return $this->getIcon();
- }
-
- /**
- * Return the relative URL of an icon for the file type,
- * based on the {@link appCategory()} value.
- * Images are searched for in "framework/images/app_icons/".
- *
- * @return string URL to icon
- */
- public function getIcon()
- {
- $filename = $this->getFilename();
- $ext = pathinfo($filename, PATHINFO_EXTENSION);
- return File::get_icon_for_extension($ext);
- }
-
- /**
- * Get Image_Backend instance for this image
- *
- * @return Image_Backend
- */
- public function getImageBackend()
- {
- if (!$this->getIsImage()) {
- return null;
- }
-
- // Create backend for this object
- /** @skipUpgrade */
- return Injector::inst()->createWithArgs('Image_Backend', array($this));
- }
-
- /**
- * Get the dimensions of this Image.
- *
- * @param string $dim One of the following:
- * - "string": return the dimensions in string form
- * - "array": it'll return the raw result
- * - 0: return the height
- * - 1: return the width
- * @return string|int|array|null
- */
- public function getDimensions($dim = "string")
- {
- if (!$this->getIsImage()) {
- return null;
- }
-
- $content = $this->getString();
- if (!$content) {
- return null;
- }
-
- // Get raw content
- $size = getimagesizefromstring($content);
- if ($size === false) {
- return null;
- }
-
- if ($dim === 'array') {
- return $size;
- }
-
- // Get single dimension
- if (is_numeric($dim)) {
- return $size[$dim];
- }
-
- return "$size[0]x$size[1]";
- }
-
- /**
- * Get the width of this image.
- *
- * @return int
- */
- public function getWidth()
- {
- return $this->getDimensions(0);
- }
-
- /**
- * Get the height of this image.
- *
- * @return int
- */
- public function getHeight()
- {
- return $this->getDimensions(1);
- }
-
- /**
- * Get the orientation of this image.
- *
- * @return int ORIENTATION_SQUARE | ORIENTATION_PORTRAIT | ORIENTATION_LANDSCAPE
- */
- public function getOrientation()
- {
- $width = $this->getWidth();
- $height = $this->getHeight();
- if ($width > $height) {
- return Image_Backend::ORIENTATION_LANDSCAPE;
- } elseif ($height > $width) {
- return Image_Backend::ORIENTATION_PORTRAIT;
- } else {
- return Image_Backend::ORIENTATION_SQUARE;
- }
- }
-
- /**
- * Determine if this image is of the specified size
- *
- * @param integer $width Width to check
- * @param integer $height Height to check
- * @return boolean
- */
- public function isSize($width, $height)
- {
- return $this->isWidth($width) && $this->isHeight($height);
- }
-
- /**
- * Determine if this image is of the specified width
- *
- * @param integer $width Width to check
- * @return boolean
- */
- public function isWidth($width)
- {
- if (empty($width) || !is_numeric($width)) {
- throw new InvalidArgumentException("Invalid value for width");
- }
- return $this->getWidth() == $width;
- }
-
- /**
- * Determine if this image is of the specified width
- *
- * @param integer $height Height to check
- * @return boolean
- */
- public function isHeight($height)
- {
- if (empty($height) || !is_numeric($height)) {
- throw new InvalidArgumentException("Invalid value for height");
- }
- return $this->getHeight() == $height;
- }
-
- /**
- * Wrapper for manipulate that passes in and stores Image_Backend objects instead of tuples
- *
- * @param string $variant
- * @param callable $callback Callback which takes an Image_Backend object, and returns an Image_Backend result
- * @return DBFile The manipulated file
- */
- public function manipulateImage($variant, $callback)
- {
- return $this->manipulate(
- $variant,
- function (AssetStore $store, $filename, $hash, $variant) use ($callback) {
- /** @var Image_Backend $backend */
- $backend = $this->getImageBackend();
-
- // If backend isn't available
- if (!$backend || !$backend->getImageResource()) {
- return null;
- }
- $backend = $callback($backend);
- if (!$backend) {
- return null;
- }
-
- $return = $backend->writeToStore(
- $store,
- $filename,
- $hash,
- $variant,
- array('conflict' => AssetStore::CONFLICT_USE_EXISTING)
- );
-
- // Enforce garbage collection on $backend, avoid increasing memory use on each manipulation
- // by holding on to the underlying GD image resource.
- // Even though it's a local variable with no other references,
- // PHP holds on to it for the entire lifecycle of the script,
- // which is potentially related to passing it into the $callback closure.
- gc_collect_cycles();
-
- return $return;
- }
- );
- }
-
- /**
- * Generate a new DBFile instance using the given callback if it hasn't been created yet, or
- * return the existing one if it has.
- *
- * @param string $variant name of the variant to create
- * @param callable $callback Callback which should return a new tuple as an array.
- * This callback will be passed the backend, filename, hash, and variant
- * This will not be called if the file does not
- * need to be created.
- * @return DBFile The manipulated file
- */
- public function manipulate($variant, $callback)
- {
- // Verify this manipulation is applicable to this instance
- if (!$this->exists()) {
- return null;
- }
-
- // Build output tuple
- $filename = $this->getFilename();
- $hash = $this->getHash();
- $existingVariant = $this->getVariant();
- if ($existingVariant) {
- $variant = $existingVariant . '_' . $variant;
- }
-
- // Skip empty files (e.g. Folder does not have a hash)
- if (empty($filename) || empty($hash)) {
- return null;
- }
-
- // Create this asset in the store if it doesn't already exist,
- // otherwise use the existing variant
- $store = Injector::inst()->get('AssetStore');
- $result = null;
- if (!$store->exists($filename, $hash, $variant)) {
- $result = call_user_func($callback, $store, $filename, $hash, $variant);
- } else {
- $result = array(
- 'Filename' => $filename,
- 'Hash' => $hash,
- 'Variant' => $variant
- );
- }
-
- // Callback may fail to perform this manipulation (e.g. resize on text file)
- if (!$result) {
- return null;
- }
-
- // Store result in new DBFile instance
- /** @var DBFile $file */
- $file = DBField::create_field('DBFile', $result);
- return $file->setOriginal($this);
- }
-
- /**
- * Name a variant based on a format with arbitrary parameters
- *
- * @param string $format The format name.
- * @param mixed $arg,... Additional arguments
- * @return string
- * @throws InvalidArgumentException
- */
- public function variantName($format, $arg = null)
- {
- $args = func_get_args();
- array_shift($args);
- return $format . Convert::base64url_encode($args);
- }
-}
diff --git a/src/Assets/Image_Backend.php b/src/Assets/Image_Backend.php
deleted file mode 100644
index a73af65e8..000000000
--- a/src/Assets/Image_Backend.php
+++ /dev/null
@@ -1,151 +0,0 @@
-loadFromContainer($assetContainer);
- }
- }
-
- public function loadFromContainer(AssetContainer $assetContainer)
- {
- $stream = $assetContainer->getStream();
- $this->readImageFile($stream);
- fclose($stream);
- $this->setDefaultQuality();
- }
-
- public function loadFrom($path)
- {
- $this->readImage($path);
- $this->setDefaultQuality();
- }
-
- protected function setDefaultQuality()
- {
- $this->setQuality(Config::inst()->get(__CLASS__, 'default_quality'));
- }
-
- public function writeToStore(AssetStore $assetStore, $filename, $hash = null, $variant = null, $config = array())
- {
- // Write to temporary file, taking care to maintain the extension
- $path = tempnam(sys_get_temp_dir(), 'imagemagick');
- if ($extension = pathinfo($filename, PATHINFO_EXTENSION)) {
- $path .= "." . $extension;
- }
- $this->writeImage($path);
- $result = $assetStore->setFromLocalFile($path, $filename, $hash, $variant, $config);
- unlink($path);
- return $result;
- }
-
- public function writeTo($path)
- {
- Filesystem::makeFolder(dirname($path));
- if (is_dir(dirname($path))) {
- $this->writeImage($path);
- }
- }
-
- public function setQuality($quality)
- {
- $this->setImageCompressionQuality($quality);
- }
-
- public function resize($width, $height)
- {
- if (!$this->valid()) {
- return null;
- }
-
- if ($width < 0 || $height < 0) {
- throw new InvalidArgumentException("Image resizing dimensions cannot be negative");
- }
- if (!$width && !$height) {
- throw new InvalidArgumentException("No dimensions given when resizing image");
- }
- if (!$width) {
- throw new InvalidArgumentException("Width not given when resizing image");
- }
- if (!$height) {
- throw new InvalidArgumentException("Height not given when resizing image");
- }
-
- //use whole numbers, ensuring that size is at least 1x1
- $width = max(1, round($width));
- $height = max(1, round($height));
-
- $geometry = $this->getImageGeometry();
-
- // Check that a resize is actually necessary.
- if ($width === $geometry["width"] && $height === $geometry["height"]) {
- return $this;
- }
-
- $new = clone $this;
- $new->resizeImage($width, $height, self::FILTER_LANCZOS, 1);
-
- return $new;
- }
-
- public function resizeRatio($maxWidth, $maxHeight, $useAsMinimum = false)
- {
- if (!$this->valid()) {
- return null;
- }
-
- $geometry = $this->getImageGeometry();
-
- $widthRatio = $maxWidth / $geometry["width"];
- $heightRatio = $maxHeight / $geometry["height"];
-
- if ($widthRatio < $heightRatio) {
- return $useAsMinimum
- ? $this->resizeByHeight($maxHeight)
- : $this->resizeByWidth($maxWidth);
- } else {
- return $useAsMinimum
- ? $this->resizeByWidth($maxWidth)
- : $this->resizeByHeight($maxHeight);
- }
- }
-
- public function resizeByWidth($width)
- {
- if (!$this->valid()) {
- return null;
- }
-
- $geometry = $this->getImageGeometry();
-
- $heightScale = $width / $geometry["width"];
- return $this->resize($width, $heightScale * $geometry["height"]);
- }
-
- public function resizeByHeight($height)
- {
- if (!$this->valid()) {
- return null;
- }
-
- $geometry = $this->getImageGeometry();
-
- $scale = $height / $geometry["height"];
- return $this->resize($scale * $geometry["width"], $height);
- }
-
- /**
- * paddedResize
- *
- * @param int $width
- * @param int $height
- * @param string $backgroundColor
- * @param int $transparencyPercent
- * @return Image_Backend
- */
- public function paddedResize($width, $height, $backgroundColor = "FFFFFF", $transparencyPercent = 0)
- {
- if (!$this->valid()) {
- return null;
- }
-
- //keep the % within bounds of 0-100
- $transparencyPercent = min(100, max(0, $transparencyPercent));
-
- $new = $this->resizeRatio($width, $height);
- if ($transparencyPercent) {
- $alphaHex = $this->calculateAlphaHex($transparencyPercent);
- $new->setImageBackgroundColor("#{$backgroundColor}{$alphaHex}");
- } else {
- $new->setImageBackgroundColor("#{$backgroundColor}");
- }
- $w = $new->getImageWidth();
- $h = $new->getImageHeight();
- $new->extentImage($width, $height, ($w-$width)/2, ($h-$height)/2);
- return $new;
- }
-
- /**
- * Convert a percentage (or 'true') to a two char hex code to signifiy the level of an alpha channel
- *
- * @param $percent
- * @return string
- */
- public function calculateAlphaHex($percent)
- {
- if ($percent > 100) {
- $percent = 100;
- }
- // unlike GD, this uses 255 instead of 127, and is reversed. Lower = more transparent
- $alphaHex = dechex(255 - floor(255 * bcdiv($percent, 100, 2)));
- if (strlen($alphaHex) == 1) {
- $alphaHex = '0' .$alphaHex;
- }
- return $alphaHex;
- }
-
-
- /**
- * croppedResize
- *
- * @param int $width
- * @param int $height
- * @return Image_Backend
- */
- public function croppedResize($width, $height)
- {
- if (!$this->valid()) {
- return null;
- }
-
- $width = round($width);
- $height = round($height);
- $geo = $this->getImageGeometry();
-
- // Check that a resize is actually necessary.
- if ($width == $geo["width"] && $height == $geo["height"]) {
- return $this;
- }
-
- $new = clone $this;
- $new->setBackgroundColor(new ImagickPixel('transparent'));
-
- if (($geo['width']/$width) < ($geo['height']/$height)) {
- $new->cropImage(
- $geo['width'],
- floor($height*$geo['width']/$width),
- 0,
- ($geo['height'] - ($height*$geo['width']/$width))/2
- );
- } else {
- $new->cropImage(
- ceil($width*$geo['height']/$height),
- $geo['height'],
- ($geo['width'] - ($width*$geo['height']/$height))/2,
- 0
- );
- }
- $new->thumbnailImage($width, $height, true);
- return $new;
- }
-
- /**
- * Crop's part of image.
- * @param int $top y position of left upper corner of crop rectangle
- * @param int $left x position of left upper corner of crop rectangle
- * @param int $width rectangle width
- * @param int $height rectangle height
- * @return Image_Backend
- */
- public function crop($top, $left, $width, $height)
- {
- $new = clone $this;
- $new->cropImage($width, $height, $left, $top);
-
- return $new;
- }
-}
diff --git a/src/Assets/Storage/AssetContainer.php b/src/Assets/Storage/AssetContainer.php
deleted file mode 100644
index 8a4a2ec77..000000000
--- a/src/Assets/Storage/AssetContainer.php
+++ /dev/null
@@ -1,187 +0,0 @@
-setAllowedCategories($allowed);
- }
-
- /**
- * Determine if a valid non-empty image exists behind this asset, which is a format
- * compatible with image manipulations
- *
- * @return boolean
- */
- public function getIsImage()
- {
- // Check file type
- $mime = $this->getMimeType();
- return $mime && in_array($mime, $this->config()->supported_images);
- }
-
- /**
- * @return AssetStore
- */
- protected function getStore()
- {
- return Injector::inst()->get('AssetStore');
- }
-
- private static $composite_db = array(
- "Hash" => "Varchar(255)", // SHA of the base content
- "Filename" => "Varchar(255)", // Path identifier of the base content
- "Variant" => "Varchar(255)", // Identifier of the variant to the base, if given
- );
-
- private static $casting = array(
- 'URL' => 'Varchar',
- 'AbsoluteURL' => 'Varchar',
- 'Basename' => 'Varchar',
- 'Title' => 'Varchar',
- 'MimeType' => 'Varchar',
- 'String' => 'Text',
- 'Tag' => 'HTMLFragment',
- 'Size' => 'Varchar'
- );
-
- public function scaffoldFormField($title = null, $params = null)
- {
- return null;
- }
-
- /**
- * Return a html5 tag of the appropriate for this file (normally img or a)
- *
- * @return string
- */
- public function XML()
- {
- return $this->getTag() ?: '';
- }
-
- /**
- * Return a html5 tag of the appropriate for this file (normally img or a)
- *
- * @return string
- */
- public function getTag()
- {
- $template = $this->getFrontendTemplate();
- if (empty($template)) {
- return '';
- }
- return (string)$this->renderWith($template);
- }
-
- /**
- * Determine the template to render as on the frontend
- *
- * @return string Name of template
- */
- public function getFrontendTemplate()
- {
- // Check that path is available
- $url = $this->getURL();
- if (empty($url)) {
- return null;
- }
-
- // Image template for supported images
- if ($this->getIsImage()) {
- return 'DBFile_image';
- }
-
- // Default download
- return 'DBFile_download';
- }
-
- /**
- * Get trailing part of filename
- *
- * @return string
- */
- public function getBasename()
- {
- if (!$this->exists()) {
- return null;
- }
- return basename($this->getSourceURL());
- }
-
- /**
- * Get file extension
- *
- * @return string
- */
- public function getExtension()
- {
- if (!$this->exists()) {
- return null;
- }
- return pathinfo($this->Filename, PATHINFO_EXTENSION);
- }
-
- /**
- * Alt title for this
- *
- * @return string
- */
- public function getTitle()
- {
- // If customised, use the customised title
- if ($this->failover && ($title = $this->failover->Title)) {
- return $title;
- }
- // fallback to using base name
- return $this->getBasename();
- }
-
- public function setFromLocalFile($path, $filename = null, $hash = null, $variant = null, $config = array())
- {
- $this->assertFilenameValid($filename ?: $path);
- $result = $this
- ->getStore()
- ->setFromLocalFile($path, $filename, $hash, $variant, $config);
- // Update from result
- if ($result) {
- $this->setValue($result);
- }
- return $result;
- }
-
- public function setFromStream($stream, $filename, $hash = null, $variant = null, $config = array())
- {
- $this->assertFilenameValid($filename);
- $result = $this
- ->getStore()
- ->setFromStream($stream, $filename, $hash, $variant, $config);
- // Update from result
- if ($result) {
- $this->setValue($result);
- }
- return $result;
- }
-
- public function setFromString($data, $filename, $hash = null, $variant = null, $config = array())
- {
- $this->assertFilenameValid($filename);
- $result = $this
- ->getStore()
- ->setFromString($data, $filename, $hash, $variant, $config);
- // Update from result
- if ($result) {
- $this->setValue($result);
- }
- return $result;
- }
-
- public function getStream()
- {
- if (!$this->exists()) {
- return null;
- }
- return $this
- ->getStore()
- ->getAsStream($this->Filename, $this->Hash, $this->Variant);
- }
-
- public function getString()
- {
- if (!$this->exists()) {
- return null;
- }
- return $this
- ->getStore()
- ->getAsString($this->Filename, $this->Hash, $this->Variant);
- }
-
- public function getURL($grant = true)
- {
- if (!$this->exists()) {
- return null;
- }
- $url = $this->getSourceURL($grant);
- $this->updateURL($url);
- $this->extend('updateURL', $url);
- return $url;
- }
-
- /**
- * Get URL, but without resampling.
- * Note that this will return the url even if the file does not exist.
- *
- * @param bool $grant Ensures that the url for any protected assets is granted for the current user.
- * @return string
- */
- public function getSourceURL($grant = true)
- {
- return $this
- ->getStore()
- ->getAsURL($this->Filename, $this->Hash, $this->Variant, $grant);
- }
-
- /**
- * Get the absolute URL to this resource
- *
- * @return string
- */
- public function getAbsoluteURL()
- {
- if (!$this->exists()) {
- return null;
- }
- return Director::absoluteURL($this->getURL());
- }
-
- public function getMetaData()
- {
- if (!$this->exists()) {
- return null;
- }
- return $this
- ->getStore()
- ->getMetadata($this->Filename, $this->Hash, $this->Variant);
- }
-
- public function getMimeType()
- {
- if (!$this->exists()) {
- return null;
- }
- return $this
- ->getStore()
- ->getMimeType($this->Filename, $this->Hash, $this->Variant);
- }
-
- public function getValue()
- {
- if (!$this->exists()) {
- return null;
- }
- return array(
- 'Filename' => $this->Filename,
- 'Hash' => $this->Hash,
- 'Variant' => $this->Variant
- );
- }
-
- public function getVisibility()
- {
- if (empty($this->Filename)) {
- return null;
- }
- return $this
- ->getStore()
- ->getVisibility($this->Filename, $this->Hash);
- }
-
- public function exists()
- {
- if (empty($this->Filename)) {
- return false;
- }
- return $this
- ->getStore()
- ->exists($this->Filename, $this->Hash, $this->Variant);
- }
-
- public function getFilename()
- {
- return $this->getField('Filename');
- }
-
- public function getHash()
- {
- return $this->getField('Hash');
- }
-
- public function getVariant()
- {
- return $this->getField('Variant');
- }
-
- /**
- * Return file size in bytes.
- *
- * @return int
- */
- public function getAbsoluteSize()
- {
- $metadata = $this->getMetaData();
- if (isset($metadata['size'])) {
- return $metadata['size'];
- }
- return 0;
- }
-
- /**
- * Customise this object with an "original" record for getting other customised fields
- *
- * @param AssetContainer $original
- * @return $this
- */
- public function setOriginal($original)
- {
- $this->failover = $original;
- return $this;
- }
-
- /**
- * Get list of allowed file categories
- *
- * @return array
- */
- public function getAllowedCategories()
- {
- return $this->allowedCategories;
- }
-
- /**
- * Assign allowed categories
- *
- * @param array|string $categories
- * @return $this
- */
- public function setAllowedCategories($categories)
- {
- if (is_string($categories)) {
- $categories = preg_split('/\s*,\s*/', $categories);
- }
- $this->allowedCategories = (array)$categories;
- return $this;
- }
-
- /**
- * Gets the list of extensions (if limited) for this field. Empty list
- * means there is no restriction on allowed types.
- *
- * @return array
- */
- protected function getAllowedExtensions()
- {
- $categories = $this->getAllowedCategories();
- return File::get_category_extensions($categories);
- }
-
- /**
- * Validate that this DBFile accepts this filename as valid
- *
- * @param string $filename
- * @throws ValidationException
- * @return bool
- */
- protected function isValidFilename($filename)
- {
- $extension = strtolower(File::get_file_extension($filename));
-
- // Validate true if within the list of allowed extensions
- $allowed = $this->getAllowedExtensions();
- if ($allowed) {
- return in_array($extension, $allowed);
- }
-
- // If no extensions are configured, fallback to global list
- $globalList = File::config()->allowed_extensions;
- if (in_array($extension, $globalList)) {
- return true;
- }
-
- // Only admins can bypass global rules
- return !File::config()->apply_restrictions_to_admin && Permission::check('ADMIN');
- }
-
- /**
- * Check filename, and raise a ValidationException if invalid
- *
- * @param string $filename
- * @throws ValidationException
- */
- protected function assertFilenameValid($filename)
- {
- $result = new ValidationResult();
- $this->validate($result, $filename);
- if (!$result->isValid()) {
- throw new ValidationException($result);
- }
- }
-
-
- /**
- * Hook to validate this record against a validation result
- *
- * @param ValidationResult $result
- * @param string $filename Optional filename to validate. If omitted, the current value is validated.
- * @return bool Valid flag
- */
- public function validate(ValidationResult $result, $filename = null)
- {
- if (empty($filename)) {
- $filename = $this->getFilename();
- }
- if (empty($filename) || $this->isValidFilename($filename)) {
- return true;
- }
-
- $message = _t('File.INVALIDEXTENSIONSHORT', 'Extension is not allowed');
- $result->addError($message);
- return false;
- }
-
- public function setField($field, $value, $markChanged = true)
- {
- // Catch filename validation on direct assignment
- if ($field === 'Filename' && $value) {
- $this->assertFilenameValid($value);
- }
-
- return parent::setField($field, $value, $markChanged);
- }
-
-
- /**
- * Returns the size of the file type in an appropriate format.
- *
- * @return string|false String value, or false if doesn't exist
- */
- public function getSize()
- {
- $size = $this->getAbsoluteSize();
- if ($size) {
- return File::format_size($size);
- }
- return false;
- }
-
- public function deleteFile()
- {
- if (!$this->Filename) {
- return false;
- }
-
- return $this
- ->getStore()
- ->delete($this->Filename, $this->Hash);
- }
-
- public function publishFile()
- {
- if ($this->Filename) {
- $this
- ->getStore()
- ->publish($this->Filename, $this->Hash);
- }
- }
-
- public function protectFile()
- {
- if ($this->Filename) {
- $this
- ->getStore()
- ->protect($this->Filename, $this->Hash);
- }
- }
-
- public function grantFile()
- {
- if ($this->Filename) {
- $this
- ->getStore()
- ->grant($this->Filename, $this->Hash);
- }
- }
-
- public function revokeFile()
- {
- if ($this->Filename) {
- $this
- ->getStore()
- ->revoke($this->Filename, $this->Hash);
- }
- }
-
- public function canViewFile()
- {
- return $this->Filename
- && $this
- ->getStore()
- ->canView($this->Filename, $this->Hash);
- }
-}
diff --git a/src/Assets/Storage/DefaultAssetNameGenerator.php b/src/Assets/Storage/DefaultAssetNameGenerator.php
deleted file mode 100644
index c445b55d6..000000000
--- a/src/Assets/Storage/DefaultAssetNameGenerator.php
+++ /dev/null
@@ -1,170 +0,0 @@
-filename = $filename;
- $this->directory = ltrim(dirname($filename), '.');
- $name = basename($this->filename);
- // Note: Unlike normal extensions, we want to split at the first period, not the last.
- if (($pos = strpos($name, '.')) !== false) {
- $this->extension = substr($name, $pos);
- $name = substr($name, 0, $pos);
- } else {
- $this->extension = null;
- }
-
- // Extract version prefix if already applied to this file
- $this->padding = 0;
- $pattern = '/^(?[^\/]+?)' . preg_quote($this->getPrefix()) . '(?[0-9]+)$/';
- if (preg_match($pattern, $name, $matches)) {
- $this->first = (int)$matches['version'];
- $this->name = $matches['name'];
- // Check if number is padded
- if (strpos($matches['version'], '0') === 0) {
- $this->padding = strlen($matches['version']);
- }
- } else {
- $this->first = 1;
- $this->name = $name;
- }
-
- $this->rewind();
- }
-
- /**
- * Get numeric prefix
- *
- * @return string
- */
- protected function getPrefix()
- {
- return Config::inst()->get(__CLASS__, 'version_prefix');
- }
-
- public function current()
- {
- $version = $this->version;
-
- // Initially suggest original name
- if ($version === $this->first) {
- return $this->filename;
- }
-
- // If there are more than $this->max files we need a new scheme
- if ($version >= $this->max + $this->first - 1) {
- $version = substr(md5(time()), 0, 10);
- } elseif ($this->padding) {
- // Else, pad
- $version = str_pad($version, $this->padding, '0', STR_PAD_LEFT);
- }
-
- // Build next name
- $filename = $this->name . $this->getPrefix() . $version . $this->extension;
- if ($this->directory) {
- $filename = $this->directory . DIRECTORY_SEPARATOR . $filename;
- }
- return $filename;
- }
-
- public function key()
- {
- return $this->version - $this->first;
- }
-
- public function next()
- {
- $this->version++;
- }
-
- public function rewind()
- {
- $this->version = $this->first;
- }
-
- public function valid()
- {
- return $this->version < $this->max + $this->first;
- }
-
- public function getMaxTries()
- {
- return $this->max;
- }
-}
diff --git a/src/Assets/Storage/GeneratedAssetHandler.php b/src/Assets/Storage/GeneratedAssetHandler.php
deleted file mode 100644
index 97ed4f68c..000000000
--- a/src/Assets/Storage/GeneratedAssetHandler.php
+++ /dev/null
@@ -1,53 +0,0 @@
-handler;
- }
-
- /**
- * @param AssetStoreRouter $handler
- * @return $this
- */
- public function setRouteHandler(AssetStoreRouter $handler)
- {
- $this->handler = $handler;
- return $this;
- }
-
- private static $url_handlers = array(
- '$Filename' => "handleFile"
- );
-
- private static $allowed_actions = array(
- 'handleFile'
- );
-
- /**
- * Provide a response for the given file request
- *
- * @param HTTPRequest $request
- * @return HTTPResponse
- */
- public function handleFile(HTTPRequest $request)
- {
- $filename = $this->parseFilename($request);
-
- // Deny requests to private file
- if (!$this->isValidFilename($filename)) {
- return $this->httpError(400, "Invalid request");
- }
-
- // Pass through to backend
- return $this->getRouteHandler()->getResponseFor($filename);
- }
-
- /**
- * Check if the given filename is safe to pass to the route handler.
- * This should block direct requests to assets/.protected/ paths
- *
- * @param $filename
- * @return bool True if the filename is allowed
- */
- public function isValidFilename($filename)
- {
- // Block hidden files
- return !preg_match('#(^|[\\\\/])\\..*#', $filename);
- }
-
- /**
- * Get the file component from the request
- *
- * @param HTTPRequest $request
- * @return string
- */
- protected function parseFilename(HTTPRequest $request)
- {
- $filename = '';
- $next = $request->param('Filename');
- while ($next) {
- $filename = $filename ? File::join_paths($filename, $next) : $next;
- $next = $request->shift();
- }
- if ($extension = $request->getExtension()) {
- $filename = $filename . "." . $extension;
- }
- return $filename;
- }
-}
diff --git a/src/Assets/Thumbnail.php b/src/Assets/Thumbnail.php
deleted file mode 100644
index b42891e7a..000000000
--- a/src/Assets/Thumbnail.php
+++ /dev/null
@@ -1,19 +0,0 @@
-Validation
- *
- * By default, a user can upload files without extension limitations,
- * which can be a security risk if the webserver is not properly secured.
- * Use {@link setAllowedExtensions()} to limit this list,
- * and ensure the "assets/" directory does not execute scripts
- * (see http://doc.silverstripe.org/secure-development#filesystem).
- * {@link File::$allowed_extensions} provides a good start for a list of "safe" extensions.
- *
- * @todo Allow for non-database uploads
- */
-class Upload extends Controller
-{
-
- private static $allowed_actions = array(
- 'index',
- 'load'
- );
-
- /**
- * A dataobject (typically {@see File}) which implements {@see AssetContainer}
- *
- * @var AssetContainer
- */
- protected $file;
-
- /**
- * Validator for this upload field
- *
- * @var Upload_Validator
- */
- protected $validator;
-
- /**
- * Information about the temporary file produced
- * by the PHP-runtime.
- *
- * @var array
- */
- protected $tmpFile;
-
- /**
- * Replace an existing file rather than renaming the new one.
- *
- * @var boolean
- */
- protected $replaceFile = false;
-
- /**
- * Processing errors that can be evaluated,
- * e.g. by Form-validation.
- *
- * @var array
- */
- protected $errors = array();
-
- /**
- * Default visibility to assign uploaded files
- *
- * @var string
- */
- protected $defaultVisibility = AssetStore::VISIBILITY_PROTECTED;
-
- /**
- * A foldername relative to /assets,
- * where all uploaded files are stored by default.
- *
- * @config
- * @var string
- */
- private static $uploads_folder = "Uploads";
-
- /**
- * A prefix for the version number added to an uploaded file
- * when a file with the same name already exists.
- * Example using no prefix: IMG001.jpg becomes IMG2.jpg
- * Example using '-v' prefix: IMG001.jpg becomes IMG001-v2.jpg
- *
- * @config
- * @var string
- */
- private static $version_prefix = '-v';
-
- public function __construct()
- {
- parent::__construct();
- $this->validator = Upload_Validator::create();
- $this->replaceFile = self::config()->replaceFile;
- }
-
- public function index()
- {
- return $this->httpError(404); // no-op
- }
-
- /**
- * Get current validator
- *
- * @return Upload_Validator $validator
- */
- public function getValidator()
- {
- return $this->validator;
- }
-
- /**
- * Set a different instance than {@link Upload_Validator}
- * for this upload session.
- *
- * @param object $validator
- */
- public function setValidator($validator)
- {
- $this->validator = $validator;
- }
-
-
- /**
- * Get an asset renamer for the given filename.
- *
- * @param string $filename Path name
- * @return AssetNameGenerator
- */
- protected function getNameGenerator($filename)
- {
- return Injector::inst()->createWithArgs('AssetNameGenerator', array($filename));
- }
-
- /**
- *
- * @return AssetStore
- */
- protected function getAssetStore()
- {
- return Injector::inst()->get('AssetStore');
- }
-
- /**
- * Save an file passed from a form post into the AssetStore directly
- *
- * @param array $tmpFile Indexed array that PHP generated for every file it uploads.
- * @param string|bool $folderPath Folder path relative to /assets
- * @return array|false Either the tuple array, or false if the file could not be saved
- */
- public function load($tmpFile, $folderPath = false)
- {
- // Validate filename
- $filename = $this->getValidFilename($tmpFile, $folderPath);
- if (!$filename) {
- return false;
- }
-
- // Save file into backend
- $result = $this->storeTempFile($tmpFile, $filename, $this->getAssetStore());
-
- //to allow extensions to e.g. create a version after an upload
- $this->extend('onAfterLoad', $result, $tmpFile);
- return $result;
- }
-
- /**
- * Save an file passed from a form post into this object.
- * File names are filtered through {@link FileNameFilter}, see class documentation
- * on how to influence this behaviour.
- *
- * @param array $tmpFile
- * @param AssetContainer $file
- * @param string|bool $folderPath
- * @return bool True if the file was successfully saved into this record
- * @throws Exception
- */
- public function loadIntoFile($tmpFile, $file = null, $folderPath = false)
- {
- $this->file = $file;
-
- // Validate filename
- $filename = $this->getValidFilename($tmpFile, $folderPath);
- if (!$filename) {
- return false;
- }
- $filename = $this->resolveExistingFile($filename);
-
- // Save changes to underlying record (if it's a DataObject)
- $this->storeTempFile($tmpFile, $filename, $this->file);
- if ($this->file instanceof DataObject) {
- $this->file->write();
- }
-
- //to allow extensions to e.g. create a version after an upload
- $this->file->extend('onAfterUpload');
- $this->extend('onAfterLoadIntoFile', $this->file);
- return true;
- }
-
- /**
- * Assign this temporary file into the given destination
- *
- * @param array $tmpFile
- * @param string $filename
- * @param AssetContainer|AssetStore $container
- * @return array
- */
- protected function storeTempFile($tmpFile, $filename, $container)
- {
- // Save file into backend
- $conflictResolution = $this->replaceFile
- ? AssetStore::CONFLICT_OVERWRITE
- : AssetStore::CONFLICT_RENAME;
- $config = array(
- 'conflict' => $conflictResolution,
- 'visibility' => $this->getDefaultVisibility()
- );
- return $container->setFromLocalFile($tmpFile['tmp_name'], $filename, null, null, $config);
- }
-
- /**
- * Given a temporary file and upload path, validate the file and determine the
- * value of the 'Filename' tuple that should be used to store this asset.
- *
- * @param array $tmpFile
- * @param string $folderPath
- * @return string|false Value of filename tuple, or false if invalid
- */
- protected function getValidFilename($tmpFile, $folderPath = null)
- {
- if (!is_array($tmpFile)) {
- throw new InvalidArgumentException(
- "Upload::load() Not passed an array. Most likely, the form hasn't got the right enctype"
- );
- }
-
- // Validate
- $this->clearErrors();
- $valid = $this->validate($tmpFile);
- if (!$valid) {
- return false;
- }
-
- // Clean filename
- if (!$folderPath) {
- $folderPath = $this->config()->uploads_folder;
- }
- $nameFilter = FileNameFilter::create();
- $file = $nameFilter->filter($tmpFile['name']);
- $filename = basename($file);
- if ($folderPath) {
- $filename = File::join_paths($folderPath, $filename);
- }
- return $filename;
- }
-
- /**
- * Given a file and filename, ensure that file renaming / replacing rules are satisfied
- *
- * If replacing, this method may replace $this->file with an existing record to overwrite.
- * If renaming, a new value for $filename may be returned
- *
- * @param string $filename
- * @return string $filename A filename safe to write to
- * @throws Exception
- */
- protected function resolveExistingFile($filename)
- {
- // Create a new file record (or try to retrieve an existing one)
- if (!$this->file) {
- $fileClass = File::get_class_for_file_extension(
- File::get_file_extension($filename)
- );
- $this->file = Object::create($fileClass);
- }
-
- // Skip this step if not writing File dataobjects
- if (! ($this->file instanceof File)) {
- return $filename;
- }
-
- // Check there is if existing file
- $existing = File::find($filename);
-
- // If replacing (or no file exists) confirm this filename is safe
- if ($this->replaceFile || !$existing) {
- // If replacing files, make sure to update the OwnerID
- if (!$this->file->ID && $this->replaceFile && $existing) {
- $this->file = $existing;
- $this->file->OwnerID = Member::currentUserID();
- }
- // Filename won't change if replacing
- return $filename;
- }
-
- // if filename already exists, version the filename (e.g. test.gif to test-v2.gif, test-v2.gif to test-v3.gif)
- $renamer = $this->getNameGenerator($filename);
- foreach ($renamer as $newName) {
- if (!File::find($newName)) {
- return $newName;
- }
- }
-
- // Fail
- $tries = $renamer->getMaxTries();
- throw new Exception("Could not rename {$filename} with {$tries} tries");
- }
-
- /**
- * @param bool $replace
- */
- public function setReplaceFile($replace)
- {
- $this->replaceFile = $replace;
- }
-
- /**
- * @return bool
- */
- public function getReplaceFile()
- {
- return $this->replaceFile;
- }
-
- /**
- * Container for all validation on the file
- * (e.g. size and extension restrictions).
- * Is NOT connected to the {Validator} classes,
- * please have a look at {FileField->validate()}
- * for an example implementation of external validation.
- *
- * @param array $tmpFile
- * @return boolean
- */
- public function validate($tmpFile)
- {
- $validator = $this->validator;
- $validator->setTmpFile($tmpFile);
- $isValid = $validator->validate();
- if ($validator->getErrors()) {
- $this->errors = array_merge($this->errors, $validator->getErrors());
- }
- return $isValid;
- }
-
- /**
- * Get file-object, either generated from {load()},
- * or manually set.
- *
- * @return AssetContainer
- */
- public function getFile()
- {
- return $this->file;
- }
-
- /**
- * Set a file-object (similiar to {loadIntoFile()})
- *
- * @param AssetContainer $file
- */
- public function setFile(AssetContainer $file)
- {
- $this->file = $file;
- }
-
- /**
- * Clear out all errors (mostly set by {loadUploaded()})
- * including the validator's errors
- */
- public function clearErrors()
- {
- $this->errors = array();
- $this->validator->clearErrors();
- }
-
- /**
- * Determines wether previous operations caused an error.
- *
- * @return boolean
- */
- public function isError()
- {
- return (count($this->errors));
- }
-
- /**
- * Return all errors that occurred while processing so far
- * (mostly set by {loadUploaded()})
- *
- * @return array
- */
- public function getErrors()
- {
- return $this->errors;
- }
-
- /**
- * Get default visibility for uploaded files. {@see AssetStore}
- * One of the values of AssetStore::VISIBILITY_* constants
- *
- * @return string
- */
- public function getDefaultVisibility()
- {
- return $this->defaultVisibility;
- }
-
- /**
- * Assign default visibility for uploaded files. {@see AssetStore}
- * One of the values of AssetStore::VISIBILITY_* constants
- *
- * @param string $visibility
- * @return $this
- */
- public function setDefaultVisibility($visibility)
- {
- $this->defaultVisibility = $visibility;
- return $this;
- }
-}
diff --git a/src/Assets/Upload_Validator.php b/src/Assets/Upload_Validator.php
deleted file mode 100644
index 11fd4c3c2..000000000
--- a/src/Assets/Upload_Validator.php
+++ /dev/null
@@ -1,313 +0,0 @@
-
- * array("jpg","GIF")
- *
- */
- public $allowedExtensions = array();
-
- /**
- * Return all errors that occurred while validating
- * the temporary file.
- *
- * @return array
- */
- public function getErrors()
- {
- return $this->errors;
- }
-
- /**
- * Clear out all errors
- */
- public function clearErrors()
- {
- $this->errors = array();
- }
-
- /**
- * Set information about temporary file produced by PHP.
- * @param array $tmpFile
- */
- public function setTmpFile($tmpFile)
- {
- $this->tmpFile = $tmpFile;
- }
-
- /**
- * Get maximum file size for all or specified file extension.
- *
- * @param string $ext
- * @return int Filesize in bytes
- */
- public function getAllowedMaxFileSize($ext = null)
- {
-
- // Check if there is any defined instance max file sizes
- if (empty($this->allowedMaxFileSize)) {
- // Set default max file sizes if there isn't
- $fileSize = Config::inst()->get(__CLASS__, 'default_max_file_size');
- if ($fileSize) {
- $this->setAllowedMaxFileSize($fileSize);
- } else {
- // When no default is present, use maximum set by PHP
- $maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
- $maxPost = File::ini2bytes(ini_get('post_max_size'));
- $this->setAllowedMaxFileSize(min($maxUpload, $maxPost));
- }
- }
-
- $ext = strtolower($ext);
- if ($ext) {
- if (isset($this->allowedMaxFileSize[$ext])) {
- return $this->allowedMaxFileSize[$ext];
- }
-
- $category = File::get_app_category($ext);
- if ($category && isset($this->allowedMaxFileSize['[' . $category . ']'])) {
- return $this->allowedMaxFileSize['[' . $category . ']'];
- }
- }
-
- return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false;
- }
-
- /**
- * Set filesize maximums (in bytes or INI format).
- * Automatically converts extensions to lowercase
- * for easier matching.
- *
- * Example:
- *
- * array('*' => 200, 'jpg' => 1000, '[doc]' => '5m')
- *
- *
- * @param array|int $rules
- */
- public function setAllowedMaxFileSize($rules)
- {
- if (is_array($rules) && count($rules)) {
- // make sure all extensions are lowercase
- $rules = array_change_key_case($rules, CASE_LOWER);
- $finalRules = array();
-
- foreach ($rules as $rule => $value) {
- if (is_numeric($value)) {
- $tmpSize = $value;
- } else {
- $tmpSize = File::ini2bytes($value);
- }
-
- $finalRules[$rule] = (int)$tmpSize;
- }
-
- $this->allowedMaxFileSize = $finalRules;
- } elseif (is_string($rules)) {
- $this->allowedMaxFileSize['*'] = File::ini2bytes($rules);
- } elseif ((int)$rules > 0) {
- $this->allowedMaxFileSize['*'] = (int)$rules;
- }
- }
-
- /**
- * @return array
- */
- public function getAllowedExtensions()
- {
- return $this->allowedExtensions;
- }
-
- /**
- * Limit allowed file extensions. Empty by default, allowing all extensions.
- * To allow files without an extension, use an empty string.
- * See {@link File::$allowed_extensions} to get a good standard set of
- * extensions that are typically not harmful in a webserver context.
- * See {@link setAllowedMaxFileSize()} to limit file size by extension.
- *
- * @param array $rules List of extensions
- */
- public function setAllowedExtensions($rules)
- {
- if (!is_array($rules)) {
- return;
- }
-
- // make sure all rules are lowercase
- foreach ($rules as &$rule) {
- $rule = strtolower($rule);
- }
-
- $this->allowedExtensions = $rules;
- }
-
- /**
- * Determines if the bytesize of an uploaded
- * file is valid - can be defined on an
- * extension-by-extension basis in {@link $allowedMaxFileSize}
- *
- * @return boolean
- */
- public function isValidSize()
- {
- // If file was blocked via PHP for being excessive size, shortcut here
- switch ($this->tmpFile['error']) {
- case UPLOAD_ERR_INI_SIZE:
- case UPLOAD_ERR_FORM_SIZE:
- return false;
- }
- $pathInfo = pathinfo($this->tmpFile['name']);
- $extension = isset($pathInfo['extension']) ? strtolower($pathInfo['extension']) : null;
- $maxSize = $this->getAllowedMaxFileSize($extension);
- return (!$this->tmpFile['size'] || !$maxSize || (int)$this->tmpFile['size'] < $maxSize);
- }
-
- /**
- * Determine if this file is valid but empty
- *
- * @return bool
- */
- public function isFileEmpty()
- {
- // Don't check file size for errors
- if ($this->tmpFile['error'] !== UPLOAD_ERR_OK) {
- return false;
- }
- return empty($this->tmpFile['size']);
- }
-
- /**
- * Determines if the temporary file has a valid extension
- * An empty string in the validation map indicates files without an extension.
- * @return boolean
- */
- public function isValidExtension()
- {
- $pathInfo = pathinfo($this->tmpFile['name']);
-
- // Special case for filenames without an extension
- if (!isset($pathInfo['extension'])) {
- return in_array('', $this->allowedExtensions, true);
- } else {
- return (!count($this->allowedExtensions)
- || in_array(strtolower($pathInfo['extension']), $this->allowedExtensions));
- }
- }
-
- /**
- * Run through the rules for this validator checking against
- * the temporary file set by {@link setTmpFile()} to see if
- * the file is deemed valid or not.
- *
- * @return boolean
- */
- public function validate()
- {
- // we don't validate for empty upload fields yet
- if (empty($this->tmpFile['name'])) {
- return true;
- }
-
- // Check file upload
- if (!$this->isValidUpload()) {
- $this->errors[] = _t('File.NOVALIDUPLOAD', 'File is not a valid upload');
- return false;
- }
-
- // Check file isn't empty
- if ($this->isFileEmpty()) {
- $this->errors[] = _t('File.NOFILESIZE', 'Filesize is zero bytes.');
- return false;
- }
-
- // filesize validation
- if (!$this->isValidSize()) {
- $pathInfo = pathinfo($this->tmpFile['name']);
- $ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
- $arg = File::format_size($this->getAllowedMaxFileSize($ext));
- $this->errors[] = _t(
- 'File.TOOLARGE',
- 'Filesize is too large, maximum {size} allowed',
- 'Argument 1: Filesize (e.g. 1MB)',
- array('size' => $arg)
- );
- return false;
- }
-
- // extension validation
- if (!$this->isValidExtension()) {
- $this->errors[] = _t(
- 'File.INVALIDEXTENSION_SHORT',
- 'Extension is not allowed'
- );
- return false;
- }
-
- return true;
- }
-
- /**
- * Check that a valid file was given for upload (ignores file size)
- *
- * @return bool
- */
- public function isValidUpload()
- {
- // Check file upload
- if ($this->tmpFile['error'] === UPLOAD_ERR_NO_FILE) {
- return false;
- }
-
- // Check if file is valid uploaded (with exception for unit testing)
- // Note that some "max file size" errors leave "temp_name" empty, so don't fail on this.
- $isRunningTests = (class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test());
- if (!empty($this->tmpFile['tmp_name']) && !is_uploaded_file($this->tmpFile['tmp_name']) && !$isRunningTests) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/Core/Extensible.php b/src/Core/Extensible.php
index 6b4953d90..7b4e4d893 100644
--- a/src/Core/Extensible.php
+++ b/src/Core/Extensible.php
@@ -36,7 +36,7 @@ trait Extensible
* @var array $extensions
* @config
*/
- private static $extensions = null;
+ private static $extensions = [];
private static $classes_constructed = array();
diff --git a/src/ORM/DataObject.php b/src/ORM/DataObject.php
index ec2614c0d..1cb80c07c 100644
--- a/src/ORM/DataObject.php
+++ b/src/ORM/DataObject.php
@@ -2,7 +2,6 @@
namespace SilverStripe\ORM;
-use SilverStripe\Assets\AssetControlExtension;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Object;
@@ -20,7 +19,6 @@ use SilverStripe\ORM\Filters\SearchFilter;
use SilverStripe\ORM\Search\SearchContext;
use SilverStripe\ORM\Queries\SQLInsert;
use SilverStripe\ORM\Queries\SQLDelete;
-use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBComposite;
@@ -247,16 +245,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
'Created' => 'DBDatetime',
);
- /**
- * Core dataobject extensions
- *
- * @config
- * @var array
- */
- private static $extensions = [
- 'AssetControl' => AssetControlExtension::class,
- ];
-
/**
* Override table name for this class. If ignored will default to FQN of class.
* This option is not inheritable, and must be set on each class.
diff --git a/templates/DBFile_download.ss b/templates/DBFile_download.ss
deleted file mode 100644
index fc9c910db..000000000
--- a/templates/DBFile_download.ss
+++ /dev/null
@@ -1 +0,0 @@
-download="$Basename.ATT"<% else %>download<% end_if %>/>
diff --git a/templates/DBFile_image.ss b/templates/DBFile_image.ss
deleted file mode 100644
index 07ccde96e..000000000
--- a/templates/DBFile_image.ss
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/templates/Image_iframe.ss b/templates/Image_iframe.ss
deleted file mode 100644
index 0f1139fbe..000000000
--- a/templates/Image_iframe.ss
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- <% base_tag %>
- <%t Image_iframe_ss.TITLE 'Image Uploading Iframe' %>
-
-
-
-
- <% if $UseSimpleForm %>
- $EditImageSimpleForm
- <% else %>
- $EditImageForm
- <% end_if %>
-
-
- <% if $Image.ID %>
-
- $Image.CMSThumbnail
- <% if $DeleteImageForm %>
- $DeleteImageForm
- <% end_if %>
-
- <% end_if %>
-
-
-
-
diff --git a/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_HTAccess.ss b/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_HTAccess.ss
deleted file mode 100644
index e324f8bb9..000000000
--- a/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_HTAccess.ss
+++ /dev/null
@@ -1,2 +0,0 @@
-Deny from all
-RewriteRule .* - [F]
diff --git a/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_WebConfig.ss b/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_WebConfig.ss
deleted file mode 100644
index bce81c05b..000000000
--- a/templates/SilverStripe/Assets/Flysystem/ProtectedAssetAdapter_WebConfig.ss
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_HTAccess.ss b/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_HTAccess.ss
deleted file mode 100644
index 54b33a129..000000000
--- a/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_HTAccess.ss
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Whitelist appropriate assets files.
-# This file is automatically generated via File.allowed_extensions configuration
-# See AssetAdapter::renderTemplate() for reference.
-#
-
-
- SetEnv HTTP_MOD_REWRITE On
- RewriteEngine On
-
- # Disable PHP handler
- RewriteCond %{REQUEST_URI} .(?i:php|phtml|php3|php4|php5|inc)$
- RewriteRule .* - [F]
-
- # Allow error pages
- RewriteCond %{REQUEST_FILENAME} -f
- RewriteRule error[^\\\\/]*\\.html$ - [L]
-
- # Block invalid file extensions
- RewriteCond %{REQUEST_URI} !\\.(?i:<% loop $AllowedExtensions %>$Extension<% if not $Last %>|<% end_if %><% end_loop %>)$
- RewriteRule .* - [F]
-
- # Non existant files passed to requesthandler
- RewriteCond %{REQUEST_URI} ^(.*)$
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteRule .* ../framework/main.php?url=%1 [QSA]
-
diff --git a/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_WebConfig.ss b/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_WebConfig.ss
deleted file mode 100644
index 18cc41ca8..000000000
--- a/templates/SilverStripe/Assets/Flysystem/PublicAssetAdapter_WebConfig.ss
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
- <% loop $AllowedExtensions %>
-
- <% end_loop %>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/php/Assets/AssetControlExtensionTest.php b/tests/php/Assets/AssetControlExtensionTest.php
deleted file mode 100644
index a9c4c41d2..000000000
--- a/tests/php/Assets/AssetControlExtensionTest.php
+++ /dev/null
@@ -1,230 +0,0 @@
-logInWithPermission('ADMIN');
-
- // Setup fixture manually
- $object1 = new AssetControlExtensionTest\VersionedObject();
- $object1->Title = 'My object';
- $fish1 = realpath(__DIR__ .'/../ORM/ImageTest/test-image-high-quality.jpg');
- $object1->Header->setFromLocalFile($fish1, 'Header/MyObjectHeader.jpg');
- $object1->Download->setFromString('file content', 'Documents/File.txt');
- $object1->write();
- $object1->publishSingle();
-
- $object2 = new AssetControlExtensionTest\TestObject();
- $object2->Title = 'Unversioned';
- $object2->Image->setFromLocalFile($fish1, 'Images/BeautifulFish.jpg');
- $object2->write();
-
- $object3 = new AssetControlExtensionTest\ArchivedObject();
- $object3->Title = 'Archived';
- $object3->Header->setFromLocalFile($fish1, 'Archived/MyObjectHeader.jpg');
- $object3->write();
- $object3->publishSingle();
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- parent::tearDown();
- }
-
- public function testFileDelete()
- {
- Versioned::set_stage(Versioned::DRAFT);
-
- /**
- * @var VersionedObject $object1
- */
- $object1 = AssetControlExtensionTest\VersionedObject::get()
- ->filter('Title', 'My object')
- ->first();
- /**
- * @var Object $object2
- */
- $object2 = AssetControlExtensionTest\TestObject::get()
- ->filter('Title', 'Unversioned')
- ->first();
-
- /**
- * @var ArchivedObject $object3
- */
- $object3 = AssetControlExtensionTest\ArchivedObject::get()
- ->filter('Title', 'Archived')
- ->first();
-
- $this->assertTrue($object1->Download->exists());
- $this->assertTrue($object1->Header->exists());
- $this->assertTrue($object2->Image->exists());
- $this->assertTrue($object3->Header->exists());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1->Download->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1->Header->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object2->Image->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object3->Header->getVisibility());
-
- // Check live stage for versioned objects
- $object1Live = Versioned::get_one_by_stage(
- VersionedObject::class,
- 'Live',
- array('"ID"' => $object1->ID)
- );
- $object3Live = Versioned::get_one_by_stage(
- ArchivedObject::class,
- 'Live',
- array('"ID"' => $object3->ID)
- );
- $this->assertTrue($object1Live->Download->exists());
- $this->assertTrue($object1Live->Header->exists());
- $this->assertTrue($object3Live->Header->exists());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1Live->Download->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1Live->Header->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object3Live->Header->getVisibility());
-
- // Delete live records; Should cause versioned records to be protected
- $object1Live->deleteFromStage('Live');
- $object3Live->deleteFromStage('Live');
- $this->assertTrue($object1->Download->exists());
- $this->assertTrue($object1->Header->exists());
- $this->assertTrue($object3->Header->exists());
- $this->assertTrue($object1Live->Download->exists());
- $this->assertTrue($object1Live->Header->exists());
- $this->assertTrue($object3Live->Header->exists());
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object1->Download->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object1->Header->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object3->Header->getVisibility());
-
- // Delete draft record; Should remove all records
- // Archived assets only should remain
- $object1->delete();
- $object2->delete();
- $object3->delete();
- $this->assertFalse($object1->Download->exists());
- $this->assertFalse($object1->Header->exists());
- $this->assertFalse($object2->Image->exists());
- $this->assertTrue($object3->Header->exists());
- $this->assertFalse($object1Live->Download->exists());
- $this->assertFalse($object1Live->Header->exists());
- $this->assertTrue($object3Live->Header->exists());
- $this->assertNull($object1->Download->getVisibility());
- $this->assertNull($object1->Header->getVisibility());
- $this->assertNull($object2->Image->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object3->Header->getVisibility());
- }
-
- /**
- * Test files being replaced
- */
- public function testReplaceFile()
- {
- Versioned::set_stage(Versioned::DRAFT);
-
- /**
- * @var VersionedObject $object1
- */
- $object1 = AssetControlExtensionTest\VersionedObject::get()
- ->filter('Title', 'My object')
- ->first();
- /**
- * @var Object $object2
- */
- $object2 = AssetControlExtensionTest\TestObject::get()
- ->filter('Title', 'Unversioned')
- ->first();
-
- /**
- * @var ArchivedObject $object3
- */
- $object3 = AssetControlExtensionTest\ArchivedObject::get()
- ->filter('Title', 'Archived')
- ->first();
-
- $object1TupleOld = $object1->Header->getValue();
- $object2TupleOld = $object2->Image->getValue();
- $object3TupleOld = $object3->Header->getValue();
-
- // Replace image and write each to filesystem
- $fish1 = realpath(__DIR__ .'/../ORM/ImageTest/test-image-high-quality.jpg');
- $object1->Header->setFromLocalFile($fish1, 'Header/Replaced_MyObjectHeader.jpg');
- $object1->write();
- $object2->Image->setFromLocalFile($fish1, 'Images/Replaced_BeautifulFish.jpg');
- $object2->write();
- $object3->Header->setFromLocalFile($fish1, 'Archived/Replaced_MyObjectHeader.jpg');
- $object3->write();
-
- // Check that old published records are left public, but removed for unversioned object2
- $this->assertEquals(
- AssetStore::VISIBILITY_PUBLIC,
- $this->getAssetStore()->getVisibility($object1TupleOld['Filename'], $object1TupleOld['Hash'])
- );
- $this->assertEquals(
- null, // Old file is destroyed
- $this->getAssetStore()->getVisibility($object2TupleOld['Filename'], $object2TupleOld['Hash'])
- );
- $this->assertEquals(
- AssetStore::VISIBILITY_PUBLIC,
- $this->getAssetStore()->getVisibility($object3TupleOld['Filename'], $object3TupleOld['Hash'])
- );
-
- // Check that visibility of new file is correct
- // Note that $object2 has no canView() is true, so assets end up public
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object1->Header->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object2->Image->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object3->Header->getVisibility());
-
- // Publish changes to versioned records
- $object1->publishSingle();
- $object3->publishSingle();
-
- // After publishing, old object1 is deleted, but since object3 has archiving enabled,
- // the orphaned file is intentionally left in the protected store
- $this->assertEquals(
- null,
- $this->getAssetStore()->getVisibility($object1TupleOld['Filename'], $object1TupleOld['Hash'])
- );
- $this->assertEquals(
- AssetStore::VISIBILITY_PROTECTED,
- $this->getAssetStore()->getVisibility($object3TupleOld['Filename'], $object3TupleOld['Hash'])
- );
-
- // And after publish, all files are public
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1->Header->getVisibility());
- $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object3->Header->getVisibility());
- }
-
- /**
- * @return AssetStore
- */
- protected function getAssetStore()
- {
- return Injector::inst()->get('AssetStore');
- }
-}
diff --git a/tests/php/Assets/AssetControlExtensionTest/ArchivedObject.php b/tests/php/Assets/AssetControlExtensionTest/ArchivedObject.php
deleted file mode 100644
index 1e8b1fda2..000000000
--- a/tests/php/Assets/AssetControlExtensionTest/ArchivedObject.php
+++ /dev/null
@@ -1,15 +0,0 @@
- 'Varchar(255)',
- 'Image' => "DBFile('image/supported')"
- );
-
- private static $table_name = 'AssetControlExtensionTest_TestObject';
-
- /**
- * @param Member $member
- * @return bool
- */
- public function canView($member = null)
- {
- return true;
- }
-}
diff --git a/tests/php/Assets/AssetControlExtensionTest/VersionedObject.php b/tests/php/Assets/AssetControlExtensionTest/VersionedObject.php
deleted file mode 100644
index e580920d1..000000000
--- a/tests/php/Assets/AssetControlExtensionTest/VersionedObject.php
+++ /dev/null
@@ -1,52 +0,0 @@
- 'Varchar(255)',
- 'Header' => "DBFile('image/supported')",
- 'Download' => 'DBFile'
- );
-
- private static $table_name = 'AssetControlExtensionTest_VersionedObject';
-
- /**
- * @param Member $member
- * @return bool
- */
- public function canView($member = null)
- {
- if (!$member) {
- $member = Member::currentUser();
- }
-
- // Expectation that versioned::canView will hide this object in draft
- $result = $this->extendedCan('canView', $member);
- if ($result !== null) {
- return $result;
- }
-
- // Open to public
- return true;
- }
-}
diff --git a/tests/php/Assets/AssetManipulationListTest.php b/tests/php/Assets/AssetManipulationListTest.php
deleted file mode 100644
index 45d6dc88b..000000000
--- a/tests/php/Assets/AssetManipulationListTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
- 'Test1.jpg', 'Hash' => '975677589962604d9e16b700cf84734f9dda2817'];
- $file2 = ['Filename' => 'Test2.jpg', 'Hash' => '22af86a45ea56287437a12cf83aded5c077a5db5'];
- $file3 = ['Filename' => 'DupeHash1.jpg', 'Hash' => 'f167433dd318e738281b845a07d7be2053b8c997'];
- $file4 = ['Filename' => 'DupeName.jpg', 'Hash' => 'afde6577a034323959b7915f41ac8d1f53bc597f'];
- $file5 = ['Filename' => 'DupeName.jpg', 'Hash' => '1e94b066e5aa16907d0e5e32556c7a2a0b692eb9'];
- $file6 = ['Filename' => 'DupeHash2.jpg', 'Hash' => 'f167433dd318e738281b845a07d7be2053b8c997'];
-
- // Non-overlapping assets remain in assigned sets
- $this->assertTrue($set->addDeletedAsset($file1));
- $this->assertTrue($set->addDeletedAsset($file2));
- $this->assertTrue($set->addProtectedAsset($file3));
- $this->assertTrue($set->addProtectedAsset($file4));
- $this->assertTrue($set->addPublicAsset($file5));
- $this->assertTrue($set->addPublicAsset($file6));
-
- // Check initial state of list
- $this->assertEquals(6, $this->countItems($set));
- $this->assertContains($file1, $set->getDeletedAssets());
- $this->assertContains($file2, $set->getDeletedAssets());
- $this->assertContains($file3, $set->getProtectedAssets());
- $this->assertContains($file4, $set->getProtectedAssets());
- $this->assertContains($file5, $set->getPublicAssets());
- $this->assertContains($file6, $set->getPublicAssets());
-
- // Public or Protected assets will not be deleted
- $this->assertFalse($set->addDeletedAsset($file3));
- $this->assertFalse($set->addDeletedAsset($file4));
- $this->assertFalse($set->addDeletedAsset($file5));
- $this->assertFalse($set->addDeletedAsset($file6));
- $this->assertEquals(6, $this->countItems($set));
- $this->assertNotContains($file3, $set->getDeletedAssets());
- $this->assertNotContains($file4, $set->getDeletedAssets());
- $this->assertNotContains($file5, $set->getDeletedAssets());
- $this->assertNotContains($file6, $set->getDeletedAssets());
-
- // Adding records as protected will remove them from the deletion list, but
- // not the public list
- $this->assertTrue($set->addProtectedAsset($file1));
- $this->assertFalse($set->addProtectedAsset($file5));
- $this->assertEquals(6, $this->countItems($set));
- $this->assertNotContains($file1, $set->getDeletedAssets());
- $this->assertContains($file1, $set->getProtectedAssets());
- $this->assertNotContains($file5, $set->getProtectedAssets());
- $this->assertContains($file5, $set->getPublicAssets());
-
- // Adding records as public will ensure they are not deleted or marked as protected
- // Existing public assets won't be re-added
- $this->assertTrue($set->addPublicAsset($file2));
- $this->assertTrue($set->addPublicAsset($file4));
- $this->assertFalse($set->addPublicAsset($file5));
- $this->assertEquals(6, $this->countItems($set));
- $this->assertNotContains($file2, $set->getDeletedAssets());
- $this->assertNotContains($file2, $set->getProtectedAssets());
- $this->assertContains($file2, $set->getPublicAssets());
- $this->assertNotContains($file4, $set->getProtectedAssets());
- $this->assertContains($file4, $set->getPublicAssets());
- $this->assertContains($file5, $set->getPublicAssets());
- }
-
- /**
- * Helper to count all items in a set
- *
- * @param AssetManipulationList $set
- * @return int
- */
- protected function countItems(AssetManipulationList $set)
- {
- return count($set->getPublicAssets()) + count($set->getProtectedAssets()) + count($set->getDeletedAssets());
- }
-}
diff --git a/tests/php/Assets/FileFinderTest.php b/tests/php/Assets/FileFinderTest.php
deleted file mode 100644
index 030e51dfa..000000000
--- a/tests/php/Assets/FileFinderTest.php
+++ /dev/null
@@ -1,136 +0,0 @@
-base = __DIR__ . '/FileFinderTest';
- parent::__construct();
- }
-
- public function testBasicOperation()
- {
- $this->assertFinderFinds(
- new FileFinder(),
- array(
- 'file1.txt',
- 'file2.txt',
- 'dir1/dir1file1.txt',
- 'dir1/dir1file2.txt',
- 'dir1/dir2/dir2file1.txt',
- 'dir1/dir2/dir3/dir3file1.txt'
- )
- );
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testInvalidOptionThrowsException()
- {
- $finder = new FileFinder();
- $finder->setOption('this_doesnt_exist', 'ok');
- }
-
- public function testFilenameRegex()
- {
- $finder = new FileFinder();
- $finder->setOption('name_regex', '/file2\.txt$/');
-
- $this->assertFinderFinds(
- $finder,
- array(
- 'file2.txt',
- 'dir1/dir1file2.txt'),
- 'The finder only returns files matching the name regex.'
- );
- }
-
- public function testIgnoreFiles()
- {
- $finder = new FileFinder();
- $finder->setOption('ignore_files', array('file1.txt', 'dir1file1.txt', 'dir2file1.txt'));
-
- $this->assertFinderFinds(
- $finder,
- array(
- 'file2.txt',
- 'dir1/dir1file2.txt',
- 'dir1/dir2/dir3/dir3file1.txt'),
- 'The finder ignores files with the basename in the ignore_files setting.'
- );
- }
-
- public function testIgnoreDirs()
- {
- $finder = new FileFinder();
- $finder->setOption('ignore_dirs', array('dir2'));
-
- $this->assertFinderFinds(
- $finder,
- array(
- 'file1.txt',
- 'file2.txt',
- 'dir1/dir1file1.txt',
- 'dir1/dir1file2.txt'),
- 'The finder ignores directories in ignore_dirs.'
- );
- }
-
- public function testMinDepth()
- {
- $finder = new FileFinder();
- $finder->setOption('min_depth', 2);
-
- $this->assertFinderFinds(
- $finder,
- array(
- 'dir1/dir2/dir2file1.txt',
- 'dir1/dir2/dir3/dir3file1.txt'
- ),
- 'The finder respects the min depth setting.'
- );
- }
-
- public function testMaxDepth()
- {
- $finder = new FileFinder();
- $finder->setOption('max_depth', 1);
-
- $this->assertFinderFinds(
- $finder,
- array(
- 'file1.txt',
- 'file2.txt',
- 'dir1/dir1file1.txt',
- 'dir1/dir1file2.txt'),
- 'The finder respects the max depth setting.'
- );
- }
-
- public function assertFinderFinds(FileFinder $finder, $expect, $message = null)
- {
- $found = $finder->find($this->base);
-
- foreach ($expect as $k => $file) {
- $expect[$k] = "{$this->base}/$file";
- }
-
- sort($expect);
- sort($found);
-
- $this->assertEquals($expect, $found, $message);
- }
-}
diff --git a/tests/php/Assets/FileFinderTest/dir1/dir1file1.txt b/tests/php/Assets/FileFinderTest/dir1/dir1file1.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileFinderTest/dir1/dir1file2.txt b/tests/php/Assets/FileFinderTest/dir1/dir1file2.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileFinderTest/dir1/dir2/dir2file1.txt b/tests/php/Assets/FileFinderTest/dir1/dir2/dir2file1.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileFinderTest/dir1/dir2/dir3/dir3file1.txt b/tests/php/Assets/FileFinderTest/dir1/dir2/dir3/dir3file1.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileFinderTest/file1.txt b/tests/php/Assets/FileFinderTest/file1.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileFinderTest/file2.txt b/tests/php/Assets/FileFinderTest/file2.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/php/Assets/FileMigrationHelperTest.php b/tests/php/Assets/FileMigrationHelperTest.php
deleted file mode 100644
index ed7eba7dc..000000000
--- a/tests/php/Assets/FileMigrationHelperTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
- array(
- Extension::class
- )
- );
-
- /**
- * get the BASE_PATH for this test
- *
- * @return string
- */
- protected function getBasePath()
- {
- // Note that the actual filesystem base is the 'assets' subdirectory within this
- return ASSETS_PATH . '/FileMigrationHelperTest';
- }
-
-
- public function setUp()
- {
- Config::nest(); // additional nesting here necessary
- Config::inst()->update(File::class, 'migrate_legacy_file', false);
- parent::setUp();
-
- // Set backend root to /FileMigrationHelperTest/assets
- TestAssetStore::activate('FileMigrationHelperTest/assets');
-
- // Ensure that each file has a local record file in this new assets base
- $from = FRAMEWORK_PATH . '/tests/php/ORM/ImageTest/test-image-low-quality.jpg';
- foreach (File::get()->exclude('ClassName', Folder::class) as $file) {
- $dest = TestAssetStore::base_path() . '/' . $file->generateFilename();
- Filesystem::makeFolder(dirname($dest));
- copy($from, $dest);
- }
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- Filesystem::removeFolder($this->getBasePath());
- parent::tearDown();
- Config::unnest();
- }
-
- /**
- * Test file migration
- */
- public function testMigration()
- {
- // Prior to migration, check that each file has empty Filename / Hash properties
- foreach (File::get()->exclude('ClassName', Folder::class) as $file) {
- $filename = $file->generateFilename();
- $this->assertNotEmpty($filename, "File {$file->Name} has a filename");
- $this->assertEmpty($file->File->getFilename(), "File {$file->Name} has no DBFile filename");
- $this->assertEmpty($file->File->getHash(), "File {$file->Name} has no hash");
- $this->assertFalse($file->exists(), "File with name {$file->Name} does not yet exist");
- $this->assertFalse($file->isPublished(), "File is not published yet");
- }
-
- // Do migration
- $helper = new FileMigrationHelper();
- $result = $helper->run($this->getBasePath());
- $this->assertEquals(5, $result);
-
- // Test that each file exists
- foreach (File::get()->exclude('ClassName', Folder::class) as $file) {
- $expectedFilename = $file->generateFilename();
- $filename = $file->File->getFilename();
- $this->assertTrue($file->exists(), "File with name {$filename} exists");
- $this->assertNotEmpty($filename, "File {$file->Name} has a Filename");
- $this->assertEquals($expectedFilename, $filename, "File {$file->Name} has retained its Filename value");
- $this->assertEquals(
- '33be1b95cba0358fe54e8b13532162d52f97421c',
- $file->File->getHash(),
- "File with name {$filename} has the correct hash"
- );
- $this->assertTrue($file->isPublished(), "File is published after migration");
- }
- }
-}
diff --git a/tests/php/Assets/FileMigrationHelperTest.yml b/tests/php/Assets/FileMigrationHelperTest.yml
deleted file mode 100644
index 53e6e00b0..000000000
--- a/tests/php/Assets/FileMigrationHelperTest.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-SilverStripe\Assets\Folder:
- parent:
- Name: ParentFolder
- subfolder:
- Name: SubFolder
- Parent: =>SilverStripe\Assets\Folder.parent
-SilverStripe\Assets\Image:
- image1:
- Name: myimage.jpg
- image2:
- Name: myimage.jpg
- ParentID: =>SilverStripe\Assets\Folder.subfolder
-SilverStripe\Assets\File:
- file1:
- Name: anotherfile.jpg
- file2:
- Name: file.jpg
- ParentID: =>SilverStripe\Assets\Folder.parent
- file3:
- Name: picture.jpg
- ParentID: =>SilverStripe\Assets\Folder.subfolder
diff --git a/tests/php/Assets/FileMigrationHelperTest/Extension.php b/tests/php/Assets/FileMigrationHelperTest/Extension.php
deleted file mode 100644
index 32c005000..000000000
--- a/tests/php/Assets/FileMigrationHelperTest/Extension.php
+++ /dev/null
@@ -1,26 +0,0 @@
- "Text",
- );
-
- public function onBeforeWrite()
- {
- // Ensure underlying filename field is written to the database
- $this->owner->setField('Filename', 'assets/' . $this->owner->generateFilename());
- }
-}
diff --git a/tests/php/Assets/FileNameFilterTest.php b/tests/php/Assets/FileNameFilterTest.php
deleted file mode 100644
index bd4261f27..000000000
--- a/tests/php/Assets/FileNameFilterTest.php
+++ /dev/null
@@ -1,152 +0,0 @@
-update(
- 'SilverStripe\\Assets\\FileNameFilter',
- 'default_replacements',
- array(
- '/\s/' => '-', // remove whitespace
- '/_/' => '-', // underscores to dashes
- '/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
- '/[\-]{2,}/' => '-', // remove duplicate dashes
- '/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
- )
- );
- }
-
- public function testFilter()
- {
- $name = 'Brötchen für allë-mit_Unterstrich!.jpg';
- $filter = new FileNameFilter();
- $filter->setTransliterator(false);
- $this->assertEquals(
- 'Brtchen-fr-all-mit-Unterstrich.jpg',
- $filter->filter($name)
- );
- }
-
- public function testFilterWithTransliterator()
- {
- $name = 'Brötchen für allë-mit_Unterstrich!.jpg';
- $filter = new FileNameFilter();
- $filter->setTransliterator(new Transliterator());
- $this->assertEquals(
- 'Broetchen-fuer-alle-mit-Unterstrich.jpg',
- $filter->filter($name)
- );
- }
-
- public function testFilterWithCustomRules()
- {
- $name = 'Kuchen ist besser.jpg';
- $filter = new FileNameFilter();
- $filter->setTransliterator(false);
- $filter->setReplacements(array('/[\s-]/' => '_'));
- $this->assertEquals(
- 'Kuchen_ist_besser.jpg',
- $filter->filter($name)
- );
- }
-
- public function testFilterWithEmptyString()
- {
- $name = 'ö ö ö.jpg';
- $filter = new FileNameFilter();
- $filter->setTransliterator(new Transliterator());
- $result = $filter->filter($name);
- $this->assertFalse(
- empty($result)
- );
- $this->assertStringEndsWith(
- '.jpg',
- $result
- );
- $this->assertGreaterThan(
- strlen('.jpg'),
- strlen($result)
- );
- }
-
- public function testUnderscoresStartOfNameRemoved()
- {
- $name = '_test.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test.txt', $filter->filter($name));
- }
-
- public function testDoubleUnderscoresStartOfNameRemoved()
- {
- $name = '__test.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test.txt', $filter->filter($name));
- }
-
- public function testDotsStartOfNameRemoved()
- {
- $name = '.test.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test.txt', $filter->filter($name));
- }
-
- public function testDoubleDotsStartOfNameRemoved()
- {
- $name = '..test.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test.txt', $filter->filter($name));
- }
-
- public function testMixedInvalidCharsStartOfNameRemoved()
- {
- $name = '..#@$#@$^__test.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test.txt', $filter->filter($name));
- }
-
- public function testWhitespaceRemoved()
- {
- $name = ' test doc.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test-doc.txt', $filter->filter($name));
- }
-
- public function testUnderscoresReplacedWithDashes()
- {
- $name = 'test_doc.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test-doc.txt', $filter->filter($name));
- }
-
- public function testNonAsciiCharsReplacedWithDashes()
- {
- $name = '!@#$%^test_123@##@$#%^.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test-123.txt', $filter->filter($name));
- }
-
- public function testDuplicateDashesRemoved()
- {
- $name = 'test--document.txt';
- $filter = new FileNameFilter();
- $this->assertEquals('test-document.txt', $filter->filter($name));
- }
-
- public function testDoesntAddExtensionWhenMissing()
- {
- $name = 'no-extension';
- $filter = new FileNameFilter();
- $this->assertEquals('no-extension', $filter->filter($name));
- }
-}
diff --git a/tests/php/Assets/FileTest.php b/tests/php/Assets/FileTest.php
deleted file mode 100644
index b38e6770c..000000000
--- a/tests/php/Assets/FileTest.php
+++ /dev/null
@@ -1,714 +0,0 @@
-logInWithPermission('ADMIN');
- Versioned::set_stage(Versioned::DRAFT);
-
- // Set backend root to /ImageTest
- TestAssetStore::activate('FileTest');
-
- // Create a test folders for each of the fixture references
- $folderIDs = $this->allFixtureIDs(Folder::class);
- foreach ($folderIDs as $folderID) {
- $folder = DataObject::get_by_id(Folder::class, $folderID);
- $filePath = ASSETS_PATH . '/FileTest/' . $folder->getFilename();
- Filesystem::makeFolder($filePath);
- }
-
- // Create a test files for each of the fixture references
- $fileIDs = $this->allFixtureIDs(File::class);
- foreach ($fileIDs as $fileID) {
- /**
- * @var File $file
- */
- $file = DataObject::get_by_id(File::class, $fileID);
- $root = ASSETS_PATH . '/FileTest/';
- if ($folder = $file->Parent()) {
- $root .= $folder->getFilename();
- }
- $path = $root . substr($file->getHash(), 0, 10) . '/' . basename($file->getFilename());
- Filesystem::makeFolder(dirname($path));
- $fh = fopen($path, "w+");
- fwrite($fh, str_repeat('x', 1000000));
- fclose($fh);
- }
-
- // Conditional fixture creation in case the 'cms' module is installed
- if (class_exists('SilverStripe\\CMS\\Model\\ErrorPage')) {
- $page = new ErrorPage(
- array(
- 'Title' => 'Page not Found',
- 'ErrorCode' => 404
- )
- );
- $page->write();
- $page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
- }
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- parent::tearDown();
- }
-
- public function testLinkShortcodeHandler()
- {
- $testFile = $this->objFromFixture(File::class, 'asdf');
-
- $parser = new ShortcodeParser();
- $parser->register('file_link', array(File::class, 'handle_shortcode'));
-
- $fileShortcode = sprintf('[file_link,id=%d]', $testFile->ID);
- $fileEnclosed = sprintf('[file_link,id=%d]Example Content[/file_link]', $testFile->ID);
-
- $fileShortcodeExpected = $testFile->Link();
- $fileEnclosedExpected = sprintf(
- 'Example Content',
- $testFile->Link()
- );
-
- $this->assertEquals($fileShortcodeExpected, $parser->parse($fileShortcode), 'Test that simple linking works.');
- $this->assertEquals($fileEnclosedExpected, $parser->parse($fileEnclosed), 'Test enclosed content is linked.');
-
- $testFile->delete();
-
- $fileShortcode = '[file_link,id="-1"]';
- $fileEnclosed = '[file_link,id="-1"]Example Content[/file_link]';
-
- $this->assertEquals('', $parser->parse('[file_link]'), 'Test that invalid ID attributes are not parsed.');
- $this->assertEquals('', $parser->parse('[file_link,id="text"]'));
- $this->assertEquals('', $parser->parse('[file_link]Example Content[/file_link]'));
-
- if (class_exists('SilverStripe\\CMS\\Model\\ErrorPage')) {
- $errorPage = ErrorPage::get()->filter('ErrorCode', 404)->first();
- $this->assertEquals(
- $errorPage->Link(),
- $parser->parse($fileShortcode),
- 'Test link to 404 page if no suitable matches.'
- );
- $this->assertEquals(
- sprintf('Example Content', $errorPage->Link()),
- $parser->parse($fileEnclosed)
- );
- } else {
- $this->assertEquals(
- '',
- $parser->parse($fileShortcode),
- 'Short code is removed if file record is not present.'
- );
- $this->assertEquals('', $parser->parse($fileEnclosed));
- }
- }
-
- public function testCreateWithFilenameWithSubfolder()
- {
- // Note: We can't use fixtures/setUp() for this, as we want to create the db record manually.
- // Creating the folder is necessary to avoid having "Filename" overwritten by setName()/setRelativePath(),
- // because the parent folders don't exist in the database
- $folder = Folder::find_or_make('/FileTest/');
- $testfilePath = BASE_PATH . '/assets/FileTest/CreateWithFilenameHasCorrectPath.txt'; // Important: No leading slash
- $fh = fopen($testfilePath, 'w');
- fwrite($fh, str_repeat('x', 1000000));
- fclose($fh);
-
- $file = new File();
- $file->setFromLocalFile($testfilePath);
- $file->ParentID = $folder->ID;
- $file->write();
-
- $this->assertEquals(
- 'CreateWithFilenameHasCorrectPath.txt',
- $file->Name,
- '"Name" property is automatically set from "Filename"'
- );
- $this->assertEquals(
- 'FileTest/CreateWithFilenameHasCorrectPath.txt',
- $file->Filename,
- '"Filename" property remains unchanged'
- );
-
- // TODO This should be auto-detected, see File->updateFilesystem()
- // $this->assertInstanceOf('Folder', $file->Parent(), 'Parent folder is created in database');
- // $this->assertFileExists($file->Parent()->getURL(), 'Parent folder is created on filesystem');
- // $this->assertEquals('FileTest', $file->Parent()->Name);
- // $this->assertInstanceOf('Folder', $file->Parent()->Parent(), 'Grandparent folder is created in database');
- // $this->assertFileExists($file->Parent()->Parent()->getURL(),
- // 'Grandparent folder is created on filesystem');
- // $this->assertEquals('assets', $file->Parent()->Parent()->Name);
- }
-
- public function testGetExtension()
- {
- $this->assertEquals(
- '',
- File::get_file_extension('myfile'),
- 'No extension'
- );
- $this->assertEquals(
- 'txt',
- File::get_file_extension('myfile.txt'),
- 'Simple extension'
- );
- $this->assertEquals(
- 'gz',
- File::get_file_extension('myfile.tar.gz'),
- 'Double-barrelled extension only returns last bit'
- );
- }
-
- public function testValidateExtension()
- {
- Session::set('loggedInAs', null);
-
- $orig = Config::inst()->get(File::class, 'allowed_extensions');
- Config::inst()->remove(File::class, 'allowed_extensions');
- Config::inst()->update(File::class, 'allowed_extensions', array('txt'));
-
- $file = $this->objFromFixture(File::class, 'asdf');
-
- // Invalid ext
- $file->Name = 'asdf.php';
- $result = $file->validate();
- $this->assertFalse($result->isValid());
- $messages = $result->getMessages();
- $this->assertEquals(1, count($messages));
- $this->assertEquals('Extension is not allowed', $messages[0]['message']);
-
- // Valid ext
- $file->Name = 'asdf.txt';
- $result = $file->validate();
- $this->assertTrue($result->isValid());
-
- // Capital extension is valid as well
- $file->Name = 'asdf.TXT';
- $result = $file->validate();
- $this->assertTrue($result->isValid());
- }
-
- public function testAppCategory()
- {
- // Test various categories
- $this->assertEquals('image', File::get_app_category('jpg'));
- $this->assertEquals('image', File::get_app_category('JPG'));
- $this->assertEquals('image', File::get_app_category('JPEG'));
- $this->assertEquals('image', File::get_app_category('png'));
- $this->assertEquals('image', File::get_app_category('tif'));
- $this->assertEquals('document', File::get_app_category('pdf'));
- $this->assertEquals('video', File::get_app_category('mov'));
- $this->assertEquals('audio', File::get_app_category('OGG'));
- }
-
- public function testGetCategoryExtensions()
- {
- // Test specific categories
- $images = array(
- 'alpha', 'als', 'bmp', 'cel', 'gif', 'ico', 'icon', 'jpeg', 'jpg', 'pcx', 'png', 'ps', 'tif', 'tiff'
- );
- $this->assertEquals($images, File::get_category_extensions('image'));
- $this->assertEquals(array('gif', 'jpeg', 'jpg', 'png'), File::get_category_extensions('image/supported'));
- $this->assertEquals($images, File::get_category_extensions(array('image', 'image/supported')));
- $this->assertEquals(
- array('fla', 'gif', 'jpeg', 'jpg', 'png', 'swf'),
- File::get_category_extensions(array('flash', 'image/supported'))
- );
-
- // Test other categories have at least one item
- $this->assertNotEmpty(File::get_category_extensions('archive'));
- $this->assertNotEmpty(File::get_category_extensions('audio'));
- $this->assertNotEmpty(File::get_category_extensions('document'));
- $this->assertNotEmpty(File::get_category_extensions('flash'));
- $this->assertNotEmpty(File::get_category_extensions('video'));
- }
-
- /**
- * @dataProvider allowedExtensions
- * @param string $extension
- */
- public function testAllFilesHaveCategory($extension)
- {
- $this->assertNotEmpty(
- File::get_app_category($extension),
- "Assert that extension {$extension} has a valid category"
- );
- }
-
- /**
- * Gets the list of all extensions for testing
- *
- * @return array
- */
- public function allowedExtensions()
- {
- $args = array();
- foreach (array_filter(File::config()->allowed_extensions) as $ext) {
- $args[] = array($ext);
- }
- return $args;
- }
-
- public function testSetNameChangesFilesystemOnWrite()
- {
- /**
- * @var File $file
- */
- $file = $this->objFromFixture(File::class, 'asdf');
- $this->logInWithPermission('ADMIN');
- $file->publishRecursive();
- $oldTuple = $file->File->getValue();
-
- // Rename
- $file->Name = 'renamed.txt';
- $newTuple = $oldTuple;
- $newTuple['Filename'] = $file->generateFilename();
-
- // Before write()
- $this->assertTrue(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old path is still present'
- );
- $this->assertFalse(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is updated in memory, not written before write() is called'
- );
-
- // After write()
- $file->write();
- $this->assertTrue(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old path exists after draft change'
- );
- $this->assertTrue(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is created after write()'
- );
-
- // After publish
- $file->publishRecursive();
- $this->assertFalse(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old file is finally removed after publishing new file'
- );
- $this->assertTrue(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is created after write()'
- );
- }
-
- public function testSetParentIDChangesFilesystemOnWrite()
- {
- $file = $this->objFromFixture(File::class, 'asdf');
- $this->logInWithPermission('ADMIN');
- $file->publishRecursive();
- $subfolder = $this->objFromFixture(Folder::class, 'subfolder');
- $oldTuple = $file->File->getValue();
-
- // set ParentID
- $file->ParentID = $subfolder->ID;
- $newTuple = $oldTuple;
- $newTuple['Filename'] = $file->generateFilename();
-
- // Before write()
- $this->assertTrue(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old path is still present'
- );
- $this->assertFalse(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is updated in memory, not written before write() is called'
- );
- $file->write();
-
- // After write()
- $file->write();
- $this->assertTrue(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old path exists after draft change'
- );
- $this->assertTrue(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is created after write()'
- );
-
- // After publish
- $file->publishSingle();
- $this->assertFalse(
- $this->getAssetStore()->exists($oldTuple['Filename'], $oldTuple['Hash']),
- 'Old file is finally removed after publishing new file'
- );
- $this->assertTrue(
- $this->getAssetStore()->exists($newTuple['Filename'], $newTuple['Hash']),
- 'New path is created after write()'
- );
- }
-
- /**
- * @see http://open.silverstripe.org/ticket/5693
- */
- public function testSetNameWithInvalidExtensionDoesntChangeFilesystem()
- {
- Config::inst()->remove(File::class, 'allowed_extensions');
- Config::inst()->update(File::class, 'allowed_extensions', array('txt'));
- $this->setExpectedException(ValidationException::class);
-
- $file = $this->objFromFixture(File::class, 'asdf');
- $file->Name = 'renamed.php'; // evil extension
- $file->write();
- }
-
- public function testGetURL()
- {
- $rootfile = $this->objFromFixture(File::class, 'asdf');
- $this->assertEquals('/assets/FileTest/55b443b601/FileTest.txt', $rootfile->getURL());
- }
-
- public function testGetAbsoluteURL()
- {
- $rootfile = $this->objFromFixture(File::class, 'asdf');
- $this->assertEquals(
- Director::absoluteBaseURL() . 'assets/FileTest/55b443b601/FileTest.txt',
- $rootfile->getAbsoluteURL()
- );
- }
-
- public function testNameAndTitleGeneration()
- {
- // When name is assigned, title is automatically assigned
- $file = $this->objFromFixture(Image::class, 'setfromname');
- $this->assertEquals('FileTest', $file->Title);
- }
-
- public function testSizeAndAbsoluteSizeParameters()
- {
- $file = $this->objFromFixture(File::class, 'asdf');
-
- /* AbsoluteSize will give the integer number */
- $this->assertEquals(1000000, $file->AbsoluteSize);
- /* Size will give a humanised number */
- $this->assertEquals('977 KB', $file->Size);
- }
-
- public function testFileType()
- {
- $file = $this->objFromFixture(Image::class, 'gif');
- $this->assertEquals("GIF image - good for diagrams", $file->FileType);
-
- $file = $this->objFromFixture(File::class, 'pdf');
- $this->assertEquals("Adobe Acrobat PDF file", $file->FileType);
-
- $file = $this->objFromFixture(Image::class, 'gifupper');
- $this->assertEquals("GIF image - good for diagrams", $file->FileType);
-
- /* Only a few file types are given special descriptions; the rest are unknown */
- $file = $this->objFromFixture(File::class, 'asdf');
- $this->assertEquals("unknown", $file->FileType);
- }
-
- /**
- * Test the File::format_size() method
- */
- public function testFormatSize()
- {
- $this->assertEquals("1000 bytes", File::format_size(1000));
- $this->assertEquals("1023 bytes", File::format_size(1023));
- $this->assertEquals("1 KB", File::format_size(1025));
- $this->assertEquals("9.8 KB", File::format_size(10000));
- $this->assertEquals("49 KB", File::format_size(50000));
- $this->assertEquals("977 KB", File::format_size(1000000));
- $this->assertEquals("1 MB", File::format_size(1024*1024));
- $this->assertEquals("954 MB", File::format_size(1000000000));
- $this->assertEquals("1 GB", File::format_size(1024*1024*1024));
- $this->assertEquals("9.3 GB", File::format_size(10000000000));
- // It use any denomination higher than GB. It also doesn't overflow with >32 bit integers
- $this->assertEquals("93132.3 GB", File::format_size(100000000000000));
- }
-
- public function testDeleteFile()
- {
- /**
- * @var File $file
- */
- $file = $this->objFromFixture(File::class, 'asdf');
- $this->logInWithPermission('ADMIN');
- $file->publishSingle();
- $tuple = $file->File->getValue();
-
- // Before delete
- $this->assertTrue(
- $this->getAssetStore()->exists($tuple['Filename'], $tuple['Hash']),
- 'File is still present'
- );
-
- // after unpublish
- $file->doUnpublish();
- $this->assertTrue(
- $this->getAssetStore()->exists($tuple['Filename'], $tuple['Hash']),
- 'File is still present after unpublish'
- );
-
- // after delete
- $file->delete();
- $this->assertFalse(
- $this->getAssetStore()->exists($tuple['Filename'], $tuple['Hash']),
- 'File is deleted after unpublish and delete'
- );
- }
-
- public function testRenameFolder()
- {
- $newTitle = "FileTest-folder-renamed";
-
- //rename a folder's title
- $folderID = $this->objFromFixture(Folder::class, "folder2")->ID;
- $folder = DataObject::get_by_id(Folder::class, $folderID);
- $folder->Title = $newTitle;
- $folder->write();
-
- //get folder again and see if the filename has changed
- $folder = DataObject::get_by_id(Folder::class, $folderID);
- $this->assertEquals(
- $newTitle . '/',
- $folder->Filename,
- "Folder Filename updated after rename of Title"
- );
-
- //rename a folder's name
- $newTitle2 = "FileTest-folder-renamed2";
- $folder->Name = $newTitle2;
- $folder->write();
-
- //get folder again and see if the Title has changed
- $folder = DataObject::get_by_id(Folder::class, $folderID);
- $this->assertEquals(
- $folder->Title,
- $newTitle2,
- "Folder Title updated after rename of Name"
- );
-
-
- //rename a folder's Filename
- $newTitle3 = "FileTest-folder-renamed3";
- $folder->Filename = $newTitle3;
- $folder->write();
-
- //get folder again and see if the Title has changed
- $folder = DataObject::get_by_id(Folder::class, $folderID);
- $this->assertEquals(
- $folder->Title,
- $newTitle3,
- "Folder Title updated after rename of Filename"
- );
- }
-
- public function testRenamesDuplicateFilesInSameFolder()
- {
- $original = new File();
- $original->update([
- 'Name' => 'file1.txt',
- 'ParentID' => 0
- ]);
- $original->write();
-
- $duplicate = new File();
- $duplicate->update([
- 'Name' => 'file1.txt',
- 'ParentID' => 0
- ]);
- $duplicate->write();
-
- $original = File::get()->byID($original->ID);
-
- $this->assertEquals($original->Name, 'file1.txt');
- $this->assertEquals($original->Title, 'file1');
- $this->assertEquals($duplicate->Name, 'file1-v2.txt');
- $this->assertEquals($duplicate->Title, 'file1 v2');
- }
-
- public function testSetsEmptyTitleToNameWithoutExtensionAndSpecialCharacters()
- {
- $fileWithTitle = new File();
- $fileWithTitle->update([
- 'Name' => 'file1-with-title.txt',
- 'Title' => 'Some Title'
- ]);
- $fileWithTitle->write();
-
- $this->assertEquals($fileWithTitle->Name, 'file1-with-title.txt');
- $this->assertEquals($fileWithTitle->Title, 'Some Title');
-
- $fileWithoutTitle = new File();
- $fileWithoutTitle->update([
- 'Name' => 'file1-without-title.txt',
- ]);
- $fileWithoutTitle->write();
-
- $this->assertEquals($fileWithoutTitle->Name, 'file1-without-title.txt');
- $this->assertEquals($fileWithoutTitle->Title, 'file1 without title');
- }
-
- public function testSetsEmptyNameToSingularNameWithoutTitle()
- {
- $fileWithTitle = new File();
- $fileWithTitle->update([
- 'Name' => '',
- 'Title' => 'Some Title',
- ]);
- $fileWithTitle->write();
-
- $this->assertEquals($fileWithTitle->Name, 'Some-Title');
- $this->assertEquals($fileWithTitle->Title, 'Some Title');
-
- $fileWithoutTitle = new File();
- $fileWithoutTitle->update([
- 'Name' => '',
- 'Title' => '',
- ]);
- $fileWithoutTitle->write();
-
- $this->assertEquals($fileWithoutTitle->Name, $fileWithoutTitle->i18n_singular_name());
- $this->assertEquals($fileWithoutTitle->Title, $fileWithoutTitle->i18n_singular_name());
- }
-
- public function testSetsEmptyNameToTitleIfPresent()
- {
- $file = new File();
- $file->update([
- 'Name' => '',
- 'Title' => 'file1',
- ]);
- $file->write();
-
- $this->assertEquals($file->Name, 'file1');
- $this->assertEquals($file->Title, 'file1');
- }
-
- public function testSetsOwnerOnFirstWrite()
- {
- Session::set('loggedInAs', null);
- $member1 = new Member();
- $member1->write();
- $member2 = new Member();
- $member2->write();
-
- $file1 = new File();
- $file1->write();
- $this->assertEquals(0, $file1->OwnerID, 'Owner not written when no user is logged in');
-
- $member1->logIn();
- $file2 = new File();
- $file2->write();
- $this->assertEquals($member1->ID, $file2->OwnerID, 'Owner written when user is logged in');
-
- $member2->logIn();
- $file2->forceChange();
- $file2->write();
- $this->assertEquals($member1->ID, $file2->OwnerID, 'Owner not overwritten on existing files');
- }
-
- public function testCanEdit()
- {
- $file = $this->objFromFixture(Image::class, 'gif');
-
- // Test anonymous permissions
- Session::set('loggedInAs', null);
- $this->assertFalse($file->canEdit(), "Anonymous users can't edit files");
-
- // Test permissionless user
- $this->objFromFixture(Member::class, 'frontend')->logIn();
- $this->assertFalse($file->canEdit(), "Permissionless users can't edit files");
-
- // Test global CMS section users
- $this->objFromFixture(Member::class, 'cms')->logIn();
- $this->assertTrue($file->canEdit(), "Users with all CMS section access can edit files");
-
- // Test cms access users without file access
- $this->objFromFixture(Member::class, 'security')->logIn();
- $this->assertFalse($file->canEdit(), "Security CMS users can't edit files");
-
- // Test asset-admin user
- $this->objFromFixture(Member::class, 'assetadmin')->logIn();
- $this->assertTrue($file->canEdit(), "Asset admin users can edit files");
-
- // Test admin
- $this->objFromFixture(Member::class, 'admin')->logIn();
- $this->assertTrue($file->canEdit(), "Admins can edit files");
- }
-
- public function testJoinPaths()
- {
- $this->assertEquals('name/file.jpg', File::join_paths('/name', 'file.jpg'));
- $this->assertEquals('name/file.jpg', File::join_paths('name', 'file.jpg'));
- $this->assertEquals('name/file.jpg', File::join_paths('/name', '/file.jpg'));
- $this->assertEquals('name/file.jpg', File::join_paths('name/', '/', 'file.jpg'));
- $this->assertEquals('file.jpg', File::join_paths('/', '/', 'file.jpg'));
- $this->assertEquals('', File::join_paths('/', '/'));
- }
-
- /**
- * Test that ini2bytes returns the number of bytes for a PHP ini style size declaration
- *
- * @param string $iniValue
- * @param int $expected
- * @dataProvider ini2BytesProvider
- */
- public function testIni2Bytes($iniValue, $expected)
- {
- $this->assertSame($expected, File::ini2bytes($iniValue));
- }
-
- /**
- * @return array
- */
- public function ini2BytesProvider()
- {
- return [
- ['2k', 2 * 1024],
- ['512M', 512 * 1024 * 1024],
- ['1024g', 1024 * 1024 * 1024 * 1024],
- ['1024G', 1024 * 1024 * 1024 * 1024]
- ];
- }
-
- /**
- * @return AssetStore
- */
- protected function getAssetStore()
- {
- return Injector::inst()->get('AssetStore');
- }
-}
diff --git a/tests/php/Assets/FileTest.yml b/tests/php/Assets/FileTest.yml
deleted file mode 100644
index c6f762a1c..000000000
--- a/tests/php/Assets/FileTest.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-SilverStripe\Assets\Folder:
- subfolder:
- Name: FileTest-subfolder
- folder1:
- Name: FileTest-folder1
- folder2:
- Name: FileTest-folder2
- folder1-subfolder1:
- Name: FileTest-folder1-subfolder1
- ParentID: =>SilverStripe\Assets\Folder.folder1
-SilverStripe\Assets\File:
- asdf:
- FileFilename: FileTest.txt
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTest.txt
- pdf:
- FileFilename: FileTest.pdf
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTest.pdf
- subfolderfile:
- FileFilename: FileTest-subfolder/FileTestSubfolder.txt
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTestSubfolder.txt
- ParentID: =>SilverStripe\Assets\Folder.subfolder
- subfolderfile-setfromname:
- FileFilename: FileTest-subfolder/FileTestSubfolder2.txt
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTestSubfolder2.txt
- ParentID: =>SilverStripe\Assets\Folder.subfolder
- file1-folder1:
- FileFilename: FileTest-folder1/File1.txt
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: File1.txt
- ParentID: =>SilverStripe\Assets\Folder.folder1
-SilverStripe\Assets\Image:
- gif:
- FileFilename: FileTest.gif
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTest.gif
- gifupper:
- FileFilename: FileTest-gifupper.GIF
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTest-gifupper.GIF
- setfromname:
- FileFilename: FileTest.png
- FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
- Name: FileTest.png
-'SilverStripe\Security\Permission':
- admin:
- Code: ADMIN
- cmsmain:
- Code: CMS_ACCESS_LeftAndMain
- assetadmin:
- Code: CMS_ACCESS_AssetAdmin
- securityadmin:
- Code: CMS_ACCESS_SecurityAdmin
-'SilverStripe\Security\Group':
- admins:
- Title: Administrators
- Permissions: '=>SilverStripe\Security\Permission.admin'
- cmsusers:
- Title: 'CMS Users'
- Permissions: '=>SilverStripe\Security\Permission.cmsmain'
- securityusers:
- Title: 'Security Users'
- Permissions: '=>SilverStripe\Security\Permission.securityadmin'
- assetusers:
- Title: 'Asset Users'
- Permissions: '=>SilverStripe\Security\Permission.assetadmin'
-'SilverStripe\Security\Member':
- frontend:
- Email: frontend@example.com
- cms:
- Email: cms@silverstripe.com
- Groups: '=>SilverStripe\Security\Group.cmsusers'
- admin:
- Email: admin@silverstripe.com
- Groups: '=>SilverStripe\Security\Group.admins'
- assetadmin:
- Email: assetadmin@silverstripe.com
- Groups: '=>SilverStripe\Security\Group.assetusers'
- security:
- Email: security@silverstripe.com
- Groups: '=>SilverStripe\Security\Group.securityusers'
diff --git a/tests/php/Assets/FileTest/MyCustomFile.php b/tests/php/Assets/FileTest/MyCustomFile.php
deleted file mode 100644
index 27d346dd9..000000000
--- a/tests/php/Assets/FileTest/MyCustomFile.php
+++ /dev/null
@@ -1,11 +0,0 @@
-rootDir = ASSETS_PATH . '/AssetAdapterTest';
- Filesystem::makeFolder($this->rootDir);
- Config::inst()->update('SilverStripe\\Control\\Director', 'alternate_base_url', '/');
- $this->originalServer = $_SERVER;
- }
-
- public function tearDown()
- {
- if ($this->rootDir) {
- Filesystem::removeFolder($this->rootDir);
- $this->rootDir = null;
- }
- if ($this->originalServer) {
- $_SERVER = $this->originalServer;
- $this->originalServer = null;
- }
- parent::tearDown();
- }
-
- public function testPublicAdapter()
- {
- $_SERVER['SERVER_SOFTWARE'] = 'Apache/2.2.22 (Win64) PHP/5.3.13';
- $adapter = new PublicAssetAdapter($this->rootDir);
- $this->assertFileExists($this->rootDir . '/.htaccess');
- $this->assertFileNotExists($this->rootDir . '/web.config');
-
- $htaccess = $adapter->read('.htaccess');
- $content = $htaccess['contents'];
- // Allowed extensions set
- $this->assertContains('RewriteCond %{REQUEST_URI} !\\.(?i:', $content);
- foreach (File::config()->allowed_extensions as $extension) {
- $this->assertRegExp('/\b'.preg_quote($extension).'\b/', $content);
- }
-
- // Rewrite rules
- $this->assertContains('RewriteRule .* ../framework/main.php?url=%1 [QSA]', $content);
- $this->assertContains('RewriteRule error[^\\\\/]*\\.html$ - [L]', $content);
-
- // Test flush restores invalid content
- \file_put_contents($this->rootDir . '/.htaccess', '# broken content');
- $adapter->flush();
- $htaccess2 = $adapter->read('.htaccess');
- $this->assertEquals($content, $htaccess2['contents']);
-
- // Test URL
- $this->assertEquals('/assets/AssetAdapterTest/file.jpg', $adapter->getPublicUrl('file.jpg'));
- }
-
- public function testProtectedAdapter()
- {
- $_SERVER['SERVER_SOFTWARE'] = 'Apache/2.2.22 (Win64) PHP/5.3.13';
- $adapter = new ProtectedAssetAdapter($this->rootDir . '/.protected');
- $this->assertFileExists($this->rootDir . '/.protected/.htaccess');
- $this->assertFileNotExists($this->rootDir . '/.protected/web.config');
-
- // Test url
- $this->assertEquals('/assets/file.jpg', $adapter->getProtectedUrl('file.jpg'));
- }
-}
diff --git a/tests/php/Assets/FolderTest.php b/tests/php/Assets/FolderTest.php
deleted file mode 100644
index 493c77984..000000000
--- a/tests/php/Assets/FolderTest.php
+++ /dev/null
@@ -1,302 +0,0 @@
-logInWithPermission('ADMIN');
- Versioned::set_stage(Versioned::DRAFT);
-
- // Set backend root to /FolderTest
- TestAssetStore::activate('FolderTest');
-
- // Set the File Name Filter replacements so files have the expected names
- Config::inst()->update(
- 'SilverStripe\\Assets\\FileNameFilter',
- 'default_replacements',
- array(
- '/\s/' => '-', // remove whitespace
- '/_/' => '-', // underscores to dashes
- '/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
- '/[\-]{2,}/' => '-', // remove duplicate dashes
- '/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
- )
- );
-
- // Create a test folders for each of the fixture references
- foreach (Folder::get() as $folder) {
- $path = TestAssetStore::getLocalPath($folder);
- Filesystem::makeFolder($path);
- }
-
- // Create a test files for each of the fixture references
- $files = File::get()->exclude('ClassName', Folder::class);
- foreach ($files as $file) {
- $path = TestAssetStore::getLocalPath($file);
- Filesystem::makeFolder(dirname($path));
- $fh = fopen($path, "w+");
- fwrite($fh, str_repeat('x', 1000000));
- fclose($fh);
- }
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- parent::tearDown();
- }
-
- public function testCreateFromNameAndParentIDSetsFilename()
- {
- $folder1 = $this->objFromFixture(Folder::class, 'folder1');
- $newFolder = new Folder();
- $newFolder->Name = 'CreateFromNameAndParentID';
- $newFolder->ParentID = $folder1->ID;
- $newFolder->write();
-
- $this->assertEquals($folder1->Filename . 'CreateFromNameAndParentID/', $newFolder->Filename);
- }
-
- public function testRenamesDuplicateFolders()
- {
- $original = new Folder();
- $original->update([
- 'Name' => 'folder1',
- 'ParentID' => 0
- ]);
- $original->write();
-
- $duplicate = new Folder();
- $duplicate->update([
- 'Name' => 'folder1',
- 'ParentID' => 0
- ]);
- $duplicate->write();
-
- $original = Folder::get()->byID($original->ID);
-
- $this->assertEquals($original->Name, 'folder1');
- $this->assertEquals($original->Title, 'folder1');
- $this->assertEquals($duplicate->Name, 'folder1-v2');
- $this->assertEquals($duplicate->Title, 'folder1-v2');
- }
-
- public function testAllChildrenIncludesFolders()
- {
- $folder1 = $this->objFromFixture(Folder::class, 'folder1');
- $subfolder1 = $this->objFromFixture(Folder::class, 'folder1-subfolder1');
- $file1 = $this->objFromFixture(File::class, 'file1-folder1');
-
- $children = $folder1->allChildren();
- $this->assertEquals(2, $children->Count());
- $this->assertContains($subfolder1->ID, $children->column('ID'));
- $this->assertContains($file1->ID, $children->column('ID'));
- }
-
- public function testFindOrMake()
- {
- $path = 'parent/testFindOrMake/';
- $folder = Folder::find_or_make($path);
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/' . $path,
- TestAssetStore::getLocalPath($folder),
- 'Nested path information is correctly saved to database (with trailing slash)'
- );
-
- // Folder does not exist until it contains files
- $this->assertFileNotExists(
- TestAssetStore::getLocalPath($folder),
- 'Empty folder does not have a filesystem record automatically'
- );
-
- $parentFolder = DataObject::get_one(
- Folder::class,
- array(
- '"File"."Name"' => 'parent'
- )
- );
- $this->assertNotNull($parentFolder);
- $this->assertEquals($parentFolder->ID, $folder->ParentID);
-
- $path = 'parent/testFindOrMake'; // no trailing slash
- $folder = Folder::find_or_make($path);
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/' . $path . '/', // Slash is automatically added here
- TestAssetStore::getLocalPath($folder),
- 'Path information is correctly saved to database (without trailing slash)'
- );
-
- $path = 'assets/'; // relative to "assets/" folder, should produce "assets/assets/"
- $folder = Folder::find_or_make($path);
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/' . $path,
- TestAssetStore::getLocalPath($folder),
- 'A folder named "assets/" within "assets/" is allowed'
- );
- }
-
- /**
- * Tests for the bug #5994 - Moving folder after executing Folder::findOrMake will not set the Filenames properly
- */
- public function testFindOrMakeFolderThenMove()
- {
- $folder1 = $this->objFromFixture(Folder::class, 'folder1');
- Folder::find_or_make($folder1->Filename);
- $folder2 = $this->objFromFixture(Folder::class, 'folder2');
-
- // Publish file1
- /**
- * @var File $file1
-*/
- $file1 = DataObject::get_by_id(File::class, $this->idFromFixture(File::class, 'file1-folder1'), false);
- $file1->publishRecursive();
-
- // set ParentID. This should cause updateFilesystem to be called on all children
- $folder1->ParentID = $folder2->ID;
- $folder1->write();
-
- // Check if the file in the folder moved along
- /**
- * @var File $file1Draft
-*/
- $file1Draft = Versioned::get_by_stage(File::class, Versioned::DRAFT)->byID($file1->ID);
- $this->assertFileExists(TestAssetStore::getLocalPath($file1Draft));
-
- $this->assertEquals(
- 'FileTest-folder2/FileTest-folder1/File1.txt',
- $file1Draft->Filename,
- 'The file DataObject has updated path'
- );
-
- // File should be located in new folder
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/.protected/FileTest-folder2/FileTest-folder1/55b443b601/File1.txt',
- TestAssetStore::getLocalPath($file1Draft)
- );
-
- // Published (live) version remains in the old location
- /**
- * @var File $file1Live
-*/
- $file1Live = Versioned::get_by_stage(File::class, Versioned::LIVE)->byID($file1->ID);
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/FileTest-folder1/55b443b601/File1.txt',
- TestAssetStore::getLocalPath($file1Live)
- );
-
- // Publishing the draft to live should move the new file to the public store
- $file1Draft->publishRecursive();
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/FileTest-folder2/FileTest-folder1/55b443b601/File1.txt',
- TestAssetStore::getLocalPath($file1Draft)
- );
- }
-
- /**
- * Tests for the bug #5994 - if you don't execute get_by_id prior to the rename or move, it will fail.
- */
- public function testRenameFolderAndCheckTheFile()
- {
- // ID is prefixed in case Folder is subclassed by project/other module.
- $folder1 = DataObject::get_one(
- Folder::class,
- array(
- '"File"."ID"' => $this->idFromFixture(Folder::class, 'folder1')
- )
- );
-
- $folder1->Name = 'FileTest-folder1-changed';
- $folder1->write();
-
- // Check if the file in the folder moved along
- $file1 = DataObject::get_by_id(File::class, $this->idFromFixture(File::class, 'file1-folder1'), false);
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file1)
- );
- $this->assertEquals(
- $file1->Filename,
- 'FileTest-folder1-changed/File1.txt',
- 'The file DataObject path uses renamed folder'
- );
-
- // File should be located in new folder
- $this->assertEquals(
- ASSETS_PATH . '/FolderTest/.protected/FileTest-folder1-changed/55b443b601/File1.txt',
- TestAssetStore::getLocalPath($file1)
- );
- }
-
- /**
- * URL and Link are undefined for folder dataobjects
- */
- public function testLinkAndRelativeLink()
- {
- $folder = $this->objFromFixture(Folder::class, 'folder1');
- $this->assertEmpty($folder->getURL());
- $this->assertEmpty($folder->Link());
- }
-
- public function testIllegalFilenames()
- {
-
- // Test that generating a filename with invalid characters generates a correctly named folder.
- $folder = Folder::find_or_make('/FolderTest/EN_US Lang');
- $this->assertEquals('FolderTest/EN-US-Lang/', $folder->getFilename());
-
- // Test repeatitions of folder
- $folder2 = Folder::find_or_make('/FolderTest/EN_US Lang');
- $this->assertEquals($folder->ID, $folder2->ID);
-
- $folder3 = Folder::find_or_make('/FolderTest/EN--US_L!ang');
- $this->assertEquals($folder->ID, $folder3->ID);
-
- $folder4 = Folder::find_or_make('/FolderTest/EN-US-Lang');
- $this->assertEquals($folder->ID, $folder4->ID);
- }
-
- public function testTitleTiedToName()
- {
- $newFolder = new Folder();
-
- $newFolder->Name = 'TestNameCopiedToTitle';
- $this->assertEquals($newFolder->Name, $newFolder->Title);
- $this->assertEquals($newFolder->Title, 'TestNameCopiedToTitle');
-
- $newFolder->Title = 'TestTitleCopiedToName';
- $this->assertEquals($newFolder->Name, $newFolder->Title);
- $this->assertEquals($newFolder->Title, 'TestTitleCopiedToName');
-
- $newFolder->Name = 'TestNameWithIllegalCharactersCopiedToTitle ';
- $this->assertEquals($newFolder->Name, $newFolder->Title);
- $this->assertEquals($newFolder->Title, 'TestNameWithIllegalCharactersCopiedToTitle ');
-
- $newFolder->Title = 'TestTitleWithIllegalCharactersCopiedToName ';
- $this->assertEquals($newFolder->Name, $newFolder->Title);
- $this->assertEquals($newFolder->Title, 'TestTitleWithIllegalCharactersCopiedToName ');
- }
-
- public function testRootFolder()
- {
- $root = Folder::singleton();
- $this->assertEquals('/', $root->getFilename());
- }
-}
diff --git a/tests/php/Assets/GDTest.php b/tests/php/Assets/GDTest.php
deleted file mode 100644
index da93fadf5..000000000
--- a/tests/php/Assets/GDTest.php
+++ /dev/null
@@ -1,221 +0,0 @@
- 'test_gif.gif',
- 'jpg' => 'test_jpg.jpg',
- 'png8' => 'test_png8.png',
- 'png32' => 'test_png32.png'
- );
-
- public function setUp()
- {
- parent::setUp();
- GDBackend::flush();
- }
-
- public function tearDown()
- {
- GDBackend::flush();
- parent::tearDown();
- }
-
- /**
- * Loads all images into an associative array of GD objects.
- * Optionally applies an operation to each GD
- *
- * @param callable $callback Action to perform on each GD
- * @return array List of GD
- */
- protected function applyToEachImage($callback = null)
- {
- $gds = array();
- foreach (self::$filenames as $type => $file) {
- $fullPath = realpath(__DIR__ . '/GDTest/images/' . $file);
- $gd = new GDBackend();
- $gd->loadFrom($fullPath);
- if ($callback) {
- $gd = $callback($gd);
- }
- $gds[$type] = $gd;
- }
- return $gds;
- }
-
- /**
- * Takes samples from the given GD at 5 pixel increments
- *
- * @param GDBackend $gd The source image
- * @param integer $horizontal Number of samples to take horizontally
- * @param integer $vertical Number of samples to take vertically
- * @return array List of colours for each sample, each given as an associative
- * array with red, blue, green, and alpha components
- */
- protected function sampleAreas(GDBackend $gd, $horizontal = 4, $vertical = 4)
- {
- $samples = array();
- for ($y = 0; $y < $vertical; $y++) {
- for ($x = 0; $x < $horizontal; $x++) {
- $colour = imagecolorat($gd->getImageResource(), $x * 5, $y * 5);
- $samples[] = imagecolorsforindex($gd->getImageResource(), $colour);
- }
- }
- return $samples;
- }
-
- /**
- * Asserts that two colour channels are equivalent within a given tolerance range
- *
- * @param integer $expected
- * @param integer $actual
- * @param integer $tolerance
- */
- protected function assertColourEquals($expected, $actual, $tolerance = 0)
- {
- $match =
- ($expected + $tolerance >= $actual) &&
- ($expected - $tolerance <= $actual);
- $this->assertTrue($match);
- }
-
- /**
- * Asserts that all samples given correctly correspond to a greyscale version
- * of the test image pattern
- *
- * @param array $samples List of 16 colour samples representing each of the 8 x 8 squares on the image pattern
- * 8 x 8 squares on the image pattern
- * @param int $alphaBits Depth of alpha channel in bits
- * @param int $tolerance Reasonable tolerance level for colour comparison
- */
- protected function assertGreyscale($samples, $alphaBits = 0, $tolerance = 0)
- {
-
- // Check that all colour samples match
- foreach ($samples as $sample) {
- $matches =
- ($sample['red'] === $sample['green']) &&
- ($sample['blue'] === $sample['green']);
- $this->assertTrue($matches, 'Assert colour is greyscale');
- if (!$matches) {
- return;
- }
- }
-
- // check various sample points
- $this->assertColourEquals(76, $samples[0]['red'], $tolerance);
- $this->assertColourEquals(149, $samples[2]['red'], $tolerance);
- $this->assertColourEquals(0, $samples[8]['red'], $tolerance);
- $this->assertColourEquals(127, $samples[9]['red'], $tolerance);
-
- // check alpha of various points
- switch ($alphaBits) {
- case 0:
- $this->assertColourEquals(0, $samples[2]['alpha'], $tolerance);
- $this->assertColourEquals(0, $samples[12]['alpha'], $tolerance);
- break;
- case 1:
- $this->assertColourEquals(0, $samples[2]['alpha'], $tolerance);
- $this->assertColourEquals(127, $samples[12]['alpha'], $tolerance);
- break;
- default:
- $this->assertColourEquals(63, $samples[2]['alpha'], $tolerance);
- $this->assertColourEquals(127, $samples[12]['alpha'], $tolerance);
- break;
- }
- }
-
- /**
- * Tests that images are correctly transformed to greyscale
- */
- function testGreyscale()
- {
-
- // Apply greyscaling to each image
- $images = $this->applyToEachImage(
- function (GDBackend $gd) {
- return $gd->greyscale();
- }
- );
-
- // Test GIF (256 colour, transparency)
- $samplesGIF = $this->sampleAreas($images['gif']);
- $this->assertGreyscale($samplesGIF, 1);
-
- // Test JPG
- $samplesJPG = $this->sampleAreas($images['jpg']);
- $this->assertGreyscale($samplesJPG, 0, 4);
-
- // Test PNG 8 (indexed with alpha transparency)
- $samplesPNG8 = $this->sampleAreas($images['png8']);
- $this->assertGreyscale($samplesPNG8, 8, 4);
-
- // Test PNG 32 (full alpha transparency)
- $samplesPNG32 = $this->sampleAreas($images['png32']);
- $this->assertGreyscale($samplesPNG32, 8);
- }
-
- /**
- * Tests that GD doesn't attempt to load images when they're deemed unavailable
- *
- * @return void
- */
- public function testImageSkippedWhenUnavailable()
- {
- $fullPath = realpath(__DIR__ . '/GDTest/images/test_jpg.jpg');
- $gd = new GDTest\ImageUnavailable();
- $gd->loadFrom($fullPath);
-
- /* Ensure no image resource is created if the image is unavailable */
- $this->assertNull($gd->getImageResource());
- }
-
- /**
- * Tests the integrity of the manipulation cache when an error occurs
- */
- public function testCacheIntegrity()
- {
- $fullPath = realpath(__DIR__ . '/GDTest/images/nonimagedata.jpg');
-
- // Load invalid file
- $gd = new GDBackend();
- $gd->loadFrom($fullPath);
-
- // Cache should refer to this file
- $cache = Injector::inst()->get(CacheInterface::class . '.GDBackend_Manipulations');
- $key = sha1(implode('|', array($fullPath, filemtime($fullPath))));
- $data = $cache->get($key);
- $this->assertEquals('1', $data);
- }
-
- /**
- * Test that GD::failedResample() returns true for the current image
- * manipulation only if it previously failed
- *
- * @return void
- */
- public function testFailedResample()
- {
- $fullPath = realpath(__DIR__ . '/GDTest/images/nonimagedata.jpg');
- $fullPath2 = realpath(__DIR__ . '/GDTest/images/test_gif.gif');
-
- // Load invalid file
- $gd = new GDBackend();
- $gd->loadFrom($fullPath);
-
- // Cache should refre to this file
- $this->assertTrue($gd->failedResample($fullPath, filemtime($fullPath)));
- $this->assertFalse($gd->failedResample($fullPath2, filemtime($fullPath2)));
- }
-}
diff --git a/tests/php/Assets/GDTest/ImageUnavailable.php b/tests/php/Assets/GDTest/ImageUnavailable.php
deleted file mode 100644
index 87fe2980b..000000000
--- a/tests/php/Assets/GDTest/ImageUnavailable.php
+++ /dev/null
@@ -1,15 +0,0 @@
-exclude('ClassName', Folder::class) as $file) {
- /**
- * @var File $file
-*/
- $path = TestAssetStore::getLocalPath($file);
- Filesystem::makeFolder(dirname($path));
- $fh = fopen($path, "w+");
- fwrite($fh, str_repeat('x', 1000000));
- fclose($fh);
-
- // Create variant for each file
- $this->getAssetStore()->setFromString(
- str_repeat('y', 100),
- $file->Filename,
- $file->Hash,
- 'variant'
- );
- }
- }
-
- /**
- * @dataProvider getFilenames
- */
- public function testIsValidFilename($name, $isValid)
- {
- $controller = new ProtectedFileController();
- $this->assertEquals(
- $isValid,
- $controller->isValidFilename($name),
- "Assert filename \"$name\" is " . $isValid ? "valid" : "invalid"
- );
- }
-
- public function getFilenames()
- {
- return array(
- // Valid names
- array('name.jpg', true),
- array('parent/name.jpg', true),
- array('parent/name', true),
- array('parent\name.jpg', true),
- array('parent\name', true),
- array('name', true),
-
- // Invalid names
- array('.invalid/name.jpg', false),
- array('.invalid\name.jpg', false),
- array('.htaccess', false),
- array('test/.htaccess.jpg', false),
- array('name/.jpg', false),
- array('test\.htaccess.jpg', false),
- array('name\.jpg', false)
- );
- }
-
- /**
- * Test that certain requests are denied
- */
- public function testInvalidRequest()
- {
- $result = $this->get('assets/.protected/file.jpg');
- $this->assertResponseEquals(400, null, $result);
- }
-
- /**
- * Test that invalid files generate 404 response
- */
- public function testFileNotFound()
- {
- $result = $this->get('assets/missing.jpg');
- $this->assertResponseEquals(404, null, $result);
- }
-
- /**
- * Check public access to assets is available at the appropriate time
- */
- public function testAccessControl()
- {
- $expectedContent = str_repeat('x', 1000000);
- $variantContent = str_repeat('y', 100);
-
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(200, $expectedContent, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(200, $variantContent, $result);
-
- // Make this file protected
- $this->getAssetStore()->protect(
- 'FileTest.txt',
- '55b443b60176235ef09801153cca4e6da7494a0c'
- );
-
- // Should now return explicitly denied errors
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(403, null, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(403, null, $result);
-
- // Other assets remain available
- $result = $this->get('assets/55b443b601/FileTest.pdf');
- $this->assertResponseEquals(200, $expectedContent, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.pdf');
- $this->assertResponseEquals(200, $variantContent, $result);
-
- // granting access will allow access
- $this->getAssetStore()->grant(
- 'FileTest.txt',
- '55b443b60176235ef09801153cca4e6da7494a0c'
- );
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(200, $expectedContent, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(200, $variantContent, $result);
-
- // Revoking access will remove access again
- $this->getAssetStore()->revoke(
- 'FileTest.txt',
- '55b443b60176235ef09801153cca4e6da7494a0c'
- );
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(403, null, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(403, null, $result);
-
- // Moving file back to public store restores access
- $this->getAssetStore()->publish(
- 'FileTest.txt',
- '55b443b60176235ef09801153cca4e6da7494a0c'
- );
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(200, $expectedContent, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(200, $variantContent, $result);
-
- // Deleting the file will make the response 404
- $this->getAssetStore()->delete(
- 'FileTest.txt',
- '55b443b60176235ef09801153cca4e6da7494a0c'
- );
- $result = $this->get('assets/55b443b601/FileTest.txt');
- $this->assertResponseEquals(404, null, $result);
- $result = $this->get('assets/55b443b601/FileTest__variant.txt');
- $this->assertResponseEquals(404, null, $result);
- }
-
- /**
- * Test that access to folders is not permitted
- */
- public function testFolders()
- {
- $result = $this->get('assets/55b443b601');
- $this->assertResponseEquals(403, null, $result);
-
- $result = $this->get('assets/FileTest-subfolder');
- $this->assertResponseEquals(403, null, $result);
-
- // Flysystem reports root folder as not present
- $result = $this->get('assets');
- $this->assertResponseEquals(404, null, $result);
- }
-
- /**
- * @return AssetStore
- */
- protected function getAssetStore()
- {
- return Injector::inst()->get('AssetStore');
- }
-
- /**
- * Assert that a response matches the given parameters
- *
- * @param int $code HTTP code
- * @param string $body Body expected for 200 responses
- * @param HTTPResponse $response
- */
- protected function assertResponseEquals($code, $body, HTTPResponse $response)
- {
- $this->assertEquals($code, $response->getStatusCode());
- if ($code === 200) {
- $this->assertFalse($response->isError());
- $this->assertEquals($body, $response->getBody());
- $this->assertEquals('text/plain', $response->getHeader('Content-Type'));
- } else {
- $this->assertTrue($response->isError());
- }
- }
-}
diff --git a/tests/php/Assets/Storage/AssetStoreTest.php b/tests/php/Assets/Storage/AssetStoreTest.php
deleted file mode 100644
index f67e63d89..000000000
--- a/tests/php/Assets/Storage/AssetStoreTest.php
+++ /dev/null
@@ -1,523 +0,0 @@
-get('AssetStore');
- }
-
- /**
- * Test different storage methods
- */
- public function testStorageMethods()
- {
- $backend = $this->getBackend();
-
- // Test setFromContent
- $puppies1 = 'puppies';
- $puppies1Tuple = $backend->setFromString($puppies1, 'pets/my-puppy.txt');
- $this->assertEquals(
- array(
- 'Hash' => '2a17a9cb4be918774e73ba83bd1c1e7d000fdd53',
- 'Filename' => 'pets/my-puppy.txt',
- 'Variant' => '',
- ),
- $puppies1Tuple
- );
-
- // Test setFromStream (seekable)
- $fish1 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-high-quality.jpg');
- $fish1Stream = fopen($fish1, 'r');
- $fish1Tuple = $backend->setFromStream($fish1Stream, 'parent/awesome-fish.jpg');
- fclose($fish1Stream);
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'parent/awesome-fish.jpg',
- 'Variant' => '',
- ),
- $fish1Tuple
- );
-
- // Test with non-seekable streams
- TestAssetStore::$seekable_override = false;
- $fish2 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-low-quality.jpg');
- $fish2Stream = fopen($fish2, 'r');
- $fish2Tuple = $backend->setFromStream($fish2Stream, 'parent/mediocre-fish.jpg');
- fclose($fish2Stream);
-
- $this->assertEquals(
- array(
- 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c',
- 'Filename' => 'parent/mediocre-fish.jpg',
- 'Variant' => '',
- ),
- $fish2Tuple
- );
- TestAssetStore::$seekable_override = null;
- }
-
- /**
- * Test that the backend correctly resolves conflicts
- */
- public function testConflictResolution()
- {
- $backend = $this->getBackend();
-
- // Put a file in
- $fish1 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-high-quality.jpg');
- $this->assertFileExists($fish1);
- $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg');
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish.jpg',
- 'Variant' => '',
- ),
- $fish1Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/a870de278b/lovely-fish.jpg',
- $backend->getAsURL($fish1Tuple['Filename'], $fish1Tuple['Hash'])
- );
-
- // Write a different file with same name. Should not detect duplicates since sha are different
- $fish2 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-low-quality.jpg');
- try {
- $fish2Tuple = $backend->setFromLocalFile(
- $fish2,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_EXCEPTION)
- );
- } catch (Exception $ex) {
- $this->fail('Writing file with different sha to same location failed with exception');
- return;
- }
- $this->assertEquals(
- array(
- 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c',
- 'Filename' => 'directory/lovely-fish.jpg',
- 'Variant' => '',
- ),
- $fish2Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/33be1b95cb/lovely-fish.jpg',
- $backend->getAsURL($fish2Tuple['Filename'], $fish2Tuple['Hash'])
- );
-
- // Write original file back with rename
- $this->assertFileExists($fish1);
- $fish3Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_RENAME)
- );
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish-v2.jpg',
- 'Variant' => '',
- ),
- $fish3Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/a870de278b/lovely-fish-v2.jpg',
- $backend->getAsURL($fish3Tuple['Filename'], $fish3Tuple['Hash'])
- );
-
- // Write another file should increment to -v3
- $fish4Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish-v2.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_RENAME)
- );
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish-v3.jpg',
- 'Variant' => '',
- ),
- $fish4Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/a870de278b/lovely-fish-v3.jpg',
- $backend->getAsURL($fish4Tuple['Filename'], $fish4Tuple['Hash'])
- );
-
- // Test conflict use existing file
- $fish5Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_USE_EXISTING)
- );
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish.jpg',
- 'Variant' => '',
- ),
- $fish5Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/a870de278b/lovely-fish.jpg',
- $backend->getAsURL($fish5Tuple['Filename'], $fish5Tuple['Hash'])
- );
-
- // Test conflict use existing file
- $fish6Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_OVERWRITE)
- );
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish.jpg',
- 'Variant' => '',
- ),
- $fish6Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/a870de278b/lovely-fish.jpg',
- $backend->getAsURL($fish6Tuple['Filename'], $fish6Tuple['Hash'])
- );
- }
-
- /**
- * Test that flysystem can regenerate the original filename from fileID
- */
- public function testGetOriginalFilename()
- {
- $store = new TestAssetStore();
- $this->assertEquals(
- 'directory/lovely-fish.jpg',
- $store->getOriginalFilename('directory/a870de278b/lovely-fish.jpg')
- );
- $this->assertEquals(
- 'directory/lovely-fish.jpg',
- $store->getOriginalFilename('directory/a870de278b/lovely-fish__variant.jpg')
- );
- $this->assertEquals(
- 'directory/lovely_fish.jpg',
- $store->getOriginalFilename('directory/a870de278b/lovely_fish__vari_ant.jpg')
- );
- $this->assertEquals(
- 'directory/lovely_fish.jpg',
- $store->getOriginalFilename('directory/a870de278b/lovely_fish.jpg')
- );
- $this->assertEquals(
- 'lovely-fish.jpg',
- $store->getOriginalFilename('a870de278b/lovely-fish.jpg')
- );
- $this->assertEquals(
- 'lovely-fish.jpg',
- $store->getOriginalFilename('a870de278b/lovely-fish__variant.jpg')
- );
- $this->assertEquals(
- 'lovely_fish.jpg',
- $store->getOriginalFilename('a870de278b/lovely_fish__vari__ant.jpg')
- );
- $this->assertEquals(
- 'lovely_fish.jpg',
- $store->getOriginalFilename('a870de278b/lovely_fish.jpg')
- );
- }
-
- /**
- * Test internal file Id generation
- */
- public function testGetFileID()
- {
- $store = new TestAssetStore();
- $this->assertEquals(
- 'directory/2a17a9cb4b/file.jpg',
- $store->getFileID('directory/file.jpg', sha1('puppies'))
- );
- $this->assertEquals(
- '2a17a9cb4b/file.jpg',
- $store->getFileID('file.jpg', sha1('puppies'))
- );
- $this->assertEquals(
- 'dir_ectory/2a17a9cb4b/fil_e.jpg',
- $store->getFileID('dir__ectory/fil__e.jpg', sha1('puppies'))
- );
- $this->assertEquals(
- 'directory/2a17a9cb4b/file_variant.jpg',
- $store->getFileID('directory/file__variant.jpg', sha1('puppies'), null)
- );
- $this->assertEquals(
- 'directory/2a17a9cb4b/file__variant.jpg',
- $store->getFileID('directory/file.jpg', sha1('puppies'), 'variant')
- );
- $this->assertEquals(
- '2a17a9cb4b/file__var__iant.jpg',
- $store->getFileID('file.jpg', sha1('puppies'), 'var__iant')
- );
- }
-
- public function testGetMetadata()
- {
- $backend = $this->getBackend();
-
- // jpg
- $fish = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-high-quality.jpg');
- $fishTuple = $backend->setFromLocalFile($fish, 'parent/awesome-fish.jpg');
- $this->assertEquals(
- 'image/jpeg',
- $backend->getMimeType($fishTuple['Filename'], $fishTuple['Hash'])
- );
- $fishMeta = $backend->getMetadata($fishTuple['Filename'], $fishTuple['Hash']);
- $this->assertEquals(151889, $fishMeta['size']);
- $this->assertEquals('file', $fishMeta['type']);
- $this->assertNotEmpty($fishMeta['timestamp']);
-
- // text
- $puppies = 'puppies';
- $puppiesTuple = $backend->setFromString($puppies, 'pets/my-puppy.txt');
- $this->assertEquals(
- 'text/plain',
- $backend->getMimeType($puppiesTuple['Filename'], $puppiesTuple['Hash'])
- );
- $puppiesMeta = $backend->getMetadata($puppiesTuple['Filename'], $puppiesTuple['Hash']);
- $this->assertEquals(7, $puppiesMeta['size']);
- $this->assertEquals('file', $puppiesMeta['type']);
- $this->assertNotEmpty($puppiesMeta['timestamp']);
- }
-
- /**
- * Test that legacy filenames work as expected
- */
- public function testLegacyFilenames()
- {
- Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', true);
-
- $backend = $this->getBackend();
-
- // Put a file in
- $fish1 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-high-quality.jpg');
- $this->assertFileExists($fish1);
- $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg');
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish.jpg',
- 'Variant' => '',
- ),
- $fish1Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/lovely-fish.jpg',
- $backend->getAsURL($fish1Tuple['Filename'], $fish1Tuple['Hash'])
- );
-
- // Write a different file with same name.
- // Since we are using legacy filenames, this should generate a new filename
- $fish2 = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-low-quality.jpg');
- try {
- $backend->setFromLocalFile(
- $fish2,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_EXCEPTION)
- );
- $this->fail('Writing file with different sha to same location should throw exception');
- return;
- } catch (Exception $ex) {
- // Success
- }
-
- // Re-attempt this file write with conflict_rename
- $fish3Tuple = $backend->setFromLocalFile(
- $fish2,
- 'directory/lovely-fish.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_RENAME)
- );
- $this->assertEquals(
- array(
- 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c',
- 'Filename' => 'directory/lovely-fish-v2.jpg',
- 'Variant' => '',
- ),
- $fish3Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/lovely-fish-v2.jpg',
- $backend->getAsURL($fish3Tuple['Filename'], $fish3Tuple['Hash'])
- );
-
- // Write back original file, but with CONFLICT_EXISTING. The file should not change
- $fish4Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish-v2.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_USE_EXISTING)
- );
- $this->assertEquals(
- array(
- 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c',
- 'Filename' => 'directory/lovely-fish-v2.jpg',
- 'Variant' => '',
- ),
- $fish4Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/lovely-fish-v2.jpg',
- $backend->getAsURL($fish4Tuple['Filename'], $fish4Tuple['Hash'])
- );
-
- // Write back original file with CONFLICT_OVERWRITE. The file sha should now be updated
- $fish5Tuple = $backend->setFromLocalFile(
- $fish1,
- 'directory/lovely-fish-v2.jpg',
- null,
- null,
- array('conflict' => AssetStore::CONFLICT_OVERWRITE)
- );
- $this->assertEquals(
- array(
- 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1',
- 'Filename' => 'directory/lovely-fish-v2.jpg',
- 'Variant' => '',
- ),
- $fish5Tuple
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/directory/lovely-fish-v2.jpg',
- $backend->getAsURL($fish5Tuple['Filename'], $fish5Tuple['Hash'])
- );
- }
-
- /**
- * Test default conflict resolution
- */
- public function testDefaultConflictResolution()
- {
- $store = $this->getBackend();
-
- // Disable legacy filenames
- Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', false);
- $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution(null));
- $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution('somevariant'));
-
- // Enable legacy filenames
- Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', true);
- $this->assertEquals(AssetStore::CONFLICT_RENAME, $store->getDefaultConflictResolution(null));
- $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution('somevariant'));
- }
-
- /**
- * Test protect / publish mechanisms
- */
- public function testProtect()
- {
- $backend = $this->getBackend();
- $fish = realpath(__DIR__ . '/../../ORM/ImageTest/test-image-high-quality.jpg');
- $fishTuple = $backend->setFromLocalFile($fish, 'parent/lovely-fish.jpg');
- $fishVariantTuple = $backend->setFromLocalFile($fish, $fishTuple['Filename'], $fishTuple['Hash'], 'copy');
-
- // Test public file storage
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish.jpg');
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish__copy.jpg');
- $this->assertEquals(
- AssetStore::VISIBILITY_PUBLIC,
- $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash'])
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/parent/a870de278b/lovely-fish.jpg',
- $backend->getAsURL($fishTuple['Filename'], $fishTuple['Hash'])
- );
- $this->assertEquals(
- '/assets/AssetStoreTest/parent/a870de278b/lovely-fish__copy.jpg',
- $backend->getAsURL($fishVariantTuple['Filename'], $fishVariantTuple['Hash'], $fishVariantTuple['Variant'])
- );
-
- // Test access rights to public files cannot be revoked
- $backend->revoke($fishTuple['Filename'], $fishTuple['Hash']); // can't revoke public assets
- $this->assertTrue($backend->canView($fishTuple['Filename'], $fishTuple['Hash']));
-
- // Test protected file storage
- $backend->protect($fishTuple['Filename'], $fishTuple['Hash']);
- $this->assertFileNotExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish.jpg');
- $this->assertFileNotExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish__copy.jpg');
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish.jpg');
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish__copy.jpg');
- $this->assertEquals(
- AssetStore::VISIBILITY_PROTECTED,
- $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash'])
- );
-
- // Test access rights
- $backend->revoke($fishTuple['Filename'], $fishTuple['Hash']);
- $this->assertFalse($backend->canView($fishTuple['Filename'], $fishTuple['Hash']));
- $backend->grant($fishTuple['Filename'], $fishTuple['Hash']);
- $this->assertTrue($backend->canView($fishTuple['Filename'], $fishTuple['Hash']));
-
- // Protected urls should go through asset routing mechanism
- $this->assertEquals(
- '/assets/parent/a870de278b/lovely-fish.jpg',
- $backend->getAsURL($fishTuple['Filename'], $fishTuple['Hash'])
- );
- $this->assertEquals(
- '/assets/parent/a870de278b/lovely-fish__copy.jpg',
- $backend->getAsURL($fishVariantTuple['Filename'], $fishVariantTuple['Hash'], $fishVariantTuple['Variant'])
- );
-
- // Publish reverts visibility
- $backend->publish($fishTuple['Filename'], $fishTuple['Hash']);
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish.jpg');
- $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/a870de278b/lovely-fish__copy.jpg');
- $this->assertFileNotExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish.jpg');
- $this->assertFileNotExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish__copy.jpg');
- $this->assertEquals(
- AssetStore::VISIBILITY_PUBLIC,
- $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash'])
- );
- }
-}
diff --git a/tests/php/Assets/Storage/AssetStoreTest/TestAssetStore.php b/tests/php/Assets/Storage/AssetStoreTest/TestAssetStore.php
deleted file mode 100644
index 6f90343d9..000000000
--- a/tests/php/Assets/Storage/AssetStoreTest/TestAssetStore.php
+++ /dev/null
@@ -1,183 +0,0 @@
- AdapterInterface::VISIBILITY_PUBLIC
- ]
- );
- $protectedAdapter = new ProtectedAssetAdapter(ASSETS_PATH . '/' . $basedir . '/.protected');
- $protectedFilesystem = new Filesystem(
- $protectedAdapter,
- [
- 'visibility' => AdapterInterface::VISIBILITY_PRIVATE
- ]
- );
-
- $backend = new TestAssetStore();
- $backend->setPublicFilesystem($publicFilesystem);
- $backend->setProtectedFilesystem($protectedFilesystem);
- Injector::inst()->registerService($backend, 'AssetStore');
-
- // Assign flysystem backend to generated asset handler at the same time
- $generated = new GeneratedAssetHandler();
- $generated->setFilesystem($publicFilesystem);
- Injector::inst()->registerService($generated, 'GeneratedAssetHandler');
- Requirements::backend()->setAssetHandler($generated);
-
- // Disable legacy and set defaults
- FlysystemAssetStore::config()->set('legacy_filenames', false);
- Director::config()->set('alternate_base_url', '/');
- DBFile::config()->set('force_resample', false);
- File::config()->set('force_resample', false);
- self::reset();
- self::$basedir = $basedir;
-
- // Ensure basedir exists
- SSFilesystem::makeFolder(self::base_path());
- }
-
- /**
- * Get absolute path to basedir
- *
- * @return string
- */
- public static function base_path()
- {
- if (!self::$basedir) {
- return null;
- }
- return ASSETS_PATH . '/' . self::$basedir;
- }
-
- /**
- * Reset defaults for this store
- */
- public static function reset()
- {
- // Remove all files in this store
- if (self::$basedir) {
- $path = self::base_path();
- if (file_exists($path)) {
- SSFilesystem::removeFolder($path);
- }
- }
- self::$seekable_override = null;
- self::$basedir = null;
- }
-
- /**
- * Helper method to get local filesystem path for this file
- *
- * @param AssetContainer $asset
- * @return string
- */
- public static function getLocalPath(AssetContainer $asset)
- {
- if ($asset instanceof Folder) {
- return self::base_path() . '/' . $asset->getFilename();
- }
- if ($asset instanceof File) {
- $asset = $asset->File;
- }
- // Extract filesystem used to store this object
- /** @var TestAssetStore $assetStore */
- $assetStore = Injector::inst()->get('AssetStore');
- $fileID = $assetStore->getFileID($asset->Filename, $asset->Hash, $asset->Variant);
- $filesystem = $assetStore->getProtectedFilesystem();
- if (!$filesystem->has($fileID)) {
- $filesystem = $assetStore->getPublicFilesystem();
- }
- /** @var Local $adapter */
- $adapter = $filesystem->getAdapter();
- return $adapter->applyPathPrefix($fileID);
- }
-
- public function cleanFilename($filename)
- {
- return parent::cleanFilename($filename);
- }
-
- public function getFileID($filename, $hash, $variant = null)
- {
- return parent::getFileID($filename, $hash, $variant);
- }
-
-
- public function getOriginalFilename($fileID)
- {
- return parent::getOriginalFilename($fileID);
- }
-
- public function removeVariant($fileID)
- {
- return parent::removeVariant($fileID);
- }
-
- public function getDefaultConflictResolution($variant)
- {
- return parent::getDefaultConflictResolution($variant);
- }
-
- protected function isSeekableStream($stream)
- {
- if (isset(self::$seekable_override)) {
- return self::$seekable_override;
- }
- return parent::isSeekableStream($stream);
- }
-}
diff --git a/tests/php/Assets/Storage/DefaultAssetNameGeneratorTest.php b/tests/php/Assets/Storage/DefaultAssetNameGeneratorTest.php
deleted file mode 100644
index 2427064ed..000000000
--- a/tests/php/Assets/Storage/DefaultAssetNameGeneratorTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-update(DefaultAssetNameGenerator::class, 'version_prefix', '');
- $generator = new DefaultAssetNameGenerator('folder/MyFile-001.jpg');
- $suggestions = iterator_to_array($generator);
-
- // Expect 100 suggestions
- $this->assertEquals(100, count($suggestions));
-
- // First item is always the same as input
- $this->assertEquals('folder/MyFile-001.jpg', $suggestions[0]);
-
- // Check that padding is respected
- $this->assertEquals('folder/MyFile-002.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile-003.jpg', $suggestions[2]);
- $this->assertEquals('folder/MyFile-004.jpg', $suggestions[3]);
- $this->assertEquals('folder/MyFile-021.jpg', $suggestions[20]);
- $this->assertEquals('folder/MyFile-099.jpg', $suggestions[98]);
-
- // Last item should be some semi-random string, not in the same numeric sequence
- $this->assertNotEquals('folder/MyFile-0100.jpg', $suggestions[99]);
- $this->assertNotEquals('folder/MyFile-100.jpg', $suggestions[99]);
-
- // Test with a value starting above 1
- $generator = new DefaultAssetNameGenerator('folder/MyFile-024.jpg');
- $suggestions = iterator_to_array($generator);
- $this->assertEquals(100, count($suggestions));
- $this->assertEquals('folder/MyFile-024.jpg', $suggestions[0]);
- $this->assertEquals('folder/MyFile-025.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile-026.jpg', $suggestions[2]);
- $this->assertEquals('folder/MyFile-048.jpg', $suggestions[24]);
- $this->assertEquals('folder/MyFile-122.jpg', $suggestions[98]);
- $this->assertNotEquals('folder/MyFile-0123.jpg', $suggestions[99]);
- $this->assertNotEquals('folder/MyFile-123.jpg', $suggestions[99]); // Last suggestion is semi-random
-
- // Test without numeric value
- $generator = new DefaultAssetNameGenerator('folder/MyFile.jpg');
- $suggestions = iterator_to_array($generator);
- $this->assertEquals(100, count($suggestions));
- $this->assertEquals('folder/MyFile.jpg', $suggestions[0]);
- $this->assertEquals('folder/MyFile2.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile3.jpg', $suggestions[2]);
- $this->assertEquals('folder/MyFile25.jpg', $suggestions[24]);
- $this->assertEquals('folder/MyFile99.jpg', $suggestions[98]);
- $this->assertNotEquals('folder/MyFile100.jpg', $suggestions[99]); // Last suggestion is semi-random
- }
-
- /**
- * Test with default -v prefix
- */
- public function testWithDefaultPrefix()
- {
- Config::inst()->update(DefaultAssetNameGenerator::class, 'version_prefix', '-v');
-
- // Test with item that doesn't contain the prefix
- $generator = new DefaultAssetNameGenerator('folder/MyFile-001.jpg');
- $suggestions = iterator_to_array($generator);
- $this->assertEquals(100, count($suggestions));
- $this->assertEquals('folder/MyFile-001.jpg', $suggestions[0]);
- $this->assertEquals('folder/MyFile-001-v2.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile-001-v4.jpg', $suggestions[3]);
- $this->assertEquals('folder/MyFile-001-v21.jpg', $suggestions[20]);
- $this->assertEquals('folder/MyFile-001-v99.jpg', $suggestions[98]);
- $this->assertNotEquals('folder/MyFile-001-v100.jpg', $suggestions[99]); // Last suggestion is semi-random
-
-
- // Test with item that contains prefix
- $generator = new DefaultAssetNameGenerator('folder/MyFile-v24.jpg');
- $suggestions = iterator_to_array($generator);
- $this->assertEquals(100, count($suggestions));
- $this->assertEquals('folder/MyFile-v24.jpg', $suggestions[0]);
- $this->assertEquals('folder/MyFile-v25.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile-v26.jpg', $suggestions[2]);
- $this->assertEquals('folder/MyFile-v48.jpg', $suggestions[24]);
- $this->assertEquals('folder/MyFile-v122.jpg', $suggestions[98]);
- $this->assertNotEquals('folder/MyFile-v123.jpg', $suggestions[99]);
- $this->assertNotEquals('folder/MyFile-123.jpg', $suggestions[99]);
-
- // Test without numeric value
- $generator = new DefaultAssetNameGenerator('folder/MyFile.jpg');
- $suggestions = iterator_to_array($generator);
- $this->assertEquals(100, count($suggestions));
- $this->assertEquals('folder/MyFile.jpg', $suggestions[0]);
- $this->assertEquals('folder/MyFile-v2.jpg', $suggestions[1]);
- $this->assertEquals('folder/MyFile-v3.jpg', $suggestions[2]);
- $this->assertEquals('folder/MyFile-v25.jpg', $suggestions[24]);
- $this->assertEquals('folder/MyFile-v99.jpg', $suggestions[98]);
- $this->assertNotEquals('folder/MyFile-v100.jpg', $suggestions[99]);
- }
-
- public function testFolderWithoutDefaultPrefix()
- {
- Config::inst()->update(DefaultAssetNameGenerator::class, 'version_prefix', '');
- $generator = new DefaultAssetNameGenerator('folder/subfolder');
- $suggestions = iterator_to_array($generator);
-
- // Expect 100 suggestions
- $this->assertEquals(100, count($suggestions));
-
- // First item is always the same as input
- $this->assertEquals('folder/subfolder', $suggestions[0]);
- $this->assertEquals('folder/subfolder2', $suggestions[1]);
- }
-
- public function testFolderWithDefaultPrefix()
- {
- Config::inst()->update(DefaultAssetNameGenerator::class, 'version_prefix', '-v');
- $generator = new DefaultAssetNameGenerator('folder/subfolder');
- $suggestions = iterator_to_array($generator);
-
- // Expect 100 suggestions
- $this->assertEquals(100, count($suggestions));
-
- // First item is always the same as input
- $this->assertEquals('folder/subfolder', $suggestions[0]);
- $this->assertEquals('folder/subfolder-v2', $suggestions[1]);
- }
-}
diff --git a/tests/php/Assets/UploadTest.php b/tests/php/Assets/UploadTest.php
deleted file mode 100644
index bdad0e8dd..000000000
--- a/tests/php/Assets/UploadTest.php
+++ /dev/null
@@ -1,850 +0,0 @@
-tmpFilePath)) {
- unlink($this->tmpFilePath);
- }
- }
-
- public function testUpload()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.txt';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
-
- // test upload into default folder
- $u1 = new Upload();
- $u1->setValidator($v);
- $u1->loadIntoFile($tmpFile);
- $file1 = $u1->getFile();
- $this->assertEquals(
- 'Uploads/UploadTest-testUpload.txt',
- $file1->getFilename()
- );
- $this->assertEquals(
- BASE_PATH . '/assets/UploadTest/.protected/Uploads/315ae4c3d4/UploadTest-testUpload.txt',
- TestAssetStore::getLocalPath($file1)
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file1),
- 'File upload to standard directory in /assets'
- );
-
- // test upload into custom folder
- $customFolder = 'UploadTest-testUpload';
- $u2 = new Upload();
- $u2->loadIntoFile($tmpFile, null, $customFolder);
- $file2 = $u2->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload/UploadTest-testUpload.txt',
- $file2->getFilename()
- );
- $this->assertEquals(
- BASE_PATH . '/assets/UploadTest/.protected/UploadTest-testUpload/315ae4c3d4/UploadTest-testUpload.txt',
- TestAssetStore::getLocalPath($file2)
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file2),
- 'File upload to custom directory in /assets'
- );
- }
-
- public function testAllowedFilesize()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.txt';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- // test upload into default folder
- $u1 = new Upload();
- $v = new UploadTest\Validator();
-
- $v->setAllowedMaxFileSize(array('txt' => 10));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertFalse($result, 'Load failed because size was too big');
-
- $v->setAllowedMaxFileSize(array('[document]' => 10));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertFalse($result, 'Load failed because size was too big');
-
- $v->setAllowedMaxFileSize(array('txt' => 200000));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertTrue($result, 'Load failed with setting max file size');
-
- // check max file size set by app category
- $tmpFileName = 'UploadTest-testUpload.jpg';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- file_put_contents($this->tmpFilePath, $tmpFileContent . $tmpFileContent);
-
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'image/jpeg',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'jpg',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v->setAllowedMaxFileSize(array('[image]' => '40k'));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertTrue($result, 'Load failed with setting max file size');
-
- $v->setAllowedMaxFileSize(array('[image]' => '1k'));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertFalse($result, 'Load failed because size was too big');
-
- $v->setAllowedMaxFileSize(array('[image]' => 1000));
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
- $this->assertFalse($result, 'Load failed because size was too big');
- }
-
- public function testPHPUploadErrors()
- {
- $configMaxFileSizes = ['*' => '1k'];
- Config::inst()->update(
- 'SilverStripe\\Assets\\Upload_Validator',
- 'default_max_file_size',
- $configMaxFileSizes
- );
- // create tmp file
- $tmpFileName = 'myfile.jpg';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent(100);
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // Build file
- $upload = new Upload();
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => '',
- 'tmp_name' => $this->tmpFilePath,
- 'size' => filesize($this->tmpFilePath),
- 'error' => UPLOAD_ERR_OK,
- );
-
- // Test ok
- $this->assertTrue($upload->validate($tmpFile));
-
- // Test zero size file
- $upload->clearErrors();
- $tmpFile['size'] = 0;
- $this->assertFalse($upload->validate($tmpFile));
- $this->assertContains(
- _t('File.NOFILESIZE', 'Filesize is zero bytes.'),
- $upload->getErrors()
- );
-
- // Test file too large
- $upload->clearErrors();
- $tmpFile['error'] = UPLOAD_ERR_INI_SIZE;
- $this->assertFalse($upload->validate($tmpFile));
- $this->assertContains(
- _t(
- 'File.TOOLARGE',
- 'Filesize is too large, maximum {size} allowed',
- 'Argument 1: Filesize (e.g. 1MB)',
- array('size' => '1 KB')
- ),
- $upload->getErrors()
- );
-
- // Test form size
- $upload->clearErrors();
- $tmpFile['error'] = UPLOAD_ERR_FORM_SIZE;
- $this->assertFalse($upload->validate($tmpFile));
- $this->assertContains(
- _t(
- 'File.TOOLARGE',
- 'Filesize is too large, maximum {size} allowed',
- 'Argument 1: Filesize (e.g. 1MB)',
- array('size' => '1 KB')
- ),
- $upload->getErrors()
- );
-
- // Test no file
- $upload->clearErrors();
- $tmpFile['error'] = UPLOAD_ERR_NO_FILE;
- $this->assertFalse($upload->validate($tmpFile));
- $this->assertContains(
- _t('File.NOVALIDUPLOAD', 'File is not a valid upload'),
- $upload->getErrors()
- );
- }
-
- public function testGetAllowedMaxFileSize()
- {
- Config::nest();
-
- // Check the max file size uses the config values
- $configMaxFileSizes = array(
- '[image]' => '1k',
- 'txt' => 1000
- );
- Config::inst()->update('SilverStripe\\Assets\\Upload_Validator', 'default_max_file_size', $configMaxFileSizes);
- $v = new UploadTest\Validator();
-
- $retrievedSize = $v->getAllowedMaxFileSize('[image]');
- $this->assertEquals(
- 1024,
- $retrievedSize,
- 'Max file size check on default values failed (config category set check)'
- );
-
- $retrievedSize = $v->getAllowedMaxFileSize('txt');
- $this->assertEquals(
- 1000,
- $retrievedSize,
- 'Max file size check on default values failed (config extension set check)'
- );
-
- // Check instance values for max file size
- $maxFileSizes = array(
- '[document]' => 2000,
- 'txt' => '4k'
- );
- $v = new UploadTest\Validator();
- $v->setAllowedMaxFileSize($maxFileSizes);
-
- $retrievedSize = $v->getAllowedMaxFileSize('[document]');
- $this->assertEquals(
- 2000,
- $retrievedSize,
- 'Max file size check on instance values failed (instance category set check)'
- );
-
- // Check that the instance values overwrote the default values
- // ie. The max file size will not exist for [image]
- $retrievedSize = $v->getAllowedMaxFileSize('[image]');
- $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (config overridden check)');
-
- // Check a category that has not been set before
- $retrievedSize = $v->getAllowedMaxFileSize('[archive]');
- $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (category not set check)');
-
- // Check a file extension that has not been set before
- $retrievedSize = $v->getAllowedMaxFileSize('mp3');
- $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (extension not set check)');
-
- $retrievedSize = $v->getAllowedMaxFileSize('txt');
- $this->assertEquals(
- 4096,
- $retrievedSize,
- 'Max file size check on instance values failed (instance extension set check)'
- );
-
- // Check a wildcard max file size against a file with an extension
- $v = new UploadTest\Validator();
- $v->setAllowedMaxFileSize(2000);
-
- $retrievedSize = $v->getAllowedMaxFileSize('.jpg');
- $this->assertEquals(
- 2000,
- $retrievedSize,
- 'Max file size check on instance values failed (wildcard max file size)'
- );
-
- Config::unnest();
- }
-
- public function testAllowedSizeOnFileWithNoExtension()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => '',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedMaxFileSize(array('' => 10));
-
- // test upload into default folder
- $u1 = new Upload();
- $u1->setValidator($v);
- $result = $u1->loadIntoFile($tmpFile);
-
- $this->assertFalse($result, 'Load failed because size was too big');
- }
-
- public function testUploadDoesNotAllowUnknownExtension()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.php';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'php',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedExtensions(array('txt'));
-
- // test upload into default folder
- $u = new Upload();
- $u->setValidator($v);
- $result = $u->loadIntoFile($tmpFile);
-
- $this->assertFalse($result, 'Load failed because extension was not accepted');
- }
-
- public function testUploadAcceptsAllowedExtension()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.txt';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedExtensions(array('txt'));
-
- // test upload into default folder
- $u = new Upload();
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- $file = $u->getFile();
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file),
- 'File upload to custom directory in /assets'
- );
- }
-
- public function testUploadDeniesNoExtensionFilesIfNoEmptyStringSetForValidatorExtensions()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => '',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedExtensions(array('txt'));
-
- // test upload into default folder
- $u = new Upload();
- $result = $u->loadIntoFile($tmpFile);
-
- $this->assertFalse($result, 'Load failed because extension was not accepted');
- $this->assertEquals(1, count($u->getErrors()), 'There is a single error of the file extension');
- }
-
- public function testUploadTarGzFileTwiceAppendsNumber()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.tar.gz';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'tar.gz',
- 'error' => UPLOAD_ERR_OK,
- );
-
- // test upload into default folder
- $u = new Upload();
- $u->loadIntoFile($tmpFile);
- $file = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload.tar.gz',
- $file->Name,
- 'File has a name without a number because it\'s not a duplicate'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file),
- 'File exists'
- );
-
- $u = new Upload();
- $u->loadIntoFile($tmpFile);
- $file2 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload-v2.tar.gz',
- $file2->Name,
- 'File receives a number attached to the end before the extension'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file2),
- 'File exists'
- );
- $this->assertGreaterThan(
- $file->ID,
- $file2->ID,
- 'File database record is not the same'
- );
-
- $u = new Upload();
- $u->loadIntoFile($tmpFile);
- $file3 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload-v3.tar.gz',
- $file3->Name,
- 'File receives a number attached to the end before the extension'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file3),
- 'File exists'
- );
- $this->assertGreaterThan(
- $file2->ID,
- $file3->ID,
- 'File database record is not the same'
- );
- }
-
- public function testUploadFileWithNoExtensionTwiceAppendsNumber()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedExtensions(array(''));
-
- // test upload into default folder
- $u = new Upload();
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- $file = $u->getFile();
-
- $this->assertEquals(
- 'UploadTest-testUpload',
- $file->Name,
- 'File is uploaded without extension'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file),
- 'File exists'
- );
-
- $u = new Upload();
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- $file2 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload-v2',
- $file2->Name,
- 'File receives a number attached to the end'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file2),
- 'File exists'
- );
- $this->assertGreaterThan(
- $file->ID,
- $file2->ID,
- 'File database record is not the same'
- );
- }
-
- public function testReplaceFile()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
- $v->setAllowedExtensions(array(''));
-
- // test upload into default folder
- $u = new Upload();
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- $file = $u->getFile();
-
- $this->assertEquals(
- 'UploadTest-testUpload',
- $file->Name,
- 'File is uploaded without extension'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file),
- 'File exists'
- );
-
- $u = new Upload();
- $u->setValidator($v);
- $u->setReplaceFile(true);
- $u->loadIntoFile($tmpFile);
- $file2 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload',
- $file2->Name,
- 'File does not receive new name'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file2),
- 'File exists'
- );
- $this->assertEquals(
- $file->ID,
- $file2->ID,
- 'File database record is the same'
- );
- }
-
- public function testReplaceFileWithLoadIntoFile()
- {
- // create tmp file
- $tmpFileName = 'UploadTest-testUpload.txt';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'txt',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
-
- // test upload into default folder
- $u = new Upload();
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- $file = $u->getFile();
-
- $this->assertEquals(
- 'UploadTest-testUpload.txt',
- $file->Name,
- 'File is uploaded without extension'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file),
- 'File exists'
- );
-
- // replace=true
- $u = new Upload();
- $u->setValidator($v);
- $u->setReplaceFile(true);
- $u->loadIntoFile($tmpFile, new File());
- $file2 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload.txt',
- $file2->Name,
- 'File does not receive new name'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file2),
- 'File exists'
- );
- $this->assertEquals(
- $file->ID,
- $file2->ID,
- 'File database record is the same'
- );
-
- // replace=false
- $u = new Upload();
- $u->setValidator($v);
- $u->setReplaceFile(false);
- $u->loadIntoFile($tmpFile, new File());
- $file3 = $u->getFile();
- $this->assertEquals(
- 'UploadTest-testUpload-v2.txt',
- $file3->Name,
- 'File does receive new name'
- );
- $this->assertFileExists(
- TestAssetStore::getLocalPath($file3),
- 'File exists'
- );
- $this->assertGreaterThan(
- $file2->ID,
- $file3->ID,
- 'File database record is not the same'
- );
- }
-
- public function testDeleteResampledImagesOnUpload()
- {
- $tmpFileName = 'UploadTest-testUpload.jpg';
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
-
- $uploadImage = function () use ($tmpFileName) {
- copy(__DIR__ . '/GDTest/images/test_jpg.jpg', $this->tmpFilePath);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'jpg',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
-
- // test upload into default folder
- $u = new Upload();
- $u->setReplaceFile(true);
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- return $u->getFile();
- };
-
- // Image upload and generate a resampled image
- $image = $uploadImage();
- $resampled = $image->ResizedImage(123, 456);
- $resampledPath = TestAssetStore::getLocalPath($resampled);
- $this->assertFileExists($resampledPath);
-
- // Re-upload the image, overwriting the original
- // Resampled images should removed when their parent file is overwritten
- $image = $uploadImage();
- $this->assertFileExists($resampledPath);
- }
-
- public function testFileVersioningWithAnExistingFile()
- {
- $upload = function ($tmpFileName) {
- // create tmp file
- $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
- $tmpFileContent = $this->getTemporaryFileContent();
- file_put_contents($this->tmpFilePath, $tmpFileContent);
-
- // emulates the $_FILES array
- $tmpFile = array(
- 'name' => $tmpFileName,
- 'type' => 'text/plaintext',
- 'size' => filesize($this->tmpFilePath),
- 'tmp_name' => $this->tmpFilePath,
- 'extension' => 'jpg',
- 'error' => UPLOAD_ERR_OK,
- );
-
- $v = new UploadTest\Validator();
-
- // test upload into default folder
- $u = new Upload();
- $u->setReplaceFile(false);
- $u->setValidator($v);
- $u->loadIntoFile($tmpFile);
- return $u->getFile();
- };
-
- // test empty file version prefix
- Config::inst()->update('SilverStripe\\Assets\\Storage\\DefaultAssetNameGenerator', 'version_prefix', '');
-
- $file1 = $upload('UploadTest-IMG001.jpg');
- $this->assertEquals(
- 'UploadTest-IMG001.jpg',
- $file1->Name,
- 'File does not receive new name'
- );
-
- $file2 = $upload('UploadTest-IMG001.jpg');
- $this->assertEquals(
- 'UploadTest-IMG002.jpg',
- $file2->Name,
- 'File does receive new name'
- );
-
- $file3 = $upload('UploadTest-IMG002.jpg');
- $this->assertEquals(
- 'UploadTest-IMG003.jpg',
- $file3->Name,
- 'File does receive new name'
- );
-
- $file4 = $upload('UploadTest-IMG3.jpg');
- $this->assertEquals(
- 'UploadTest-IMG3.jpg',
- $file4->Name,
- 'File does not receive new name'
- );
-
- $file1->delete();
- $file2->delete();
- $file3->delete();
- $file4->delete();
-
- // test '-v' file version prefix
- Config::inst()->update('SilverStripe\\Assets\\Storage\\DefaultAssetNameGenerator', 'version_prefix', '-v');
-
- $file1 = $upload('UploadTest2-IMG001.jpg');
- $this->assertEquals(
- 'UploadTest2-IMG001.jpg',
- $file1->Name,
- 'File does not receive new name'
- );
-
- $file2 = $upload('UploadTest2-IMG001.jpg');
- $this->assertEquals(
- 'UploadTest2-IMG001-v2.jpg',
- $file2->Name,
- 'File does receive new name'
- );
-
- $file3 = $upload('UploadTest2-IMG001.jpg');
- $this->assertEquals(
- 'UploadTest2-IMG001-v3.jpg',
- $file3->Name,
- 'File does receive new name'
- );
-
- $file4 = $upload('UploadTest2-IMG001-v3.jpg');
- $this->assertEquals(
- 'UploadTest2-IMG001-v4.jpg',
- $file4->Name,
- 'File does receive new name'
- );
- }
-
- /**
- * Generate some dummy file content
- *
- * @param int $reps How many zeros to return
- * @return string
- */
- protected function getTemporaryFileContent($reps = 10000)
- {
- return str_repeat('0', $reps);
- }
-}
diff --git a/tests/php/Assets/UploadTest/Validator.php b/tests/php/Assets/UploadTest/Validator.php
deleted file mode 100644
index 13b878533..000000000
--- a/tests/php/Assets/UploadTest/Validator.php
+++ /dev/null
@@ -1,44 +0,0 @@
-tmpFile['name']);
- // filesize validation
-
- if (!$this->isValidSize()) {
- $ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
- $arg = File::format_size($this->getAllowedMaxFileSize($ext));
- $this->errors[] = _t(
- 'File.TOOLARGE',
- 'File size is too large, maximum {size} allowed',
- 'Argument 1: File size (e.g. 1MB)',
- array('size' => $arg)
- );
- return false;
- }
-
- // extension validation
- if (!$this->isValidExtension()) {
- $this->errors[] = _t('File.INVALIDEXTENSIONSHORT', 'Extension is not allowed');
- return false;
- }
-
- return true;
- }
-}
diff --git a/tests/php/Forms/DBFileTest.php b/tests/php/Forms/DBFileTest.php
deleted file mode 100644
index 0047104b2..000000000
--- a/tests/php/Forms/DBFileTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-update('alternate_base_url', '/mysite/');
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- parent::tearDown();
- }
-
- /**
- * Test that images in a DBFile are rendered properly
- */
- public function testRender()
- {
- $obj = new DBFileTest\TestObject();
-
- // Test image tag
- $fish = realpath(__DIR__ .'/../ORM/ImageTest/test-image-high-quality.jpg');
- $this->assertFileExists($fish);
- $obj->MyFile->setFromLocalFile($fish, 'awesome-fish.jpg');
- $this->assertEquals(
- '',
- trim($obj->MyFile->forTemplate())
- );
-
- // Test download tag
- $obj->MyFile->setFromString('puppies', 'subdir/puppy-document.txt');
- $this->assertEquals(
- '',
- trim($obj->MyFile->forTemplate())
- );
- }
-
- public function testValidation()
- {
- $obj = new DBFileTest\ImageOnly();
-
- // Test from image
- $fish = realpath(__DIR__ .'/../ORM/ImageTest/test-image-high-quality.jpg');
- $this->assertFileExists($fish);
- $obj->MyFile->setFromLocalFile($fish, 'awesome-fish.jpg');
-
- // This should fail
- $this->setExpectedException('SilverStripe\\ORM\\ValidationException');
- $obj->MyFile->setFromString('puppies', 'subdir/puppy-document.txt');
- }
-
- public function testPermission()
- {
- $obj = new DBFileTest\TestObject();
-
- // Test from image
- $fish = realpath(__DIR__ .'/../ORM/ImageTest/test-image-high-quality.jpg');
- $this->assertFileExists($fish);
- $obj->MyFile->setFromLocalFile(
- $fish,
- 'private/awesome-fish.jpg',
- null,
- null,
- array(
- 'visibility' => AssetStore::VISIBILITY_PROTECTED
- )
- );
-
- // Test various file permissions work on DBFile
- $this->assertFalse($obj->MyFile->canViewFile());
- $obj->MyFile->getURL();
- $this->assertTrue($obj->MyFile->canViewFile());
- $obj->MyFile->revokeFile();
- $this->assertFalse($obj->MyFile->canViewFile());
- $obj->MyFile->getURL(false);
- $this->assertFalse($obj->MyFile->canViewFile());
- $obj->MyFile->grantFile();
- $this->assertTrue($obj->MyFile->canViewFile());
- }
-}
diff --git a/tests/php/Forms/DBFileTest/ImageOnly.php b/tests/php/Forms/DBFileTest/ImageOnly.php
deleted file mode 100644
index 708ce3c5f..000000000
--- a/tests/php/Forms/DBFileTest/ImageOnly.php
+++ /dev/null
@@ -1,19 +0,0 @@
- "DBFile('image/supported')"
- );
-}
diff --git a/tests/php/Forms/DBFileTest/Subclass.php b/tests/php/Forms/DBFileTest/Subclass.php
deleted file mode 100644
index 26990ff10..000000000
--- a/tests/php/Forms/DBFileTest/Subclass.php
+++ /dev/null
@@ -1,18 +0,0 @@
- "DBFile"
- );
-}
diff --git a/tests/php/Forms/DBFileTest/TestObject.php b/tests/php/Forms/DBFileTest/TestObject.php
deleted file mode 100644
index 6ed1f3883..000000000
--- a/tests/php/Forms/DBFileTest/TestObject.php
+++ /dev/null
@@ -1,19 +0,0 @@
- "DBFile"
- );
-}
diff --git a/tests/php/ORM/DataDifferencerTest.php b/tests/php/ORM/DataDifferencerTest.php
index 1d4d072ae..3601317c0 100644
--- a/tests/php/ORM/DataDifferencerTest.php
+++ b/tests/php/ORM/DataDifferencerTest.php
@@ -13,7 +13,6 @@ use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
class DataDifferencerTest extends SapphireTest
{
-
protected static $fixture_file = 'DataDifferencerTest.yml';
protected $extraDataObjects = array(
@@ -33,7 +32,7 @@ class DataDifferencerTest extends SapphireTest
// Create a test files for each of the fixture references
$files = File::get()->exclude('ClassName', Folder::class);
foreach ($files as $file) {
- $fromPath = __DIR__ . '/ImageTest/' . $file->Name;
+ $fromPath = __DIR__ . '/DataDifferencerTest/images/' . $file->Name;
$destPath = TestAssetStore::getLocalPath($file); // Only correct for test asset store
Filesystem::makeFolder(dirname($destPath));
copy($fromPath, $destPath);
diff --git a/tests/php/ORM/ImageTest/test-image-high-quality.jpg b/tests/php/ORM/DataDifferencerTest/images/test-image-high-quality.jpg
similarity index 100%
rename from tests/php/ORM/ImageTest/test-image-high-quality.jpg
rename to tests/php/ORM/DataDifferencerTest/images/test-image-high-quality.jpg
diff --git a/tests/php/ORM/ImageTest/test-image-low-quality.jpg b/tests/php/ORM/DataDifferencerTest/images/test-image-low-quality.jpg
similarity index 100%
rename from tests/php/ORM/ImageTest/test-image-low-quality.jpg
rename to tests/php/ORM/DataDifferencerTest/images/test-image-low-quality.jpg
diff --git a/tests/php/ORM/ImageTest/test-image.png b/tests/php/ORM/DataDifferencerTest/images/test-image.png
similarity index 100%
rename from tests/php/ORM/ImageTest/test-image.png
rename to tests/php/ORM/DataDifferencerTest/images/test-image.png
diff --git a/tests/php/ORM/ImageTest/test.image.with.dots.png b/tests/php/ORM/DataDifferencerTest/images/test.image.with.dots.png
similarity index 100%
rename from tests/php/ORM/ImageTest/test.image.with.dots.png
rename to tests/php/ORM/DataDifferencerTest/images/test.image.with.dots.png
diff --git a/tests/php/ORM/GDImageTest.php b/tests/php/ORM/GDImageTest.php
deleted file mode 100644
index f6169156d..000000000
--- a/tests/php/ORM/GDImageTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-markTestSkipped("The GD extension is required");
- return;
- }
-
- /**
- * @skipUpgrade
-*/
- Config::inst()->update(
- 'SilverStripe\\Core\\Injector\\Injector',
- 'Image_Backend',
- 'SilverStripe\\Assets\\GDBackend'
- );
- }
-
- public function tearDown()
- {
- $cache = Injector::inst()->get(CacheInterface::class . '.GDBackend_Manipulations');
- $cache->clear();
-
- parent::tearDown();
- }
-}
diff --git a/tests/php/ORM/ImageTest.php b/tests/php/ORM/ImageTest.php
deleted file mode 100644
index 9808420f2..000000000
--- a/tests/php/ORM/ImageTest.php
+++ /dev/null
@@ -1,328 +0,0 @@
-markTestSkipped(sprintf('Skipping %s ', get_class($this)));
- return;
- }
-
- // Set backend root to /ImageTest
- TestAssetStore::activate('ImageTest');
-
- // Copy test images for each of the fixture references
- $files = File::get()->exclude('ClassName', Folder::class);
- foreach ($files as $image) {
- $filePath = TestAssetStore::getLocalPath($image); // Only correct for test asset store
- $sourcePath = __DIR__ . '/ImageTest/' . $image->Name;
- if (!file_exists($filePath)) {
- SSFilesystem::makeFolder(dirname($filePath));
- if (!copy($sourcePath, $filePath)) {
- user_error('Failed to copy test images', E_USER_ERROR);
- }
- }
- }
- }
-
- public function tearDown()
- {
- TestAssetStore::reset();
- parent::tearDown();
- }
-
- public function testGetTagWithTitle()
- {
- Config::inst()->update(DBFile::class, 'force_resample', false);
-
- $image = $this->objFromFixture(Image::class, 'imageWithTitle');
- $expected = '';
- $actual = trim($image->getTag());
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testGetTagWithoutTitle()
- {
- Config::inst()->update(DBFile::class, 'force_resample', false);
-
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $expected = '';
- $actual = trim($image->getTag());
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testGetTagWithoutTitleContainingDots()
- {
- Config::inst()->update(DBFile::class, 'force_resample', false);
-
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitleContainingDots');
- $expected = '';
- $actual = trim($image->getTag());
-
- $this->assertEquals($expected, $actual);
- }
-
- /**
- * Tests that multiple image manipulations may be performed on a single Image
- */
- public function testMultipleGenerateManipulationCalls()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
-
- $imageFirst = $image->ScaleWidth(200);
- $this->assertNotNull($imageFirst);
- $expected = 200;
- $actual = $imageFirst->getWidth();
-
- $this->assertEquals($expected, $actual);
-
- $imageSecond = $imageFirst->ScaleHeight(100);
- $this->assertNotNull($imageSecond);
- $expected = 100;
- $actual = $imageSecond->getHeight();
- $this->assertEquals($expected, $actual);
- }
-
- /**
- * Tests that image manipulations that do not affect the resulting dimensions
- * of the output image do not resample the file.
- */
- public function testReluctanceToResampling()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $this->assertTrue($image->isSize(300, 300));
-
- // Set width to 300 pixels
- $imageScaleWidth = $image->ScaleWidth(300);
- $this->assertEquals($imageScaleWidth->getWidth(), 300);
- $this->assertEquals($image->Filename, $imageScaleWidth->Filename);
-
- // Set height to 300 pixels
- $imageScaleHeight = $image->ScaleHeight(300);
- $this->assertEquals($imageScaleHeight->getHeight(), 300);
- $this->assertEquals($image->Filename, $imageScaleHeight->Filename);
-
- // Crop image to 300 x 300
- $imageCropped = $image->Fill(300, 300);
- $this->assertTrue($imageCropped->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageCropped->Filename);
-
- // Resize (padded) to 300 x 300
- $imageSized = $image->Pad(300, 300);
- $this->assertTrue($imageSized->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageSized->Filename);
-
- // Padded image 300 x 300 (same as above)
- $imagePadded = $image->Pad(300, 300);
- $this->assertTrue($imagePadded->isSize(300, 300));
- $this->assertEquals($image->Filename, $imagePadded->Filename);
-
- // Resized (stretched) to 300 x 300
- $imageStretched = $image->ResizedImage(300, 300);
- $this->assertTrue($imageStretched->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageStretched->Filename);
-
- // Fit (various options)
- $imageFit = $image->Fit(300, 600);
- $this->assertTrue($imageFit->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageFit->Filename);
- $imageFit = $image->Fit(600, 300);
- $this->assertTrue($imageFit->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageFit->Filename);
- $imageFit = $image->Fit(300, 300);
- $this->assertTrue($imageFit->isSize(300, 300));
- $this->assertEquals($image->Filename, $imageFit->Filename);
- }
-
- /**
- * Tests that a URL to a resampled image is provided when force_resample is
- * set to true, if the resampled file is smaller than the original.
- */
- public function testForceResample()
- {
- $imageHQ = $this->objFromFixture(Image::class, 'highQualityJPEG');
- $imageHQR = $imageHQ->Resampled();
- $imageLQ = $this->objFromFixture(Image::class, 'lowQualityJPEG');
- $imageLQR = $imageLQ->Resampled();
-
- // Test resampled file is served when force_resample = true
- Config::inst()->update(DBFile::class, 'force_resample', true);
- $this->assertLessThan($imageHQ->getAbsoluteSize(), $imageHQR->getAbsoluteSize(), 'Resampled image is smaller than original');
- $this->assertEquals($imageHQ->getURL(), $imageHQR->getSourceURL(), 'Path to a resampled image was returned by getURL()');
-
- // Test original file is served when force_resample = true but original file is low quality
- $this->assertGreaterThanOrEqual($imageLQ->getAbsoluteSize(), $imageLQR->getAbsoluteSize(), 'Resampled image is larger or same size as original');
- $this->assertNotEquals($imageLQ->getURL(), $imageLQR->getSourceURL(), 'Path to the original image file was returned by getURL()');
-
- // Test original file is served when force_resample = false
- Config::inst()->update(DBFile::class, 'force_resample', false);
- $this->assertNotEquals($imageHQ->getURL(), $imageHQR->getSourceURL(), 'Path to the original image file was returned by getURL()');
- }
-
- public function testImageResize()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $this->assertTrue($image->isSize(300, 300));
-
- // Test normal resize
- $resized = $image->Pad(150, 100);
- $this->assertTrue($resized->isSize(150, 100));
-
- // Test cropped resize
- $cropped = $image->Fill(100, 200);
- $this->assertTrue($cropped->isSize(100, 200));
-
- // Test padded resize
- $padded = $image->Pad(200, 100);
- $this->assertTrue($padded->isSize(200, 100));
-
- // Test Fit
- $ratio = $image->Fit(80, 160);
- $this->assertTrue($ratio->isSize(80, 80));
-
- // Test FitMax
- $fitMaxDn = $image->FitMax(200, 100);
- $this->assertTrue($fitMaxDn->isSize(100, 100));
- $fitMaxUp = $image->FitMax(500, 400);
- $this->assertTrue($fitMaxUp->isSize(300, 300));
-
- //Test ScaleMax
- $scaleMaxWDn = $image->ScaleMaxWidth(200);
- $this->assertTrue($scaleMaxWDn->isSize(200, 200));
- $scaleMaxWUp = $image->ScaleMaxWidth(400);
- $this->assertTrue($scaleMaxWUp->isSize(300, 300));
- $scaleMaxHDn = $image->ScaleMaxHeight(200);
- $this->assertTrue($scaleMaxHDn->isSize(200, 200));
- $scaleMaxHUp = $image->ScaleMaxHeight(400);
- $this->assertTrue($scaleMaxHUp->isSize(300, 300));
-
- // Test FillMax
- $cropMaxDn = $image->FillMax(200, 100);
- $this->assertTrue($cropMaxDn->isSize(200, 100));
- $cropMaxUp = $image->FillMax(400, 200);
- $this->assertTrue($cropMaxUp->isSize(300, 150));
-
- // Test Clip
- $clipWDn = $image->CropWidth(200);
- $this->assertTrue($clipWDn->isSize(200, 300));
- $clipWUp = $image->CropWidth(400);
- $this->assertTrue($clipWUp->isSize(300, 300));
- $clipHDn = $image->CropHeight(200);
- $this->assertTrue($clipHDn->isSize(300, 200));
- $clipHUp = $image->CropHeight(400);
- $this->assertTrue($clipHUp->isSize(300, 300));
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testGenerateImageWithInvalidParameters()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $image->ScaleHeight('String');
- $image->Pad(600, 600, 'XXXXXX');
- }
-
- public function testCacheFilename()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $imageFirst = $image->Pad(200, 200, 'CCCCCC');
- $imageFilename = $imageFirst->getURL();
- // Encoding of the arguments is duplicated from cacheFilename
- $neededPart = 'Pad' . Convert::base64url_encode(array(200,200,'CCCCCC'));
- $this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated');
- }
-
- /**
- * Test that propertes from the source Image are inherited by resampled images
- */
- public function testPropertyInheritance()
- {
- $testString = 'This is a test';
- $origImage = $this->objFromFixture(Image::class, 'imageWithTitle');
- $origImage->TestProperty = $testString;
- $resampled = $origImage->ScaleWidth(10);
- $this->assertEquals($resampled->TestProperty, $testString);
- $resampled2 = $resampled->ScaleWidth(5);
- $this->assertEquals($resampled2->TestProperty, $testString);
- }
-
- public function testShortcodeHandlerFallsBackToFileProperties()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithTitle');
- $parser = new ShortcodeParser();
- $parser->register('image', array(Image::class, 'handle_shortcode'));
-
- $this->assertEquals(
- sprintf(
- '',
- $image->Link(),
- $image->Title
- ),
- $parser->parse(sprintf('[image id=%d]', $image->ID))
- );
- }
-
- public function testShortcodeHandlerUsesShortcodeProperties()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithTitle');
- $parser = new ShortcodeParser();
- $parser->register('image', array(Image::class, 'handle_shortcode'));
-
- $this->assertEquals(
- sprintf(
- '',
- $image->Link()
- ),
- $parser->parse(
- sprintf(
- '[image id="%d" alt="Alt content" title="Title content"]',
- $image->ID
- )
- )
- );
- }
-
- public function testShortcodeHandlerAddsDefaultAttributes()
- {
- $image = $this->objFromFixture(Image::class, 'imageWithoutTitle');
- $parser = new ShortcodeParser();
- $parser->register('image', array(Image::class, 'handle_shortcode'));
-
- $this->assertEquals(
- sprintf(
- '',
- $image->Link(),
- $image->Title
- ),
- $parser->parse(
- sprintf(
- '[image id="%d"]',
- $image->ID
- )
- )
- );
- }
-}
diff --git a/tests/php/ORM/ImageTest.yml b/tests/php/ORM/ImageTest.yml
deleted file mode 100644
index e73229acd..000000000
--- a/tests/php/ORM/ImageTest.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-SilverStripe\Assets\Folder:
- folder1:
- Name: folder
-SilverStripe\Assets\Image:
- imageWithTitle:
- Title: This is a image Title
- FileFilename: folder/test-image.png
- FileHash: 444065542b5dd5187166d8e1cd684e0d724c5a97
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test-image.png
- imageWithoutTitle:
- FileFilename: folder/test-image.png
- FileHash: 444065542b5dd5187166d8e1cd684e0d724c5a97
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test-image.png
- imageWithoutTitleContainingDots:
- FileFilename: folder/test.image.with.dots.png
- FileHash: 46affab7043cfd9f1ded919dd24affd08e926eca
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test.image.with.dots.png
- imageWithMetacharacters:
- Title: This is a/an image Title
- FileFilename: folder/test-image.png
- FileHash: 444065542b5dd5187166d8e1cd684e0d724c5a97
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test-image.png
- lowQualityJPEG:
- Title: This is a low quality JPEG
- FileFilename: folder/test-image-low-quality.jpg
- FileHash: 33be1b95cba0358fe54e8b13532162d52f97421c
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test-image-low-quality.jpg
- highQualityJPEG:
- Title: This is a high quality JPEG
- FileFilename: folder/test-image-high-quality.jpg
- FileHash: a870de278b475cb75f5d9f451439b2d378e13af1
- Parent: =>SilverStripe\Assets\Folder.folder1
- Name: test-image-high-quality.jpg
diff --git a/tests/php/ORM/ImagickImageTest.php b/tests/php/ORM/ImagickImageTest.php
deleted file mode 100644
index 2a3db4799..000000000
--- a/tests/php/ORM/ImagickImageTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-markTestSkipped("The Imagick extension is not available.");
- return;
- }
-
- /**
- * @skipUpgrade
-*/
- Config::inst()->update(Injector::class, 'Image_Backend', ImagickBackend::class);
- }
-}