Skip to content

Commit

Permalink
Merge pull request #21 from venveo/develop
Browse files Browse the repository at this point in the history
4.0.1
  • Loading branch information
Mosnar authored Jun 17, 2022
2 parents 9fca39c + 40f13d0 commit 456b9bb
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 105 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Compress Changelog

## 4.0.1 - 2022-06-17
### Added
- Added "Default Volume Subdirectory" settings to control where assets are stored.
- Added ability to specify output archive name
- Added setting to delete stale archives during garbage collection.
### Changed
- Archives created in volumes without public URLs will now be proxied through the server to be fulfilled
- Files are now streamed into archives instead of being copied to temporary files
- Running "getLazyLink" will now always return a controller URL instead of a direct link to assets. This should help prevent caching issues.

## 4.0.0 - 2022-06-16
### Change
- Compress now requires Craft 4
Expand Down
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ settings and life choices.
Second parameter is whether we want the archive generated on page
load or lazily.
#}
{% set archive = craft.compress.zip(assets, true) %}
{% set archive = craft.compress.zip(assets, true, 'My Photos') %}
{#
the archive variable is now set to an Archive model, but since
we're in lazy mode, the getAsset() response may be null. We can
either check the .isReady method or we can just get the lazyLink
either check the .isReady method or we can just get the lazyLink, which will give us an indirect link to the asset.
#}
{% if archive.isReady %}
{% set archiveAsset = archive.getAsset() %}
Expand Down Expand Up @@ -98,11 +98,8 @@ generates a lazy link to download all assets of a particular kind.
## Caveats & Limitations
- Consider the Assets created by Compress to be temporary. Don't try
to use them in Asset relation fields.
- There's currently no way to override the filename on archives due to
technical limitations.
- There's currently nothing to purge stale archive assets, so if you have a
template that queries a variable set of assets, each time the result
set changes, a new archive asset will be created and the prior will not
be automatically deleted.
- When you provide a name for your archive, it's a good idea to ensure that name is unique to the files you're zipping up. Failure to do so could result in the file not being cached well and being constantly overwritten.

Brought to you by [Venveo](https://www.venveo.com)
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "venveo/craft-compress",
"description": "Create smart zip files from Craft assets on the fly",
"type": "craft-plugin",
"version": "4.0.0",
"version": "4.0.1",
"keywords": [
"craft",
"cms",
Expand Down
36 changes: 21 additions & 15 deletions src/Compress.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use craft\elements\Asset;
use craft\events\ModelEvent;
use craft\events\RegisterComponentTypesEvent;
use craft\services\Gc;
use craft\services\Utilities;
use craft\web\twig\variables\CraftVariable;
use venveo\compress\models\Settings;
Expand All @@ -34,27 +35,18 @@
*/
class Compress extends Plugin
{
// Static Properties
// =========================================================================

/**
* @var Compress
*/
public static $plugin;

// Public Properties
// =========================================================================

/**
* @var string
*/
public string $schemaVersion = '1.0.0';
public string $schemaVersion = '4.0.1';

public bool $hasCpSettings = true;

// Public Methods
// =========================================================================

/**
* @inheritdoc
*/
Expand All @@ -63,10 +55,6 @@ public function init()
parent::init();
self::$plugin = $this;

$this->setComponents([
'compress' => CompressService::class
]);

Event::on(
CraftVariable::class,
CraftVariable::EVENT_INIT,
Expand Down Expand Up @@ -99,6 +87,12 @@ function (ModelEvent $event) {
}
);

Event::on(Gc::class, Gc::EVENT_RUN, function () {
if ($this->getSettings()->deleteStaleArchivesHours) {
$this->compress->deleteStaleArchives(25);
}
});


// Register our utility
Event::on(
Expand All @@ -111,13 +105,25 @@ function (RegisterComponentTypesEvent $event) {

}

/**
* @inheritdoc
*/
public static function config(): array
{
return [
'components' => [
'compress' => ['class' => CompressService::class]
],
];
}

// Protected Methods
// =========================================================================

/**
* @inheritdoc
*/
protected function createSettingsModel(): ?\craft\base\Model
protected function createSettingsModel(): Settings
{
return new Settings();
}
Expand Down
10 changes: 10 additions & 0 deletions src/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
* Default: null
*/
'defaultVolumeHandle' => null,

/**
* How many hours do we wait before an archive is considered stale?
*/
'deleteStaleArchivesHours' => 0,

/**
* An optional subdirectory to put zipped files in
*/
'defaultVolumeSubdirectory' => '',

/**
* If set to true, queue jobs will be dispatched to regenerate an archive
Expand Down
47 changes: 35 additions & 12 deletions src/controllers/CompressController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@

namespace venveo\compress\controllers;

use craft\helpers\App;
use craft\helpers\DateTimeHelper;
use craft\web\Controller;
use venveo\compress\Compress as Plugin;
use venveo\compress\errors\CompressException;
use venveo\compress\models\Archive as ArchiveModel;
use venveo\compress\records\Archive as ArchiveRecord;
use yii\base\InvalidConfigException;
use yii\web\RangeNotSatisfiableHttpException;

/**
* Class CompressController
Expand All @@ -32,6 +34,7 @@ class CompressController extends Controller
* @return \yii\web\Response
* @throws CompressException
* @throws InvalidConfigException
* @throws RangeNotSatisfiableHttpException
*/
public function actionGetLink($uid)
{
Expand All @@ -41,19 +44,15 @@ public function actionGetLink($uid)
return \Craft::$app->response->setStatusCode(404, 'Archive could not be found');
}

// If the asset is ready, redirect to its URL
// If the asset is ready, send it over
if ($record->assetId) {
$archiveModel = ArchiveModel::hydrateFromRecord($record);
// It's possible for an asset ID to exist, but getAsset to return false on soft-deleted assets
if ($archiveModel->getAsset()) {
$assetUrl = $archiveModel->getAsset()->getUrl();
if ($assetUrl) {
$record->dateLastAccessed = DateTimeHelper::currentUTCDateTime();
$record->save();
return \Craft::$app->response->redirect($archiveModel->getAsset()->getUrl());
}

return \Craft::$app->response->setStatusCode(404, 'Could not produce zip file URL.');
$archiveAsset = $archiveModel->getAsset();
if ($archiveAsset) {
$record->dateLastAccessed = DateTimeHelper::currentUTCDateTime();
$record->save();
return $this->getAssetResponse($archiveModel->getAsset());
}
}

Expand All @@ -71,11 +70,35 @@ public function actionGetLink($uid)
\Craft::$app->cache->delete($cacheKey . ':jobId');
}
}
return \Craft::$app->response->redirect($archiveModel->asset->getUrl());
return $this->getAssetResponse($archiveModel->getAsset());
} catch (\Exception $e) {
\Craft::error('Archive could not be generated: ' . $e->getMessage(), __METHOD__);
\Craft::error($e->getTraceAsString(), __METHOD__);
throw new CompressException('Archive could not be generated: ' . $e->getMessage());
return \Craft::$app->response->setStatusCode(404, 'Could not produce zip file URL.');
}
}

/**
* @param $archiveAsset
* @return \craft\web\Response|\yii\console\Response|\yii\web\Response
* @throws RangeNotSatisfiableHttpException
*/
protected function getAssetResponse($archiveAsset) {
if (!$archiveAsset) {
return \Craft::$app->response->setStatusCode(404, 'Could not produce zip file URL.');
}
$assetUrl = $archiveAsset->getUrl();

// If we have a public URL for the asset, we'll just 302 redirect to it
if ($assetUrl) {
return \Craft::$app->response->redirect($archiveAsset->getUrl());
}
App::maxPowerCaptain();
// No public URLs, we'll need to stream the response.
return $this->response
->sendStreamAsFile($archiveAsset->getStream(), $archiveAsset->getFilename(), [
'fileSize' => $archiveAsset->size,
'mimeType' => $archiveAsset->getMimeType(),
]);
}
}
1 change: 1 addition & 0 deletions src/migrations/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected function createTables()
'dateUpdated' => $this->dateTime()->notNull(),
'dateLastAccessed' => $this->dateTime()->notNull(),
'uid' => $this->uid(),
'filename' => $this->string(),
'assetId' => $this->integer(),
'hash' => $this->string()->notNull(),
]
Expand Down
30 changes: 30 additions & 0 deletions src/migrations/m220617_145938_add_filename_support.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace venveo\compress\migrations;

