ThemeData model now busts the [front-end] combiner cache

Separation of concerns, move the combiner asset variable injection logic to the cms module
Tweak readme
This commit is contained in:
Samuel Georges 2016-06-03 07:22:05 +10:00
parent cd5a579c04
commit 744d5654b3
4 changed files with 132 additions and 64 deletions

View File

@ -21,14 +21,6 @@ Instructions on how to install October can be found at the [installation guide](
October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](http://au.linkedin.com/pub/sam-georges/31/641/a9), who both continue to develop the platform.
### Coding standards
Please follow the following guides and code standards:
* [PSR 4 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)
* [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
* [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
### Foundation library
The CMS uses [Laravel](http://laravel.com) as a foundation PHP framework.
@ -48,3 +40,11 @@ The OctoberCMS platform is open-sourced software licensed under the [MIT license
### Contributing
Before sending a Pull Request, be sure to review the [Contributing Guidelines](CONTRIBUTING.md) first.
### Coding standards
Please follow the following guides and code standards:
* [PSR 4 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)
* [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
* [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)

View File

@ -12,7 +12,7 @@ use System\Classes\SettingsManager;
use System\Classes\CombineAssets;
use Cms\Classes\ComponentManager;
use Cms\Classes\Page as CmsPage;
use Cms\Classes\Theme as CmsTheme;
use Cms\Models\ThemeData;
class ServiceProvider extends ModuleServiceProvider
{
@ -27,6 +27,7 @@ class ServiceProvider extends ModuleServiceProvider
$this->registerComponents();
$this->registerAssetBundles();
$this->registerCombinerEvents();
/*
* Backend specific
@ -77,6 +78,25 @@ class ServiceProvider extends ModuleServiceProvider
});
}
/**
* Registers events for the asset combiner.
*/
protected function registerCombinerEvents()
{
if (App::runningInBackend() || App::runningInConsole()) {
return;
}
Event::listen('cms.combiner.beforePrepare', function ($combiner, $assets) {
$filters = array_flatten($combiner->getFilters());
ThemeData::applyAssetVariablesToCombinerFilters($filters);
});
Event::listen('cms.combiner.getCacheKey', function ($combiner, $holder) {
$holder->key = $holder->key . ThemeData::getCombinerCacheKey();
});
}
/*
* Register navigation
*/

View File

@ -64,7 +64,10 @@ class ThemeData extends Model
public function afterSave()
{
CombineAssets::resetCache();
try {
CombineAssets::resetCache();
}
catch (Exception $ex) {}
}
/**
@ -85,6 +88,8 @@ class ThemeData extends Model
public function afterFetch()
{
$data = (array) $this->data + $this->getDefaultValues();
/*
* Repeater form fields store arrays and must be jsonable.
*/
@ -98,13 +103,14 @@ class ThemeData extends Model
}
elseif ($field['type'] == 'fileupload') {
$this->attachOne[$id] = 'System\Models\File';
unset($data[$id]);
}
}
/*
* Fill this model with the jsonable attributes kept in 'data'.
*/
$this->setRawAttributes((array) $this->getAttributes() + (array) $this->data, true);
$this->setRawAttributes((array) $this->getAttributes() + $data, true);
}
public function beforeValidate()
@ -127,13 +133,27 @@ class ThemeData extends Model
*/
public function setDefaultValues()
{
foreach ($this->getDefaultValues() as $attribute => $field) {
$this->{$attribute} = $value;
}
}
/**
* Gets default values for this model based on form field definitions.
*/
public function getDefaultValues()
{
$result = [];
foreach ($this->getFormFields() as $attribute => $field) {
if (!$value = array_get($field, 'default')) {
continue;
}
$this->{$attribute} = $value;
$result[$attribute] = $value;
}
return $result;
}
/**
@ -145,9 +165,11 @@ class ThemeData extends Model
if (!$theme = CmsTheme::load($this->theme))
throw new Exception(Lang::get('Unable to find theme with name :name', $this->theme));
return $theme->getConfigValue('form.fields', []) +
$theme->getConfigValue('form.tabs.fields', []) +
$theme->getConfigValue('form.secondaryTabs.fields', []);
$config = $theme->getConfigArray('form');
return array_get($config, 'fields', []) +
array_get($config, 'tabs.fields', []) +
array_get($config, 'secondaryTabs.fields', []);
}
/**
@ -168,4 +190,40 @@ class ThemeData extends Model
return $result;
}
/**
* Applies asset variables to the combiner filters that support it.
* @return void
*/
public static function applyAssetVariablesToCombinerFilters($filters)
{
$theme = CmsTheme::getActiveTheme();
if (!$theme->hasCustomData()) {
return;
}
$assetVars = $theme->getCustomData()->getAssetVariables();
foreach ($filters as $filter) {
if (method_exists($filter, 'setPresets')) {
$filter->setPresets($assetVars);
}
}
}
/**
* Generate a cache key for the combiner, this allows variables to bust the cache.
* @return string
*/
public static function getCombinerCacheKey()
{
$theme = CmsTheme::getActiveTheme();
if (!$theme->hasCustomData()) {
return '';
}
$customData = $theme->getCustomData();
return (string) $customData->updated_at ?: '';
}
}

View File

@ -4,12 +4,12 @@ use App;
use Url;
use File;
use Lang;
use Event;
use Cache;
use Route;
use Config;
use Request;
use Response;
use Cms\Classes\Theme;
use Assetic\Asset\FileAsset;
use Assetic\Asset\GlobAsset;
use Assetic\Asset\AssetCache;
@ -165,11 +165,11 @@ class CombineAssets
* Returns the combined contents from a prepared cache identifier.
* @return string Combined file contents.
*/
public function getContents($cacheId)
public function getContents($cacheKey)
{
$cacheInfo = $this->getCache($cacheId);
$cacheInfo = $this->getCache($cacheKey);
if (!$cacheInfo) {
throw new ApplicationException(Lang::get('system::lang.combiner.not_found', ['name'=>$cacheId]));
throw new ApplicationException(Lang::get('system::lang.combiner.not_found', ['name'=>$cacheKey]));
}
$this->localPath = $cacheInfo['path'];
@ -287,23 +287,23 @@ class CombineAssets
/*
* Cache and process
*/
$cacheId = $this->makeCacheId($assets);
$cacheInfo = $this->useCache ? $this->getCache($cacheId) : false;
$cacheKey = $this->getCacheKey($assets);
$cacheInfo = $this->useCache ? $this->getCache($cacheKey) : false;
if (!$cacheInfo) {
$combiner = $this->prepareCombiner($assets);
$lastMod = $combiner->getLastModified();
$cacheInfo = [
'version' => $cacheId.'-'.$lastMod,
'etag' => $cacheId,
'version' => $cacheKey.'-'.$lastMod,
'etag' => $cacheKey,
'lastMod' => $lastMod,
'files' => $assets,
'path' => $this->localPath,
'extension' => $extension
];
$this->putCache($cacheId, $cacheInfo);
$this->putCache($cacheKey, $cacheInfo);
}
return $this->getCombinedUrl($cacheInfo['version']);
@ -315,11 +315,15 @@ class CombineAssets
*/
protected function prepareCombiner(array $assets, $rewritePath = null)
{
/*
* Extensibility
*/
Event::fire('cms.combiner.beforePrepare', [$this, $assets]);
$files = [];
$filesSalt = null;
foreach ($assets as $asset) {
$filters = $this->getFilters(File::extension($asset)) ?: [];
$filters = $this->processFilters($filters);
$path = File::symbolizePath($asset, null) ?: $this->localPath . $asset;
$files[] = new FileAsset($path, $filters, public_path());
$filesSalt .= $this->localPath . $asset;
@ -468,29 +472,6 @@ class CombineAssets
}
}
/**
* Preprocess filters to use standard configuration provided by the system.
* @param array $filters
* @return array
*/
protected function processFilters($filters)
{
$theme = Theme::getActiveTheme();
if (!$theme->hasCustomData()) {
return $filters;
}
$assetVars = $theme->getCustomData()->getAssetVariables();
foreach ($filters as $filter) {
if (method_exists($filter, 'setPresets')) {
$filter->setPresets($assetVars);
}
}
return $filters;
}
//
// Bundles
//
@ -632,16 +613,16 @@ class CombineAssets
* @var array List of asset files.
* @return bool Successful
*/
protected function putCache($cacheId, array $cacheInfo)
protected function putCache($cacheKey, array $cacheInfo)
{
$cacheId = 'combiner.'.$cacheId;
$cacheKey = 'combiner.'.$cacheKey;
if (Cache::has($cacheId)) {
if (Cache::has($cacheKey)) {
return false;
}
$this->putCacheIndex($cacheId);
Cache::forever($cacheId, base64_encode(serialize($cacheInfo)));
$this->putCacheIndex($cacheKey);
Cache::forever($cacheKey, base64_encode(serialize($cacheInfo)));
return true;
}
@ -650,15 +631,15 @@ class CombineAssets
* @var string Cache identifier
* @return array Cache information
*/
protected function getCache($cacheId)
protected function getCache($cacheKey)
{
$cacheId = 'combiner.'.$cacheId;
$cacheKey = 'combiner.'.$cacheKey;
if (!Cache::has($cacheId)) {
if (!Cache::has($cacheKey)) {
return false;
}
return @unserialize(@base64_decode(Cache::get($cacheId)));
return @unserialize(@base64_decode(Cache::get($cacheKey)));
}
/**
@ -666,9 +647,18 @@ class CombineAssets
* @var array Asset files
* @return string Unique identifier
*/
protected function makeCacheId(array $assets)
protected function getCacheKey(array $assets)
{
return md5($this->localPath . implode('|', $assets));
$cacheKey = $this->localPath . implode('|', $assets);
/*
* Extensibility
*/
$dataHolder = (object) ['key' => $cacheKey];
Event::fire('cms.combiner.getCacheKey', [$this, $dataHolder]);
$cacheKey = $dataHolder->key;
return md5($cacheKey);
}
/**
@ -680,8 +670,8 @@ class CombineAssets
if (Cache::has('combiner.index')) {
$index = (array) @unserialize(@base64_decode(Cache::get('combiner.index'))) ?: [];
foreach ($index as $cacheId) {
Cache::forget($cacheId);
foreach ($index as $cacheKey) {
Cache::forget($cacheKey);
}
Cache::forget('combiner.index');
@ -696,7 +686,7 @@ class CombineAssets
* @var string Cache identifier
* @return bool Returns false if identifier is already in store
*/
protected function putCacheIndex($cacheId)
protected function putCacheIndex($cacheKey)
{
$index = [];
@ -704,11 +694,11 @@ class CombineAssets
$index = (array) @unserialize(@base64_decode(Cache::get('combiner.index'))) ?: [];
}
if (in_array($cacheId, $index)) {
if (in_array($cacheKey, $index)) {
return false;
}
$index[] = $cacheId;
$index[] = $cacheKey;
Cache::forever('combiner.index', base64_encode(serialize($index)));