Skip to content

Commit

Permalink
Merge pull request #22 from fortrabbit/craft4
Browse files Browse the repository at this point in the history
Craft4 support
  • Loading branch information
Oliver Stark authored May 25, 2022
2 parents 9edac3a + e59f667 commit c72dda4
Show file tree
Hide file tree
Showing 12 changed files with 367 additions and 201 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
*Thumbs.db
vendor/
composer.lock
.phpunit.result.cache
tests/storage
build/phpstan
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Changelog
=========
## 3.0.0 - 2022-05-25
* Craft 4 and PHP 8 support

## 2.1.0 - 2021-03-03

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This plugin provides an [Object Storage](https://help.fortrabbit.com/object-stor

## Requirements

The 2.0 branch of this plugin requires Craft CMS 3.5.15 and PHP 7.3 or later.
The 3.0 branch of this plugin requires Craft CMS 4.0 and PHP 8.0 or later.


## Installation
Expand All @@ -19,7 +19,6 @@ To install the plugin, follow these instructions.
```
cd /path/to/project
composer config platform --unset
composer require fortrabbit/craft-object-storage
```

Expand Down
24 changes: 19 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fortrabbit/craft-object-storage",
"description": "A Craft 3 plugin that integrates with fortrabbit's S3 compatible Object Storage",
"description": "A Craft 4 plugin that integrates with fortrabbit's S3 compatible Object Storage",
"type": "craft-plugin",
"keywords": [
"craftcms",
Expand All @@ -18,11 +18,16 @@
}
],
"require": {
"php": "^7.3.0|^8.0",
"craftcms/cms": "^3.5.15",
"league/flysystem-aws-s3-v3": "^1.0.18",
"php": "^8.0",
"craftcms/cms": "^4.0.0",
"craftcms/flysystem": "^1.0.0",
"league/flysystem-aws-s3-v3": "^3.0.0",
"symfony/console": "^5.1"
},
"require-dev": {
"craftcms/phpstan": "dev-main",
"craftcms/rector": "dev-main"
},
"autoload": {
"psr-4": {
"fortrabbit\\ObjectStorage\\": "src/"
Expand All @@ -36,5 +41,14 @@
},
"bin": [
"bin/object-storage-init"
]
],
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"allow-plugins": {
"yiisoft/yii2-composer": true,
"craftcms/plugin-installer": true,
"pestphp/pest-plugin": true
}
}
}
8 changes: 8 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
includes:
- vendor/craftcms/phpstan/phpstan.neon

parameters:
level: 5
paths:
- src
- bin
39 changes: 39 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

use craft\rector\SetList as CraftSetList;
use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector;
use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector;
use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector;
use Rector\CodingStyle\Rector\ClassConst\RemoveFinalFromConstRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
// Optional directories to skip
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80);
$parameters->set(Option::SKIP, [
EncapsedStringsToSprintfRector::class,
RemoveFinalFromConstRector::class,
SimplifyIfReturnBoolRector::class,
UnnecessaryTernaryExpressionRector::class,
ExplicitBoolCompareRector::class
]);


// Craft Version
$containerConfigurator->import(CraftSetList::CRAFT_CMS_40);
// PHP Version
$containerConfigurator->import(LevelSetList::UP_TO_PHP_80);
// Other
$containerConfigurator->import(SetList::CODE_QUALITY);
$containerConfigurator->import(SetList::CODING_STYLE);
$containerConfigurator->import(SetList::DEAD_CODE);
$containerConfigurator->import(SetList::TYPE_DECLARATION);
};
204 changes: 204 additions & 0 deletions src/Fs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<?php

declare(strict_types=1);

namespace fortrabbit\ObjectStorage;

use Aws\Handler\GuzzleV6\GuzzleHandler;
use Craft;
use craft\flysystem\base\FlysystemFs;
use craft\helpers\App;
use craft\helpers\DateTimeHelper;
use DateTime;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use League\Flysystem\AwsS3V3\PortableVisibilityConverter;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\Visibility;
use League\MimeTypeDetection\FinfoMimeTypeDetector;

