diff --git a/modules/cms/classes/Asset.php b/modules/cms/classes/Asset.php index 569e8fbb3..3723425ee 100644 --- a/modules/cms/classes/Asset.php +++ b/modules/cms/classes/Asset.php @@ -1,7 +1,13 @@ theme = $theme; - self::$allowedExtensions = self::getEditableExtensions(); + $this->allowedExtensions = self::getEditableExtensions(); + + parent::__construct(); } /** - * Sets path for new asset files created from the back-end. - * @param string $path Specifies the path. + * Loads the object from a file. + * This method is used in the CMS back-end. It doesn't use any caching. + * @param \Cms\Classes\Theme $theme Specifies the theme the object belongs to. + * @param string $fileName Specifies the file name, with the extension. + * The file name can contain only alphanumeric symbols, dashes and dots. + * @return mixed Returns a CMS object instance or null if the object wasn't found. */ - public function setInitialPath($path) + public static function load($theme, $fileName) { - $this->fileName = $path; + return (new static($theme))->find($fileName); + } + + /** + * Find a single template by its file name. + * + * @param string $fileName + * @return mixed|static + */ + public function find($fileName) + { + $filePath = $this->getFilePath($fileName); + + if (!File::isFile($filePath)) { + return null; + } + + if (($content = @File::get($filePath)) === false) { + return null; + } + + $this->fileName = $fileName; + $this->originalFileName = $fileName; + $this->mtime = File::lastModified($filePath); + $this->content = $content; + $this->exists = true; + return $this; + } + + /** + * Sets the object attributes. + * @param array $attributes A list of attributes to set. + */ + public function fill(array $attributes) + { + foreach ($attributes as $key => $value) { + if (!in_array($key, $this->fillable)) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.invalid_property', + ['name' => $key] + )); + } + + $this->$key = $value; + } + } + + /** + * Saves the object to the disk. + */ + public function save() + { + $this->validateFileName(); + + $fullPath = $this->getFilePath(); + + if (File::isFile($fullPath) && $this->originalFileName !== $this->fileName) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.file_already_exists', + ['name'=>$this->fileName] + )); + } + + $dirPath = $this->theme->getPath().'/'.$this->dirName; + if (!file_exists($dirPath) || !is_dir($dirPath)) { + if (!File::makeDirectory($dirPath, 0777, true, true)) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.error_creating_directory', + ['name'=>$dirPath] + )); + } + } + + if (($pos = strpos($this->fileName, '/')) !== false) { + $dirPath = dirname($fullPath); + + if (!is_dir($dirPath) && !File::makeDirectory($dirPath, 0777, true, true)) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.error_creating_directory', + ['name'=>$dirPath] + )); + } + } + + $newFullPath = $fullPath; + if (@File::put($fullPath, $this->content) === false) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.error_saving', + ['name'=>$this->fileName] + )); + } + + if (strlen($this->originalFileName) && $this->originalFileName !== $this->fileName) { + $fullPath = $this->getFilePath($this->originalFileName); + + if (File::isFile($fullPath)) { + @unlink($fullPath); + } + } + + clearstatcache(); + + $this->mtime = @File::lastModified($newFullPath); + $this->originalFileName = $this->fileName; + $this->exists = true; + } + + /** + * Validate the supplied filename, extension and path. + * @param string $fileName + */ + protected function validateFileName($fileName = null) + { + if ($fileName === null) { + $fileName = $this->fileName; + } + + $fileName = trim($fileName); + + if (!strlen($fileName)) { + throw new ValidationException(['fileName' => + Lang::get('cms::lang.cms_object.file_name_required', [ + 'allowed' => implode(', ', $this->allowedExtensions), + 'invalid' => pathinfo($fileName, PATHINFO_EXTENSION) + ]) + ]); + } + + if (!FileHelper::validateExtension($fileName, $this->allowedExtensions, false)) { + throw new ValidationException(['fileName' => + Lang::get('cms::lang.cms_object.invalid_file_extension', [ + 'allowed' => implode(', ', $this->allowedExtensions), + 'invalid' => pathinfo($fileName, PATHINFO_EXTENSION) + ]) + ]); + } + } + + /** + * Returns the file name. + * @return string + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns the absolute file path. + * @param string $fileName Specifies the file name to return the path to. + * @return string + */ + public function getFilePath($fileName = null) + { + if ($fileName === null) { + $fileName = $this->fileName; + } + + return $this->theme->getPath().'/'.$this->dirName.'/'.$fileName; } /** @@ -48,21 +267,4 @@ class Asset extends CmsObject return $configTypes; } - /** - * Returns the directory name corresponding to the object type. - * For pages the directory name is "pages", for layouts - "layouts", etc. - * @return string - */ - public static function getObjectTypeDirName() - { - return 'assets'; - } - - /** - * {@inheritDoc} - */ - protected static function getMaxAllowedPathNesting() - { - return null; - } } diff --git a/modules/cms/classes/ComponentPartial.php b/modules/cms/classes/ComponentPartial.php index 775867da8..6dfc7a104 100644 --- a/modules/cms/classes/ComponentPartial.php +++ b/modules/cms/classes/ComponentPartial.php @@ -4,6 +4,8 @@ use File; use Lang; use Cms\Contracts\CmsObject as CmsObjectContract; use Cms\Helpers\File as FileHelper; +use October\Rain\Extension\Extendable; +use ApplicationException; /** * The CMS component partial class. These objects are read-only. @@ -11,7 +13,7 @@ use Cms\Helpers\File as FileHelper; * @package october\cms * @author Alexey Bobkov, Samuel Georges */ -class ComponentPartial implements CmsObjectContract +class ComponentPartial extends Extendable implements CmsObjectContract { /** * @var \Cms\Classes\ComponentBase A reference to the CMS component containing the object. @@ -57,6 +59,8 @@ class ComponentPartial implements CmsObjectContract public function __construct(ComponentBase $component) { $this->component = $component; + + parent::__construct(); } /** diff --git a/modules/cms/controllers/Index.php b/modules/cms/controllers/Index.php index e7783813a..9f2bedf21 100644 --- a/modules/cms/controllers/Index.php +++ b/modules/cms/controllers/Index.php @@ -202,7 +202,7 @@ class Index extends Controller $template = $this->createTemplate($type); if ($type == 'asset') { - $template->setInitialPath($this->widget->assetList->getCurrentRelativePath()); + $template->fileName = $this->widget->assetList->getCurrentRelativePath(); } $widget = $this->makeTemplateFormWidget($type, $template);