feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
922
modules/cms/controllers/Index.php
Normal file
922
modules/cms/controllers/Index.php
Normal file
@@ -0,0 +1,922 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Controllers;
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
use Backend\Facades\BackendMenu;
|
||||
use Backend\Widgets\Form;
|
||||
use Cms\Classes\Asset;
|
||||
use Cms\Classes\CmsCompoundObject;
|
||||
use Cms\Classes\CmsObject;
|
||||
use Cms\Classes\ComponentManager;
|
||||
use Cms\Classes\ComponentPartial;
|
||||
use Cms\Classes\Content;
|
||||
use Cms\Classes\Layout;
|
||||
use Cms\Classes\Page;
|
||||
use Cms\Classes\Partial;
|
||||
use Cms\Classes\Router;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Helpers\Cms as CmsHelpers;
|
||||
use Cms\Widgets\AssetList;
|
||||
use Cms\Widgets\ComponentList;
|
||||
use Cms\Widgets\TemplateList;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use System\Helpers\DateTime;
|
||||
use Winter\Storm\Auth\AuthorizationException;
|
||||
use Winter\Storm\Exception\ApplicationException;
|
||||
use Winter\Storm\Halcyon\Datasource\DatasourceInterface;
|
||||
use Winter\Storm\Router\Router as StormRouter;
|
||||
use Winter\Storm\Support\Facades\Config;
|
||||
use Winter\Storm\Support\Facades\Event;
|
||||
use Winter\Storm\Support\Facades\Flash;
|
||||
use Winter\Storm\Support\Facades\Url;
|
||||
|
||||
/**
|
||||
* CMS index
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Index extends Controller
|
||||
{
|
||||
use \Backend\Traits\InspectableContainer;
|
||||
|
||||
/**
|
||||
* @var Theme
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var array Permissions required to view this page.
|
||||
*/
|
||||
public $requiredPermissions = [
|
||||
'cms.manage_content',
|
||||
'cms.manage_assets',
|
||||
'cms.manage_pages',
|
||||
'cms.manage_layouts',
|
||||
'cms.manage_partials'
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
Event::listen('backend.form.extendFieldsBefore', function ($widget) {
|
||||
if (!$widget->getController() instanceof Index) {
|
||||
return;
|
||||
}
|
||||
if (!$widget->model instanceof CmsCompoundObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($widget->secondaryTabs['fields'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('code', $widget->secondaryTabs['fields']) && CmsHelpers::safeModeEnabled()) {
|
||||
$widget->secondaryTabs['fields']['safemode_notice']['hidden'] = false;
|
||||
$widget->secondaryTabs['fields']['code']['readOnly'] = true;
|
||||
};
|
||||
});
|
||||
|
||||
BackendMenu::setContext('Winter.Cms', 'cms', true);
|
||||
|
||||
try {
|
||||
if (!($theme = Theme::getEditTheme())) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.theme.edit.not_found'));
|
||||
}
|
||||
|
||||
$this->theme = $theme;
|
||||
|
||||
if ($this->user?->hasAccess('cms.manage_pages')) {
|
||||
new TemplateList($this, 'pageList', function () use ($theme) {
|
||||
return Page::listInTheme($theme, true);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->user?->hasAccess('cms.manage_partials')) {
|
||||
new TemplateList($this, 'partialList', function () use ($theme) {
|
||||
return Partial::listInTheme($theme, true);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->user?->hasAccess('cms.manage_layouts')) {
|
||||
new TemplateList($this, 'layoutList', function () use ($theme) {
|
||||
return Layout::listInTheme($theme, true);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->user?->hasAccess('cms.manage_content')) {
|
||||
new TemplateList($this, 'contentList', function () use ($theme) {
|
||||
return Content::listInTheme($theme, true);
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
$this->user?->hasAccess([
|
||||
'cms.manage_pages',
|
||||
'cms.manage_partials',
|
||||
'cms.manage_layouts',
|
||||
], false)
|
||||
) {
|
||||
new ComponentList($this, 'componentList');
|
||||
}
|
||||
|
||||
if ($this->user?->hasAccess('cms.manage_assets')) {
|
||||
new AssetList($this, 'assetList');
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pages
|
||||
//
|
||||
|
||||
/**
|
||||
* Index page action
|
||||
*/
|
||||
public function index(): void
|
||||
{
|
||||
$this->addJs('/modules/cms/assets/js/winter.cmspage.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/winter.dragcomponents.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/winter.tokenexpander.js', 'core');
|
||||
$this->addCss('/modules/cms/assets/css/winter.components.css', 'core');
|
||||
|
||||
$this->bodyClass = 'compact-container';
|
||||
$this->pageTitle = 'cms::lang.cms.menu_label';
|
||||
$this->pageTitleTemplate = '%s '.Lang::get($this->pageTitle);
|
||||
|
||||
if (Request::ajax() && Request::input('formWidgetAlias')) {
|
||||
$this->bindFormWidgetToController();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an existing template from the index page
|
||||
*/
|
||||
public function index_onOpenTemplate(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('type');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$template = $this->loadTemplate($type, Request::input('path'));
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = Request::input('path');
|
||||
$this->vars['lastModified'] = DateTime::makeCarbon($template->mtime);
|
||||
$this->vars['canCommit'] = $this->canCommitTemplate($template);
|
||||
$this->vars['canReset'] = $this->canResetTemplate($template);
|
||||
|
||||
if ($type === 'page') {
|
||||
$router = new StormRouter;
|
||||
$this->vars['pageUrl'] = $router->urlFromPattern($template->url);
|
||||
}
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateTheme' => $this->theme->getDirName(),
|
||||
'templateMtime' => $template->mtime
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the template currently open
|
||||
* @throws ApplicationException if the file in the datasource has been modified since the file was loaded in the browser
|
||||
*/
|
||||
public function onSave(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('templateType');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$templatePath = trim(Request::input('templatePath'));
|
||||
$template = $templatePath ? $this->loadTemplate($type, $templatePath) : $this->createTemplate($type);
|
||||
$formWidget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$saveData = $formWidget->getSaveData();
|
||||
$postData = post();
|
||||
$templateData = [];
|
||||
|
||||
$settings = array_get($saveData, 'settings', []) + Request::input('settings', []);
|
||||
$settings = $this->upgradeSettings($settings, $template->settings);
|
||||
|
||||
if ($settings) {
|
||||
$templateData['settings'] = $settings;
|
||||
}
|
||||
|
||||
$fields = ['markup', 'code', 'fileName', 'content'];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $saveData)) {
|
||||
$templateData[$field] = $saveData[$field];
|
||||
}
|
||||
elseif (array_key_exists($field, $postData)) {
|
||||
$templateData[$field] = $postData[$field];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($templateData['markup']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['markup'] = $this->convertLineEndings($templateData['markup']);
|
||||
}
|
||||
|
||||
if (!empty($templateData['code']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['code'] = $this->convertLineEndings($templateData['code']);
|
||||
}
|
||||
|
||||
if (
|
||||
!Request::input('templateForceSave') && $template->mtime
|
||||
&& Request::input('templateMtime') != $template->mtime
|
||||
) {
|
||||
throw new ApplicationException('mtime-mismatch');
|
||||
}
|
||||
|
||||
$template->attributes = [];
|
||||
$template->fill($templateData);
|
||||
$template->save();
|
||||
|
||||
/**
|
||||
* @event cms.template.save
|
||||
* Fires after a CMS template (page|partial|layout|content|asset) has been saved.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.template.save', function ((\Cms\Controllers\Index) $controller, (mixed) $templateObject, (string) $type) {
|
||||
* \Log::info("A $type has been saved");
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $CmsIndexController->bindEvent('template.save', function ((mixed) $templateObject, (string) $type) {
|
||||
* \Log::info("A $type has been saved");
|
||||
* });
|
||||
*
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.save', [$template, $type]);
|
||||
|
||||
Flash::success(Lang::get('cms::lang.template.saved'));
|
||||
|
||||
return $this->getUpdateResponse($template, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a form that suggests the template has been edited elsewhere
|
||||
*/
|
||||
public function onOpenConcurrencyResolveForm(): string
|
||||
{
|
||||
return $this->makePartial('concurrency_resolve_form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new template
|
||||
*/
|
||||
public function onCreateTemplate(): array
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$template = $this->createTemplate($type);
|
||||
|
||||
if ($type === 'asset') {
|
||||
$template->fileName = $this->widget->assetList->getCurrentRelativePath();
|
||||
}
|
||||
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = '';
|
||||
$this->vars['canCommit'] = $this->canCommitTemplate($template);
|
||||
$this->vars['canReset'] = $this->canResetTemplate($template);
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateTheme' => $this->theme->getDirName(),
|
||||
'templateMtime' => null
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple templates at the same time
|
||||
*/
|
||||
public function onDeleteTemplates(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('type');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$templates = Request::input('template');
|
||||
$error = null;
|
||||
$deleted = [];
|
||||
|
||||
try {
|
||||
foreach ($templates as $path => $selected) {
|
||||
if ($selected) {
|
||||
$this->loadTemplate($type, $path)->delete();
|
||||
$deleted[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @event cms.template.delete
|
||||
* Fires after a CMS template (page|partial|layout|content|asset) has been deleted.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.template.delete', function ((\Cms\Controllers\Index) $controller, (string) $type) {
|
||||
* \Log::info("A $type has been deleted");
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $CmsIndexController->bindEvent('template.delete', function ((string) $type) {
|
||||
* \Log::info("A $type has been deleted");
|
||||
* });
|
||||
*
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
|
||||
return [
|
||||
'deleted' => $deleted,
|
||||
'error' => $error,
|
||||
'theme' => Request::input('theme')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a template
|
||||
*/
|
||||
public function onDelete(): void
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('templateType');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$this->loadTemplate($type, trim(Request::input('templatePath')))->delete();
|
||||
|
||||
/*
|
||||
* Extensibility - documented above
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of available templates
|
||||
*/
|
||||
public function onGetTemplateList(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$page = Page::inTheme($this->theme);
|
||||
return [
|
||||
'layouts' => $page->getLayoutOptions()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers an open or closed state for a supplied token, for example, component folders.
|
||||
*/
|
||||
public function onExpandMarkupToken(): string
|
||||
{
|
||||
if (!$alias = post('tokenName')) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.no_records'));
|
||||
}
|
||||
|
||||
// Can only expand components at this stage
|
||||
if ((!$type = post('tokenType')) && $type !== 'component') {
|
||||
throw new ApplicationException("Unsupported token type: $type");
|
||||
}
|
||||
|
||||
if (!($names = (array) post('component_names')) || !($aliases = (array) post('component_aliases'))) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.not_found', ['name' => $alias]));
|
||||
}
|
||||
|
||||
if (($index = array_get(array_flip($aliases), $alias, false)) === false) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.not_found', ['name' => $alias]));
|
||||
}
|
||||
|
||||
if (!$componentName = array_get($names, $index)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.not_found', ['name' => $alias]));
|
||||
}
|
||||
|
||||
$manager = ComponentManager::instance();
|
||||
$componentObj = $manager->makeComponent($componentName);
|
||||
/**
|
||||
* @var ?ComponentPartial
|
||||
*/
|
||||
$partial = ComponentPartial::load($componentObj, 'default');
|
||||
|
||||
if (!$partial) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.no_default_partial'));
|
||||
}
|
||||
|
||||
$content = $partial->getContent();
|
||||
$content = str_replace('__SELF__', $alias, $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits the DB changes of a template to the filesystem
|
||||
*/
|
||||
public function onCommit(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('templateType');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$template = $this->loadTemplate($type, trim(Request::input('templatePath')));
|
||||
|
||||
if ($this->canCommitTemplate($template)) {
|
||||
// Populate the filesystem with the template and then remove it from the db
|
||||
/**
|
||||
* @var AutoDatasource
|
||||
*/
|
||||
$datasource = $this->getThemeDatasource();
|
||||
$datasource->pushToSource($template, 'filesystem');
|
||||
$datasource->removeFromSource($template, 'database');
|
||||
|
||||
Flash::success(Lang::get('cms::lang.editor.commit_success', ['type' => $type]));
|
||||
}
|
||||
|
||||
return array_merge($this->getUpdateResponse($template, $type), ['forceReload' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a template to the version on the filesystem
|
||||
*/
|
||||
public function onReset(): array
|
||||
{
|
||||
$this->validateRequestTheme();
|
||||
|
||||
$type = Request::input('templateType');
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$template = $this->loadTemplate($type, trim(Request::input('templatePath')));
|
||||
|
||||
if ($this->canResetTemplate($template)) {
|
||||
// Remove the template from the DB
|
||||
/**
|
||||
* @var AutoDatasource
|
||||
*/
|
||||
$datasource = $this->getThemeDatasource();
|
||||
$datasource->removeFromSource($template, 'database');
|
||||
|
||||
Flash::success(Lang::get('cms::lang.editor.reset_success', ['type' => $type]));
|
||||
}
|
||||
|
||||
return array_merge($this->getUpdateResponse($template, $type), ['forceReload' => true]);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for internal use
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the response to return in an AJAX request that updates a template
|
||||
*
|
||||
* @param object $template The template that has been affected
|
||||
* @param string $type The type of template being affected
|
||||
*/
|
||||
protected function getUpdateResponse($template, string $type): array
|
||||
{
|
||||
$result = [
|
||||
'templatePath' => $template->fileName,
|
||||
'templateMtime' => $template->mtime,
|
||||
'tabTitle' => $this->getTabTitle($type, $template)
|
||||
];
|
||||
|
||||
if ($type === 'page') {
|
||||
$result['pageUrl'] = Url::to($template->url);
|
||||
$router = new Router($this->theme);
|
||||
$router->clearCache();
|
||||
CmsCompoundObject::clearCache($this->theme);
|
||||
}
|
||||
|
||||
$result['canCommit'] = $this->canCommitTemplate($template);
|
||||
$result['canReset'] = $this->canResetTemplate($template);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active theme's datasource
|
||||
*/
|
||||
protected function getThemeDatasource(): DatasourceInterface
|
||||
{
|
||||
return $this->theme->getDatasource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the provided template can be committed
|
||||
* Only available in debug mode, the DB layer must be enabled, and the template must exist in the database
|
||||
*/
|
||||
protected function canCommitTemplate(CmsObject|Asset $template): bool
|
||||
{
|
||||
if (
|
||||
$template instanceof CmsObject === false
|
||||
|| Config::get('app.debug', false)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
|
||||
if (Theme::databaseLayerEnabled()) {
|
||||
/**
|
||||
* @var AutoDatasource
|
||||
*/
|
||||
$datasource = $this->getThemeDatasource();
|
||||
$result = $datasource->sourceHasModel('database', $template);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the provided template can be reset
|
||||
* Only available when the DB layer is enabled and the template exists in both the DB & Filesystem
|
||||
*/
|
||||
protected function canResetTemplate(CmsObject|Asset $template): bool
|
||||
{
|
||||
if ($template instanceof CmsObject === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
|
||||
if (Theme::databaseLayerEnabled()) {
|
||||
/**
|
||||
* @var AutoDatasource
|
||||
*/
|
||||
$datasource = $this->getThemeDatasource();
|
||||
$result = $datasource->sourceHasModel('database', $template) && $datasource->sourceHasModel('filesystem', $template);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the current request is within the active theme
|
||||
* @throws ApplicationException if the requested theme does not match the currently loaded theme
|
||||
*/
|
||||
protected function validateRequestTheme(): void
|
||||
{
|
||||
if ($this->theme->getDirName() != Request::input('theme')) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.theme.edit.not_match'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given request type is a valid type, and that the user has the relevant
|
||||
* permission to access it.
|
||||
*
|
||||
* @throws AuthorizationException if the user doesn't have permission to access the type
|
||||
*/
|
||||
protected function validateRequestType(string $type): void
|
||||
{
|
||||
$this->resolveTypeClassName($type);
|
||||
|
||||
if (!$this->user?->hasAccess($this->getRelevantPermissionForType($type))) {
|
||||
throw new AuthorizationException(Lang::get('cms::lang.template.type_not_permitted', [
|
||||
'type' => str_plural($type),
|
||||
'permission' => $this->getRelevantPermissionForType($type),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relevant permission required for a specific template type.
|
||||
*/
|
||||
protected function getRelevantPermissionForType(string $type): string
|
||||
{
|
||||
return match ($type) {
|
||||
'page' => 'cms.manage_pages',
|
||||
'partial' => 'cms.manage_partials',
|
||||
'layout' => 'cms.manage_layouts',
|
||||
'content' => 'cms.manage_content',
|
||||
'asset' => 'cms.manage_assets',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a template type to its class name
|
||||
*/
|
||||
protected function resolveTypeClassName(string $type): string
|
||||
{
|
||||
$types = [
|
||||
'page' => Page::class,
|
||||
'partial' => Partial::class,
|
||||
'layout' => Layout::class,
|
||||
'content' => Content::class,
|
||||
'asset' => Asset::class
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $types)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.template.invalid_type'));
|
||||
}
|
||||
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing template of a given type
|
||||
*/
|
||||
protected function loadTemplate(string $type, string $path): CmsObject|Asset
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = call_user_func([$class, 'load'], $this->theme, $path))) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @event cms.template.processSettingsAfterLoad
|
||||
* Fires immediately after a CMS template (page|partial|layout|content|asset) has been loaded and provides an opportunity to interact with it.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.template.processSettingsAfterLoad', function ((\Cms\Controllers\Index) $controller, (mixed) $templateObject) {
|
||||
* // Make some modifications to the $template object
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $CmsIndexController->bindEvent('template.processSettingsAfterLoad', function ((mixed) $templateObject) {
|
||||
* // Make some modifications to the $template object
|
||||
* });
|
||||
*
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.processSettingsAfterLoad', [$template]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new template of a given type
|
||||
* @throws ApplicationException if the requested type can't be initialized with the current theme
|
||||
*/
|
||||
protected function createTemplate(string $type): CmsObject|Asset
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = $class::inTheme($this->theme))) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text for a template tab
|
||||
*/
|
||||
protected function getTabTitle(string $type, CmsObject|Asset $template): string
|
||||
{
|
||||
if ($type === 'page') {
|
||||
$result = $template->title ?: $template->getFileName();
|
||||
if (!$result) {
|
||||
$result = Lang::get('cms::lang.page.new');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($type === 'partial' || $type === 'layout' || $type === 'content' || $type === 'asset') {
|
||||
$result = in_array($type, ['asset', 'content']) ? $template->getFileName() : $template->getBaseFileName();
|
||||
if (!$result) {
|
||||
$result = Lang::get('cms::lang.'.$type.'.new');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $template->getFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a form widget for a specified template type.
|
||||
*/
|
||||
protected function makeTemplateFormWidget(string $type, CmsObject|Asset $template, ?string $alias = null): Form
|
||||
{
|
||||
$this->validateRequestType($type);
|
||||
|
||||
$formConfigs = [
|
||||
'page' => '~/modules/cms/classes/page/fields.yaml',
|
||||
'partial' => '~/modules/cms/classes/partial/fields.yaml',
|
||||
'layout' => '~/modules/cms/classes/layout/fields.yaml',
|
||||
'content' => '~/modules/cms/classes/content/fields.yaml',
|
||||
'asset' => '~/modules/cms/classes/asset/fields.yaml'
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $formConfigs)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
$widgetConfig = $this->makeConfig($formConfigs[$type]);
|
||||
|
||||
$ext = pathinfo($template->fileName, PATHINFO_EXTENSION);
|
||||
if ($type === 'content') {
|
||||
switch ($ext) {
|
||||
case 'htm':
|
||||
$type = 'richeditor';
|
||||
break;
|
||||
case 'md':
|
||||
$type = 'markdown';
|
||||
break;
|
||||
default:
|
||||
$type = 'codeeditor';
|
||||
break;
|
||||
}
|
||||
array_set($widgetConfig->secondaryTabs, 'fields.markup.type', $type);
|
||||
}
|
||||
|
||||
$codeField = ($template instanceof Asset) ? 'content' : 'markup';
|
||||
|
||||
$lang = match ($ext) {
|
||||
'', 'htm' => 'twig',
|
||||
'html' => 'html',
|
||||
'css' => 'css',
|
||||
'js', 'json' => 'javascript',
|
||||
'less' => 'less',
|
||||
'sass', 'scss' => 'scss',
|
||||
'txt' => 'txt',
|
||||
default => 'php',
|
||||
};
|
||||
|
||||
if (array_get($widgetConfig->secondaryTabs, "fields.$codeField.type") === 'codeeditor') {
|
||||
array_set($widgetConfig->secondaryTabs, "fields.$codeField.language", $lang);
|
||||
}
|
||||
|
||||
$widgetConfig->model = $template;
|
||||
$widgetConfig->alias = $alias ?: 'form'.studly_case($type).md5($template->exists ? $template->getFileName() : uniqid());
|
||||
|
||||
return $this->makeWidget(Form::class, $widgetConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the component settings so they are ready to be saved.
|
||||
* @param array $settings The new settings for this template.
|
||||
* @param array $prevSettings The previous settings for this template.
|
||||
*/
|
||||
protected function upgradeSettings($settings, $prevSettings): array
|
||||
{
|
||||
/*
|
||||
* Handle component usage
|
||||
*/
|
||||
$componentProperties = post('component_properties');
|
||||
$componentNames = post('component_names');
|
||||
$componentAliases = post('component_aliases');
|
||||
|
||||
if ($componentProperties !== null) {
|
||||
if ($componentNames === null || $componentAliases === null) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
$count = count($componentProperties);
|
||||
if (count($componentNames) != $count || count($componentAliases) != $count) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
for ($index = 0; $index < $count; $index++) {
|
||||
$componentName = $componentNames[$index];
|
||||
$componentAlias = $componentAliases[$index];
|
||||
|
||||
$isSoftComponent = (substr($componentAlias, 0, 1) === '@');
|
||||
$componentName = ltrim($componentName, '@');
|
||||
$componentAlias = ltrim($componentAlias, '@');
|
||||
|
||||
if ($componentAlias !== $componentName) {
|
||||
$section = $componentName . ' ' . $componentAlias;
|
||||
} else {
|
||||
$section = $componentName;
|
||||
}
|
||||
if ($isSoftComponent) {
|
||||
$section = '@' . $section;
|
||||
}
|
||||
|
||||
$properties = json_decode($componentProperties[$index], true);
|
||||
unset($properties['oc.alias'], $properties['inspectorProperty'], $properties['inspectorClassName']);
|
||||
|
||||
if (!$properties) {
|
||||
$oldComponentSettings = array_key_exists($section, $prevSettings['components'])
|
||||
? $prevSettings['components'][$section]
|
||||
: null;
|
||||
if ($isSoftComponent && $oldComponentSettings) {
|
||||
$settings[$section] = $oldComponentSettings;
|
||||
} else {
|
||||
$settings[$section] = $properties;
|
||||
}
|
||||
} else {
|
||||
$settings[$section] = $properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle view bag
|
||||
*/
|
||||
$viewBag = post('viewBag');
|
||||
if ($viewBag !== null) {
|
||||
$settings['viewBag'] = $viewBag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @event cms.template.processSettingsBeforeSave
|
||||
* Fires before a CMS template (page|partial|layout|content|asset) is saved and provides an opportunity to interact with the settings data. `$dataHolder` = {settings: []}
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.template.processSettingsBeforeSave', function ((\Cms\Controllers\Index) $controller, (object) $dataHolder) {
|
||||
* // Make some modifications to the $dataHolder object
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $CmsIndexController->bindEvent('template.processSettingsBeforeSave', function ((object) $dataHolder) {
|
||||
* // Make some modifications to the $dataHolder object
|
||||
* });
|
||||
*
|
||||
*/
|
||||
$dataHolder = (object) ['settings' => $settings];
|
||||
$this->fireSystemEvent('cms.template.processSettingsBeforeSave', [$dataHolder]);
|
||||
|
||||
return $dataHolder->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a given component by its alias.
|
||||
*
|
||||
* If found, this will return the component's name, alias and properties.
|
||||
*
|
||||
* @param string $aliasQuery The alias to search for
|
||||
* @param array $components The array of components to look within.
|
||||
*/
|
||||
protected function findComponentByAlias(string $aliasQuery, array $components = []): ?array
|
||||
{
|
||||
$found = null;
|
||||
|
||||
foreach ($components as $name => $properties) {
|
||||
list($name, $alias) = strpos($name, ' ') ? explode(' ', $name) : [$name, $name];
|
||||
|
||||
if (ltrim($alias, '@') === ltrim($aliasQuery, '@')) {
|
||||
$found = [
|
||||
'name' => ltrim($name, '@'),
|
||||
'alias' => $alias,
|
||||
'properties' => $properties
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the active form widget to the controller
|
||||
*/
|
||||
protected function bindFormWidgetToController(): void
|
||||
{
|
||||
$alias = Request::input('formWidgetAlias');
|
||||
$type = Request::input('templateType');
|
||||
if (!empty(Request::input('templatePath'))) {
|
||||
$object = $this->loadTemplate($type, Request::input('templatePath'));
|
||||
} else {
|
||||
$object = $this->createTemplate($type);
|
||||
}
|
||||
$widget = $this->makeTemplateFormWidget($type, $object, $alias);
|
||||
|
||||
$widget->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces Windows style (/r/n) line endings with unix style (/n)
|
||||
* line endings.
|
||||
*/
|
||||
protected function convertLineEndings(string $markup): string
|
||||
{
|
||||
return str_replace(["\r\n", "\r"], "\n", $markup);
|
||||
}
|
||||
}
|
||||
22
modules/cms/controllers/Media.php
Normal file
22
modules/cms/controllers/Media.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php namespace Cms\Controllers;
|
||||
|
||||
use Backend\Controllers\Media as MediaController;
|
||||
|
||||
/**
|
||||
* CMS Media Manager
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
* @deprecated Use Backend\Controllers\Media. Remove if year >= 2020.
|
||||
*/
|
||||
class Media extends MediaController
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
traceLog('Controller Cms\Controllers\Media has been deprecated, use ' . MediaController::class . ' instead.');
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
81
modules/cms/controllers/ThemeLogs.php
Normal file
81
modules/cms/controllers/ThemeLogs.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php namespace Cms\Controllers;
|
||||
|
||||
use Lang;
|
||||
use Flash;
|
||||
use BackendMenu;
|
||||
use Backend\Classes\Controller;
|
||||
use System\Classes\SettingsManager;
|
||||
use Cms\Models\ThemeLog;
|
||||
|
||||
/**
|
||||
* Request Logs controller
|
||||
*
|
||||
* @package winter\wn-system-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ThemeLogs extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array Extensions implemented by this controller.
|
||||
*/
|
||||
public $implement = [
|
||||
\Backend\Behaviors\FormController::class,
|
||||
\Backend\Behaviors\ListController::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Permissions required to view this page.
|
||||
*/
|
||||
public $requiredPermissions = ['system.access_logs'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
BackendMenu::setContext('Winter.System', 'system', 'settings');
|
||||
SettingsManager::setContext('Winter.Cms', 'theme_logs');
|
||||
}
|
||||
|
||||
public function index_onRefresh()
|
||||
{
|
||||
return $this->listRefresh();
|
||||
}
|
||||
|
||||
public function index_onEmptyLog()
|
||||
{
|
||||
ThemeLog::truncate();
|
||||
Flash::success(Lang::get('cms::lang.theme_log.empty_success'));
|
||||
return $this->listRefresh();
|
||||
}
|
||||
|
||||
public function index_onDelete()
|
||||
{
|
||||
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
|
||||
foreach ($checkedIds as $recordId) {
|
||||
if (!$record = ThemeLog::find($recordId)) {
|
||||
continue;
|
||||
}
|
||||
$record->delete();
|
||||
}
|
||||
|
||||
Flash::success(Lang::get('backend::lang.list.delete_selected_success'));
|
||||
}
|
||||
else {
|
||||
Flash::error(Lang::get('backend::lang.list.delete_selected_empty'));
|
||||
}
|
||||
|
||||
return $this->listRefresh();
|
||||
}
|
||||
|
||||
public function preview($id)
|
||||
{
|
||||
$this->addCss('/modules/cms/assets/css/themelogs/template-diff.css', 'core');
|
||||
$this->addJs('/modules/cms/assets/vendor/jsdiff/diff.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/themelogs/template-diff.js', 'core');
|
||||
|
||||
return $this->asExtension('FormController')->preview($id);
|
||||
}
|
||||
}
|
||||
144
modules/cms/controllers/ThemeOptions.php
Normal file
144
modules/cms/controllers/ThemeOptions.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php namespace Cms\Controllers;
|
||||
|
||||
use Backend;
|
||||
use BackendMenu;
|
||||
use ApplicationException;
|
||||
use Cms\Models\ThemeData;
|
||||
use Cms\Classes\Theme as CmsTheme;
|
||||
use System\Classes\SettingsManager;
|
||||
use Backend\Classes\Controller;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Theme customization controller
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class ThemeOptions extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array Extensions implemented by this controller.
|
||||
*/
|
||||
public $implement = [
|
||||
\Backend\Behaviors\FormController::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Permissions required to view this page.
|
||||
*/
|
||||
public $requiredPermissions = ['cms.manage_themes', 'cms.manage_theme_options'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->pageTitle = 'cms::lang.theme.settings_menu';
|
||||
|
||||
BackendMenu::setContext('Winter.System', 'system', 'settings');
|
||||
SettingsManager::setContext('Winter.Cms', 'theme');
|
||||
}
|
||||
|
||||
public function update($dirName = null)
|
||||
{
|
||||
$dirName = $this->getDirName($dirName);
|
||||
|
||||
try {
|
||||
$model = $this->getThemeData($dirName);
|
||||
|
||||
$this->asExtension('FormController')->update($model->id);
|
||||
|
||||
$this->vars['hasCustomData'] = $this->hasThemeData($dirName);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
public function update_onSave($dirName = null)
|
||||
{
|
||||
$model = $this->getThemeData($this->getDirName($dirName));
|
||||
$result = $this->asExtension('FormController')->update_onSave($model->id);
|
||||
|
||||
// Redirect close requests to the settings index when user doesn't have access
|
||||
// to go back to the theme selection page
|
||||
if (!$this->user->hasAccess('cms.manage_themes') && input('close')) {
|
||||
$result = Backend::redirect('system/settings');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function update_onResetDefault($dirName = null)
|
||||
{
|
||||
$model = $this->getThemeData($this->getDirName($dirName));
|
||||
$model->delete();
|
||||
|
||||
return Backend::redirect('cms/themeoptions/update/'.$dirName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form fields defined in theme.yaml
|
||||
*/
|
||||
public function formExtendFieldsBefore($form)
|
||||
{
|
||||
$model = $form->model;
|
||||
$theme = $this->findThemeObject($model->theme);
|
||||
$form->config = $this->mergeConfig($form->config, $theme->getFormConfig());
|
||||
$form->init();
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
/**
|
||||
* Default to the active theme if user doesn't have access to manage all themes
|
||||
*
|
||||
* @param string $dirName
|
||||
* @return string
|
||||
*/
|
||||
protected function getDirName(?string $dirName = null)
|
||||
{
|
||||
/*
|
||||
* Only the active theme can be managed without this permission
|
||||
*/
|
||||
if ($dirName && !$this->user->hasAccess('cms.manage_themes')) {
|
||||
$dirName = null;
|
||||
}
|
||||
|
||||
if ($dirName === null) {
|
||||
$dirName = CmsTheme::getActiveThemeCode();
|
||||
}
|
||||
|
||||
return $dirName;
|
||||
}
|
||||
|
||||
protected function hasThemeData($dirName)
|
||||
{
|
||||
return $this->findThemeObject($dirName)->hasCustomData();
|
||||
}
|
||||
|
||||
protected function getThemeData($dirName)
|
||||
{
|
||||
$theme = $this->findThemeObject($dirName);
|
||||
return ThemeData::forTheme($theme);
|
||||
}
|
||||
|
||||
protected function findThemeObject($name = null)
|
||||
{
|
||||
if ($name === null) {
|
||||
$name = post('theme');
|
||||
}
|
||||
|
||||
if (!$name || (!$theme = CmsTheme::load($name))) {
|
||||
throw new ApplicationException(trans('cms::lang.theme.not_found_name', ['name' => $name]));
|
||||
}
|
||||
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
359
modules/cms/controllers/Themes.php
Normal file
359
modules/cms/controllers/Themes.php
Normal file
@@ -0,0 +1,359 @@
|
||||
<?php namespace Cms\Controllers;
|
||||
|
||||
use ApplicationException;
|
||||
use Artisan;
|
||||
use Backend;
|
||||
use Backend\Classes\Controller;
|
||||
use Backend\Models\BrandSetting;
|
||||
use Backend\Widgets\Form;
|
||||
use BackendMenu;
|
||||
use Cms\Classes\Theme as CmsTheme;
|
||||
use Cms\Classes\ThemeManager;
|
||||
use Cms\Helpers\Cms as CmsHelper;
|
||||
use Cms\Models\ThemeExport;
|
||||
use Cms\Models\ThemeImport;
|
||||
use Exception;
|
||||
use File;
|
||||
use Flash;
|
||||
use Lang;
|
||||
use Redirect;
|
||||
use System\Classes\SettingsManager;
|
||||
use Url;
|
||||
use ValidationException;
|
||||
use Winter\Storm\Support\Str;
|
||||
|
||||
/**
|
||||
* Theme selector controller
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class Themes extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array Permissions required to view this page.
|
||||
*/
|
||||
public $requiredPermissions = [
|
||||
'cms.manage_themes',
|
||||
'cms.manage_theme_options',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->addCss('/modules/cms/assets/css/winter.theme-selector.css', 'core');
|
||||
|
||||
$this->pageTitle = 'cms::lang.theme.settings_menu';
|
||||
BackendMenu::setContext('Winter.System', 'system', 'settings');
|
||||
SettingsManager::setContext('Winter.Cms', 'theme');
|
||||
|
||||
/*
|
||||
* Custom redirect for unauthorized request
|
||||
*/
|
||||
$this->bindEvent('page.beforeDisplay', function () {
|
||||
if (!$this->user->hasAccess('cms.manage_themes')) {
|
||||
return Backend::redirect('cms/themeoptions/update');
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Enable AJAX for Form widgets
|
||||
*/
|
||||
if (post('mode') === 'import') {
|
||||
$this->makeImportFormWidget($this->findThemeObject())->bindToController();
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->bodyClass = 'compact-container';
|
||||
}
|
||||
|
||||
public function index_onSetActiveTheme()
|
||||
{
|
||||
CmsTheme::setActiveTheme(post('theme'));
|
||||
|
||||
return [
|
||||
'#theme-list' => $this->makePartial('theme_list')
|
||||
];
|
||||
}
|
||||
|
||||
public function index_onDelete()
|
||||
{
|
||||
ThemeManager::instance()->deleteTheme(post('theme'));
|
||||
|
||||
Flash::success(Lang::get('cms::lang.theme.delete_theme_success'));
|
||||
return Redirect::refresh();
|
||||
}
|
||||
|
||||
//
|
||||
// Theme properties
|
||||
//
|
||||
|
||||
public function index_onLoadFieldsForm()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$this->vars['widget'] = $this->makeFieldsFormWidget($theme);
|
||||
$this->vars['themeDir'] = $theme->getDirName();
|
||||
|
||||
return $this->makePartial('theme_fields_form');
|
||||
}
|
||||
|
||||
public function index_onSaveFields()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$widget = $this->makeFieldsFormWidget($theme);
|
||||
$theme->writeConfig($widget->getSaveData());
|
||||
|
||||
return ['#themeListItem-'.$theme->getId() => $this->makePartial('theme_list_item', ['theme' => $theme])];
|
||||
}
|
||||
|
||||
protected function makeFieldsFormWidget($theme)
|
||||
{
|
||||
$widgetConfig = $this->makeConfig('~/modules/cms/classes/theme/fields.yaml');
|
||||
$widgetConfig->alias = 'form'.studly_case($theme->getDirName());
|
||||
$widgetConfig->model = $theme;
|
||||
$widgetConfig->data = $theme->getConfig();
|
||||
$widgetConfig->data['dir_name'] = $theme->getDirName();
|
||||
$widgetConfig->arrayName = 'Theme';
|
||||
$widgetConfig->context = 'update';
|
||||
|
||||
return $this->makeWidget(Form::class, $widgetConfig);
|
||||
}
|
||||
|
||||
//
|
||||
// Create theme
|
||||
//
|
||||
|
||||
public function index_onLoadCreateForm()
|
||||
{
|
||||
$this->vars['widget'] = $this->makeCreateFormWidget();
|
||||
return $this->makePartial('theme_create_form');
|
||||
}
|
||||
|
||||
public function index_onCreate()
|
||||
{
|
||||
$widget = $this->makeCreateFormWidget();
|
||||
$data = $widget->getSaveData();
|
||||
$newDirName = trim(array_get($data, 'dir_name'));
|
||||
$destinationPath = themes_path($newDirName);
|
||||
|
||||
$data = array_except($data, 'dir_name');
|
||||
|
||||
if (!strlen(trim(array_get($data, 'name')))) {
|
||||
throw new ValidationException(['name' => Lang::get('cms::lang.theme.create_theme_required_name')]);
|
||||
}
|
||||
|
||||
if (!CmsTheme::isValidDirName($newDirName)) {
|
||||
throw new ValidationException(['dir_name' => Lang::get('cms::lang.theme.dir_name_invalid')]);
|
||||
}
|
||||
|
||||
if (File::isDirectory($destinationPath)) {
|
||||
throw new ValidationException(['dir_name' => Lang::get('cms::lang.theme.dir_name_taken')]);
|
||||
}
|
||||
|
||||
switch ($data['scaffold']) {
|
||||
case 'empty':
|
||||
File::makeDirectory($destinationPath);
|
||||
File::makeDirectory($destinationPath.'/assets');
|
||||
File::makeDirectory($destinationPath.'/content');
|
||||
File::makeDirectory($destinationPath.'/layouts');
|
||||
File::makeDirectory($destinationPath.'/pages');
|
||||
File::makeDirectory($destinationPath.'/partials');
|
||||
File::put($destinationPath.'/theme.yaml', '');
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
Artisan::call('create:theme', [
|
||||
'theme' => $newDirName,
|
||||
'scaffold' => $data['scaffold']
|
||||
]);
|
||||
} catch (\Exception $ex) {
|
||||
throw new ApplicationException($ex->getMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
unset($data['scaffold']);
|
||||
|
||||
$theme = CmsTheme::load($newDirName);
|
||||
$theme->writeConfig($data);
|
||||
|
||||
Flash::success(Lang::get('cms::lang.theme.create_theme_success'));
|
||||
return Redirect::refresh();
|
||||
}
|
||||
|
||||
protected function makeCreateFormWidget()
|
||||
{
|
||||
$theme = new CmsTheme;
|
||||
$theme->setDirName('newtheme');
|
||||
$widgetConfig = $this->makeConfig('~/modules/cms/classes/theme/fields.yaml');
|
||||
$widgetConfig->alias = 'formCreateTheme';
|
||||
$widgetConfig->model = $theme;
|
||||
$widgetConfig->arrayName = 'Theme';
|
||||
$widgetConfig->context = 'create';
|
||||
|
||||
// Setup default values
|
||||
$name = BrandSetting::get('app_name');
|
||||
$slug = Str::slug($name);
|
||||
$url = Url::to('/');
|
||||
$widgetConfig->data = [
|
||||
'name' => $name,
|
||||
'dir_name' => $slug,
|
||||
'description' => Lang::get('cms::lang.theme.default_description', ['url' => $url]),
|
||||
'code' => $slug,
|
||||
'author' => $this->user->full_name,
|
||||
'homepage' => $url,
|
||||
];
|
||||
|
||||
return $this->makeWidget('Backend\Widgets\Form', $widgetConfig);
|
||||
}
|
||||
|
||||
//
|
||||
// Duplicate
|
||||
//
|
||||
|
||||
public function index_onLoadDuplicateForm()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$this->vars['themeDir'] = $theme->getDirName();
|
||||
|
||||
return $this->makePartial('theme_duplicate_form');
|
||||
}
|
||||
|
||||
public function index_onDuplicateTheme()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$newDirName = trim(post('new_dir_name'));
|
||||
$sourcePath = $theme->getPath();
|
||||
$destinationPath = themes_path().'/'.$newDirName;
|
||||
|
||||
if (!CmsTheme::isValidDirName($newDirName)) {
|
||||
throw new ValidationException(['new_dir_name' => Lang::get('cms::lang.theme.dir_name_invalid')]);
|
||||
}
|
||||
|
||||
if (File::isDirectory($destinationPath)) {
|
||||
throw new ValidationException(['new_dir_name' => Lang::get('cms::lang.theme.dir_name_taken')]);
|
||||
}
|
||||
|
||||
File::copyDirectory($sourcePath, $destinationPath);
|
||||
$newTheme = CmsTheme::load($newDirName);
|
||||
$newName = $newTheme->getConfigValue('name') . ' - Copy';
|
||||
$newTheme->writeConfig(['name' => $newName]);
|
||||
|
||||
Flash::success(Lang::get('cms::lang.theme.duplicate_theme_success'));
|
||||
return Redirect::refresh();
|
||||
}
|
||||
|
||||
//
|
||||
// Theme export
|
||||
//
|
||||
|
||||
public function index_onLoadExportForm()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$this->vars['widget'] = $this->makeExportFormWidget($theme);
|
||||
$this->vars['themeDir'] = $theme->getDirName();
|
||||
|
||||
return $this->makePartial('theme_export_form');
|
||||
}
|
||||
|
||||
public function index_onExport()
|
||||
{
|
||||
$theme = $this->findThemeObject();
|
||||
$widget = $this->makeExportFormWidget($theme);
|
||||
|
||||
$model = new ThemeExport;
|
||||
$file = $model->export($theme, $widget->getSaveData());
|
||||
|
||||
return Backend::redirect('cms/themes/download/'.$file.'/'.$theme->getDirName().'.zip');
|
||||
}
|
||||
|
||||
public function download($name, $outputName = null)
|
||||
{
|
||||
try {
|
||||
$this->pageTitle = 'Download theme export archive';
|
||||
return ThemeExport::download($name, $outputName);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected function makeExportFormWidget($theme)
|
||||
{
|
||||
$widgetConfig = $this->makeConfig('~/modules/cms/models/themeexport/fields.yaml');
|
||||
$widgetConfig->alias = 'form'.studly_case($theme->getDirName());
|
||||
$widgetConfig->model = new ThemeExport;
|
||||
$widgetConfig->model->theme = $theme;
|
||||
$widgetConfig->arrayName = 'ThemeExport';
|
||||
|
||||
return $this->makeWidget('Backend\Widgets\Form', $widgetConfig);
|
||||
}
|
||||
|
||||
//
|
||||
// Theme import
|
||||
//
|
||||
|
||||
public function index_onLoadImportForm()
|
||||
{
|
||||
if (CmsHelper::safeModeEnabled()) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.cms_object.safe_mode_enabled'));
|
||||
}
|
||||
|
||||
$theme = $this->findThemeObject();
|
||||
$this->vars['widget'] = $this->makeImportFormWidget($theme);
|
||||
$this->vars['themeDir'] = $theme->getDirName();
|
||||
|
||||
return $this->makePartial('theme_import_form');
|
||||
}
|
||||
|
||||
public function index_onImport()
|
||||
{
|
||||
if (CmsHelper::safeModeEnabled()) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.cms_object.safe_mode_enabled'));
|
||||
}
|
||||
|
||||
$theme = $this->findThemeObject();
|
||||
$widget = $this->makeImportFormWidget($theme);
|
||||
|
||||
$model = new ThemeImport;
|
||||
$model->import($theme, $widget->getSaveData(), $widget->getSessionKey());
|
||||
|
||||
Flash::success(Lang::get('cms::lang.theme.import_theme_success'));
|
||||
return Redirect::refresh();
|
||||
}
|
||||
|
||||
protected function makeImportFormWidget($theme)
|
||||
{
|
||||
$widgetConfig = $this->makeConfig('~/modules/cms/models/themeimport/fields.yaml');
|
||||
$widgetConfig->alias = 'form'.studly_case($theme->getDirName());
|
||||
$widgetConfig->model = new ThemeImport;
|
||||
$widgetConfig->model->theme = $theme;
|
||||
$widgetConfig->arrayName = 'ThemeImport';
|
||||
|
||||
return $this->makeWidget('Backend\Widgets\Form', $widgetConfig);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function findThemeObject($name = null)
|
||||
{
|
||||
if ($name === null) {
|
||||
$name = post('theme');
|
||||
}
|
||||
|
||||
if (!$name || (!$theme = CmsTheme::load($name))) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.theme.not_found_name', ['name' => $name]));
|
||||
}
|
||||
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
14
modules/cms/controllers/index/_button_commit.php
Normal file
14
modules/cms/controllers/index/_button_commit.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<button
|
||||
type="button"
|
||||
class="
|
||||
btn btn-danger wn-icon-download
|
||||
<?php if (!$canCommit): ?>
|
||||
hide
|
||||
<?php endif ?>
|
||||
"
|
||||
data-request="onCommit"
|
||||
data-request-confirm="<?= e(trans('cms::lang.editor.commit_confirm')) ?>"
|
||||
data-load-indicator="<?= e(trans('cms::lang.editor.committing')) ?>"
|
||||
data-control="commit-button">
|
||||
<?= e(trans('cms::lang.editor.commit')) ?>
|
||||
</button>
|
||||
8
modules/cms/controllers/index/_button_lastmodified.php
Normal file
8
modules/cms/controllers/index/_button_lastmodified.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php if (isset($lastModified)): ?>
|
||||
<span
|
||||
class="btn empty wn-icon-calendar"
|
||||
title="<?= e(trans('backend::lang.media.last_modified')) ?>: <?= $lastModified ?>"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right">
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
14
modules/cms/controllers/index/_button_reset.php
Normal file
14
modules/cms/controllers/index/_button_reset.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<button
|
||||
type="button"
|
||||
class="
|
||||
btn btn-danger wn-icon-bomb
|
||||
<?php if (!$canReset): ?>
|
||||
hide
|
||||
<?php endif ?>
|
||||
"
|
||||
data-request="onReset"
|
||||
data-request-confirm="<?= e(trans('cms::lang.editor.reset_confirm')) ?>"
|
||||
data-load-indicator="<?= e(trans('cms::lang.editor.resetting')) ?>"
|
||||
data-control="reset-button">
|
||||
<?= e(trans('cms::lang.editor.reset')) ?>
|
||||
</button>
|
||||
19
modules/cms/controllers/index/_common_toolbar_actions.php
Normal file
19
modules/cms/controllers/index/_common_toolbar_actions.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?= $this->makePartial('button_commit'); ?>
|
||||
|
||||
<?= $this->makePartial('button_reset'); ?>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="
|
||||
btn btn-danger empty wn-icon-trash-o
|
||||
<?php if (!$templatePath): ?>
|
||||
hide
|
||||
<?php endif ?>
|
||||
"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('cms::lang.' . $toolbarSource . '.delete_confirm_single')) ?>"
|
||||
data-request-success="$.wn.cmsPage.updateTemplateList('<?= $toolbarSource ?>'); $(this).trigger('close.oc.tab', [{force: true}])"
|
||||
data-control="delete-button">
|
||||
</button>
|
||||
|
||||
<?= $this->makePartial('button_lastmodified'); ?>
|
||||
29
modules/cms/controllers/index/_concurrency_resolve_form.php
Normal file
29
modules/cms/controllers/index/_concurrency_resolve_form.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?= Form::open(['onsubmit'=>'return false']) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.form.concurrency_file_changed_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><?= e(trans('backend::lang.form.concurrency_file_changed_description')) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
data-action="reload"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.reload')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
data-action="save"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
12
modules/cms/controllers/index/_content_toolbar.php
Normal file
12
modules/cms/controllers/index/_content_toolbar.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="form-buttons loading-indicator-container" data-toolbar-type="content">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary wn-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<?= $this->makePartial('common_toolbar_actions', ['toolbarSource' => 'content']); ?>
|
||||
</div>
|
||||
16
modules/cms/controllers/index/_form_page.php
Normal file
16
modules/cms/controllers/index/_form_page.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
|
||||
'data-inspector-external-parameters' => true
|
||||
]) ?>
|
||||
<?= $form->render() ?>
|
||||
|
||||
<input type="hidden" value="<?= e($form->alias) ?>" name="formWidgetAlias" />
|
||||
<input type="hidden" value="<?= ($templateType) ?>" name="templateType" />
|
||||
<input type="hidden" value="<?= ($templatePath) ?>" name="templatePath" />
|
||||
<input type="hidden" value="<?= ($templateTheme) ?>" name="theme" />
|
||||
<input type="hidden" value="<?= ($templateMtime) ?>" name="templateMtime" />
|
||||
<input type="hidden" value="0" name="templateForceSave" />
|
||||
|
||||
<?= Form::close() ?>
|
||||
12
modules/cms/controllers/index/_layout_toolbar.php
Normal file
12
modules/cms/controllers/index/_layout_toolbar.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="form-buttons loading-indicator-container">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary wn-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<?= $this->makePartial('common_toolbar_actions', ['toolbarSource' => 'layout']); ?>
|
||||
</div>
|
||||
28
modules/cms/controllers/index/_page_toolbar.php
Normal file
28
modules/cms/controllers/index/_page_toolbar.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<div class="form-buttons loading-indicator-container">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary wn-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<?php
|
||||
$pageUrl = isset($pageUrl) ? $pageUrl : null;
|
||||
?>
|
||||
<a
|
||||
href="<?= Url::to($pageUrl) ?>"
|
||||
target="_blank"
|
||||
class="
|
||||
btn btn-primary wn-icon-crosshairs
|
||||
<?php if (!$templatePath): ?>
|
||||
hide
|
||||
<?php endif ?>
|
||||
"
|
||||
data-control="preview-button">
|
||||
<?= e(trans('cms::lang.editor.preview')) ?>
|
||||
</a>
|
||||
|
||||
<?= $this->makePartial('common_toolbar_actions', ['toolbarSource' => 'page']); ?>
|
||||
</div>
|
||||
12
modules/cms/controllers/index/_partial_toolbar.php
Normal file
12
modules/cms/controllers/index/_partial_toolbar.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="form-buttons loading-indicator-container">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary wn-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<?= $this->makePartial('common_toolbar_actions', ['toolbarSource' => 'partial']); ?>
|
||||
</div>
|
||||
6
modules/cms/controllers/index/_safemode_notice.php
Normal file
6
modules/cms/controllers/index/_safemode_notice.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="callout callout-warning no-subheader">
|
||||
<div class="header" style="border-radius: 0">
|
||||
<i class="icon-warning"></i>
|
||||
<h3><?= e(trans('cms::lang.cms_object.safe_mode_enabled')) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
80
modules/cms/controllers/index/_sidepanel.php
Normal file
80
modules/cms/controllers/index/_sidepanel.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
$visibleCount = 0;
|
||||
?>
|
||||
<div class="layout control-scrollpanel" id="cms-side-panel">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative fix-button-container">
|
||||
<?php if ($this->user->hasAccess('cms.manage_pages')): ?>
|
||||
<!-- Pages -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="pages"
|
||||
data-template-type="page"
|
||||
data-type-icon="wn-icon-copy"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->pageList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php if ($this->user->hasAccess('cms.manage_partials')): ?>
|
||||
<!-- Partials -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="partials"
|
||||
data-template-type="partial"
|
||||
data-type-icon="wn-icon-tags"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->partialList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php if ($this->user->hasAccess('cms.manage_layouts')): ?>
|
||||
<!-- Layouts -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="layouts"
|
||||
data-template-type="layout"
|
||||
data-type-icon="wn-icon-th-large"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->layoutList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php if ($this->user->hasAccess('cms.manage_content')): ?>
|
||||
<!-- Content -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="content"
|
||||
data-template-type="content"
|
||||
data-type-icon="wn-icon-file-text-o"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->contentList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php if ($this->user->hasAccess('cms.manage_assets')): ?>
|
||||
<!-- Assets -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="assets"
|
||||
data-template-type="asset"
|
||||
data-type-icon="wn-icon-file-text-o"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->assetList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php if ($this->user->hasAccess(['cms.manage_pages', 'cms.manage_layouts', 'cms.manage_partials'], false)): ?>
|
||||
<!-- Components -->
|
||||
<form
|
||||
role="form"
|
||||
class="layout <?= ++$visibleCount == 1 ? '' : 'hide' ?>"
|
||||
data-content-id="components"
|
||||
onsubmit="return false"
|
||||
id="cms-component-list">
|
||||
<?= $this->widget->componentList->render() ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
7
modules/cms/controllers/index/config_content_list.yaml
Normal file
7
modules/cms/controllers/index/config_content_list.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
# ===================================
|
||||
# Configures the layout list widget
|
||||
# ===================================
|
||||
|
||||
noRecordsMessage: 'cms::lang.content.no_list_records'
|
||||
deleteConfirmation: 'cms::lang.content.delete_confirm_multiple'
|
||||
itemType: content
|
||||
8
modules/cms/controllers/index/config_layout_list.yaml
Normal file
8
modules/cms/controllers/index/config_layout_list.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
# ===================================
|
||||
# Configures the layout list widget
|
||||
# ===================================
|
||||
|
||||
descriptionProperty: description
|
||||
noRecordsMessage: 'cms::lang.layout.no_list_records'
|
||||
deleteConfirmation: 'cms::lang.layout.delete_confirm_multiple'
|
||||
itemType: layout
|
||||
15
modules/cms/controllers/index/config_page_list.yaml
Normal file
15
modules/cms/controllers/index/config_page_list.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
# ===================================
|
||||
# Configures the page list widget
|
||||
# ===================================
|
||||
|
||||
titleProperty: 'title'
|
||||
descriptionProperty: description
|
||||
descriptionProperties:
|
||||
url: URL
|
||||
noRecordsMessage: 'cms::lang.page.no_list_records'
|
||||
deleteConfirmation: 'cms::lang.page.delete_confirm_multiple'
|
||||
itemType: page
|
||||
sortingProperties:
|
||||
url: 'cms::lang.page.url'
|
||||
title: 'cms::lang.page.title'
|
||||
fileName: 'cms::lang.page.file_name'
|
||||
8
modules/cms/controllers/index/config_partial_list.yaml
Normal file
8
modules/cms/controllers/index/config_partial_list.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
# ===================================
|
||||
# Configures the partial list widget
|
||||
# ===================================
|
||||
|
||||
descriptionProperty: description
|
||||
noRecordsMessage: 'cms::lang.partial.no_list_records'
|
||||
deleteConfirmation: 'cms::lang.partial.delete_confirm_multiple'
|
||||
itemType: partial
|
||||
31
modules/cms/controllers/index/index.php
Normal file
31
modules/cms/controllers/index/index.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?= Block::put('sidepanel') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<?= $this->makePartial('sidepanel') ?>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
|
||||
<?= Block::put('body') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<div
|
||||
data-control="tab"
|
||||
data-closable
|
||||
data-close-confirmation="<?= e(trans('backend::lang.form.confirm_tab_close')) ?>"
|
||||
data-pane-classes="layout-cell"
|
||||
data-max-title-symbols="15"
|
||||
data-title-as-file-names="true"
|
||||
class="layout control-tabs master-tabs fancy-layout wn-logo-transparent"
|
||||
id="cms-master-tabs">
|
||||
|
||||
<div class="layout-row min-size">
|
||||
<div class="tabs-container">
|
||||
<ul class="nav nav-tabs"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content layout-row">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
7
modules/cms/controllers/media/index.php
Normal file
7
modules/cms/controllers/media/index.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?= Block::put('head') ?><?= Block::endPut() ?>
|
||||
|
||||
<?= Block::put('body') ?>
|
||||
<?= Form::open(['class'=>'layout', 'onsubmit'=>'return false']) ?>
|
||||
<?= $this->widget->manager->render() ?>
|
||||
<?= Form::close() ?>
|
||||
<?= Block::endPut() ?>
|
||||
3
modules/cms/controllers/themelogs/_field_content.php
Normal file
3
modules/cms/controllers/themelogs/_field_content.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="form-control">
|
||||
<pre><?= e($value.PHP_EOL) ?></pre>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="form-control">
|
||||
<pre
|
||||
data-plugin="template-diff"
|
||||
data-old-field-name="old_content"
|
||||
data-new-field-name="content"
|
||||
data-content-tag="pre"></pre>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div
|
||||
class="form-control"
|
||||
data-plugin="template-diff"
|
||||
data-old-field-name="old_template"
|
||||
data-diff-type="words"
|
||||
data-new-field-name="template">
|
||||
</div>
|
||||
1
modules/cms/controllers/themelogs/_field_template.php
Normal file
1
modules/cms/controllers/themelogs/_field_template.php
Normal file
@@ -0,0 +1 @@
|
||||
<div class="form-control"><?= e($value) ?></div>
|
||||
4
modules/cms/controllers/themelogs/_hint.php
Normal file
4
modules/cms/controllers/themelogs/_hint.php
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
<p>
|
||||
<?= e(trans('cms::lang.theme_log.hint')) ?>
|
||||
</p>
|
||||
22
modules/cms/controllers/themelogs/_hint_preview.php
Normal file
22
modules/cms/controllers/themelogs/_hint_preview.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php if ($formModel->type == $formModel::TYPE_DELETE): ?>
|
||||
<div class="callout fade in callout-danger no-subheader m-b">
|
||||
<div class="header">
|
||||
<i class="icon-minus"></i>
|
||||
<h3><?= e(trans('cms::lang.theme_log.template_deleted')) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($formModel->type == $formModel::TYPE_CREATE): ?>
|
||||
<div class="callout fade in callout-success no-subheader m-b">
|
||||
<div class="header">
|
||||
<i class="icon-plus"></i>
|
||||
<h3><?= e(trans('cms::lang.theme_log.template_created')) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="callout fade in callout-info no-subheader">
|
||||
<div class="header">
|
||||
<i class="icon-pencil"></i>
|
||||
<h3><?= e(trans('cms::lang.theme_log.template_updated')) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
31
modules/cms/controllers/themelogs/_list_toolbar.php
Normal file
31
modules/cms/controllers/themelogs/_list_toolbar.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<div data-control="toolbar" class="loading-indicator-container">
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-request="onRefresh"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.updating')) ?>"
|
||||
class="btn btn-primary wn-icon-refresh">
|
||||
<?= e(trans('backend::lang.list.refresh')) ?>
|
||||
</a>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-request="onEmptyLog"
|
||||
data-request-confirm="<?= e(trans('backend::lang.list.delete_selected_confirm')) ?>"
|
||||
data-load-indicator="<?= e(trans('cms::lang.theme_log.empty_loading')) ?>"
|
||||
class="btn btn-default wn-icon-eraser">
|
||||
<?= e(trans('cms::lang.theme_log.empty_link')) ?>
|
||||
</a>
|
||||
<button
|
||||
class="btn btn-danger wn-icon-trash-o"
|
||||
disabled="disabled"
|
||||
onclick="$(this).data('request-data', {
|
||||
checked: $('.control-list').listWidget('getChecked')
|
||||
})"
|
||||
data-request="onDelete"
|
||||
data-trigger-action="enable"
|
||||
data-trigger=".control-list input[type=checkbox]"
|
||||
data-trigger-condition="checked"
|
||||
data-request-success="$(this).prop('disabled', true)"
|
||||
data-stripe-load-indicator>
|
||||
<?= e(trans('backend::lang.list.delete_selected')) ?>
|
||||
</button>
|
||||
</div>
|
||||
18
modules/cms/controllers/themelogs/_preview_scoreboard.php
Normal file
18
modules/cms/controllers/themelogs/_preview_scoreboard.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4><?= e(trans('cms::lang.theme_log.id_label')) ?></h4>
|
||||
<p>#<?= e($formModel->id) ?></p>
|
||||
</div>
|
||||
<?php if ($formModel->user): ?>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4><?= e(trans('cms::lang.theme_log.user')) ?></h4>
|
||||
<p><?= e($formModel->user->full_name) ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4><?= e(trans('cms::lang.theme_log.created_at')) ?></h4>
|
||||
<p><?= Backend::dateTime($formModel->created_at) ?></p>
|
||||
</div>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4><?= e(trans('cms::lang.theme_log.theme_name')) ?></h4>
|
||||
<p><?= e($formModel->theme_name) ?></p>
|
||||
</div>
|
||||
16
modules/cms/controllers/themelogs/config_filter.yaml
Normal file
16
modules/cms/controllers/themelogs/config_filter.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# ===================================
|
||||
# Filter Scope Definitions
|
||||
# ===================================
|
||||
|
||||
scopes:
|
||||
|
||||
created_at:
|
||||
label: backend::lang.access_log.created_at
|
||||
type: daterange
|
||||
conditions: created_at >= ':after' AND created_at <= ':before'
|
||||
|
||||
user:
|
||||
label: backend::lang.access_log.login
|
||||
modelClass: Backend\Models\User
|
||||
conditions: user_id in (:filtered)
|
||||
nameFrom: login
|
||||
19
modules/cms/controllers/themelogs/config_form.yaml
Normal file
19
modules/cms/controllers/themelogs/config_form.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
# Record name
|
||||
name: system::lang.event_log.menu_label
|
||||
|
||||
# Model Form Field configuration
|
||||
form: ~/modules/cms/models/themelog/fields.yaml
|
||||
|
||||
# Model Class name
|
||||
modelClass: Cms\Models\ThemeLog
|
||||
|
||||
# Default redirect location
|
||||
defaultRedirect: cms/themelogs
|
||||
|
||||
# Preview page
|
||||
preview:
|
||||
title: cms::lang.theme_log.preview_title
|
||||
22
modules/cms/controllers/themelogs/config_list.yaml
Normal file
22
modules/cms/controllers/themelogs/config_list.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
# ===================================
|
||||
# List Behavior Config
|
||||
# ===================================
|
||||
|
||||
title: cms::lang.theme_log.menu_label
|
||||
list: ~/modules/cms/models/themelog/columns.yaml
|
||||
modelClass: Cms\Models\ThemeLog
|
||||
recordUrl: cms/themelogs/preview/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage: 30
|
||||
showSetup: true
|
||||
showCheckboxes: true
|
||||
defaultSort:
|
||||
column: count
|
||||
direction: desc
|
||||
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
search:
|
||||
prompt: backend::lang.list.search_prompt
|
||||
|
||||
filter: config_filter.yaml
|
||||
5
modules/cms/controllers/themelogs/index.php
Normal file
5
modules/cms/controllers/themelogs/index.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="padded-container container-flush">
|
||||
<?= $this->makeHintPartial('system_requestlogs_hint', 'hint') ?>
|
||||
</div>
|
||||
|
||||
<?= $this->listRender() ?>
|
||||
34
modules/cms/controllers/themelogs/preview.php
Normal file
34
modules/cms/controllers/themelogs/preview.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('cms/themelogs') ?>"><?= e(trans('cms::lang.theme_log.menu_label')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="scoreboard">
|
||||
<div data-control="toolbar">
|
||||
<?= $this->makePartial('preview_scoreboard') ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<?= $this->makePartial('hint_preview') ?>
|
||||
</div>
|
||||
|
||||
<div class="layout-item stretch layout-column">
|
||||
<?= $this->formRenderPreview() ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<p>
|
||||
<a href="<?= Backend::url('cms/themelogs') ?>" class="btn btn-default wn-icon-chevron-left">
|
||||
<?= e(trans('cms::lang.theme_log.return_link')) ?>
|
||||
</a>
|
||||
</p>
|
||||
21
modules/cms/controllers/themeoptions/config_form.yaml
Normal file
21
modules/cms/controllers/themeoptions/config_form.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
# Record name
|
||||
name: cms::lang.theme.theme_label
|
||||
|
||||
# Fields are defined by extension
|
||||
form: []
|
||||
|
||||
# Model Class name
|
||||
modelClass: Cms\Models\ThemeData
|
||||
|
||||
# Default redirect location
|
||||
defaultRedirect: cms/themes
|
||||
|
||||
# Update page
|
||||
update:
|
||||
title: cms::lang.theme.customize_theme
|
||||
redirect: cms/themes
|
||||
redirectClose: cms/themes
|
||||
70
modules/cms/controllers/themeoptions/update.php
Normal file
70
modules/cms/controllers/themeoptions/update.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('cms/themes') ?>"><?= e(trans('cms::lang.theme.theme_title')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?php if ($hasCustomData): ?>
|
||||
<?= Form::open(['class'=>'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-browser-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('cms::lang.theme.saving')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-browser-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('cms::lang.theme.saving')) ?>"
|
||||
class="btn btn-default">
|
||||
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||
</button>
|
||||
|
||||
<span class="btn-text">
|
||||
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('cms/themes') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||
</span>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger pull-right"
|
||||
data-request="onResetDefault"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.resetting')) ?>"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.action_confirm')) ?>">
|
||||
<?= e(trans('backend::lang.form.reset_default')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="callout callout-info">
|
||||
<div class="content">
|
||||
<p>There are no theme options available to customize.</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
<p><a href="<?= Backend::url('cms/themes') ?>" class="btn btn-default"><?= e(trans('cms::lang.theme.return')) ?></a></p>
|
||||
|
||||
<?php endif ?>
|
||||
54
modules/cms/controllers/themes/_theme_create_form.php
Normal file
54
modules/cms/controllers/themes/_theme_create_form.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?= Form::ajax('onCreate', [
|
||||
'id' => 'themeCreateForm',
|
||||
'data-popup-load-indicator' => true
|
||||
]) ?>
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.theme.create_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<?= $widget->render() ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('cms::lang.theme.create_button')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#themeCreateForm input.form-control:first').focus() },
|
||||
310
|
||||
)
|
||||
</script>
|
||||
|
||||
<?= Form::close() ?>
|
||||
75
modules/cms/controllers/themes/_theme_duplicate_form.php
Normal file
75
modules/cms/controllers/themes/_theme_duplicate_form.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?= Form::ajax('onDuplicateTheme', [
|
||||
'id' => 'themeDuplicateForm',
|
||||
'data-popup-load-indicator' => true,
|
||||
]) ?>
|
||||
|
||||
<input type="hidden" name="theme" value="<?= $themeDir ?>" />
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.theme.duplicate_title')) ?>: <?= $themeDir ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group text-field span-full">
|
||||
<label for="Form-ThemeDuplicate-newDirName">
|
||||
<?= e(trans('cms::lang.theme.new_directory_name_label')) ?>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="new_dir_name"
|
||||
id="Form-ThemeDuplicate-newDirName"
|
||||
value="<?= $themeDir ?>"
|
||||
placeholder=""
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
maxlength="255" />
|
||||
|
||||
<p class="help-block">
|
||||
<?= e(trans('cms::lang.theme.new_directory_name_comment')) ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-success">
|
||||
<?= e(trans('cms::lang.theme.duplicate_button')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#themeDuplicateForm input.form-control:first').focus() },
|
||||
310
|
||||
)
|
||||
</script>
|
||||
|
||||
<?= Form::close() ?>
|
||||
63
modules/cms/controllers/themes/_theme_export_form.php
Normal file
63
modules/cms/controllers/themes/_theme_export_form.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?= Form::ajax('onExport', [
|
||||
'id' => 'themeExportForm',
|
||||
'data-popup-load-indicator' => true,
|
||||
'data-request-success' => 'closeExportThemePopup()'
|
||||
]) ?>
|
||||
|
||||
<input type="hidden" name="theme" value="<?= $themeDir ?>" />
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.theme.export_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<?= $widget->render() ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-success">
|
||||
<?= e(trans('cms::lang.theme.export_button')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#themeExportForm input.form-control:first').focus() },
|
||||
310
|
||||
)
|
||||
|
||||
function closeExportThemePopup() {
|
||||
$('#themeExportForm')
|
||||
.closest('.control-popup')
|
||||
.popup('hideLoading')
|
||||
}
|
||||
</script>
|
||||
|
||||
<?= Form::close() ?>
|
||||
56
modules/cms/controllers/themes/_theme_fields_form.php
Normal file
56
modules/cms/controllers/themes/_theme_fields_form.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?= Form::ajax('onSaveFields', [
|
||||
'id' => 'themeFieldsForm',
|
||||
'data-popup-load-indicator' => true
|
||||
]) ?>
|
||||
|
||||
<input type="hidden" name="theme" value="<?= $themeDir ?>" />
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.theme.edit_properties_title')) ?>: <?= $themeDir ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<?= $widget->render() ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('cms::lang.theme.save_properties')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#themeFieldsForm input.form-control:first').focus() },
|
||||
310
|
||||
)
|
||||
</script>
|
||||
|
||||
<?= Form::close() ?>
|
||||
57
modules/cms/controllers/themes/_theme_import_form.php
Normal file
57
modules/cms/controllers/themes/_theme_import_form.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?= Form::ajax('onImport', [
|
||||
'id' => 'themeImportForm',
|
||||
'data-popup-load-indicator' => true,
|
||||
]) ?>
|
||||
|
||||
<input type="hidden" name="theme" value="<?= $themeDir ?>" />
|
||||
<input type="hidden" name="mode" value="import" />
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.theme.import_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<?= $widget->render() ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-success">
|
||||
<?= e(trans('cms::lang.theme.import_button')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#themeImportForm input.form-control:first').focus() },
|
||||
310
|
||||
)
|
||||
</script>
|
||||
|
||||
<?= Form::close() ?>
|
||||
31
modules/cms/controllers/themes/_theme_list.php
Normal file
31
modules/cms/controllers/themes/_theme_list.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
$themes = Cms\Classes\Theme::all();
|
||||
?>
|
||||
<?php foreach ($themes as $index => $theme): ?>
|
||||
|
||||
<div id="themeListItem-<?= $theme->getId() ?>" class="layout-row min-size <?= $theme->isActiveTheme() ? 'active' : null ?>">
|
||||
<?= $this->makePartial('theme_list_item', ['theme' => $theme]) ?>
|
||||
</div>
|
||||
|
||||
<?php endforeach ?>
|
||||
|
||||
<div class="layout-row links">
|
||||
<div class="layout-cell theme-thumbnail">
|
||||
<!-- Spacer -->
|
||||
</div>
|
||||
<div class="layout-cell theme-description">
|
||||
<a
|
||||
class="create-new-theme"
|
||||
data-control="popup"
|
||||
data-handler="onLoadCreateForm"
|
||||
data-size="huge"
|
||||
href="javascript:;">
|
||||
<?= e(trans('cms::lang.theme.create_new_blank_theme')) ?>
|
||||
</a>
|
||||
<a
|
||||
class="find-more-themes"
|
||||
href="<?= Backend::url('system/updates/install/themes') ?>">
|
||||
<?= e(trans('cms::lang.theme.find_more_themes')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
121
modules/cms/controllers/themes/_theme_list_item.php
Normal file
121
modules/cms/controllers/themes/_theme_list_item.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
$author = $theme->getConfigValue('author');
|
||||
?>
|
||||
|
||||
<div class="layout-cell min-height theme-thumbnail">
|
||||
<div class="thumbnail-container"><img src="<?= $theme->getPreviewImageUrl() ?>" alt="" /></div>
|
||||
</div>
|
||||
<div class="layout-cell min-height theme-description">
|
||||
<h3><?= e($theme->getConfigValue('name', $theme->getDirName())) ?></h3>
|
||||
<?php if (strlen($author)): ?>
|
||||
<p class="author"><?= trans('cms::lang.theme.by_author', ['name' => '<a href="'.e($theme->getConfigValue('homepage', '#')).'">'.e($author).'</a>']) ?></p>
|
||||
<?php endif ?>
|
||||
<p class="description">
|
||||
<?= e($theme->getConfigValue('description', 'The theme description is not provided.')) ?>
|
||||
</p>
|
||||
<div class="controls">
|
||||
|
||||
<?php if ($theme->isActiveTheme()): ?>
|
||||
<button
|
||||
type="submit"
|
||||
disabled
|
||||
class="btn btn-secondary btn-disabled">
|
||||
<i class="icon-star"></i>
|
||||
<?= e(trans('cms::lang.theme.active_button')) ?>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSetActiveTheme"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
data-stripe-load-indicator
|
||||
class="btn btn-primary">
|
||||
<i class="icon-check"></i>
|
||||
<?= e(trans('cms::lang.theme.activate_button')) ?>
|
||||
</button>
|
||||
<?php endif ?>
|
||||
<?php if ($theme->hasCustomData()): ?>
|
||||
<a
|
||||
href="<?= Backend::url('cms/themeoptions/update/'.$theme->getDirName()) ?>"
|
||||
class="btn btn-secondary<?= $theme->isActiveTheme() === false ? ' disabled' : '' ?>">
|
||||
<i class="icon-paint-brush"></i>
|
||||
<?= e(trans('cms::lang.theme.customize_button')) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<div class="dropdown">
|
||||
<button
|
||||
data-toggle="dropdown"
|
||||
class="btn btn-secondary">
|
||||
<i class="icon-wrench"></i>
|
||||
<?= e(trans('cms::lang.theme.manage_button')) ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" data-dropdown-title="<?= e(trans('cms::lang.theme.manage_title')) ?>">
|
||||
<li role="presentation">
|
||||
<a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
data-control="popup"
|
||||
data-size="huge"
|
||||
data-handler="onLoadFieldsForm"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
href="javascript:;"
|
||||
class="wn-icon-pencil">
|
||||
<?= e(trans('cms::lang.theme.edit_properties_button')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
data-control="popup"
|
||||
data-handler="onLoadDuplicateForm"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
href="javascript:;"
|
||||
class="wn-icon-copy">
|
||||
<?= e(trans('cms::lang.theme.duplicate_button')) ?>
|
||||
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
data-control="popup"
|
||||
data-handler="onLoadImportForm"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
href="javascript:;"
|
||||
class="wn-icon-upload">
|
||||
<?= e(trans('cms::lang.theme.import_button')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
data-control="popup"
|
||||
data-handler="onLoadExportForm"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
href="javascript:;"
|
||||
class="wn-icon-download">
|
||||
<?= e(trans('cms::lang.theme.export_button')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php if (!$theme->isActiveTheme()): ?>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation">
|
||||
<a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('cms::lang.theme.delete_confirm')) ?>"
|
||||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
href="javascript:;"
|
||||
class="wn-icon-trash">
|
||||
<?= e(trans('cms::lang.theme.delete_button')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
13
modules/cms/controllers/themes/download.php
Normal file
13
modules/cms/controllers/themes/download.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('cms/themes') ?>"><?= e(trans('cms::lang.theme.theme_title')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if ($this->fatalError): ?>
|
||||
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
<p><a href="<?= Backend::url('cms/themes') ?>" class="btn btn-default"><?= e(trans('cms::lang.theme.return')) ?></a></p>
|
||||
|
||||
<?php endif ?>
|
||||
11
modules/cms/controllers/themes/index.php
Normal file
11
modules/cms/controllers/themes/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?= Block::put('body') ?>
|
||||
<div class="layout">
|
||||
<div class="layout-row">
|
||||
<?= Form::open(['onsubmit'=>'return false']) ?>
|
||||
<div class="layout theme-selector-layout" id="theme-list">
|
||||
<?= $this->makePartial('theme_list') ?>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
</div>
|
||||
</div>
|
||||
<?= Block::endPut() ?>
|
||||
Reference in New Issue
Block a user