/**
* Class ObjectStorageFs
*
* @property mixed $settingsHtml
* @property string $rootUrl
*/
class Fs extends FlysystemFs
{

public static function displayName(): string
{
return 'fortrabbit Object Storage';
}

/**
* @var string Subfolder to use
*/
public string $subfolder = '';

/**
* @var string AWS key ID
*/
public string $keyId = '';

/**
* @var string AWS key secret
*/
public string $secret = '';

/**
* @var string Bucket to use
*/
public string $bucket = '';

/**
* @var string Region to use
*/
public string $region = '';

/**
* @var string Cache expiration period as a string, like '2 hours'
*/
public string $expires = '';

/**
* @var string fortrabbit endpoint hostname
*/
public string $endpoint = '';
/**
* @var bool Set ACL for Uploads
*/
public bool $makeUploadsPublic = true;

/**
* @var bool Whether the specified sub folder should be added to the root URL
*/
public bool $addSubfolderToRootUrl = true;


/**
* @inheritdoc
*/
protected function defineRules(): array
{
return array_merge(parent::defineRules(), [
[['bucket', 'keyId', 'secret', 'endpoint'], 'required'],
]);
}


/**
* @inheritdoc
*/
public function getSettingsHtml(): ?string
{
return Craft::$app->getView()->renderTemplate('fortrabbit-object-storage/fsSettings', [
'fs' => $this,
]);
}

/**
* @inheritdoc
*/
public function getRootUrl(): ?string
{
$rootUrl = parent::getRootUrl();

if ($this->url === '$OBJECT_STORAGE_HOST' || $this->url === '') {
$rootUrl = 'https://' . Craft::parseEnv('$OBJECT_STORAGE_HOST') . '/';
}

if ($rootUrl && $this->subfolder) {
$rootUrl .= rtrim(App::parseEnv($this->subfolder), '/') . '/';
}

return $rootUrl;
}

/**
* @inheritdoc
* @return AwsS3V3Adapter
*/
protected function createAdapter(): FilesystemAdapter
{
$client = static::client($this->getConfigArray());

return new AwsS3V3Adapter(
$client,
App::parseEnv($this->bucket),
$this->getParsedSubfolder(),
new PortableVisibilityConverter(),
new FinfoMimeTypeDetector()
);
}

protected static function client(array $config = []): S3Client
{
return new S3Client($config);
}

/**
* @inheritdoc
*/
protected function addFileMetadataToConfig(array $config): array
{
if (DateTimeHelper::isValidIntervalString($this->expires)) {
$expires = new DateTime();
$now = new DateTime();
$expires->modify('+' . $this->expires);
$diff = (int)$expires->format('U') - (int)$now->format('U');
$config['CacheControl'] = 'max-age=' . $diff;
}

return parent::addFileMetadataToConfig($config);
}


private function getParsedSubfolder(): string
{
if ($this->subfolder && ($subfolder = rtrim(App::parseEnv($this->subfolder), '/')) !== '') {
return $subfolder . '/';
}

return '';
}


/**
* Get the config array for AWS Clients.
*/
protected function getConfigArray(): array
{
$endpoint = App::parseEnv($this->endpoint);

if (!str_contains($endpoint, 'https')) {
$endpoint = 'https://' . $endpoint;
}

return [
'version' => 'latest',
'region' => App::parseEnv($this->region),
'endpoint' => $endpoint,
'http_handler' => new GuzzleHandler(Craft::createGuzzleClient()),
'credentials' => [
'key' => App::parseEnv($this->keyId),
'secret' => App::parseEnv($this->secret)
]
];
}


/**
* Returns the visibility setting for the Fs.
*
* @return string
*/
protected function visibility(): string
{
return $this->makeUploadsPublic ? Visibility::PUBLIC : Visibility::PRIVATE;
}

protected function invalidateCdnPath(string $path): bool
{
return true;
}
}
28 changes: 7 additions & 21 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,27 @@

namespace fortrabbit\ObjectStorage;

use Craft;
use craft\console\controllers\SetupController;
use craft\events\RegisterComponentTypesEvent;
use craft\services\Volumes;
use craft\services\Fs as FsService;
use yii\base\Event;

/**
* fortrabbit Object Storage plugin
* provides a fortrabbit\ObjectStorage\Volume
* provides a fortrabbit\ObjectStorage\Fs
*/
class Plugin extends \craft\base\Plugin
{
/**
* @var Plugin
*/
public static $plugin;
public string $schemaVersion = '2.0';

/**
* @inheritdoc
*/
public function init()
public function init(): void
{
self::$plugin = $this;
parent::init();


\Craft::$app->controllerMap['setup'] = [
'class' => SetupController::class,
];

Event::on(
Volumes::class,
Volumes::EVENT_REGISTER_VOLUME_TYPES,
FsService::class,
FsService::EVENT_REGISTER_FILESYSTEM_TYPES,
function (RegisterComponentTypesEvent $event) {
$event->types[] = Volume::class;
$event->types[] = Fs::class;
}
);
}
Expand Down
Loading

0 comments on commit c72dda4

Please sign in to comment.