Adds Theme customization feature

Adds reset button to settings pages
authorUrl -> homepage
This commit is contained in:
Samuel Georges 2014-12-04 17:41:07 +11:00
parent 58b7ee5f7e
commit 70eb7eaf51
15 changed files with 287 additions and 12 deletions

View File

@ -1,3 +1,8 @@
* **Build 16x** (2014-12-xx)
- Settings pages now have a *Reset to default* button.
- The field `authorUrl` has been renamed to `homepage` in theme.yaml files.
- Adds Theme customization feature (see Themes > Development docs).
* **Build 166** (2014-11-27)
- Plugin details method now support "homepage" property (see Plugins > Registration & Versions docs).
- Fixes a bug in the Datepicker using `time` mode.

View File

@ -222,7 +222,7 @@ class FormController extends ControllerBehavior
$this->controller->formBeforeSave($model);
$this->controller->formBeforeUpdate($model);
$modelsToSave =$this->prepareModelsToSave($model, $this->formWidget->getSaveData());
$modelsToSave = $this->prepareModelsToSave($model, $this->formWidget->getSaveData());
foreach ($modelsToSave as $modelToSave) {
$modelToSave->save(null, $this->formWidget->getSessionKey());
}

View File

@ -155,6 +155,9 @@ return [
'delete' => 'Delete',
'deleting' => 'Deleting...',
'deleting_name' => 'Deleting :name...',
'reset_default' => 'Reset to default',
'resetting' => 'Resetting',
'resetting_name' => 'Resetting :name',
'undefined_tab' => 'Misc',
'field_off' => 'Off',
'field_on' => 'On',

View File

@ -257,12 +257,7 @@ class Theme
*/
public function getConfigValue($name, $default = null)
{
$config = $this->getConfig();
if (isset($config[$name])) {
return $config[$name];
}
return $default;
return array_get($this->getConfig(), $name, $default);
}
/**

View File

@ -1,11 +1,16 @@
<?php namespace Cms\Controllers;
use Config;
use BackendMenu;
use Lang;
use Input;
use Config;
use Backend;
use Redirect;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\SettingsManager;
use Cms\Models\ThemeData;
use Cms\Classes\Theme as CmsTheme;
use Exception;
/**
* Theme selector controller
@ -16,9 +21,13 @@ use Cms\Classes\Theme as CmsTheme;
*/
class Themes extends Controller
{
public $requiredPermissions = ['cms.manage_themes'];
public $implement = [
'Backend.Behaviors.FormController'
];
public $bodyClass = 'compact-container';
public $formConfig = 'config_form.yaml';
public $requiredPermissions = ['cms.manage_themes'];
/**
* Constructor.
@ -36,6 +45,7 @@ class Themes extends Controller
public function index()
{
$this->bodyClass = 'compact-container';
}
public function index_onSetActiveTheme()
@ -46,4 +56,67 @@ class Themes extends Controller
'#theme-list' => $this->makePartial('theme_list')
];
}
//
// Theme customization
//
public function update($dirName)
{
try {
$model = $this->getThemeData($dirName);
$this->asExtension('FormController')->update($model->id);
}
catch (Exception $ex) {
$this->handleError($ex);
}
}
public function update_onSave($dirName)
{
$model = $this->getThemeData($dirName);
$this->asExtension('FormController')->update_onSave($model->id);
}
public function update_onResetDefault($dirName)
{
$model = $this->getThemeData($dirName);
$model->delete();
$redirectUrl = Backend::url('cms/themes/update/'.$dirName);
return Redirect::to($redirectUrl);
}
protected function getThemeData($dirName)
{
if (!$theme = CmsTheme::load($dirName))
throw new Exception(Lang::get('Unable to find theme with name :name', $dirName));
$model = ThemeData::firstOrCreate(['theme' => $theme->getDirName()]);
return $model;
}
/**
* Add form fields defined in theme.yaml
*/
protected function formExtendFields($form)
{
$model = $form->model;
if (!$theme = CmsTheme::load($model->theme))
throw new Exception(Lang::get('Unable to find theme with name :name', $dirName));
if ($fields = $theme->getConfigValue('form.fields')) {
$form->addFields($fields);
}
if ($fields = $theme->getConfigValue('form.tabs.fields')) {
$form->addTabFields($fields);
}
if ($fields = $theme->getConfigValue('form.secondaryTabs.fields')) {
$form->addSecondaryTabFields($fields);
}
}
}

View File

@ -7,6 +7,7 @@
<?php
$isLast = $index == $cnt-1;
$isActive = $activeTheme && $activeTheme->getDirName() == $theme->getDirName();
$hasForm = $theme->getConfigValue('form');
$author = $theme->getConfigValue('author');
?>
<div class="layout-row <?= $isLast ? 'last' : null ?> min-size <?= $isActive ? 'active' : null ?>">
@ -16,7 +17,7 @@
<div class="layout-cell min-height theme-description">
<h3><?= e($theme->getConfigValue('name', $theme->getDirName())) ?></h3>
<?php if (strlen($author)): ?>
<p class="author">by <a href="<?= e($theme->getConfigValue('authorUrl', '#')) ?>"><?= e($author) ?></a></p>
<p class="author">by <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">
@ -35,9 +36,18 @@
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 ($hasForm): ?>
<a
href="<?= Backend::url('cms/themes/update/'.$theme->getDirName()) ?>"
class="btn btn-default">
<i class="icon-pencil"></i>
<?= e(trans('cms::lang.theme.customize_button')) ?>
</a>
<?php endif ?>
</div>
</div>
</div>

View File

@ -0,0 +1,21 @@
# ===================================
# Form Behavior Config
# ===================================
# Record name
name: Theme
# Fields are defined by extension
form: []
# Model Class name
modelClass: Cms\Models\ThemeData
# Default redirect location
defaultRedirect: cms/themes
# Update page
update:
title: Customize Theme
redirect: cms/themes
redirectClose: cms/themes

View File

@ -0,0 +1,59 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('cms/themes') ?>">Themes</a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?= 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-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="Saving theme..."
class="btn btn-primary">
<u>S</u>ave
</button>
<button
type="button"
data-request="onSave"
data-request-data="close:1"
data-hotkey="ctrl+enter, cmd+enter"
data-load-indicator="Saving theme..."
class="btn btn-default">
Save and Close
</button>
<span class="btn-text">
or <a href="<?= Backend::url('cms/themes') ?>">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: ?>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<p><a href="<?= Backend::url('cms/themes') ?>" class="btn btn-default">Return to themes list</a></p>
<?php endif ?>

View File

@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DbCmsThemeData extends Migration
{
public function up()
{
Schema::create('cms_theme_data', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('theme')->nullable()->index();
$table->mediumtext('data')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('cms_theme_data');
}
}

View File

@ -27,6 +27,7 @@ return [
'find_more_themes' => 'Find more themes on OctoberCMS Theme Marketplace.',
'activate_button' => 'Activate',
'active_button' => 'Activate',
'customize_button' => 'Customize',
],
'maintenance' => [
'settings_menu' => 'Maintenance mode',

View File

@ -0,0 +1,54 @@
<?php namespace Cms\Models;
use Model;
/**
* Customization data used by a theme
*
* @package october\cms
* @author Alexey Bobkov, Samuel Georges
*/
class ThemeData extends Model
{
// use \October\Rain\Database\Traits\Validation;
/**
* @var string The database table used by the model.
*/
public $table = 'cms_theme_data';
/**
* @var array Guarded fields
*/
protected $guarded = [];
/**
* @var array Fillable fields
*/
protected $fillable = [];
/**
* @var array List of attribute names which are json encoded and decoded from the database.
*/
protected $jsonable = ['data'];
public function beforeSave()
{
/*
* Dynamic attributes are stored in the jsonable attribute 'data'.
*/
$staticAttributes = ['id', 'theme', 'data'];
$dynamicAttributes = array_except($this->getAttributes(), $staticAttributes);
$this->data = $dynamicAttributes;
$this->setRawAttributes(array_only($this->getAttributes(), $staticAttributes));
}
public function afterFetch()
{
/*
* Fill this model with the jsonable attributes kept in 'data'.
*/
$this->setRawAttributes((array) $this->getAttributes() + (array) $this->data, true);
}
}

View File

@ -83,6 +83,18 @@ class SettingsModel extends ModelBehavior
return self::$instances[$this->recordCode] = $item;
}
/**
* Reset the settings to their defaults, this will delete the record model
*/
public function resetDefault()
{
if ($record = $this->getSettingsRecord()) {
$record->delete();
unset(self::$instances[$this->recordCode]);
Cache::forget($this->getCacheKey());
}
}
/**
* Checks if the model has been set up previously, intended as a static method
*/

View File

@ -111,6 +111,16 @@ class Settings extends Controller
}
}
public function update_onResetDefault($author, $plugin, $code = null)
{
$item = $this->findSettingItem($author, $plugin, $code);
$model = $this->createModel($item);
$model->resetDefault();
$redirectUrl = Backend::url('system/settings/update/'.$author.'/'.$plugin.'/'.$code);
return Redirect::to($redirectUrl);
}
/**
* Render the form.
*/

View File

@ -29,6 +29,15 @@
<span class="btn-text">
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('system/settings') ?>"><?= 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() ?>