use Craft;
use craft\db\Migration;

/**
* m220617_145938_add_filename_support migration.
*/
class m220617_145938_add_filename_support extends Migration
{
/**
* @inheritdoc
*/
public function safeUp(): bool
{
$this->addColumn('{{%compress_archives}}', 'filename', $this->string()->after('uid'));
return true;
}

/**
* @inheritdoc
*/
public function safeDown(): bool
{
echo "m220617_145938_add_filename_support cannot be reverted.\n";
return false;
}
}
21 changes: 11 additions & 10 deletions src/models/Archive.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
use craft\base\Model;
use craft\db\ActiveRecord;
use craft\elements\Asset;
use craft\elements\db\AssetQuery;
use craft\helpers\UrlHelper;
use DateTime;
use venveo\compress\Compress as Plugin;
use yii\base\InvalidConfigException;

/**
* @author Venveo
Expand All @@ -31,6 +33,7 @@ class Archive extends Model
public ?string $uid = null;
public ?int $assetId = null;
public ?string $hash = null;
public ?string $filename = null;

public ?DateTime $dateUpdated;
public ?DateTime $dateCreated;
Expand Down Expand Up @@ -74,22 +77,20 @@ public function getAsset($siteId = null): ?Asset

/**
* @return string|null
* @throws InvalidConfigException
*/
public function getLazyLink(): ?string
{
if ($this->asset instanceof Asset) {
// Ensure we _can_ get a url for the asset
$assetUrl = $this->asset->getUrl();
if($assetUrl) {
return $assetUrl;
}
}
return UrlHelper::actionUrl('compress/compress/get-link', ['uid' => $this->uid]);
}

