diff --git a/app/Enumerations/TemplatePaths.php b/app/Enumerations/TemplatePaths.php new file mode 100644 index 00000000..e9e8cb95 --- /dev/null +++ b/app/Enumerations/TemplatePaths.php @@ -0,0 +1,23 @@ +id, $path->path); + } + } +} \ No newline at end of file diff --git a/app/Enumerations/TemplatePathsHumanReadable.php b/app/Enumerations/TemplatePathsHumanReadable.php new file mode 100644 index 00000000..0e4d7149 --- /dev/null +++ b/app/Enumerations/TemplatePathsHumanReadable.php @@ -0,0 +1,23 @@ +id, $path->name); + } + } +} \ No newline at end of file diff --git a/app/Enumerations/TemplatePathsHumanReadableRootOnly.php b/app/Enumerations/TemplatePathsHumanReadableRootOnly.php new file mode 100644 index 00000000..baa07e5e --- /dev/null +++ b/app/Enumerations/TemplatePathsHumanReadableRootOnly.php @@ -0,0 +1,25 @@ +select(['id', 'name']) + ->get(); + + foreach ($paths as $path) { + self::push($path->id, $path->name); + } + } +} \ No newline at end of file diff --git a/app/Enumerations/Templates.php b/app/Enumerations/Templates.php new file mode 100644 index 00000000..a2c836b8 --- /dev/null +++ b/app/Enumerations/Templates.php @@ -0,0 +1,23 @@ +id, $template->template_name); + } + } +} \ No newline at end of file diff --git a/app/Handlers/Registrators/TemplateRegistrator.php b/app/Handlers/Registrators/TemplateRegistrator.php new file mode 100644 index 00000000..eee83a31 --- /dev/null +++ b/app/Handlers/Registrators/TemplateRegistrator.php @@ -0,0 +1,262 @@ +baseDirectory = $baseDirectory; + } + + /** + * Set namespace for registered template path + * + * @param string $namespace + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace . '::'; + } + + /** + * Resets namespace + */ + public function unsetNamespace() + { + $this->namespace = null; + } + + /** + * Registers new template path. + * + * This method registeres given path itself with all subdirectories + * + * @param string $path path relatively to root of the project + * @param string $name human readable identification of given template path + * + * @throws \Exception + */ + public function register($path, $name) + { + // check if base directory was set + if (!$this->baseDirectory || !is_dir($this->baseDirectory)) { + throw new \Exception('Set $baseDirectory before registering template path.'); + } + + $this->registeredPath = $this->baseDirectory . DIRECTORY_SEPARATOR . $path; + + // register path + $this->registerPath($this->registeredPath, $name); + } + + /** + * Get relative path to project root + * + * @param $directory + * + * @return string + */ + protected function relativePath($directory) + { + return trim(str_replace(base_path(), '', $directory), '/'); + } + + /** + * Crawl subdirectories and register them + * + * @param string $directory path relative to $baseDirectory variable + * @param string $name name of parent directory + */ + protected function scanForSubdirectories($directory, $name) + { + $directories = File::directories($directory); + + foreach ($directories as $dir) { + $base = basename($dir); + $name = preg_replace('/\.(.+)/', '.' . $base, $name); + + $this->registerPath($dir, $name, $directory); + } + } + + /** + * Registers template path and templates into system + * + * @param string $path relative path to $baseDirectory variable + * @param string $name human readable name of this path (used in overview) + * @param null|string $parent path to parent directory + */ + protected function registerPath($path, $name, $parent = null) + { + // skip custom directories (contains updated templates only) + $dirName = basename($path); + if ($dirName == Template::EDITED_TEMPLATES_DIRECTORY) { + return; + } + + // save template path into database + $templatePathId = $this->saveTemplatePath($path, $name, $parent); + + // save all templates into database + $this->registerTemplates($path, $templatePathId); + + // try to find some subdirectories and other templates + $this->scanForSubdirectories($path, $name); + } + + /** + * Registers all templates + * + * This method takes all templates in given directory and registers them into database. Templates registered in + * this way may be used for further manipulation. + * + * @param $path + * @param $templatePathId + * + * @throws \App\Enumerations\Exceptions\EnumerationExistsException + */ + protected function registerTemplates($path, $templatePathId) + { + $templates = File::files($path); + + foreach ($templates as $template) { + + // skip all non-template files - we assume that every file with suffix ".blade.php" is Blade template + if (!Str::endsWith($template, Template::TEMPLATE_SUFFIX)) { + continue; + } + + // get all templates and template name (path/to/template.blade.php -> path.to.template) + $templateName = $this->getTemplateName($template); + $registeredTemplates = Templates::getWithTranslations(); + + if (!in_array($templateName, $registeredTemplates)) { + + $params = [ + 'template_name' => $templateName, + 'name' => $templateName, + 'path' => $templatePathId, + 'description' => '', + 'editable' => 1 + ]; + + // save template into database + $newTemplate = Template::create($params); + + // push template into enumeration - so it will not be registered again + Templates::push($newTemplate->id, $templateName); + } + } + } + + /** + * Save template into database and return its ID + * + * @param $path + * @param $name + * @param null $parent + * + * @return mixed|null + * @throws \App\Enumerations\Exceptions\EnumerationExistsException + */ + protected function saveTemplatePath($path, $name, $parent = null) + { + $registeredPaths = TemplatePaths::getWithTranslations(); + $relativePath = $this->relativePath($path); + + if (!in_array($relativePath, $registeredPaths)) { + $parentId = $this->getId($this->relativePath($parent)); + + $params = [ + 'path' => $relativePath, + 'name' => $name, + 'namespace' => $this->namespace, + 'parent_id' => $parentId + ]; + + // save into database + $path = TemplatePath::create($params); + + // add to enumeration + TemplatePaths::push($path->id, $relativePath); + + return $path->id; + } + + return $this->getId($relativePath); + } + + /** + * Get ID of registered template + * + * @param $path + * + * @return null|int null when parent not found, ID of parent otherwise + */ + private function getId($path) + { + // get registered paths + $paths = TemplatePaths::getWithTranslations(); + $paths = array_flip($paths); + + if (is_null($path) || !!isset($paths[$path])) { + return null; + } + + return $paths[$path]; + } + + /** + * Get template name + * + * Converts path to template to corresponding name + * + * @param $template + * + * @return string + */ + private function getTemplateName($template) + { + $templateName = str_replace($this->registeredPath . DIRECTORY_SEPARATOR, '', $template); + $templateName = str_replace(DIRECTORY_SEPARATOR, '.', $templateName); + $templateName = str_replace(Template::TEMPLATE_SUFFIX, '', $templateName); + + return $this->namespace . $templateName; + } +} diff --git a/app/Models/Template.php b/app/Models/Template.php new file mode 100644 index 00000000..7b520d57 --- /dev/null +++ b/app/Models/Template.php @@ -0,0 +1,35 @@ +belongsTo('App\Models\TemplatePath', 'id'); + } +} \ No newline at end of file diff --git a/app/Models/TemplatePath.php b/app/Models/TemplatePath.php index 66070fe4..07271869 100644 --- a/app/Models/TemplatePath.php +++ b/app/Models/TemplatePath.php @@ -10,6 +10,15 @@ use Illuminate\Database\Eloquent\Model; class TemplatePath extends Model { public $timestamps = true; - protected $fillable = ['name']; + protected $fillable = ['name', 'path', 'parent_id', 'namespace']; protected $guarded = []; + + /** + * In one path may be many templates + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function templates() { + return $this->hasMany('App\Models\Template', 'path'); + } } \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3ac836c7..29ccb1e5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Handlers\Registrators\TemplateRegistrator; use App\Handlers\Registrators\WidgetRegistrator; use App\Widgets\HtmlCode\HtmlCode; use Illuminate\Support\ServiceProvider; @@ -16,6 +17,7 @@ class AppServiceProvider extends ServiceProvider public function boot() { $this->registerWidgets(); + $this->registerTemplates(); } /** @@ -38,6 +40,11 @@ class AppServiceProvider extends ServiceProvider $this->app->singleton('registerWidget', function ($app) { return new WidgetRegistrator($app); }); + + // create registrator for template directories + $this->app->singleton('registerTemplateDir', function ($app) { + return new TemplateRegistrator($app); + }); } /** @@ -53,4 +60,18 @@ class AppServiceProvider extends ServiceProvider // register all available widgets $registrator->register(HtmlCode::class); } + + /** + * Register path with editable contents + */ + private function registerTemplates() + { + /** + * @var $registrator TemplateRegistrator + */ + $registrator = $this->app->make('registerTemplateDir'); + $registrator->setBaseDirectory('resources/views/visitor'); + + $registrator->register(config('app.theme'), trans('admin/templates.core')); + } } diff --git a/database/migrations/2016_02_18_141703_AddNamespaceToTemplatePaths.php b/database/migrations/2016_02_18_141703_AddNamespaceToTemplatePaths.php new file mode 100644 index 00000000..778269e5 --- /dev/null +++ b/database/migrations/2016_02_18_141703_AddNamespaceToTemplatePaths.php @@ -0,0 +1,35 @@ +string('namespace', 64) + ->comment('Namespace used for all templates in this path') + ->nullable() + ->default(null) + ->after('path'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('template_paths', function (Blueprint $table) { + $table->dropColumn('namespace'); + }); + } +}