|null
- */
- public ?array $assetId = null;
-
- public function options($actionID): array
- {
- return array_merge(parent::options($actionID), match ($actionID) {
- 'replace-metadata' => ['volume', 'assetId'],
- default => []
- });
- }
-
- public function actionReplaceMetadata(): int
- {
- $assets = Asset::find()
- ->volume($this->volume)
- ->id($this->assetId)
- ->collect();
-
- $assets->each(function(Asset $asset) {
- $this->do(
- "Replacing metadata for `$asset->path`",
- fn() => $this->replaceAssetMetadata($asset),
- );
- });
-
- return ExitCode::OK;
- }
-
- protected function replaceAssetMetadata(Asset $asset): void
- {
- $fs = $asset->getVolume()->getFs();
-
- if (!$fs instanceof Fs) {
- throw new Exception('Invalid filesystem type.');
- }
-
- $path = $asset->getPath();
-
- $config = [
- 'ContentType' => FileHelper::getMimeType($path),
- 'MetadataDirective' => 'REPLACE',
- ];
-
- $fs->replaceMetadata($path, $config);
- }
-}
diff --git a/src/cli/controllers/BuildController.php b/src/cli/controllers/BuildController.php
deleted file mode 100644
index 6b4e838..0000000
--- a/src/cli/controllers/BuildController.php
+++ /dev/null
@@ -1,56 +0,0 @@
-validateEdition($this->craftEdition);
-
- return $this->run('/cloud/asset-bundles/publish', [
- 'to' => $this->publishAssetBundlesTo,
- ]);
- }
-
- private function validateEdition(string $edition): void
- {
- $craftVersion = Craft::$app->getVersion();
- $editionFromEnv = App::env('CRAFT_EDITION');
-
- // CRAFT_EDITION is enforced in these versions, so we don't need to validate
- if ($editionFromEnv && Semver::satisfies($craftVersion, '^4.10 || ^5.2')) {
- return;
- }
-
- $editionFromProjectConfig = Craft::$app->getProjectConfig()->get('system.edition', true);
-
- if (!$editionFromProjectConfig || !$edition) {
- throw new Exception('Unable to determine the Craft CMS edition.');
- }
-
- if ($edition !== $editionFromProjectConfig) {
- throw new Exception("This Craft Cloud project is only valid for the Craft CMS edition “{$edition}”.");
- }
- }
-}
diff --git a/src/cli/controllers/InfoController.php b/src/cli/controllers/InfoController.php
deleted file mode 100644
index 6a8fbd9..0000000
--- a/src/cli/controllers/InfoController.php
+++ /dev/null
@@ -1,43 +0,0 @@
-table([
- 'Extension',
- 'App ID',
- 'Environment ID',
- 'Build ID',
- ], [
- [
- "$packageName:$packageVersion",
- Craft::$app->id,
- Module::getInstance()->getConfig()->environmentId,
- Module::getInstance()->getConfig()->buildId,
- ],
- ]);
- return ExitCode::OK;
- }
-
- public function actionPhpInfo(): int
- {
- ob_start();
- phpinfo(INFO_ALL);
- $phpInfoStr = ob_get_clean();
-
- $this->stdout($phpInfoStr);
-
- return ExitCode::OK;
- }
-}
diff --git a/src/cli/controllers/QueueController.php b/src/cli/controllers/QueueController.php
deleted file mode 100644
index fe329f1..0000000
--- a/src/cli/controllers/QueueController.php
+++ /dev/null
@@ -1,116 +0,0 @@
- [
- 'message',
- 'run',
- 'throw',
- 'seconds',
- 'count',
- ],
- 'fail' => [
- 'message',
- ],
- default => [],
- });
- }
-
- public function actionFail(string $jobId): int
- {
- $this->do("Failing job #$jobId", function() use ($jobId) {
- /** @var Queue $queue */
- $queue = Craft::$app->getQueue();
- $event = new ExecEvent([
- 'id' => $jobId,
- 'error' => $this->message ? new \yii\base\Exception($this->message) : null,
-
- // Prevent retry
- 'attempt' => 1,
- ]);
- $queue->handleError($event);
- });
-
- return ExitCode::OK;
- }
-
- public function actionExec(string $jobId): int
- {
- $this->do("Executing job #$jobId", function() use ($jobId) {
- /** @var Queue $queue */
- $queue = Craft::$app->getQueue();
- $jobFound = $queue->executeJob($jobId);
-
- if (!$jobFound) {
- $this->stdout($this->markdownToAnsi("Job not found: `$jobId`"));
- $this->stdout("\n");
- }
- });
-
- return ExitCode::OK;
- }
-
- public function actionPushTestJob(): int
- {
- for ($i = 0; $i < $this->count; $i++) {
- $job = new TestJob([
- 'message' => $this->message ?? '',
- 'throw' => $this->throw,
- 'seconds' => $this->seconds,
- ]);
-
- $this->do('Pushing test job', function() use ($job) {
- $jobId = Craft::$app->getQueue()->push($job);
-
- if ($this->run) {
- $this->do('Running test job', fn() => $this->actionExec($jobId));
- }
- });
- }
-
- return ExitCode::OK;
- }
-}
diff --git a/src/cli/controllers/RunningTimeTrait.php b/src/cli/controllers/RunningTimeTrait.php
deleted file mode 100644
index 0014bb7..0000000
--- a/src/cli/controllers/RunningTimeTrait.php
+++ /dev/null
@@ -1,29 +0,0 @@
-runningTime = microtime(true);
-
- return parent::beforeAction($action);
- }
-
- public function afterAction($action, $result)
- {
- $this->runningTime = microtime(true) - $this->runningTime;
-
- $runningTime = round($this->runningTime, 2);
- $message = "`{$this->getRoute()}` completed in `{$runningTime}s`.";
-
- $this->stdout("\n");
- $this->stdout($this->markdownToAnsi($message));
- $this->stdout("\n");
-
- return parent::afterAction($action, $result);
- }
-}
diff --git a/src/cli/controllers/SetupController.php b/src/cli/controllers/SetupController.php
deleted file mode 100644
index b7c6e33..0000000
--- a/src/cli/controllers/SetupController.php
+++ /dev/null
@@ -1,161 +0,0 @@
-runAction('config');
- $this->stdout(PHP_EOL);
- $this->stdout("Your project is ready to deploy to Craft Cloud!\n", BaseConsole::FG_GREEN);
- $this->stdout("See https://craftcms.com/docs/cloud/config.html\n");
-
- return ExitCode::OK;
- }
-
- public function actionConfig(): int
- {
- $config = [];
- $filePath = Craft::getAlias('@root/craft-cloud.yaml');
- $fileName = basename($filePath);
- $defaultPhpVersion = Version::parse('8.2');
- $defaultNodeVersion = Version::parse('20.9');
- $defaultNpmScript = 'build';
- $ddevConfigFile = Craft::getAlias('@root/.ddev/config.yaml');
- $ddevConfig = file_exists($ddevConfigFile)
- ? Yaml::parseFile($ddevConfigFile)
- : null;
- $ddevPhpVersion = $ddevConfig['php_version'] ?? null;
- $ddevNodeVersion = $ddevConfig['nodejs_version'] ?? null;
- $composerJsonFile = Craft::getAlias('@root/composer.json');
- $composerJson = file_exists($composerJsonFile)
- ? json_decode(file_get_contents($composerJsonFile), true)
- : null;
- $composerJsonPhpVersion = $composerJson['config']['platform']['php'] ?? null;
- $packageJsonFile = Craft::getAlias('@root/package.json');
- $packageJson = file_exists($packageJsonFile)
- ? json_decode(file_get_contents($packageJsonFile), true)
- : null;
- $packageJsonNodeVersion = $packageJson['engines']['node'] ?? null;
- $packageJsonScripts = Collection::make($packageJson['scripts'] ?? null)->keys();
- $confirmMessage = file_exists($filePath)
- ? $this->markdownToAnsi("`{$fileName}` already exists. Overwrite?")
- : $this->markdownToAnsi("Create `{$fileName}`?");
-
- if (!$this->confirm($confirmMessage, true)) {
- return ExitCode::OK;
- }
-
- if ($ddevPhpVersion) {
- try {
- $this->do(
- "Detected PHP version from DDEV config: `{$ddevPhpVersion}`",
- function() use ($ddevPhpVersion, &$defaultPhpVersion) {
- $defaultPhpVersion = Version::parse($ddevPhpVersion);
- }
- );
- } catch (InvalidVersionException $e) {
- }
- }
-
- if ($composerJsonPhpVersion) {
- try {
- $this->do(
- "Detected PHP version from composer.json (_config.platforms.php_): `{$composerJsonPhpVersion}`",
- function() use ($composerJsonPhpVersion, &$defaultPhpVersion) {
- $defaultPhpVersion = Version::parse($composerJsonPhpVersion);
- }
- );
- } catch (InvalidVersionException $e) {
- }
- }
-
- $config['php-version'] = $this->prompt('PHP version:', array(
- 'required' => true,
- 'default' => "$defaultPhpVersion->major.$defaultPhpVersion->minor",
- 'validator' => function(string $value, &$error) {
- if (!preg_match('/^[0-9]+\.[0-9]+$/', $value)) {
- $error = $this->markdownToAnsi('PHP version must be specified as `major.minor`.');
- return false;
- }
- return true;
- },
- ));
-
- if ($packageJsonScripts->isNotEmpty() && $this->confirm('Run npm script on deploy?', true)) {
- $config['npm-script'] = $this->prompt('npm script to run:', [
- 'default' => $packageJsonScripts->contains($defaultNpmScript) ? $defaultNpmScript : null,
- 'required' => true,
- 'validator' => function(string $value, &$error) use ($packageJsonScripts) {
- if (!$packageJsonScripts->contains($value)) {
- $error = $this->markdownToAnsi("npm script not found in package.json: `{$value}`");
- return false;
- }
-
- return true;
- },
- ]);
-
- if ($defaultNpmScript === $config['npm-script']) {
- unset($config['npm-script']);
- }
-
- if ($ddevNodeVersion) {
- try {
- $this->do(
- "Detected Node.js version from DDEV config: `{$ddevNodeVersion}`",
- function() use ($ddevNodeVersion, &$defaultNodeVersion) {
- $defaultNodeVersion = Version::parse($ddevNodeVersion);
- }
- );
- } catch (InvalidVersionException $e) {
- }
- }
-
- if ($packageJsonNodeVersion) {
- try {
- $this->do(
- "Detected Node.js version from package.json (_engines.node_): `{$packageJsonNodeVersion}`",
- function() use ($packageJsonNodeVersion, &$defaultNodeVersion) {
- $defaultNodeVersion = Version::parse($packageJsonNodeVersion);
- }
- );
- } catch (InvalidVersionException $e) {
- }
- }
-
- $config['node-version'] = $this->prompt('Node version:', [
- 'required' => false,
- 'default' => "$defaultNodeVersion->major.$defaultNodeVersion->minor",
- 'validator' => function(string $input, ?string &$error = null) {
- if (!preg_match('/^[0-9]+\.[0-9]+$/', $input)) {
- $error = $this->markdownToAnsi('Node version must be specified as `major.minor`.');
- return false;
- }
- return true;
- },
- ]);
- }
-
- $output = "# Craft Cloud configuration file\n";
- $output .= "# https://craftcms.com/knowledge-base/cloud-config\n";
- $output .= Yaml::dump($config, 20, 2);
-
- $this->writeToFile(
- $filePath,
- $output,
- );
-
- return ExitCode::OK;
- }
-}
diff --git a/src/cli/controllers/StaticCacheController.php b/src/cli/controllers/StaticCacheController.php
deleted file mode 100644
index c398296..0000000
--- a/src/cli/controllers/StaticCacheController.php
+++ /dev/null
@@ -1,28 +0,0 @@
-do('Purging prefixes', function() use ($prefixes) {
- Module::getInstance()->getStaticCache()->purgeUrlPrefixes(...$prefixes);
- });
-
- return ExitCode::OK;
- }
-
- public function actionPurgeTags(string ...$tags): int
- {
- $this->do('Purging tags', function() use ($tags) {
- Module::getInstance()->getStaticCache()->purgeTags(...$tags);
- });
-
- return ExitCode::OK;
- }
-}
diff --git a/src/cli/controllers/UpController.php b/src/cli/controllers/UpController.php
deleted file mode 100644
index e5b6876..0000000
--- a/src/cli/controllers/UpController.php
+++ /dev/null
@@ -1,52 +0,0 @@
-trigger(self::EVENT_BEFORE_UP, $event);
-
- if (!$event->isValid) {
- return ExitCode::UNSPECIFIED_ERROR;
- }
-
- $this->mustRun('/setup/php-session-table');
- $this->mustRun('/setup/db-cache-table');
-
- if (Craft::$app->getIsInstalled()) {
- $this->mustRun('/up');
- Module::getInstance()->getStaticCache()->purgeGateway();
- }
-
- $event = new CancelableEvent();
- $this->trigger(self::EVENT_AFTER_UP, $event);
-
- if (!$event->isValid) {
- return ExitCode::UNSPECIFIED_ERROR;
- }
-
- return ExitCode::OK;
- }
-
- private function mustRun(string $route): void
- {
- $exitCode = $this->run($route);
-
- if ($exitCode !== ExitCode::OK) {
- throw new Exception("Exit code \"$exitCode\" returned from \"{$route}\"");
- }
- }
-}
diff --git a/src/controllers/AssetsController.php b/src/controllers/AssetsController.php
deleted file mode 100644
index 16fca1b..0000000
--- a/src/controllers/AssetsController.php
+++ /dev/null
@@ -1,399 +0,0 @@
-requireAcceptsJson();
- $this->requirePostRequest();
- $originalFilename = $this->request->getRequiredBodyParam('filename');
- $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
- $filename = sprintf('%s.%s', uniqid('upload', true), $extension);
- $fieldId = $this->request->getBodyParam('fieldId');
- $assetId = $this->request->getBodyParam('assetId');
- $folderId = $this->request->getBodyParam('folderId');
-
- if ($assetId && !$folderId) {
- $folderId = Craft::$app->getAssets()->getAssetById($assetId)->folderId;
- }
-
- if (!$folderId && !$fieldId) {
- throw new BadRequestHttpException('No target destination provided for uploading');
- }
-
- if (!$folderId) {
- /** @var AssetsField|null $field */
- $field = Craft::$app->getFields()->getFieldById($fieldId);
- $elementId = $this->request->getBodyParam('elementId');
- $siteId = $this->request->getBodyParam('siteId');
- $element = $elementId
- ? Craft::$app->getElements()->getElementById($elementId, null, $siteId)
- : null;
-
- $folderId = $field->resolveDynamicPathToFolderId($element);
- }
-
- if (!$folderId) {
- throw new BadRequestHttpException('The target destination provided for uploading is not valid');
- }
-
- $folder = Craft::$app->getAssets()->findFolder(['id' => $folderId]);
-
- if (!$folder) {
- throw new BadRequestHttpException('The target folder provided for uploading is not valid');
- }
-
- $volume = $folder->getVolume();
- $pathInVolume = sprintf('%s%s%s', $this->volumeSubpath($volume), $folder->path, $filename);
-
- /** @var Fs $fs */
- $fs = $folder->getVolume()->getFs();
-
- // TODO: use setting for expiry
- // TODO: tagging isn't working
- $url = $fs->presignedUrl('PutObject', $pathInVolume, new DateTime('+20 minutes'));
-
- return $this->asJson([
- 'url' => $url,
- 'originalFilename' => $originalFilename,
- 'targetFilename' => Assets::prepareAssetName($originalFilename),
- 'filename' => $filename,
- 'bucket' => $fs->getBucketName(),
- 'key' => $fs->createBucketPath($pathInVolume)->toString(),
- 'folderId' => $folder->id,
- ]);
- }
-
- public function actionCreateAsset(): Response
- {
- $this->requireAcceptsJson();
-
- $filename = $this->request->getRequiredBodyParam('filename');
- $originalFilename = $this->request->getRequiredBodyParam('originalFilename');
- $targetFilename = $this->request->getRequiredBodyParam('targetFilename');
- $size = $this->request->getBodyParam('size');
- $width = $this->request->getBodyParam('width');
- $height = $this->request->getBodyParam('height');
- $elementsService = Craft::$app->getElements();
- $lastModifiedMs = (int) $this->request->getBodyParam('lastModified');
- $dateModified = $lastModifiedMs
- ? DateTime::createFromFormat('U', (string) floor($lastModifiedMs / 1000))
- : new DateTime();
-
- if (!$filename) {
- throw new BadRequestHttpException('No file was uploaded');
- }
-
- $folderId = (int)$this->request->getBodyParam('folderId') ?: null;
-
- // TODO: do I need to account for fieldId, since we resolve it in get-url?
- $fieldId = (int)$this->request->getBodyParam('fieldId') ?: null;
-
- if (!$folderId && !$fieldId) {
- throw new BadRequestHttpException('No target destination provided for uploading');
- }
-
- $assets = Craft::$app->getAssets();
- $selectionCondition = null;
- $element = null;
-
- if ($fieldId) {
- /** @var AssetsField|null $field */
- $field = Craft::$app->getFields()->getFieldById((int)$fieldId);
-
- if (!$field instanceof AssetsField) {
- throw new BadRequestHttpException('The field provided is not an Assets field');
- }
-
- if ($elementId = $this->request->getBodyParam('elementId')) {
- $siteId = $this->request->getBodyParam('siteId') ?: null;
- $element = $elementsService->getElementById($elementId, null, $siteId);
- }
-
- $folderId = $field->resolveDynamicPathToFolderId($element);
- $selectionCondition = $field->getSelectionCondition();
- if ($selectionCondition instanceof ElementCondition) {
- $selectionCondition->referenceElement = $element;
- }
- }
-
- if (empty($folderId)) {
- throw new BadRequestHttpException('The target destination provided for uploading is not valid');
- }
-
- $folder = $assets->findFolder(['id' => $folderId]);
-
- if (!$folder) {
- throw new BadRequestHttpException('The target folder provided for uploading is not valid');
- }
-
- // Check the permissions to upload in the resolved folder.
- $this->requireVolumePermissionByFolder('saveAssets', $folder);
-
- $asset = new Asset();
- $asset->setFilename($filename);
- $asset->setVolumeId($folder->volumeId);
- $asset->uploaderId = Craft::$app->getUser()->getId();
- $asset->avoidFilenameConflicts = true;
- $asset->dateModified = $dateModified;
- $asset->size = $size;
- $asset->width = $width;
- $asset->height = $height;
-
- // Setting newFolderId, so that extension validation on newLocation occurs
- $asset->newFolderId = $folder->id;
-
- // Setting these so that Asset::_relocateFile doesn't try to download
- $asset->folderId = $folder->id;
-
- // Handle special characters that have been encoded from the presigned URL
- $asset->folderPath = is_string($folder->path) ? Fs::urlEncodePathSegments($folder->path) : $asset->folderPath;
-
- if (!$selectionCondition) {
- $asset->newFilename = $targetFilename;
- }
-
- if ($originalFilename) {
- $asset->title = Assets::filename2Title(pathinfo($originalFilename, PATHINFO_FILENAME));
- }
-
- $asset->setScenario(Asset::SCENARIO_CREATE);
- $saved = $this->saveAsset($asset);
-
- // In case of error, let user know about it.
- if (!$saved) {
- // TODO: delete stray file
- $errors = $asset->getFirstErrors();
- return $this->asFailure(implode("\n", $errors));
- }
-
- if ($selectionCondition) {
- if (!$selectionCondition->matchElement($asset)) {
- // delete and reject it
- $elementsService->deleteElement($asset, true);
- return $this->asFailure(Craft::t('app', '{filename} isn’t selectable for this field.', [
- 'filename' => $originalFilename,
- ]));
- }
-
- $asset->newFilename = $targetFilename;
- $asset->setScenario(Asset::SCENARIO_MOVE);
-
- if (!$elementsService->saveElement($asset)) {
- $errors = $asset->getFirstErrors();
- return $this->asJson([
- 'error' => $this->asFailure(implode("\n", $errors)),
- ]);
- }
- }
-
- if ($asset->conflictingFilename !== null) {
- $conflictingAsset = Asset::findOne(['folderId' => $folder->id, 'filename' => $asset->conflictingFilename]);
-
- return $this->asJson([
- 'conflict' => Craft::t('app', 'A file with the name “{filename}” already exists.', ['filename' => $asset->conflictingFilename]),
- 'assetId' => $asset->id,
- 'filename' => $asset->conflictingFilename,
- 'conflictingAssetId' => $conflictingAsset->id ?? null,
- 'suggestedFilename' => $asset->suggestedFilename,
- 'conflictingAssetUrl' => ($conflictingAsset && ($conflictingAsset->getVolume()->getFs()->hasUrls ?? false))
- ? $conflictingAsset->getUrl()
- : null,
- ]);
- }
-
- return $this->asSuccess(data: [
- 'filename' => $asset->getFilename(),
- 'assetId' => $asset->id,
- ]);
- }
-
- public function actionReplaceFile(): Response
- {
- $this->requireAcceptsJson();
-
- $assetId = $this->request->getBodyParam('assetId');
- $sourceAssetId = $this->request->getBodyParam('sourceAssetId');
- $filename = $this->request->getBodyParam('filename');
- $targetFilename = $this->request->getBodyParam('targetFilename');
- $size = $this->request->getBodyParam('size');
- $width = $this->request->getBodyParam('width');
- $height = $this->request->getBodyParam('height');
- $lastModifiedMs = (int) $this->request->getBodyParam('lastModified');
- $dateModified = $lastModifiedMs
- ? DateTime::createFromFormat('U', (string) floor($lastModifiedMs / 1000))
- : new DateTime();
-
- $assets = Craft::$app->getAssets();
-
- // Must have at least one existing asset (source or target).
- // Must have either target asset or target filename.
- // Must have either uploaded file or source asset.
- if (empty($assetId) && empty($sourceAssetId) && empty($targetFilename)) {
- throw new BadRequestHttpException('Incorrect combination of parameters.');
- }
-
- $sourceAsset = null;
- $assetToReplace = null;
-
- if ($assetId && !$assetToReplace = $assets->getAssetById($assetId)) {
- throw new NotFoundHttpException('Asset not found.');
- }
-
- if ($sourceAssetId && !$sourceAsset = $assets->getAssetById($sourceAssetId)) {
- throw new NotFoundHttpException('Asset not found.');
- }
-
- $this->requireVolumePermissionByAsset('replaceFiles', $assetToReplace ?: $sourceAsset);
- $this->requirePeerVolumePermissionByAsset('replacePeerFiles', $assetToReplace ?: $sourceAsset);
-
- // Handle the Element Action
- if ($assetToReplace !== null && $filename) {
- $assetToReplace->width = $width;
- $assetToReplace->height = $height;
- $assetToReplace->size = $size;
- $assetToReplace->dateModified = $dateModified;
- if (!$this->replaceAssetFile($assetToReplace, $filename, $targetFilename)) {
- throw new Exception('Unable to replace asset.');
- }
- } elseif ($sourceAsset !== null) {
- // Or replace using an existing Asset
-
- // See if we can find an Asset to replace.
- if ($assetToReplace === null) {
- if ($targetFilename === null) {
- throw new Exception('No target filename provided.');
- }
-
- // Make sure the extension didn't change
- if (pathinfo($targetFilename, PATHINFO_EXTENSION) !== $sourceAsset->getExtension()) {
- throw new Exception($targetFilename . ' doesn\'t have the original file extension.');
- }
-
- /** @var Asset|null $assetToReplace */
- $assetToReplace = Asset::find()
- ->select(['elements.id'])
- ->folderId($sourceAsset->folderId)
- ->filename(Db::escapeParam($targetFilename))
- ->one();
- }
-
- // If we have an actual asset for which to replace the file, just do it.
- // e.g. triggered by selecting "replace it" in asset index modal
- if ($assetToReplace) {
- // TODO: do this without downloading local file if both Cloud FSs
- $assets->replaceAssetFile(
- $assetToReplace,
- $sourceAsset->getCopyOfFile(),
- $assetToReplace->getFilename()
- );
- Craft::$app->getElements()->deleteElement($sourceAsset);
- } else {
- // TODO: when/how does this occur? (possible dead-code)
- // If all we have is the filename, then make sure that the destination is empty and go for it.
- $volume = $sourceAsset->getVolume();
- $volume->deleteFile(rtrim($sourceAsset->folderPath, '/') . '/' . $targetFilename);
- $sourceAsset->newFilename = $targetFilename;
- // Don't validate required custom fields
- Craft::$app->getElements()->saveElement($sourceAsset);
- $assetId = $sourceAsset->id;
- }
- }
-
- $resultingAsset = $assetToReplace ?: $sourceAsset;
-
- return $this->asSuccess(data: [
- 'assetId' => $assetId,
- 'filename' => $resultingAsset->getFilename(),
- 'formattedSize' => $resultingAsset->getFormattedSize(0),
- 'formattedSizeInBytes' => $resultingAsset->getFormattedSizeInBytes(false),
- 'formattedDateUpdated' => Craft::$app->getFormatter()->asDatetime(
- $resultingAsset->dateUpdated,
- \yii\i18n\Formatter::FORMAT_WIDTH_SHORT,
- ),
- 'dimensions' => $resultingAsset->getDimensions(),
- ]);
- }
-
- public function replaceAssetFile(Asset $asset, string $filename, string $targetFilename): bool
- {
- $assets = Craft::$app->getAssets();
-
- if ($assets->hasEventHandlers($assets::EVENT_BEFORE_REPLACE_ASSET)) {
- $event = new ReplaceAssetEvent([
- 'asset' => $asset,
- 'replaceWith' => '',
- 'filename' => '',
- ]);
- $assets->trigger($assets::EVENT_BEFORE_REPLACE_ASSET, $event);
- $targetFilename = $event->filename ?: $targetFilename;
- }
-
- $oldPath = $asset->getPath();
- $asset->uploaderId = Craft::$app->getUser()->getId();
- $asset->avoidFilenameConflicts = true;
- $asset->setScenario(Asset::SCENARIO_REPLACE);
- $asset->setFilename($filename);
- $asset->newFilename = $targetFilename;
-
- $saved = $this->saveAsset($asset);
-
- $asset->getVolume()->deleteFile($oldPath);
-
- // Try again, in case the resulting filename has a tmp suffix from `avoidFilenameConflicts`
- if ($saved && $targetFilename !== $asset->getFilename()) {
- $asset->newFilename = $targetFilename;
- $saved = $this->saveAsset($asset);
- }
-
- if ($assets->hasEventHandlers($assets::EVENT_AFTER_REPLACE_ASSET)) {
- $assets->trigger($assets::EVENT_AFTER_REPLACE_ASSET, new ReplaceAssetEvent([
- 'asset' => $asset,
- 'filename' => $filename,
- ]));
- }
-
- return $saved;
- }
-
- protected function saveAsset(Asset $asset): bool
- {
- $asset->setScenario(Asset::SCENARIO_CREATE);
-
- // Set tempFilePath to pass required validation, but unset immediately after
- $asset->tempFilePath = '__tempFilePath__';
- $asset->on(Model::EVENT_AFTER_VALIDATE, function(Event $event) {
- $event->sender->tempFilePath = null;
- });
-
- return Craft::$app->getElements()->saveElement($asset);
- }
-
- private function volumeSubpath(Volume $volume): string
- {
- return method_exists($volume, 'getSubpath') ? $volume->getSubpath() : '';
- }
-}
diff --git a/src/controllers/EsiController.php b/src/controllers/EsiController.php
deleted file mode 100644
index 4e73ab7..0000000
--- a/src/controllers/EsiController.php
+++ /dev/null
@@ -1,34 +0,0 @@
-getUrlSigner()
- ->verify(Craft::$app->getRequest()->getAbsoluteUrl());
- }
-
- public function actionRenderTemplate(string $template, array $variables = []): Response
- {
- // No-cache headers are applied to all action requests by default. Remove them.
- // @see https://github.com/craftcms/cms/pull/16364
- Craft::$app->getResponse()->getHeaders()->remove('Expires');
- Craft::$app->getResponse()->getHeaders()->remove('Pragma');
- Craft::$app->getResponse()->getHeaders()->remove('Cache-Control');
-
- return $this->renderTemplate($template, $variables);
- }
-}
diff --git a/src/fs/AssetsFs.php b/src/fs/AssetsFs.php
deleted file mode 100644
index 68e1d75..0000000
--- a/src/fs/AssetsFs.php
+++ /dev/null
@@ -1,32 +0,0 @@
-useLocalFs = !Module::getInstance()->getConfig()->useAssetCdn;
- }
-
- /**
- * @inheritDoc
- */
- public static function displayName(): string
- {
- return 'Craft Cloud';
- }
-
- public function createBucketPrefix(): SegmentedPathInterface
- {
- return parent::createBucketPrefix()->append('assets');
- }
-}
diff --git a/src/fs/BuildArtifactsFs.php b/src/fs/BuildArtifactsFs.php
deleted file mode 100644
index bd1e6e7..0000000
--- a/src/fs/BuildArtifactsFs.php
+++ /dev/null
@@ -1,31 +0,0 @@
-useLocalFs = !Helper::isCraftCloud();
- $this->baseUrl = Module::getInstance()->getConfig()->artifactBaseUrl;
-
- // Allow local override via config/env
- if ($this->baseUrl) {
- $this->localFsUrl = $this->baseUrl;
- }
- }
-
- public function createBucketPrefix(): SegmentedPathInterface
- {
- return parent::createBucketPrefix()->append('artifacts');
- }
-}
diff --git a/src/fs/BuildsFs.php b/src/fs/BuildsFs.php
deleted file mode 100644
index 51343b2..0000000
--- a/src/fs/BuildsFs.php
+++ /dev/null
@@ -1,19 +0,0 @@
-append('builds')
- ->append(Module::getInstance()->getConfig()->buildId);
- }
-}
diff --git a/src/fs/CpResourcesFs.php b/src/fs/CpResourcesFs.php
deleted file mode 100644
index bb5abc7..0000000
--- a/src/fs/CpResourcesFs.php
+++ /dev/null
@@ -1,13 +0,0 @@
-append('cpresources');
- }
-}
diff --git a/src/fs/Fs.php b/src/fs/Fs.php
deleted file mode 100644
index 5bdbd44..0000000
--- a/src/fs/Fs.php
+++ /dev/null
@@ -1,573 +0,0 @@
- fn(self $fs) => $fs->hasUrls,
- ];
-
- return $rules;
- }
-
- protected function getLocalFs(): Local
- {
- $path = $this->localFsPath
- ? $this->createPath('')->prepend($this->localFsPath)
- : null;
-
- $this->localFs = $this->localFs ?? Craft::createObject([
- 'class' => Local::class,
- 'hasUrls' => $this->hasUrls,
- 'path' => $path?->toString(),
- 'url' => $this->localFsUrl,
- ]);
-
- return $this->localFs;
- }
-
- /**
- * This should never be null, as the Cloud resizer can render asset transforms for the CP,
- * even if `$hasUrls` is `false`.
- *
- * @inheritdoc
- */
- public function getRootUrl(): ?string
- {
- return $this->createUrl()->toString();
- }
-
- public function createUrl(string $path = ''): UriInterface
- {
- if ($this->useLocalFs) {
- return Modifier::wrap($this->getLocalFs()->getRootUrl() ?? '/')
- ->appendSegment($this->createPath($path))
- ->unwrap();
- }
-
- $baseUrl = App::parseEnv($this->baseUrl);
-
- if ($baseUrl) {
- return Modifier::wrap($baseUrl)
- ->appendSegment($this->createPath($path))
- ->unwrap();
- }
-
- return Modifier::wrap(Module::getInstance()->getConfig()->cdnBaseUrl)
- ->appendSegment($this->createBucketPath($path))
- ->unwrap();
- }
-
- /**
- * @inheritdoc
- */
- public function attributeLabels(): array
- {
- return [
- 'localFsPath' => Craft::t('app', 'Base Path'),
- 'localFsUrl' => Craft::t('app', 'Base URL'),
- ];
- }
-
- /**
- * @inheritdoc
- */
- public function behaviors(): array
- {
- $behaviors = parent::behaviors();
- $behaviors['parser'] = [
- 'class' => EnvAttributeParserBehavior::class,
- 'attributes' => [
- 'subpath',
- 'baseUrl',
- 'localFsPath',
- 'localFsUrl',
- ],
- ];
-
- return $behaviors;
- }
-
- /**
- * @inheritDoc
- */
- public function settingsAttributes(): array
- {
- return array_merge(parent::settingsAttributes(), [
- 'expires',
- 'subpath',
- 'baseUrl',
- 'localFsPath',
- 'localFsUrl',
- ]);
- }
-
- public function getExpires(): ?string
- {
- return $this->expires;
- }
-
- public function setExpires(null|string|array $expires): void
- {
- $this->expires = is_array($expires) ? $this->normalizeExpires($expires) : $expires;
- }
-
- protected function normalizeExpires(array $expires): ?string
- {
- $amount = (int)$expires['amount'];
- $period = $expires['period'];
-
- if (!$amount || !$period) {
- return null;
- }
-
- return "$amount $period";
- }
-
- /**
- * @inheritDoc
- */
- protected function createAdapter(): AwsS3V3Adapter
- {
- return new AwsS3V3Adapter(
- client: $this->getClient(),
- bucket: $this->getBucketName(),
- prefix: $this->createBucketPath('')->toString(),
- );
- }
-
- /**
- * @inheritDoc
- */
- protected function invalidateCdnPath(string $path): bool
- {
- try {
- $prefix = StaticCache::CDN_PREFIX . Module::getInstance()->getConfig()->environmentId . ':';
- $tag = StaticCacheTag::create($this->createBucketPath($path)->toString())
- ->minify(false)
- ->withPrefix($prefix);
-
- Module::getInstance()->getStaticCache()->purgeTags($tag);
-
- return true;
- } catch (\Throwable $e) {
- return false;
- }
- }
-
- /**
- * @inheritDoc
- */
- protected function addFileMetadataToConfig(array $config): array
- {
- if (!empty($this->getExpires()) && DateTimeHelper::isValidIntervalString($this->getExpires())) {
- $expires = new DateTime();
- $now = new DateTime();
- $expires->modify('+' . $this->getExpires());
- $diff = (int)$expires->format('U') - (int)$now->format('U');
-
- // Setting this in metadata instead of `CacheControl` because
- // `CacheControl` is not respected by S3 when using presigned PUT URLs.
- // @see https://github.com/aws/aws-sdk-php/issues/1691
- $config['Metadata']['max-age'] = $diff;
- }
-
- $config['Metadata']['visibility'] = $this->hasUrls
- ? Visibility::PUBLIC
- : Visibility::PRIVATE;
-
- return parent::addFileMetadataToConfig($config);
- }
-
- /**
- * @inheritDoc
- */
- public function getSettingsHtml(): ?string
- {
- return Craft::$app->getView()->renderTemplate('cloud/fsSettings', [
- 'fs' => $this,
- 'periods' => Assets::periodList(),
- ]);
- }
-
- protected function createBucketPrefix(): SegmentedPathInterface
- {
- // Note: ENVIRONMENT_ID may not be set when running cloud/build
- return HierarchicalPath::fromRelative(Module::getInstance()->getConfig()->environmentId ?? '');
- }
-
- protected function createPath(string $path): SegmentedPathInterface
- {
- return HierarchicalPath::fromRelative(
- App::parseEnv($this->subpath) ?? '',
- $path,
- )->withoutEmptySegments();
- }
-
- public function createBucketPath(string $path): SegmentedPathInterface
- {
- return $this->createBucketPrefix()->append($this->createPath($path));
- }
-
- public function getBucketName(): ?string
- {
- return Module::getInstance()->getConfig()->projectId;
- }
-
- public function createCredentials(): ?Credentials
- {
- $key = Module::getInstance()->getConfig()->accessKey;
-
- return $key ? new Credentials(
- $key,
- Module::getInstance()->getConfig()->accessSecret,
- Module::getInstance()->getConfig()->accessToken,
- ) : null;
- }
-
- public function createClient(array $config = []): S3Client
- {
- $config = array_merge(
- [
- 'region' => Module::getInstance()->getConfig()->getRegion(),
- 'version' => 'latest',
- 'http_handler' => new GuzzleHandler(Craft::createGuzzleClient()),
- 'credentials' => $this->createCredentials(),
- ],
- Module::getInstance()->getConfig()->getS3ClientOptions(),
- $config
- );
-
- return new S3Client($config);
- }
-
- public function getClient(): S3Client
- {
- if (!isset($this->client)) {
- $this->client = $this->createClient();
- }
-
- return $this->client;
- }
-
- /**
- * @inheritDoc
- * All s3 objects are non-public
- */
- protected function visibility(): string
- {
- return Visibility::PRIVATE;
- }
-
- public function presignedUrl(string $command, string $path, DateTimeInterface $expiresAt, array $config = []): string
- {
- if ($this->useLocalFs) {
- throw new InvalidConfigException();
- }
-
- try {
- $commandConfig = $this->addFileMetadataToConfig($config);
-
- $command = $this->getClient()->getCommand($command, [
- 'Bucket' => $this->getBucketName(),
- 'Key' => $this->createBucketPath($path)->toString(),
- ] + $commandConfig);
-
- $request = $this->getClient()->createPresignedRequest(
- $command,
- $expiresAt,
- );
-
- return (string)$request->getUri();
- } catch (Throwable $exception) {
- throw new FsException($exception->getMessage(), 0, $exception);
- }
- }
-
- /**
- * @inheritdoc
- */
- public function copyFile(string $path, string $newPath, $config = []): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->copyFile($path, $newPath);
- return;
- }
-
- try {
- $this->filesystem()->copy(
- $path,
- $newPath,
- $this->addFileMetadataToConfig($config),
- );
- } catch (FilesystemException|UnableToCopyFile $exception) {
- throw new FsException($exception->getMessage(), 0, $exception);
- }
- }
-
- /**
- * @inheritdoc
- */
- public function renameFile(string $path, string $newPath, $config = []): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->renameFile($path, $newPath);
- return;
- }
-
- try {
- $this->filesystem()->move(
- $path,
- $newPath,
- $this->addFileMetadataToConfig($config),
- );
- } catch (FilesystemException|UnableToMoveFile $exception) {
- throw new FsException($exception->getMessage(), 0, $exception);
- }
-
- $this->invalidateCdnPath($path);
- }
-
- /**
- * @inheritdoc
- */
- public function createDirectory(string $path, array $config = []): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->createDirectory($path, $config);
- return;
- }
-
- parent::createDirectory(
- $path,
- $this->addFileMetadataToConfig($config),
- );
- }
-
- /**
- * @inheritDoc
- */
- public function getFileList(string $directory = '', bool $recursive = true): Generator
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->getFileList($directory, $recursive);
- }
-
- return parent::getFileList($directory, $recursive);
- }
-
- /**
- * @inheritDoc
- */
- public function getFileSize(string $uri): int
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->getFileSize($uri);
- }
-
- return parent::getFileSize($uri);
- }
-
- /**
- * @inheritDoc
- */
- public function getDateModified(string $uri): int
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->getDateModified($uri);
- }
-
- return parent::getDateModified($uri);
- }
-
-
- /**
- * @inheritDoc
- */
- public function write(string $path, string $contents, array $config = []): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->write($path, $contents, $config);
- return;
- }
-
- $this->invalidateCdnPath($path);
- parent::write(
- $path,
- $contents,
- $this->addFileMetadataToConfig($config),
- );
- }
-
- /**
- * @inheritDoc
- */
- public function read(string $path): string
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->read($path);
- }
-
- return parent::read($path);
- }
-
- /**
- * @inheritDoc
- */
- public function writeFileFromStream(string $path, $stream, array $config = []): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->writeFileFromStream($path, $stream, $config);
- return;
- }
-
- $this->invalidateCdnPath($path);
- parent::writeFileFromStream(
- $path,
- $stream,
- $this->addFileMetadataToConfig($config),
- );
- }
-
- /**
- * @inheritDoc
- */
- public function fileExists(string $path): bool
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->fileExists($path);
- }
-
- return parent::fileExists($path);
- }
-
- /**
- * @inheritDoc
- */
- public function deleteFile(string $path): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->deleteFile($path);
- return;
- }
-
- parent::deleteFile($path);
- }
-
- /**
- * @inheritDoc
- */
- public function getFileStream(string $uriPath)
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->getFileStream($uriPath);
- }
-
- return parent::getFileStream($uriPath);
- }
-
- /**
- * @inheritDoc
- */
- public function directoryExists(string $path): bool
- {
- if ($this->useLocalFs) {
- return $this->getLocalFs()->directoryExists($path);
- }
-
- return parent::directoryExists($path);
- }
-
- /**
- * @inheritDoc
- */
- public function deleteDirectory(string $path): void
- {
- if ($this->useLocalFs) {
- $this->getLocalFs()->deleteDirectory($path);
- return;
- }
-
- parent::deleteDirectory($path);
- }
-
- public function replaceMetadata(string $path, array $config = []): void
- {
- if ($this->useLocalFs) {
- return;
- }
-
- try {
- $this->filesystem()->copy(
- $path,
- $path,
- $this->addFileMetadataToConfig($config),
- );
- } catch (FilesystemException|UnableToCopyFile $exception) {
- throw new FsException($exception->getMessage(), 0, $exception);
- }
- }
-
- /**
- * S3 encodes path segments when generating presigned PUT urls, so we need to do the same.
- */
- public static function urlEncodePathSegments(string $path): string
- {
- return Collection::make(explode('/', $path))
- ->map(fn($segment) => rawurlencode($segment))
- ->implode('/');
- }
-}
diff --git a/src/fs/StorageFs.php b/src/fs/StorageFs.php
deleted file mode 100644
index 62f3c5e..0000000
--- a/src/fs/StorageFs.php
+++ /dev/null
@@ -1,13 +0,0 @@
-append('storage');
- }
-}
diff --git a/src/fs/TmpFs.php b/src/fs/TmpFs.php
deleted file mode 100644
index fff4bab..0000000
--- a/src/fs/TmpFs.php
+++ /dev/null
@@ -1,13 +0,0 @@
-append('tmp');
- }
-}
diff --git a/src/imagetransforms/ImageTransformBehavior.php b/src/imagetransforms/ImageTransformBehavior.php
deleted file mode 100644
index 345b392..0000000
--- a/src/imagetransforms/ImageTransformBehavior.php
+++ /dev/null
@@ -1,207 +0,0 @@
-|null
- */
- public ?int $blur = null;
-
- /**
- * @var array{color: string, width: int}|array{color: string, top: int, right: int, bottom: int, left: int}|null
- */
- public ?array $border = null;
-
- /**
- * @var float|null
- */
- public ?float $brightness = null;
-
- /**
- * @var 'fast'|null
- */
- public ?string $compression = null;
-
- /**
- * @var float|null
- */
- public ?float $contrast = null;
-
- /**
- * @var float|null
- */
- public ?float $dpr = null;
-
- /**
- * @var array{url: string, opacity?: float, repeat?: true|'x'|'y', top?: int, left?: int, bottom?: int, right?: int, width?: int, height?: int, fit?: 'scale-down'|'contain'|'cover'|'crop'|'pad'|'squeeze', gravity?: 'face'|'left'|'right'|'top'|'bottom'|'center'|'auto'|'entropy'|array{x?: float, y?: float, mode?: 'remainder'|'box-center'}, background?: string, rotate?: 0|90|180|270|360, segment?: 'foreground'}[]|null Draw overlays
- */
- public ?array $draw = null;
-
- /**
- * @var 'scale-down'|'contain'|'cover'|'crop'|'pad'|'squeeze'|null
- */
- public ?string $fit = null;
-
- /**
- * @var 'h'|'v'|'hv'|null
- */
- public ?string $flip = null;
-
- /**
- * @var float|null
- */
- public ?float $gamma = null;
-
- /**
- * @var 'auto'|'face'|'left'|'right'|'top'|'bottom'|array{x?: float, y?: float}|null
- */
- public string|array|null $gravity = null;
-
- /**
- * @var 'keep'|'copyright'|'none'|null
- */
- public ?string $metadata = null;
-
- /**
- * @var int|null
- */
- public ?int $rotate = null;
-
- /**
- * @var float|null
- */
- public ?float $saturation = null;
-
- /**
- * @var 'foreground'|null
- */
- public ?string $segment = null;
-
- /**
- * @var float|null
- */
- public ?float $sharpen = null;
-
- /**
- * @var 'border'|array{top?: int, bottom?: int, left?: int, right?: int, width?: int, height?: int, border?: bool|array{color?: string, tolerance?: int, keep?: int}}|null
- */
- public null|string|array $trim = null;
-
- public ?float $zoom = null;
-
- public function toOptions(array|string|null $gravity = null): array
- {
- $reflection = new \ReflectionClass($this);
-
- $options = Collection::make($reflection->getProperties(\ReflectionProperty::IS_PUBLIC))
- ->filter(fn($property) => $property->getDeclaringClass()->getName() === self::class)
- ->mapWithKeys(fn($property) => [$property->getName() => $property->getValue($this)])
- ->all();
-
- // Compute derived Cloudflare values from Craft's base transform settings,
- // without mutating the model (so the same instance can be safely reused).
- $options['format'] = $this->computeFormat();
- $options['fit'] = $this->computeFit();
- $options['background'] = $this->computeBackground();
- $options['gravity'] ??= $gravity ?? $this->computeGravity();
- $options['height'] = $this->owner->height;
- $options['width'] = $this->owner->width;
-
- return Collection::make($options)
- ->filter(fn($value) => $value !== null)
- ->all();
- }
-
- private function computeFormat(): ?string
- {
- if ($this->owner->format === 'jpg' && $this->owner->interlace === 'none') {
- return 'baseline-jpeg';
- }
-
- return match ($this->owner->format) {
- 'jpg' => 'jpeg',
- default => $this->owner->format,
- };
- }
-
- /**
- * @see https://developers.cloudflare.com/images/transform-images/transform-via-url/#fit
- */
- private function computeFit(): string
- {
- if ($this->fit !== null) {
- return $this->fit;
- }
-
- return match ($this->owner->mode) {
- 'fit' => $this->owner->upscale ? 'contain' : 'scale-down',
- 'stretch' => 'squeeze',
- 'letterbox' => 'pad',
- default => $this->owner->upscale ? 'cover' : 'crop',
- };
- }
-
- private function computeBackground(): ?string
- {
- if ($this->background !== null) {
- return $this->background;
- }
-
- return $this->owner->mode === 'letterbox'
- ? $this->owner->fill ?? '#FFFFFF'
- : null;
- }
-
- /**
- * @return array{x: float, y: float}|null|'face'
- */
- private function computeGravity(): array|null|string
- {
- if ($this->gravity !== null) {
- return $this->gravity;
- }
-
- if ($this->owner->position === 'center-center') {
- return null;
- }
-
- $parts = explode('-', $this->owner->position);
-
- try {
- $x = match ($parts[1] ?? null) {
- 'left' => 0,
- 'center' => 0.5,
- 'right' => 1,
- };
- $y = match ($parts[0] ?? null) {
- 'top' => 0,
- 'center' => 0.5,
- 'bottom' => 1,
- };
- } catch (\UnhandledMatchError $e) {
- Craft::warning("Invalid position value: `{$this->owner->position}`", __METHOD__);
- return null;
- }
-
- return [
- 'x' => $x,
- 'y' => $y,
- ];
- }
-}
diff --git a/src/imagetransforms/ImageTransformer.php b/src/imagetransforms/ImageTransformer.php
deleted file mode 100644
index f55066a..0000000
--- a/src/imagetransforms/ImageTransformer.php
+++ /dev/null
@@ -1,106 +0,0 @@
-version, '5.0', '>=')) {
- // @phpstan-ignore argument.type, arguments.count (Craft 5 compatibility)
- $assetUrl = Html::encodeSpaces(Assets::generateUrl($asset));
- } else {
- $fs = $asset->getVolume()->getTransformFs();
- // @phpstan-ignore argument.type, arguments.count (Craft 4 compatibility)
- $assetUrl = Html::encodeSpaces(Assets::generateUrl($fs, $asset));
- }
-
- $mimeType = $asset->getMimeType();
-
- if ($mimeType === 'image/gif' && !Craft::$app->getConfig()->getGeneral()->transformGifs) {
- throw new NotSupportedException('GIF files shouldn’t be transformed.');
- }
-
- if ($mimeType === 'image/svg+xml' && !Craft::$app->getConfig()->getGeneral()->transformSvgs) {
- throw new NotSupportedException('SVG files shouldn’t be transformed.');
- }
-
- $behavior = $imageTransform->getBehavior('cloud');
-
- if (!$behavior instanceof ImageTransformBehavior) {
- throw new \RuntimeException('Cloud image transform behavior is not attached.');
- }
-
- $gravity = $this->applyAssetFocalPointGravity($asset, $imageTransform);
-
- // @phpstan-ignore-next-line method.notFound
- $query = Query::fromVariable($imageTransform->toOptions($gravity));
- $uri = Modifier::wrap(Uri::new($assetUrl))
- ->mergeQuery($query)
- ->unwrap();
-
- return (string) $this->sign($uri);
- }
-
- public function invalidateAssetTransforms(Asset $asset): void
- {
- }
-
- protected function applyAssetFocalPointGravity(Asset $asset, ImageTransform $imageTransform): array|string|null
- {
- // @phpstan-ignore-next-line property.notFound
- if (!$asset->getHasFocalPoint() || isset($imageTransform->gravity)) {
- return null;
- }
-
- return $asset->getFocalPoint();
- }
-
- private function sign(UriInterface $uri): UriInterface
- {
- $data = "{$uri->getPath()}#?{$uri->getQuery()}";
-
- Craft::info("Signing transform: `{$data}`", __METHOD__);
-
- // https://developers.cloudflare.com/workers/examples/signing-requests
- $hash = hash_hmac(
- 'sha256',
- $data,
- Module::getInstance()->getConfig()->signingKey,
- true,
- );
-
- $signature = $this->base64UrlEncode($hash);
-
- return Modifier::wrap($uri)
- ->mergeQueryParameters([self::SIGNING_PARAM => $signature])
- ->unwrap();
- }
-
- private function base64UrlEncode(string $data): string
- {
- $base64Url = strtr(base64_encode($data), '+/', '-_');
-
- return rtrim($base64Url, '=');
- }
-}
diff --git a/src/queue/ReleasableQueueInterface.php b/src/queue/ReleasableQueueInterface.php
deleted file mode 100644
index a000d45..0000000
--- a/src/queue/ReleasableQueueInterface.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getDb()->onAfterTransaction(function() use ($message, $ttr, $delay) {
- return parent::pushMessage(
- $message,
- $ttr,
- $delay,
-
- // Priority is not supported by SQS
- null,
- );
- });
-
- // Return anything but null, as we don't have an SQS message ID yet.
- return '';
- }
-
- public function releaseAll(): void
- {
- $this->clear();
- }
-
- public function release(string $id): void
- {
- Craft::info('Releasing single jobs is not supported.');
- }
-}
diff --git a/src/queue/TestJob.php b/src/queue/TestJob.php
deleted file mode 100644
index 3148f63..0000000
--- a/src/queue/TestJob.php
+++ /dev/null
@@ -1,43 +0,0 @@
-seconds) {
- Console::stdout("Sleeping for {$this->seconds} seconds…");
- sleep($this->seconds);
- }
-
- if ($this->throw) {
- Console::stdout('Throwing exception…');
- throw new Exception($this->message);
- }
-
- Console::stdout('Test job completed.');
- }
-}
diff --git a/src/templates/fsSettings.html b/src/templates/fsSettings.html
deleted file mode 100644
index fefd472..0000000
--- a/src/templates/fsSettings.html
+++ /dev/null
@@ -1,116 +0,0 @@
-{% import "_includes/forms" as forms %}
-
-{{ forms.autosuggestField({
- label: "Subpath"|t('cloud'),
- instructions: "An optional subpath where assets should be stored within the filesystem."|t('cloud'),
- id: 'subpath',
- class: 'ltr',
- name: 'subpath',
- suggestEnvVars: true,
- suggestAliases: true,
- value: fs.subpath,
- errors: fs.getErrors('subpath'),
- placeholder: "path/to/subpath"|t('cloud'),
- tip: "This value will be appended to all paths and URLs for the filesystem."|t('cloud'),
-}) }}
-
-
- {{ forms.autosuggestField({
- label: "Base URL"|t('app'),
- instructions: "The base URL to the files in this filesystem."|t('app'),
- id: 'baseUrl',
- class: ['ltr'],
- name: 'baseUrl',
- suggestEnvVars: true,
- suggestAliases: true,
- value: (fs is defined ? fs.baseUrl : null),
- errors: (fs is defined ? fs.getErrors('baseUrl') : null),
- placeholder: "//example.com/path/to/folder",
- tip: 'Use **rewrite rules** to override the default `cdn.craft.cloud` URL.
Learn more'|t('cloud'),
- }) }}
-
-
-{% set cacheDurationInput %}
- {% set expires = fs.expires ? fs.expires|split(' ') : null %}
-
-
-
- {{ forms.text({
- id: 'expiresAmount',
- value: expires[0] ?? null,
- size: 2,
- class: 's3-expires-amount',
- name: 'expires[amount]',
- }) }}
-
- {{ forms.select({
- id: 'expiresPeriod',
- options: periods,
- value: expires[1] ?? null,
- class: 's3-expires-period',
- name: 'expires[period]',
- }) }}
-
-
-{% endset %}
-
-{{ forms.field({
- label: "Cache Duration"|t,
- instructions: 'The Cache-Control duration set on assets in this filesystem.',
- id: 'cacheDuration',
- tip: 'Caching files for one year is recommended for optimal performance. Learn more',
-}, cacheDurationInput) }}
-
-
-
-Local Filesystem
-
-
-
-
- When running outside of Craft Cloud, this filesystem will fall back to your local disk for storage.
- {{ tag('a', {
- href: 'https://craftcms.com/knowledge-base/cloud-assets',
- class: 'go',
- text: 'Learn more'|t('app'),
- }) }}
-
-
-
-
-
- {{ forms.autosuggestField({
- label: "Base URL"|t('app'),
- instructions: "The base URL to the files in this filesystem."|t('app'),
- id: 'local-fs-url',
- class: ['ltr', 'fs-url'],
- name: 'localFsUrl',
- suggestEnvVars: true,
- suggestAliases: true,
- value: fs.localFsUrl ?? null,
- errors: fs.getErrors('localFsUrl') ?? null,
- required: true,
- placeholder: "@web/path/to/folder",
- }) }}
-
-
-{{ forms.autosuggestField({
- label: "Base Path"|t('app'),
- instructions: "The base folder path that should be used as the root of the filesystem."|t('app'),
- id: 'local-fs-path',
- class: 'ltr',
- name: 'localFsPath',
- suggestEnvVars: true,
- suggestAliases: true,
- value: fs.localFsPath ?? null,
- errors: fs.getErrors('localFsPath') ?? null,
- required: true,
- placeholder: "@webroot/path/to/folder"|t('app'),
-}) }}
-
-{% js %}
- const $urlField = $('#types-craft-cloud-fs-AssetsFs-has-urls')
- .addClass('fieldtoggle')
- .data({target: '#types-craft-cloud-fs-AssetsFs-local-fs-url-field, #types-craft-cloud-fs-AssetsFs-cacheDuration-field, #types-craft-cloud-fs-AssetsFs-base-url-field-container'});
- new Craft.FieldToggle($urlField);
-{% endjs %}
diff --git a/src/twig/CloudVariable.php b/src/twig/CloudVariable.php
deleted file mode 100644
index 70de20e..0000000
--- a/src/twig/CloudVariable.php
+++ /dev/null
@@ -1,39 +0,0 @@
-getEsi()->prepareResponse();
- }
-
- public function esi(string $template, $variables = []): Markup
- {
- return Template::raw(
- Module::getInstance()->getEsi()->render($template, $variables)
- );
- }
-}
diff --git a/src/twig/TwigExtension.php b/src/twig/TwigExtension.php
deleted file mode 100644
index 740764a..0000000
--- a/src/twig/TwigExtension.php
+++ /dev/null
@@ -1,47 +0,0 @@
- new CloudVariable(),
- 'isCraftCloud' => $this->isCraftCloud(),
- ];
- }
-
- public function getFunctions(): array
- {
- return [
- new TwigFunction('artifactUrl', [$this, 'artifactUrl']),
- ];
- }
-
- /**
- * @deprecated in 1.4.8
- */
- public function isCraftCloud(): bool
- {
- return Helper::isCraftCloud();
- }
-
- /**
- * @deprecated in 1.4.8
- */
- public function artifactUrl(string $path): string
- {
- Craft::$app->getDeprecator()->log(
- __METHOD__,
- 'The `artifactUrl` Twig function has been deprecated. Use `cloud.artifactUrl` instead.',
- );
- return Helper::artifactUrl($path);
- }
-}
diff --git a/src/web/AssetManager.php b/src/web/AssetManager.php
deleted file mode 100644
index 80aab21..0000000
--- a/src/web/AssetManager.php
+++ /dev/null
@@ -1,61 +0,0 @@
-preparePaths();
- parent::init();
- }
-
- public function publish($path, $options = []): array
- {
- $this->preparePaths();
- return parent::publish($path, $options);
- }
-
- protected function preparePaths(): void
- {
- $this->basePath = Craft::getAlias($this->basePath);
-
- if (!Helper::isCraftCloud()) {
- FileHelper::createDirectory($this->basePath);
- }
-
- $this->baseUrl = Modifier::wrap((new CpResourcesFs())->createUrl())->removeTrailingSlash();
- }
-
- protected function hash($path): string
- {
- $dir = is_file($path) ? dirname($path) : $path;
- $rebrandPath = Craft::$app->getPath()->getRebrandPath();
-
- // Account for rebrand, as it lives in @storage by default,
- // which will be different in Cloud runtime vs. Cloud build.
- if (str_starts_with($dir, $rebrandPath)) {
- return HierarchicalPath::new(StringHelper::removeLeft($dir, $rebrandPath))
- ->prepend('rebrand')
- ->withoutTrailingSlash()
- ->toString();
- }
-
- $pathFromRoot = StringHelper::removeLeft($dir, Craft::getAlias('@root/'));
-
- return FileHelper::sanitizeFilename(
- preg_replace('/\/|@/', '-', $pathFromRoot),
- ['asciiOnly' => true]
- );
- }
-}
diff --git a/src/web/ResponseEventHandler.php b/src/web/ResponseEventHandler.php
deleted file mode 100644
index a1ceca3..0000000
--- a/src/web/ResponseEventHandler.php
+++ /dev/null
@@ -1,160 +0,0 @@
-response = Craft::$app->getResponse();
- }
-
- public function handle(): void
- {
- Event::on(
- Response::class,
- YiiResponse::EVENT_AFTER_PREPARE,
- fn(Event $event) => $this->afterPrepare($event),
- );
- }
-
- private function afterPrepare(Event $event): void
- {
- if (Module::getInstance()->getConfig()->getDevMode()) {
- $this->addDevModeHeader();
- }
-
- $this->normalizeHeaders();
-
- if (Module::getInstance()->getConfig()->gzipResponse) {
- $this->gzipResponse();
- }
-
- if (
- $this->response->stream &&
- !str_starts_with($this->response->getContentType(), 'text/')
- ) {
- $this->serveBinaryFromS3();
- }
- }
-
- private function gzipResponse(): void
- {
- $accepts = preg_split(
- '/\s*\,\s*/',
- Craft::$app->getRequest()->getHeaders()->get('Accept-Encoding') ?? ''
- );
-
- if (Collection::make($accepts)->doesntContain('gzip') || $this->response->content === null) {
- return;
- }
-
- $this->response->content = gzencode($this->response->content, 9);
- $this->response->getHeaders()->set('Content-Encoding', 'gzip');
- }
-
- /**
- * @throws ServerErrorHttpException
- */
- private function serveBinaryFromS3(): void
- {
- $stream = $this->response->stream[0] ?? null;
-
- if (!$stream) {
- throw new ServerErrorHttpException('Invalid stream in response.');
- }
-
- $path = uniqid('binary', true);
-
- /** @var TmpFs $fs */
- $fs = Craft::createObject([
- 'class' => TmpFs::class,
- ]);
-
- // TODO: set expiry
- $fs->writeFileFromStream($path, $stream);
-
- // TODO: use \League\Flysystem\AwsS3V3\AwsS3V3Adapter::temporaryUrl?
- $cmd = $fs->getClient()->getCommand('GetObject', [
- 'Bucket' => $fs->getBucketName(),
- 'Key' => $fs->createBucketPath($path)->toString(),
- 'ResponseContentDisposition' => $this->response->getHeaders()->get('content-disposition'),
- ]);
-
- // TODO: expiry config
- $s3Request = $fs->getClient()->createPresignedRequest($cmd, '+20 minutes');
- $url = (string) $s3Request->getUri();
-
- // Clear response so stream is reset and we don't recursively call this method.
- $this->response->clear();
-
- // Don't cache the redirect, as its validity is short-lived.
- $this->response->setNoCacheHeaders();
-
- $this->response->redirect($url);
-
- // Ensure we don't recursively call send()
- // @see https://github.com/craftcms/cms/pull/15014
- Craft::$app->end();
- }
-
- private function normalizeHeaders(): void
- {
- Collection::make($this->response->getHeaders())
- ->each(function(array $values, string $name) {
- if (HeaderEnum::SET_COOKIE->matches($name)) {
- return;
- }
-
- $this->response->getHeaders()->set(
- $name,
- $this->joinHeaderValues($values),
- );
- });
- }
-
- private function joinHeaderValues(array $values): string
- {
- return Collection::make($values)
- ->filter()
- ->reduce(function($result, $value) {
- $newResult = $result === '' ? $value : $result . ',' . $value;
-
- if (StringHelper::byteLength($newResult) > self::MAX_HEADER_LENGTH) {
- Craft::warning(
- sprintf("Header value exceeds the maximum length of %s bytes; truncating response.", self::MAX_HEADER_LENGTH),
- __METHOD__,
- );
-
- return $result;
- }
-
- return $newResult;
- }, '');
- }
-
- private function addDevModeHeader(): void
- {
- $this->response->getHeaders()->set(HeaderEnum::DEV_MODE->value, '1');
- }
-}
diff --git a/src/web/assets/rebrand/RebrandAsset.php b/src/web/assets/rebrand/RebrandAsset.php
deleted file mode 100644
index 41c020a..0000000
--- a/src/web/assets/rebrand/RebrandAsset.php
+++ /dev/null
@@ -1,14 +0,0 @@
-sourcePath = Craft::$app->getPath()->getRebrandPath();
- parent::init();
- }
-}
diff --git a/src/web/assets/uploader/UploaderAsset.php b/src/web/assets/uploader/UploaderAsset.php
deleted file mode 100644
index 1ea98d4..0000000
--- a/src/web/assets/uploader/UploaderAsset.php
+++ /dev/null
@@ -1,42 +0,0 @@
-getConfig()->useAssetCdn) {
- return;
- }
-
- parent::registerAssetFiles($view);
-
- $maxFileSize = ConfigHelper::sizeInBytes(Craft::$app->getConfig()->getGeneral()->maxUploadFileSize);
- $js = <<registerJs($js, \yii\web\View::POS_END);
- }
-}
diff --git a/src/web/assets/uploader/dist/Uploader.js b/src/web/assets/uploader/dist/Uploader.js
deleted file mode 100644
index da7ed06..0000000
--- a/src/web/assets/uploader/dist/Uploader.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! For license information please see Uploader.js.LICENSE.txt */
-!function(){function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(){"use strict";e=function(){return n};var r,n={},o=Object.prototype,a=o.hasOwnProperty,i=Object.defineProperty||function(t,e,r){t[e]=r.value},s="function"==typeof Symbol?Symbol:{},l=s.iterator||"@@iterator",u=s.asyncIterator||"@@asyncIterator",c=s.toStringTag||"@@toStringTag";function f(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{f({},"")}catch(r){f=function(t,e,r){return t[e]=r}}function h(t,e,r,n){var o=e&&e.prototype instanceof b?e:b,a=Object.create(o.prototype),s=new I(n||[]);return i(a,"_invoke",{value:O(t,r,s)}),a}function d(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}n.wrap=h;var p="suspendedStart",v="suspendedYield",y="executing",g="completed",m={};function b(){}function w(){}function x(){}var E={};f(E,l,(function(){return this}));var _=Object.getPrototypeOf,L=_&&_(_(U([])));L&&L!==o&&a.call(L,l)&&(E=L);var C=x.prototype=b.prototype=Object.create(E);function j(t){["next","throw","return"].forEach((function(e){f(t,e,(function(t){return this._invoke(e,t)}))}))}function S(e,r){function n(o,i,s,l){var u=d(e[o],e,i);if("throw"!==u.type){var c=u.arg,f=c.value;return f&&"object"==t(f)&&a.call(f,"__await")?r.resolve(f.__await).then((function(t){n("next",t,s,l)}),(function(t){n("throw",t,s,l)})):r.resolve(f).then((function(t){c.value=t,s(c)}),(function(t){return n("throw",t,s,l)}))}l(u.arg)}var o;i(this,"_invoke",{value:function(t,e){function a(){return new r((function(r,o){n(t,e,r,o)}))}return o=o?o.then(a,a):a()}})}function O(t,e,n){var o=p;return function(a,i){if(o===y)throw Error("Generator is already running");if(o===g){if("throw"===a)throw i;return{value:r,done:!0}}for(n.method=a,n.arg=i;;){var s=n.delegate;if(s){var l=F(s,n);if(l){if(l===m)continue;return l}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===p)throw o=g,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=y;var u=d(t,e,n);if("normal"===u.type){if(o=n.done?g:v,u.arg===m)continue;return{value:u.arg,done:n.done}}"throw"===u.type&&(o=g,n.method="throw",n.arg=u.arg)}}}function F(t,e){var n=e.method,o=t.iterator[n];if(o===r)return e.delegate=null,"throw"===n&&t.iterator.return&&(e.method="return",e.arg=r,F(t,e),"throw"===e.method)||"return"!==n&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+n+"' method")),m;var a=d(o,t.iterator,e.arg);if("throw"===a.type)return e.method="throw",e.arg=a.arg,e.delegate=null,m;var i=a.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=r),e.delegate=null,m):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,m)}function A(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function k(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function I(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(A,this),this.reset(!0)}function U(e){if(e||""===e){var n=e[l];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,i=function t(){for(;++o=0;--o){var i=this.tryEntries[o],s=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var l=a.call(i,"catchLoc"),u=a.call(i,"finallyLoc");if(l&&u){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&a.call(n,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),k(r),m}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;k(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:U(t),resultName:e,nextLoc:n},"next"===this.method&&(this.arg=r),m}},n}function r(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!r){if(Array.isArray(t)||(r=i(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,l=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return s=t.done,t},e:function(t){l=!0,a=t},f:function(){try{s||null==r.return||r.return()}finally{if(l)throw a}}}}function n(t,e,r,n,o,a,i){try{var s=t[a](i),l=s.value}catch(t){return void r(t)}s.done?e(l):Promise.resolve(l).then(n,o)}function o(t){return function(){var e=this,r=arguments;return new Promise((function(o,a){var i=t.apply(e,r);function s(t){n(i,o,a,s,l,"next",t)}function l(t){n(i,o,a,s,l,"throw",t)}s(void 0)}))}}function a(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var n,o,a,i,s=[],l=!0,u=!1;try{if(a=(r=r.call(t)).next,0===e){if(Object(r)!==r)return;l=!1}else for(;!(l=(n=a.call(r)).done)&&(s.push(n.value),s.length!==e);l=!0);}catch(t){u=!0,o=t}finally{try{if(!l&&null!=r.return&&(i=r.return(),Object(i)!==i))return}finally{if(u)throw o}}return s}}(t,e)||i(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){if(t){if("string"==typeof t)return s(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(t,e):void 0}}function s(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);rf.settings.maxFileSize&&(f._rejectedFiles.size.push("“"+t.name+"”"),r=!1),r&&"function"==typeof f.settings.canAddMoreFiles&&!f.settings.canAddMoreFiles(f._validFileCounter)&&(f._rejectedFiles.limit.push("“"+t.name+"”"),r=!1),r&&(f._totalBytes+=t.size,f._validFileCounter++,f._inProgressCounter++),r})),this.processErrorMessages(),!(this._validFileCounter>0)){t.next=23;break}this.element.dispatchEvent(new Event("fileuploadstart")),l=r(a),t.prev=6,l.s();case 8:if((u=l.n()).done){t.next=15;break}return c=u.value,t.next=12,this.uploadFile(c);case 12:this._inProgressCounter--;case 13:t.next=8;break;case 15:t.next=20;break;case 17:t.prev=17,t.t0=t.catch(6),l.e(t.t0);case 20:return t.prev=20,l.f(),t.finish(20);case 23:this._validFileCounter=0,this._totalBytes=0,this._uploadedBytes=0,this._lastUploadedBytes=0,this._inProgressCounter=0;case 28:case"end":return t.stop()}var e}),t,this,[[6,17,20,23]])}))),function(t){return c.apply(this,arguments)}),uploadFile:(u=o(e().mark((function t(r){var n,o,a,i,s,l,u=this;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n=Object.assign({},this.formData,{filename:r.name}),t.prev=1,t.next=4,Craft.sendActionRequest("POST","cloud/assets/get-upload-url",{data:n});case 4:return o=t.sent,t.next=7,axios.put(o.data.url,r,{headers:{"Content-Type":r.type},onUploadProgress:function(t){u._uploadedBytes=u._uploadedBytes+t.loaded-u._lastUploadedBytes,u._lastUploadedBytes=t.loaded,u.element.dispatchEvent(new CustomEvent("fileuploadprogressall",{detail:{loaded:u._uploadedBytes,total:u._totalBytes}}))}});case 7:return Object.assign(n,o.data,{size:r.size,lastModified:r.lastModified}),t.next=10,this.getImage(r);case 10:return(a=t.sent)&&(i=a.width,s=a.height,Object.assign(n,{width:i,height:s})),t.next=14,axios.post(this.settings.url,n);case 14:o=t.sent,this.element.dispatchEvent(new CustomEvent("fileuploaddone",{detail:o.data})),t.next=21;break;case 18:t.prev=18,t.t0=t.catch(1),this.element.dispatchEvent(new CustomEvent("fileuploadfail",{detail:{message:null===t.t0||void 0===t.t0||null===(l=t.t0.response)||void 0===l||null===(l=l.data)||void 0===l?void 0:l.message,filename:r.name}}));case 21:return t.prev=21,this._lastUploadedBytes=0,this.element.dispatchEvent(new Event("fileuploadalways")),t.finish(21);case 25:case"end":return t.stop()}}),t,this,[[1,18,21,25]])}))),function(t){return u.apply(this,arguments)}),handleChange:function(t){this.uploadFiles(t.target.files),this.$fileInput.val("")},getImage:(l=o(e().mark((function t(r){var n;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n=new Image,t.prev=1,n.src=URL.createObjectURL(r),t.next=5,n.decode();case 5:URL.revokeObjectURL(n.src),t.next=11;break;case 8:return t.prev=8,t.t0=t.catch(1),t.abrupt("return",null);case 11:return t.abrupt("return",n);case 12:case"end":return t.stop()}}),t,null,[[1,8]])}))),function(t){return l.apply(this,arguments)}),destroy:function(){var t=this;this.$fileInput.off("change",this._handleChange),this.$dropZone.off("dragover drop dragenter dragleave"),Object.entries(this.settings.events).forEach((function(e){var r=a(e,2),n=r[0],o=r[1];t.element.removeEventListener(n,o)}))}},{defaults:{maxFileSize:null,createAction:"cloud/assets/create-asset",replaceAction:"cloud/assets/replace-file"}}),Craft.registerUploaderClass("craft\\cloud\\fs\\AssetsFs",Craft.CloudUploader)}();
-//# sourceMappingURL=Uploader.js.map
\ No newline at end of file
diff --git a/src/web/assets/uploader/dist/Uploader.js.LICENSE.txt b/src/web/assets/uploader/dist/Uploader.js.LICENSE.txt
deleted file mode 100644
index ae386fb..0000000
--- a/src/web/assets/uploader/dist/Uploader.js.LICENSE.txt
+++ /dev/null
@@ -1 +0,0 @@
-/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
diff --git a/src/web/assets/uploader/dist/Uploader.js.map b/src/web/assets/uploader/dist/Uploader.js.map
deleted file mode 100644
index c9fe613..0000000
--- a/src/web/assets/uploader/dist/Uploader.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"Uploader.js","mappings":"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","sources":["webpack:///./Uploader.js"],"sourcesContent":["/** global: Craft */\n/** global: Garnish */\n\nCraft.CloudUploader = Craft.BaseUploader.extend(\n {\n element: null,\n $fileInput: null,\n _totalBytes: 0,\n _uploadedBytes: 0,\n _lastUploadedBytes: 0,\n _validFileCounter: 0,\n _handleChange: null,\n\n init: function ($element, settings) {\n settings = $.extend({}, Craft.CloudUploader.defaults, settings);\n this.base($element, settings);\n this.element = $element[0];\n this.$dropZone = settings.dropZone;\n this._handleChange = this.handleChange.bind(this);\n this.$fileInput.on('change', this._handleChange);\n\n Object.entries(this.settings.events).forEach(([name, handler]) => {\n this.element.addEventListener(name, handler);\n });\n\n if (this.allowedKinds && !this._extensionList) {\n this._createExtensionList();\n }\n\n if (this.$dropZone) {\n this.$dropZone.on({\n dragover: (event) => {\n if (this.handleDragEvent(event)) {\n event.originalEvent.dataTransfer.dropEffect = 'copy';\n }\n },\n drop: (event) => {\n if (this.handleDragEvent(event)) {\n this.uploadFiles(event.originalEvent.dataTransfer.files);\n }\n },\n dragenter: this.handleDragEvent,\n dragleave: this.handleDragEvent,\n });\n }\n },\n\n handleDragEvent: function (event) {\n if (!event?.originalEvent?.dataTransfer?.files) {\n return false;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n return true;\n },\n\n uploadFiles: async function (FileList) {\n const files = [...FileList];\n const validFiles = files.filter((file) => {\n let valid = true;\n\n if (this._extensionList?.length) {\n const matches = file.name.match(/\\.([a-z0-4_]+)$/i);\n const fileExtension = matches[1];\n\n if (!this._extensionList.includes(fileExtension.toLowerCase())) {\n this._rejectedFiles.type.push('“' + file.name + '”');\n valid = false;\n }\n }\n\n if (\n this.settings.maxFileSize &&\n file.size > this.settings.maxFileSize\n ) {\n this._rejectedFiles.size.push('“' + file.name + '”');\n valid = false;\n }\n\n if (\n valid &&\n typeof this.settings.canAddMoreFiles === 'function' &&\n !this.settings.canAddMoreFiles(this._validFileCounter)\n ) {\n this._rejectedFiles.limit.push('“' + file.name + '”');\n valid = false;\n }\n\n if (valid) {\n this._totalBytes += file.size;\n this._validFileCounter++;\n this._inProgressCounter++;\n }\n\n return valid;\n });\n\n this.processErrorMessages();\n\n if (this._validFileCounter > 0) {\n this.element.dispatchEvent(new Event('fileuploadstart'));\n\n for (const file of validFiles) {\n await this.uploadFile(file);\n this._inProgressCounter--;\n }\n }\n\n this._validFileCounter = 0;\n this._totalBytes = 0;\n this._uploadedBytes = 0;\n this._lastUploadedBytes = 0;\n this._inProgressCounter = 0;\n },\n\n uploadFile: async function (file) {\n const formData = Object.assign({}, this.formData, {\n filename: file.name,\n });\n\n try {\n let response = await Craft.sendActionRequest(\n 'POST',\n 'cloud/assets/get-upload-url',\n {\n data: formData,\n },\n );\n\n await axios.put(response.data.url, file, {\n headers: {\n 'Content-Type': file.type,\n },\n onUploadProgress: (axiosProgressEvent) => {\n this._uploadedBytes =\n this._uploadedBytes +\n axiosProgressEvent.loaded -\n this._lastUploadedBytes;\n this._lastUploadedBytes = axiosProgressEvent.loaded;\n\n this.element.dispatchEvent(\n new CustomEvent('fileuploadprogressall', {\n detail: {\n loaded: this._uploadedBytes,\n total: this._totalBytes,\n },\n }),\n );\n },\n });\n\n Object.assign(formData, response.data, {\n size: file.size,\n lastModified: file.lastModified,\n });\n\n const image = await this.getImage(file);\n\n if (image) {\n const {width, height} = image;\n Object.assign(formData, {width, height});\n }\n\n response = await axios.post(this.settings.url, formData);\n this.element.dispatchEvent(\n new CustomEvent('fileuploaddone', {detail: response.data}),\n );\n } catch (error) {\n this.element.dispatchEvent(\n new CustomEvent('fileuploadfail', {\n detail: {\n message: error?.response?.data?.message,\n filename: file.name,\n },\n }),\n );\n } finally {\n this._lastUploadedBytes = 0;\n this.element.dispatchEvent(new Event('fileuploadalways'));\n }\n },\n\n handleChange: function (event) {\n this.uploadFiles(event.target.files);\n this.$fileInput.val('');\n },\n\n getImage: async function (file) {\n const image = new Image();\n\n try {\n image.src = URL.createObjectURL(file);\n await image.decode();\n URL.revokeObjectURL(image.src);\n } catch {\n return null;\n }\n\n return image;\n },\n\n destroy: function () {\n this.$fileInput.off('change', this._handleChange);\n this.$dropZone.off('dragover drop dragenter dragleave');\n\n Object.entries(this.settings.events).forEach(([name, handler]) => {\n this.element.removeEventListener(name, handler);\n });\n },\n },\n {\n defaults: {\n maxFileSize: null,\n createAction: 'cloud/assets/create-asset',\n replaceAction: 'cloud/assets/replace-file',\n },\n },\n);\n\n// Register it!\nCraft.registerUploaderClass('craft\\\\cloud\\\\fs\\\\AssetsFs', Craft.CloudUploader);\n"],"names":["_regeneratorRuntime","e","t","r","Object","prototype","n","hasOwnProperty","o","defineProperty","value","i","Symbol","a","iterator","c","asyncIterator","u","toStringTag","define","enumerable","configurable","writable","wrap","Generator","create","Context","makeInvokeMethod","tryCatch","type","arg","call","h","l","f","s","y","GeneratorFunction","GeneratorFunctionPrototype","p","d","getPrototypeOf","v","values","g","defineIteratorMethods","forEach","_invoke","AsyncIterator","invoke","_typeof","resolve","__await","then","callInvokeWithMethodAndArg","Error","done","method","delegate","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","TypeError","resultName","next","nextLoc","pushTryEntry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","isNaN","length","displayName","isGeneratorFunction","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","keys","reverse","pop","prev","charAt","slice","stop","rval","handle","complete","finish","delegateYield","_createForOfIteratorHelper","Array","isArray","_unsupportedIterableToArray","_n","F","asyncGeneratorStep","_asyncToGenerator","arguments","apply","_next","_throw","_slicedToArray","_arrayWithHoles","_iterableToArrayLimit","_nonIterableRest","_arrayLikeToArray","toString","from","test","_getImage","_uploadFile","_uploadFiles","Craft","CloudUploader","BaseUploader","extend","element","$fileInput","_totalBytes","_uploadedBytes","_lastUploadedBytes","_validFileCounter","_handleChange","init","$element","settings","_this","$","defaults","this","base","$dropZone","dropZone","handleChange","bind","on","entries","events","_ref","_ref2","handler","addEventListener","allowedKinds","_extensionList","_createExtensionList","dragover","event","handleDragEvent","originalEvent","dataTransfer","dropEffect","drop","uploadFiles","files","dragenter","dragleave","_event$originalEvent","preventDefault","stopPropagation","_callee","FileList","validFiles","_iterator","_step","file","_this2","_context","_arrayWithoutHoles","_iterableToArray","_nonIterableSpread","filter","_this2$_extensionList","valid","fileExtension","match","includes","toLowerCase","_rejectedFiles","maxFileSize","size","canAddMoreFiles","limit","_inProgressCounter","processErrorMessages","dispatchEvent","Event","uploadFile","t0","_x","_callee2","formData","response","image","width","height","_error$response","_this3","_context2","assign","filename","sendActionRequest","data","axios","put","url","headers","onUploadProgress","axiosProgressEvent","loaded","CustomEvent","detail","total","lastModified","getImage","post","message","_x2","target","val","_callee3","_context3","Image","src","URL","createObjectURL","decode","revokeObjectURL","_x3","destroy","_this4","off","_ref3","_ref4","removeEventListener","createAction","replaceAction","registerUploaderClass"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/web/assets/uploader/src/Uploader.js b/src/web/assets/uploader/src/Uploader.js
deleted file mode 100644
index 29ee718..0000000
--- a/src/web/assets/uploader/src/Uploader.js
+++ /dev/null
@@ -1,223 +0,0 @@
-/** global: Craft */
-/** global: Garnish */
-
-Craft.CloudUploader = Craft.BaseUploader.extend(
- {
- element: null,
- $fileInput: null,
- _totalBytes: 0,
- _uploadedBytes: 0,
- _lastUploadedBytes: 0,
- _validFileCounter: 0,
- _handleChange: null,
-
- init: function ($element, settings) {
- settings = $.extend({}, Craft.CloudUploader.defaults, settings);
- this.base($element, settings);
- this.element = $element[0];
- this.$dropZone = settings.dropZone;
- this._handleChange = this.handleChange.bind(this);
- this.$fileInput.on('change', this._handleChange);
-
- Object.entries(this.settings.events).forEach(([name, handler]) => {
- this.element.addEventListener(name, handler);
- });
-
- if (this.allowedKinds && !this._extensionList) {
- this._createExtensionList();
- }
-
- if (this.$dropZone) {
- this.$dropZone.on({
- dragover: (event) => {
- if (this.handleDragEvent(event)) {
- event.originalEvent.dataTransfer.dropEffect = 'copy';
- }
- },
- drop: (event) => {
- if (this.handleDragEvent(event)) {
- this.uploadFiles(event.originalEvent.dataTransfer.files);
- }
- },
- dragenter: this.handleDragEvent,
- dragleave: this.handleDragEvent,
- });
- }
- },
-
- handleDragEvent: function (event) {
- if (!event?.originalEvent?.dataTransfer?.files) {
- return false;
- }
-
- event.preventDefault();
- event.stopPropagation();
-
- return true;
- },
-
- uploadFiles: async function (FileList) {
- const files = [...FileList];
- const validFiles = files.filter((file) => {
- let valid = true;
-
- if (this._extensionList?.length) {
- const matches = file.name.match(/\.([a-z0-4_]+)$/i);
- const fileExtension = matches[1];
-
- if (!this._extensionList.includes(fileExtension.toLowerCase())) {
- this._rejectedFiles.type.push('“' + file.name + '”');
- valid = false;
- }
- }
-
- if (
- this.settings.maxFileSize &&
- file.size > this.settings.maxFileSize
- ) {
- this._rejectedFiles.size.push('“' + file.name + '”');
- valid = false;
- }
-
- if (
- valid &&
- typeof this.settings.canAddMoreFiles === 'function' &&
- !this.settings.canAddMoreFiles(this._validFileCounter)
- ) {
- this._rejectedFiles.limit.push('“' + file.name + '”');
- valid = false;
- }
-
- if (valid) {
- this._totalBytes += file.size;
- this._validFileCounter++;
- this._inProgressCounter++;
- }
-
- return valid;
- });
-
- this.processErrorMessages();
-
- if (this._validFileCounter > 0) {
- this.element.dispatchEvent(new Event('fileuploadstart'));
-
- for (const file of validFiles) {
- await this.uploadFile(file);
- this._inProgressCounter--;
- }
- }
-
- this._validFileCounter = 0;
- this._totalBytes = 0;
- this._uploadedBytes = 0;
- this._lastUploadedBytes = 0;
- this._inProgressCounter = 0;
- },
-
- uploadFile: async function (file) {
- const formData = Object.assign({}, this.formData, {
- filename: file.name,
- });
-
- try {
- let response = await Craft.sendActionRequest(
- 'POST',
- 'cloud/assets/get-upload-url',
- {
- data: formData,
- },
- );
-
- await axios.put(response.data.url, file, {
- headers: {
- 'Content-Type': file.type,
- },
- onUploadProgress: (axiosProgressEvent) => {
- this._uploadedBytes =
- this._uploadedBytes +
- axiosProgressEvent.loaded -
- this._lastUploadedBytes;
- this._lastUploadedBytes = axiosProgressEvent.loaded;
-
- this.element.dispatchEvent(
- new CustomEvent('fileuploadprogressall', {
- detail: {
- loaded: this._uploadedBytes,
- total: this._totalBytes,
- },
- }),
- );
- },
- });
-
- Object.assign(formData, response.data, {
- size: file.size,
- lastModified: file.lastModified,
- });
-
- const image = await this.getImage(file);
-
- if (image) {
- const {width, height} = image;
- Object.assign(formData, {width, height});
- }
-
- response = await axios.post(this.settings.url, formData);
- this.element.dispatchEvent(
- new CustomEvent('fileuploaddone', {detail: response.data}),
- );
- } catch (error) {
- this.element.dispatchEvent(
- new CustomEvent('fileuploadfail', {
- detail: {
- message: error?.response?.data?.message,
- filename: file.name,
- },
- }),
- );
- } finally {
- this._lastUploadedBytes = 0;
- this.element.dispatchEvent(new Event('fileuploadalways'));
- }
- },
-
- handleChange: function (event) {
- this.uploadFiles(event.target.files);
- this.$fileInput.val('');
- },
-
- getImage: async function (file) {
- const image = new Image();
-
- try {
- image.src = URL.createObjectURL(file);
- await image.decode();
- URL.revokeObjectURL(image.src);
- } catch {
- return null;
- }
-
- return image;
- },
-
- destroy: function () {
- this.$fileInput.off('change', this._handleChange);
- this.$dropZone.off('dragover drop dragenter dragleave');
-
- Object.entries(this.settings.events).forEach(([name, handler]) => {
- this.element.removeEventListener(name, handler);
- });
- },
- },
- {
- defaults: {
- maxFileSize: null,
- createAction: 'cloud/assets/create-asset',
- replaceAction: 'cloud/assets/replace-file',
- },
- },
-);
-
-// Register it!
-Craft.registerUploaderClass('craft\\cloud\\fs\\AssetsFs', Craft.CloudUploader);
diff --git a/src/web/assets/uploader/webpack.config.js b/src/web/assets/uploader/webpack.config.js
deleted file mode 100644
index db964d3..0000000
--- a/src/web/assets/uploader/webpack.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const {getConfig} = require('@craftcms/webpack');
-const CopyWebpackPlugin = require('copy-webpack-plugin');
-
-module.exports = getConfig({
- context: __dirname,
- config: {
- entry: {
- Uploader: './Uploader.js',
- },
- },
-});
diff --git a/tests/.env.example b/tests/.env.example
deleted file mode 100644
index eb93f27..0000000
--- a/tests/.env.example
+++ /dev/null
@@ -1,8 +0,0 @@
-CRAFT_DB_SERVER="127.0.0.1"
-CRAFT_DB_PORT="33066"
-CRAFT_DB_DATABASE="db"
-CRAFT_DB_USER="db"
-CRAFT_DB_PASSWORD="db"
-CRAFT_DB_DRIVER="mysql"
-SECURITY_KEY=""
-PRIMARY_SITE_URL="http://my-project.test/" # Set this to the `entryUrl` param in the `codeception.yml` file.
diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php
deleted file mode 100644
index 13cb439..0000000
--- a/tests/_bootstrap.php
+++ /dev/null
@@ -1,25 +0,0 @@
-load();
-}
-
-TestSetup::configureCraft();
diff --git a/tests/_craft/config/test.php b/tests/_craft/config/test.php
deleted file mode 100644
index 1a539a0..0000000
--- a/tests/_craft/config/test.php
+++ /dev/null
@@ -1,4 +0,0 @@
-markTestSkipped('Craft 5 volume subpath behavior is covered by a separate test.');
- }
-
- $this->assertSame('', $this->invokeVolumeSubpath($volume));
- }
-
- public function testVolumeSubpathReturnsVolumeSubpathOnCraft5(): void
- {
- $volume = new Volume();
-
- if (!method_exists($volume, 'getSubpath')) {
- $this->markTestSkipped('Craft 4 volumes do not implement getSubpath().');
- }
-
- $volume->setSubpath('volume-prefix');
-
- $this->assertSame('volume-prefix/', $this->invokeVolumeSubpath($volume));
- }
-
- private function invokeVolumeSubpath(Volume $volume): string
- {
- $controller = new AssetsController('cloud-assets', Craft::$app);
- $method = new ReflectionMethod($controller, 'volumeSubpath');
- $method->setAccessible(true);
-
- return $method->invoke($controller, $volume);
- }
-}
diff --git a/tests/unit/EsiTest.php b/tests/unit/EsiTest.php
deleted file mode 100644
index b555cf8..0000000
--- a/tests/unit/EsiTest.php
+++ /dev/null
@@ -1,81 +0,0 @@
-urlSigner = new UrlSigner('test-signing-key');
- }
-
- public function testValidateVariablesThrowsExceptionForObjects()
- {
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Value must be a primitive value or array');
-
- $esi = new Esi($this->urlSigner, true);
- $template = 'test-template';
- $variables = [
- 'object' => new \stdClass(),
- ];
-
- $esi->render($template, $variables);
- }
-
- public function testRenderAcceptsScalarValuesAndArrays()
- {
- $esi = new Esi($this->urlSigner, true);
- $template = 'test-template';
-
- $this->assertStringContainsString('render($template, []));
-
- $this->assertStringContainsString('render($template, [
- 'string' => 'value',
- 'int' => 123,
- 'float' => 45.67,
- 'bool' => true,
- 'null' => null,
- 'array' => ['foo', 'bar', 'baz'],
- 'nested' => [
- 'foo' => 'bar',
- 'deep' => [
- 'value' => 123,
- 'items' => [1, 2, 3],
- ],
- ],
- 'mixed' => [
- 'scalar' => 'value',
- 'number' => 42,
- 'list' => ['a', 'b', 'c'],
- ],
- ]));
- }
-
- public function testRendersTemplateWhenInstructed()
- {
- $this->expectException(TemplateLoaderException::class);
- $this->expectExceptionMessage('Unable to find the template “test-template”.');
-
- $esi = new Esi($this->urlSigner, false);
- $template = 'test-template';
- $esi->render($template);
- }
-}
diff --git a/tests/unit/ImageTransformTest.php b/tests/unit/ImageTransformTest.php
deleted file mode 100644
index aa2be32..0000000
--- a/tests/unit/ImageTransformTest.php
+++ /dev/null
@@ -1,236 +0,0 @@
-bootstrap(Craft::$app);
- }
- }
-
- public function testCropModeWithExplicitGravityPreservesItInOptions(): void
- {
- $transform = new ImageTransform([
- 'mode' => 'crop',
- 'width' => 1200,
- 'height' => 750,
- 'gravity' => [
- 'x' => 0.57,
- 'y' => 0.7707,
- ],
- ]);
-
- $this->assertSame([
- 'fit' => 'cover',
- 'gravity' => [
- 'x' => 0.57,
- 'y' => 0.7707,
- ],
- 'height' => 750,
- 'width' => 1200,
- ], $this->behavior($transform)->toOptions());
- }
-
- public function testCropModeWithoutGravityUsesPositionMapping(): void
- {
- $transform = new ImageTransform([
- 'mode' => 'crop',
- 'position' => 'top-center',
- 'width' => 1200,
- 'height' => 750,
- ]);
-
- $this->assertSame([
- 'fit' => 'cover',
- 'gravity' => [
- 'x' => 0.5,
- 'y' => 0,
- ],
- 'height' => 750,
- 'width' => 1200,
- ], $this->behavior($transform)->toOptions());
- }
-
- public function testFocalPointGravityPassesThroughUnchanged(): void
- {
- $asset = $this->makeAssetStub(['x' => 0.474, 'y' => 0.3064]);
- $transform = new ImageTransform([
- 'mode' => 'crop',
- 'position' => 'top-center',
- 'width' => 1200,
- 'height' => 750,
- ]);
-
- $gravity = (new TestImageTransformer())->applyFocalPointGravity($asset, $transform);
-
- $this->assertSame([
- 'x' => 0.474,
- 'y' => 0.3064,
- ], $gravity);
- $this->assertNull($this->behavior($transform)->gravity);
- }
-
- public function testInlineCloudPropertyDoesNotBreakBaseTransform(): void
- {
- $transform = new ImageTransform([
- 'width' => 200,
- 'blur' => 5,
- ]);
-
- $this->assertSame(5, $this->behavior($transform)->blur);
- $this->assertSame([
- 'blur' => 5,
- 'fit' => 'cover',
- 'width' => 200,
- ], $this->behavior($transform)->toOptions());
- }
-
- public function testGetTransformUrlDoesNotLeakGravityBetweenAssets(): void
- {
- $transform = new ImageTransform([
- 'mode' => 'crop',
- 'position' => 'top-center',
- 'width' => 1200,
- 'height' => 750,
- ]);
-
- $firstAsset = $this->makeUrlAssetStub(1, 'first.jpg', 3402, 4253, ['x' => 0.57, 'y' => 0.7707]);
- $secondAsset = $this->makeUrlAssetStub(2, 'second.jpg', 3402, 4253, ['x' => 0.4631, 'y' => 0.308]);
-
- $transformer = new UrlTestImageTransformer();
-
- $firstUrl = $transformer->buildTransformQuery($firstAsset, $transform);
- $secondUrl = $transformer->buildTransformQuery($secondAsset, $transform);
-
- $this->assertStringContainsString('gravity%5Bx%5D=0.57', $firstUrl);
- $this->assertStringContainsString('gravity%5By%5D=0.7707', $firstUrl);
- $this->assertStringContainsString('gravity%5Bx%5D=0.4631', $secondUrl);
- $this->assertStringContainsString('gravity%5By%5D=0.308', $secondUrl);
- $this->assertStringNotContainsString('gravity%5Bx%5D=0.57', $secondUrl);
- }
-
- private function makeAssetStub(array $focalPoint): Asset
- {
- return new class($focalPoint) extends Asset {
- public function __construct(private array $focalPointValue)
- {
- parent::__construct();
- }
-
- public function getHasFocalPoint(): bool
- {
- return true;
- }
-
- public function getFocalPoint(bool $asCss = false): array|string|null
- {
- if ($asCss) {
- return ($this->focalPointValue['x'] * 100) . '% ' . ($this->focalPointValue['y'] * 100) . '%';
- }
-
- return $this->focalPointValue;
- }
- };
- }
-
- private function makeUrlAssetStub(int $id, string $filename, int $width, int $height, array $focalPoint): Asset
- {
- return new class($id, $filename, $width, $height, $focalPoint) extends Asset {
- public function __construct(
- int $id,
- private string $filenameValue,
- private int $widthValue,
- private int $heightValue,
- private array $focalPointValue,
- ) {
- parent::__construct();
- $this->id = $id;
- $this->kind = self::KIND_IMAGE;
- }
-
- public function getHasFocalPoint(): bool
- {
- return true;
- }
-
- public function getFocalPoint(bool $asCss = false): array|string|null
- {
- if ($asCss) {
- return ($this->focalPointValue['x'] * 100) . '% ' . ($this->focalPointValue['y'] * 100) . '%';
- }
-
- return $this->focalPointValue;
- }
-
- public function getWidth(array|string|\craft\models\ImageTransform $transform = null): ?int
- {
- return $this->widthValue;
- }
-
- public function getHeight(mixed $transform = null): ?int
- {
- return $this->heightValue;
- }
-
- public function getFilename(bool $withExtension = true): string
- {
- return $this->filenameValue;
- }
-
- public function getPath(?string $filename = null): string
- {
- return 'tests/' . ($filename ?? $this->filenameValue);
- }
-
- public function getMimeType(mixed $transform = null): ?string
- {
- return 'image/jpeg';
- }
- };
- }
-
- private function behavior(ImageTransform $transform): ImageTransformBehavior
- {
- $behavior = $transform->getBehavior('cloud');
-
- $this->assertInstanceOf(ImageTransformBehavior::class, $behavior);
-
- return $behavior;
- }
-
-}
-
-class TestImageTransformer extends ImageTransformer
-{
- public function applyFocalPointGravity(Asset $asset, ImageTransform $imageTransform): array|string|null
- {
- return $this->applyAssetFocalPointGravity($asset, $imageTransform);
- }
-}
-
-class UrlTestImageTransformer extends ImageTransformer
-{
- public function buildTransformQuery(Asset $asset, ImageTransform $imageTransform): string
- {
- $gravity = $this->applyAssetFocalPointGravity($asset, $imageTransform);
-
- /** @var ImageTransformBehavior $behavior */
- $behavior = $imageTransform->getBehavior('cloud');
-
- return (string) \League\Uri\Components\Query::fromVariable($behavior->toOptions($gravity));
- }
-}
diff --git a/tests/unit/UrlSignerTest.php b/tests/unit/UrlSignerTest.php
deleted file mode 100644
index c14ac27..0000000
--- a/tests/unit/UrlSignerTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-urlSigner = new UrlSigner('test-signing-key');
- }
-
- public function testSignAddsSignatureParameter()
- {
- $url = 'https://example.com/test';
- $signedUrl = $this->urlSigner->sign($url);
-
- $this->tester->assertStringContainsString('?s=', $signedUrl);
- $this->tester->assertStringStartsWith($url, $signedUrl);
- }
-
- public function testSignedUrlContainsValidSignature()
- {
- $url = 'https://example.com/test';
- $signedUrl = $this->urlSigner->sign($url);
-
- $this->tester->assertFalse($this->urlSigner->verify($url));
- $this->tester->assertTrue($this->urlSigner->verify($signedUrl));
- }
-
- public function testVerifyReturnsFalseForTamperedUrl()
- {
- $url = 'https://example.com/test';
- $signedUrl = $this->urlSigner->sign($url);
- $tamperedUrl = $signedUrl . '&tamper=true';
-
- $this->tester->assertFalse($this->urlSigner->verify($tamperedUrl));
- }
-
- public function testVerifyReturnsFalseForWrongSignature()
- {
- $url = 'https://example.com/test?s=wrongsignature';
-
- $this->tester->assertFalse($this->urlSigner->verify($url));
- }
-
- public function testSignWithExistingQueryParameters()
- {
- $url = 'https://example.com/test?foo=bar&baz=qux';
- $signedUrl = $this->urlSigner->sign($url);
-
- $this->tester->assertStringContainsString('foo=bar', $signedUrl);
- $this->tester->assertStringContainsString('baz=qux', $signedUrl);
- $this->tester->assertStringContainsString('&s=', $signedUrl);
- $this->tester->assertTrue($this->urlSigner->verify($signedUrl));
- }
-
- public function testCustomSignatureParameter()
- {
- $customSigner = new UrlSigner('test-key', 'signature');
- $url = 'https://example.com/test';
- $signedUrl = $customSigner->sign($url);
-
- $this->tester->assertStringContainsString('signature=', $signedUrl);
- $this->tester->assertTrue($customSigner->verify($signedUrl));
- }
-
- public function testDifferentKeysProduceDifferentSignatures()
- {
- $signer1 = new UrlSigner('key1');
- $signer2 = new UrlSigner('key2');
-
- $url = 'https://example.com/test';
- $signed1 = $signer1->sign($url);
- $signed2 = $signer2->sign($url);
-
- $this->tester->assertNotEquals($signed1, $signed2);
-
- // Each signer should only verify its own signature
- $this->tester->assertTrue($signer1->verify($signed1));
- $this->tester->assertFalse($signer1->verify($signed2));
- $this->tester->assertTrue($signer2->verify($signed2));
- $this->tester->assertFalse($signer2->verify($signed1));
- }
-}
diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php
deleted file mode 100644
index b3d9bbc..0000000
--- a/tests/unit/_bootstrap.php
+++ /dev/null
@@ -1 +0,0 @@
-