public function getContents()
/**
* Returns an AssetQuery configured to include the assets within this archive
* @return \craft\elements\db\AssetQuery
*/
public function getContents(): AssetQuery
{
return Plugin::$plugin->compress->getArchiveContents($this);
return Plugin::getInstance()->compress->getArchiveContents($this);
}

/**
Expand All @@ -101,7 +102,7 @@ public function isReady(): bool
if (!$this->assetId) {
return false;
}
if ($this->getAsset() instanceof Asset) {
if ($this->getAsset()) {
return true;
}

Expand Down
33 changes: 26 additions & 7 deletions src/models/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,44 @@ class Settings extends Model
// =========================================================================

/**
* @var string
* The handle for the volume where archives are stored
*/
public $defaultVolumeHandle = '';
public string $defaultVolumeHandle = '';

public $autoRegenerate = true;
/**
* An optional subdirectory to put zipped files in
*/
public ?string $defaultVolumeSubdirectory = '';

/**
* Should we automatically regenerate
*/
public bool $autoRegenerate = true;

/**
* How many hours do we wait before an archive is considered stale?
*/
public int $deleteStaleArchivesHours = 0;

public $maxFileSize = 0;
/**
* The maximum sum of input files we can compress. Set to 0 for no limit
*/
public int $maxFileSize = 0;

public $maxFileCount = 0;
/**
* The maximum number of input files we can compress. Set to 0 for no limit
*/
public int $maxFileCount = 0;

/**
* @inheritdoc
*/
public function rules(): array
{
return [
['defaultVolumeHandle', 'string'],
[['defaultVolumeHandle', 'defaultVolumeSubdirectory'], 'string'],
[['autoRegenerate'], 'boolean'],
[['maxFileSize', 'maxFileCount'], 'integer'],
[['maxFileSize', 'maxFileCount', 'deleteStaleArchivesHours'], 'integer'],
];
}
}
Loading

0 comments on commit 456b9bb

Please sign in to comment.