Skip to content

Commit

Permalink
NEW Allow file variants with different extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Jan 17, 2024
1 parent 46e07eb commit 8d5eb94
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 6 deletions.
80 changes: 80 additions & 0 deletions src/FilenameParsing/AlternativeFileExtensionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace SilverStripe\Assets\FilenameParsing;

use SilverStripe\Core\Convert;

/**
* This trait can be applied to a `FileIDHelper` imlementation to add the baility to encode alternative file extension.
*
* This allows the creation of file variant with different extension to the original file.
*/
trait AlternativeFileExtensionTrait
{

/**
* Rewrite a filename with a different extension
* @param string $filename
* @param string $variant
* @return string
*/
public function rewriteVariantExtension(string $filename, string $variant): string
{
return $this->swapExtension($filename, $variant, 1);
}

/**
* Rewrite a filename to use the original extension for the provided variant.
* @param string $filename
* @param string $variant
* @return string
*/
public function restoreOriginalExtension(string $filename, string $variant): string
{
return $this->swapExtension($filename, $variant, 0);
}

/**
* Construct the original or alternative filname extension for the given filename and variant string.
* @param string $filename Original filename without variant
* @param string $variant Full variant list
* @param int $extIndex Wether we want the original extension (0) or the new extension (1)
* @return string
*/
private function swapExtension(
string $filename,
string $variant,
int $extIndex
): string {
// If there's no variant at all, we can rewrite the filenmane
if (empty($variant)) {
return $filename;
}

// Split variant string in variant list
$subVariants = explode('_', $variant);

// Split our filename into a filename and extension part
if (!preg_match('/(.+)\.([a-z\d]+)$/i', $filename, $matches)) {
return $filename;
}
[$_, $filenameWitoutExtension, $extension] = $matches;

// Loop our variant list until we find our special file extension swap variant
foreach ($subVariants as $subVariant) {
$extSwapVariant = FileIDHelper::EXTENSION_REWRITE_VARIANT;
if (preg_match("/^$extSwapVariant(.+)$/", $subVariant, $matches)) {
[$_, $base64] = $matches;

/**
* This array always contain 2 values: The orignial extension and the new extension
* @var array $extensionData
*/
$extensionData = Convert::base64url_decode($base64);
$extension = $extensionData[$extIndex];
}
}

return $filenameWitoutExtension . '.' . $extension;
}
}
4 changes: 4 additions & 0 deletions src/FilenameParsing/FileIDHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
*/
interface FileIDHelper
{
/**
* A special variant type that can be used to encode a variant filename with a different extension.
*/
public const EXTENSION_REWRITE_VARIANT = 'extRewrite';

/**
* Map file tuple (hash, name, variant) to a filename to be used by flysystem
Expand Down
14 changes: 13 additions & 1 deletion src/FilenameParsing/HashFileIDHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
class HashFileIDHelper implements FileIDHelper
{
use Injectable;
use AlternativeFileExtensionTrait;

/**
* Default length at which hashes are truncated.
Expand All @@ -39,6 +40,11 @@ public function buildFileID($filename, $hash = null, $variant = null, $cleanfile
if ($cleanfilename) {
$filename = $this->cleanFilename($filename);
}

if ($variant) {
$filename = $this->rewriteVariantExtension($filename, $variant);
}

$name = basename($filename ?? '');

// Split extension
Expand Down Expand Up @@ -88,10 +94,16 @@ public function parseFileID($fileID)
}

$filename = $matches['folder'] . $matches['basename'] . $matches['extension'];
$variant = isset($matches['variant']) ? $matches['variant'] : '';

if (isset($variant)) {
$filename = $this->restoreOriginalExtension($filename, $variant);
}

return new ParsedFileID(
$filename,
$matches['hash'],
isset($matches['variant']) ? $matches['variant'] : '',
$variant,
$fileID
);
}
Expand Down
15 changes: 13 additions & 2 deletions src/FilenameParsing/NaturalFileIDHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
class NaturalFileIDHelper implements FileIDHelper
{
use Injectable;
use AlternativeFileExtensionTrait;

public function buildFileID($filename, $hash = null, $variant = null, $cleanfilename = true)
{
Expand All @@ -27,6 +28,11 @@ public function buildFileID($filename, $hash = null, $variant = null, $cleanfile
if ($cleanfilename) {
$filename = $this->cleanFilename($filename);
}

if ($variant) {
$filename = $this->rewriteVariantExtension($filename, $variant);
}

$name = basename($filename ?? '');

// Split extension
Expand Down Expand Up @@ -57,7 +63,6 @@ public function buildFileID($filename, $hash = null, $variant = null, $cleanfile
return $fileID;
}


public function cleanFilename($filename)
{
// Swap backslash for forward slash
Expand All @@ -77,10 +82,16 @@ public function parseFileID($fileID)
}

$filename = $matches['folder'] . $matches['basename'] . $matches['extension'];
$variant = isset($matches['variant']) ? $matches['variant'] : '';

if (isset($variant)) {
$filename = $this->restoreOriginalExtension($filename, $variant);
}

return new ParsedFileID(
$filename,
'',
isset($matches['variant']) ? $matches['variant'] : '',
$variant,
$fileID
);
}
Expand Down
10 changes: 8 additions & 2 deletions src/ImageBackendFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,15 @@ public function __construct(Factory $creator)
*/
public function create($service, array $params = [])
{
/** @var AssetContainer $assetContainer */
/** @var AssetContainer|null $assetContainer */
$assetContainer = reset($params);
if (!$assetContainer instanceof AssetContainer) {

// If no asset container was passed in, create a new uncached image backend
if (!$assetContainer) {
return $this->creator->create($service, $params);
}

if (!($assetContainer instanceof AssetContainer)) {
throw new BadMethodCallException("Can only create Image_Backend for " . AssetContainer::class);
}

Expand Down
13 changes: 13 additions & 0 deletions src/ImageManipulation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use InvalidArgumentException;
use LogicException;
use SilverStripe\Assets\FilenameParsing\FileIDHelper;
use SilverStripe\Assets\Storage\AssetContainer;
use SilverStripe\Assets\Storage\AssetStore;
use SilverStripe\Assets\Storage\DBFile;
Expand Down Expand Up @@ -840,6 +841,18 @@ public function isHeight($height)
return $this->getHeight() === $height;
}

/**
* @param string $newExtension
* @param callable $callback
* @return DBFile The manipulated file
*/
public function manipulateExtension(string $newExtension, callable $callback)
{
$pathParts = pathinfo($this->getFilename());
$variant = $this->variantName(FileIDHelper::EXTENSION_REWRITE_VARIANT, $pathParts['extension'], $newExtension);
return $this->manipulate($variant, $callback);
}

/**
* Wrapper for manipulate that passes in and stores Image_Backend objects instead of tuples
*
Expand Down
4 changes: 3 additions & 1 deletion src/InterventionBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,10 @@ public function writeToStore(AssetStore $assetStore, $filename, $hash = null, $v
throw new BadMethodCallException("Cannot write corrupt file to store");
}

// Make sure we're using the extension of the variant file, which can differ from the original file
$url = $assetStore->getAsURL($filename, $hash, $variant, false);
$extension = pathinfo($url, PATHINFO_EXTENSION);
// Save file
$extension = pathinfo($filename ?? '', PATHINFO_EXTENSION);
$result = $assetStore->setFromString(
$resource->encode($extension, $this->getQuality())->getEncoded(),
$filename,
Expand Down

0 comments on commit 8d5eb94

Please sign in to comment.