diff --git a/README.md b/README.md index 3b454de19..45e1b6455 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/modules/cms/ServiceProvider.php b/modules/cms/ServiceProvider.php index fdfac9623..56891f9a8 100644 --- a/modules/cms/ServiceProvider.php +++ b/modules/cms/ServiceProvider.php @@ -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 */ diff --git a/modules/cms/models/ThemeData.php b/modules/cms/models/ThemeData.php index 1b02a99e2..8ebd51d86 100644 --- a/modules/cms/models/ThemeData.php +++ b/modules/cms/models/ThemeData.php @@ -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 ?: ''; + } } diff --git a/modules/system/classes/CombineAssets.php b/modules/system/classes/CombineAssets.php index 217a51267..05a2e7b1b 100644 --- a/modules/system/classes/CombineAssets.php +++ b/modules/system/classes/CombineAssets.php @@ -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)));