Fixes #255 - Adds a code editor settings page

Email layouts now work
Form fields now support context in their names
This commit is contained in:
Sam Georges 2014-06-10 19:30:06 +10:00
parent 12a7e7776b
commit 8b9eb52afd
15 changed files with 295 additions and 40 deletions

View File

@ -1,3 +1,7 @@
* **Build x** (2014-06-10)
- Form fields can now pass context via their name definnition using syntax `field@context`.
- Added a code editor preferences page.
* **Build 101** (2014-06-06) * **Build 101** (2014-06-06)
- Added a global traceLog() helper for help with debugging. - Added a global traceLog() helper for help with debugging.
- New settings area added to manage Email templates and layouts. - New settings area added to manage Email templates and layouts.

View File

@ -8,6 +8,7 @@ use BackendAuth;
use Backend\Classes\WidgetManager; use Backend\Classes\WidgetManager;
use October\Rain\Support\ModuleServiceProvider; use October\Rain\Support\ModuleServiceProvider;
use System\Models\EmailTemplate; use System\Models\EmailTemplate;
use System\Classes\SettingsManager;
class ServiceProvider extends ModuleServiceProvider class ServiceProvider extends ModuleServiceProvider
{ {
@ -62,6 +63,22 @@ class ServiceProvider extends ModuleServiceProvider
]); ]);
}); });
/*
* Register settings
*/
SettingsManager::instance()->registerCallback(function($manager){
$manager->registerSettingItems('October.Backend', [
'editor' => [
'label' => 'backend::lang.editor.menu_label',
'description' => 'backend::lang.editor.menu_description',
'category' => 'System',
'icon' => 'icon-code',
'class' => 'Backend\Models\EditorSettings',
'sort' => 200
],
]);
});
/* /*
* Register permissions * Register permissions
*/ */

View File

@ -0,0 +1,94 @@
<?php namespace Backend\Behaviors;
use System\Behaviors\SettingsModel;
use Backend\Models\UserPreferences;
/**
* User Settings model extension, identical to System.Behaviors.SettingsModel
* except values are set against the logged in user's preferences via Backend\Models\UserPreferences.
*
* Usage:
*
* In the model class definition:
*
* public $implement = ['Backend.Behaviors.UserSettingsModel'];
* public $settingsCode = 'author.plugin::code';
* public $settingsFields = 'fields.yaml';
*
*/
class UserSettingsModel extends SettingsModel
{
private static $instances = [];
/**
* Constructor
*/
public function __construct($model)
{
parent::__construct($model);
$this->model->table = 'backend_user_preferences';
}
/**
* Create an instance of the settings model, intended as a static method
*/
public function instance()
{
if (isset(self::$instances[$this->recordCode]))
return self::$instances[$this->recordCode];
$item = UserPreferences::forUser();
$item = $item->scopeFindRecord($this->model, $this->recordCode, $item->userContext)->first();
if (!$item) {
$this->model->initSettingsData();
$this->model->forceSave();
$this->model->reload();
$item = $this->model;
}
return self::$instances[$this->recordCode] = $item;
}
/**
* Checks if the model has been set up previously, intended as a static method
*/
public function isConfigured()
{
return UserPreferences::forUser()->findRecord($this->recordCode, $item->userContext)->count() > 0;
}
/**
* Before the model is saved, ensure the record code is set
* and the jsonable field values
*/
public function beforeModelSave()
{
$preferences = UserPreferences::forUser();
list($namespace, $group, $item) = $preferences->parseKey($this->recordCode);
$this->model->item = $item;
$this->model->group = $group;
$this->model->namespace = $namespace;
$this->model->user_id = $preferences->userContext->id;
if ($this->fieldValues)
$this->model->value = $this->fieldValues;
}
/**
* Checks if a key is legitimate or should be added to
* the field value collection
*/
private function isKeyAllowed($key)
{
/*
* Let the core columns through
*/
if ($key == 'namespace' || $key == 'group')
return true;
return parent::isKeyAllowed($key);
}
}

View File

@ -1,5 +1,6 @@
<?php namespace Backend\FormWidgets; <?php namespace Backend\FormWidgets;
use Backend\Models\EditorSettings;
use Backend\Classes\FormWidgetBase; use Backend\Classes\FormWidgetBase;
/** /**
@ -31,6 +32,16 @@ class CodeEditor extends FormWidgetBase
*/ */
public $wrapWords = true; public $wrapWords = true;
/**
* @var boolean Indicates whether the the editor uses spaces for indentation
*/
public $useSoftTabs = true;
/**
* @var boolean Sets the size of the indentation
*/
public $tabSize = 4;
/** /**
* @var integer Sets the font size * @var integer Sets the font size
*/ */
@ -51,11 +62,16 @@ class CodeEditor extends FormWidgetBase
*/ */
public function init() public function init()
{ {
// Load the editor system settings
$editorSettings = EditorSettings::instance();
$this->language = $this->getConfig('language', 'php'); $this->language = $this->getConfig('language', 'php');
$this->showGutter = $this->getConfig('showGutter', true); $this->showGutter = $this->getConfig('showGutter', true);
$this->theme = $this->getConfig('theme', 'twilight'); $this->theme = $this->getConfig('theme', $editorSettings->theme);
$this->wrapWords = $this->getConfig('wrapWords', false); $this->wrapWords = $this->getConfig('wrapWords', $editorSettings->use_wrap);
$this->fontSize = $this->getConfig('fontSize', false); $this->fontSize = $this->getConfig('fontSize', $editorSettings->font_size);
$this->tabSize = $this->getConfig('tabSize', $editorSettings->tab_size);
$this->useSoftTabs = $this->getConfig('useSoftTabs', !$editorSettings->use_hard_tabs);
$this->margin = $this->getConfig('margin', 0); $this->margin = $this->getConfig('margin', 0);
} }
@ -79,6 +95,8 @@ class CodeEditor extends FormWidgetBase
$this->vars['showGutter'] = $this->showGutter; $this->vars['showGutter'] = $this->showGutter;
$this->vars['wrapWords'] = $this->wrapWords; $this->vars['wrapWords'] = $this->wrapWords;
$this->vars['fontSize'] = $this->fontSize; $this->vars['fontSize'] = $this->fontSize;
$this->vars['tabSize'] = $this->tabSize;
$this->vars['useSoftTabs'] = $this->useSoftTabs;
$this->vars['theme'] = $this->theme; $this->vars['theme'] = $this->theme;
$this->vars['name'] = $this->formField->getName(); $this->vars['name'] = $this->formField->getName();
$this->vars['value'] = $this->model->{$this->columnName}; $this->vars['value'] = $this->model->{$this->columnName};

View File

@ -11,6 +11,8 @@
data-theme="<?= $theme ?>" data-theme="<?= $theme ?>"
data-margin="<?= $margin ?>" data-margin="<?= $margin ?>"
data-font-size="<?= $fontSize ?>" data-font-size="<?= $fontSize ?>"
data-tab-size="<?= $tabSize ?>"
data-use-soft-tabs="<?= $useSoftTabs ?>"
data-vendor-path="<?= URL::to('/modules/backend/formwidgets/codeeditor/assets/vendor/ace') ?>/"> data-vendor-path="<?= URL::to('/modules/backend/formwidgets/codeeditor/assets/vendor/ace') ?>/">
<div class="editor-toolbar"> <div class="editor-toolbar">
<ul> <ul>

View File

@ -145,4 +145,15 @@ return [
'permissions' => 'Directory :name or its subdirectories is not writable for PHP. Please set corresponding permissions for the webserver on this directory.', 'permissions' => 'Directory :name or its subdirectories is not writable for PHP. Please set corresponding permissions for the webserver on this directory.',
'extension' => 'The PHP extension :name is not installed. Please install this library and activate the extension.' 'extension' => 'The PHP extension :name is not installed. Please install this library and activate the extension.'
], ],
'editor' => [
'menu_label' => 'Editor Configuration',
'menu_description' => 'Manage editor configuration.',
'font_size' => 'Font Size',
'tab_size' => 'Indentation Width',
'use_hard_tabs' => 'Indent Using Tabs',
'use_hard_tabs_comment' => 'Use this checkbox if you would like to use hard tabs instead of spaces.',
'use_wrap' => 'Enable Word Wrap',
'use_wrap_comment' => 'Use this checkbox if you want your text to wrap instead of overflowing.',
'theme' => 'Text Color Scheme',
],
]; ];

View File

@ -0,0 +1,62 @@
<?php namespace Backend\Models;
use App;
use Model;
use DirectoryIterator;
class EditorSettings extends Model
{
public $implement = ['Backend.Behaviors.UserSettingsModel'];
public $settingsCode = 'system::editor.settings';
public $settingsFields = 'fields.yaml';
const DEFAULT_THEME = 'twilight';
public function initSettingsData()
{
$config = App::make('config');
$this->font_size = $config->get('editor.font.size', 12);
$this->tab_size = $config->get('editor.tab.size', 4);
$this->use_hard_tabs = $config->get('editor.tab.usehard', false);
$this->use_wrap = $config->get('editor.wrap.enable', false);
$this->theme = $config->get('editor.theme', static::DEFAULT_THEME);
}
public static function applyConfigValues()
{
$config = App::make('config');
$settings = self::instance();
$config->set('editor.font.size', $settings->font_size);
$config->set('editor.tab.size', $settings->tab_size);
$config->set('editor.tab.usehard', $settings->use_hard_tabs);
$config->set('editor.wrap.enable', $settings->use_wrap);
$config->set('editor.theme', $settings->theme);
}
public function getThemeOptions()
{
$themeDir = new DirectoryIterator("modules/backend/formwidgets/codeeditor/assets/vendor/ace/");
$themes = [];
// Iterate through the themes
foreach ($themeDir as $node) {
// If this file is a theme (starting by "theme-")
if (!$node->isDir() && substr($node->getFileName(), 0, 6) == 'theme-') {
// Remove the theme- prefix and the .js suffix, create an user friendly and capitalized name
$themeId = substr($node->getFileName(), 6, -3);
$themeName = ucwords(str_replace("_", " ", $themeId));
// Add the values to the themes array
if ($themeId != static::DEFAULT_THEME) {
$themes[$themeId] = $themeName;
}
}
}
// Sort the theme alphabetically, and push the default theme
asort($themes);
return [static::DEFAULT_THEME => 'Twilight'] + $themes;
}
}

View File

@ -0,0 +1,29 @@
# ===================================
# Field Definitions
# ===================================
fields:
font_size:
label: backend::lang.editor.font_size
span: left
tab_size:
label: backend::lang.editor.tab_size
span: right
theme:
label: backend::lang.editor.theme
type: dropdown
span: full
use_hard_tabs:
label: backend::lang.editor.use_hard_tabs
type: checkbox
span: full
comment: backend::lang.editor.use_hard_tabs_comment
use_wrap:
label: backend::lang.editor.use_wrap
type: checkbox
span: full
comment: backend::lang.editor.use_wrap_comment

View File

@ -16,10 +16,6 @@ After signing in you should change your password by clicking your name on the to
You can use the following link to sign in: You can use the following link to sign in:
{{ link }} {{ link }}
---
This is an automatic message. Do not reply to it.
== ==
<p>Hi {{ name }},</p> <p>Hi {{ name }},</p>
@ -37,6 +33,3 @@ This is an automatic message. Do not reply to it.
You can use the following link to sign in:<br /> You can use the following link to sign in:<br />
<a href="{{ link }}">{{ link }}</a> <a href="{{ link }}">{{ link }}</a>
</p> </p>
<hr />
<strong>This is an automatic message. Do not reply to it.</strong>

View File

@ -9,10 +9,6 @@ Somebody has requested a password reset for your account, if this was not you, p
You can use the following link to restore your password: You can use the following link to restore your password:
{{ link }} {{ link }}
---
This is an automatic message. Do not reply to it.
== ==
<p>Hello {{ name }},</p> <p>Hello {{ name }},</p>
@ -23,6 +19,3 @@ This is an automatic message. Do not reply to it.
You can use the following link to restore your password:<br /> You can use the following link to restore your password:<br />
<a href="{{ link }}">{{ link }}</a> <a href="{{ link }}">{{ link }}</a>
</p> </p>
<hr />
<strong>This is an automatic message. Do not reply to it.</strong>

View File

@ -353,7 +353,10 @@ class Form extends WidgetBase
protected function makeFormField($name, $config) protected function makeFormField($name, $config)
{ {
$label = (isset($config['label'])) ? $config['label'] : null; $label = (isset($config['label'])) ? $config['label'] : null;
$field = new FormField($name, $label); list($fieldName, $fieldContext) = $this->getFieldName($name);
$field = new FormField($fieldName, $label);
if ($fieldContext) $field->context = $fieldContext;
$field->arrayName = $this->arrayName; $field->arrayName = $this->arrayName;
$field->idPrefix = $this->getId(); $field->idPrefix = $this->getId();
@ -473,6 +476,19 @@ class Form extends WidgetBase
return $this->formWidgets[$field->columnName] = $widget; return $this->formWidgets[$field->columnName] = $widget;
} }
/**
* Parses a field's name
* @param stirng $field Field name
* @return array [columnName, context]
*/
public function getFieldName($field)
{
if (strpos($field, '@') === false)
return [$field, null];
return explode('@', $field);
}
/** /**
* Looks up the column * Looks up the column
*/ */

View File

@ -27,15 +27,15 @@ p {
$html = '<html> $html = '<html>
<head> <head>
<style type="text/css" media="screen"> <style type="text/css" media="screen">
{{ css }} {{ css|raw }}
</style> </style>
</head> </head>
<body> <body>
{{ message }} {{ message|raw }}
</body> </body>
</html>'; </html>';
$text = '{{message}}'; $text = '{{ message|raw }}';
EmailLayout::create([ EmailLayout::create([
'is_locked' => true, 'is_locked' => true,
@ -49,17 +49,17 @@ $text = '{{message}}';
$html = '<html> $html = '<html>
<head> <head>
<style type="text/css" media="screen"> <style type="text/css" media="screen">
{{ css }} {{ css|raw }}
</style> </style>
</head> </head>
<body> <body>
{{ message }} {{ message|raw }}
<hr /> <hr />
<p>This is an automatic message. Please do not reply to it.</p> <p>This is an automatic message. Please do not reply to it.</p>
</body> </body>
</html>'; </html>';
$text = '{{message}} $text = '{{ message|raw }}
--- ---

View File

@ -91,7 +91,7 @@ class EmailTemplate extends Model
public static function addContentToMailer($message, $code, $data) public static function addContentToMailer($message, $code, $data)
{ {
if (!isset(self::$cache[$code])) { if (!isset(self::$cache[$code])) {
if (!$template = self::where('is_custom', true)->whereCode($code)->first()) if (!$template = self::whereCode($code)->first())
return false; return false;
self::$cache[$code] = $template; self::$cache[$code] = $template;
@ -107,9 +107,31 @@ class EmailTemplate extends Model
$twig->setLoader(new \Twig_Loader_String); $twig->setLoader(new \Twig_Loader_String);
$message->subject($twig->render($template->subject, $data)); $message->subject($twig->render($template->subject, $data));
$message->setBody($twig->render($template->content_html, $data), 'text/html');
if (strlen($template->content_text)) /*
$message->addPart($twig->render($template->content_text, $data), 'text/plain'); * HTML contents
*/
$html = $twig->render($template->content_html, $data);
if ($template->layout) {
$html = $twig->render($template->layout->content_html, [
'message' => $html,
'css' => $template->layout->content_css
]);
}
$message->setBody($html, 'text/html');
/*
* Text contents
*/
if (strlen($template->content_text)) {
$text = $twig->render($template->content_text, $data);
if ($template->layout) {
$text = $twig->render($template->layout->content_text, ['message' => $text]);
}
$message->addPart($text, 'text/plain');
}
$twig->setLoader($oldLoader); $twig->setLoader($oldLoader);
return true; return true;

View File

@ -10,15 +10,12 @@ fields:
span: left span: left
context: create context: create
name: name@create:
label: system::lang.email_templates.name label: system::lang.email_templates.name
span: right span: right
# context: create
# @todo Fix duplicate key problem name@update:
# name: label: system::lang.email_templates.name
# label: system::lang.email_templates.name
# context: update
secondaryTabs: secondaryTabs:
fields: fields:

View File

@ -16,16 +16,13 @@ fields:
span: left span: left
context: create context: create
subject: subject@create:
label: system::lang.email_templates.subject label: system::lang.email_templates.subject
comment: system::lang.email_templates.subject_comment comment: system::lang.email_templates.subject_comment
span: right span: right
# context: create
# @todo Fix duplicate key problem subject@update:
# subject: label: system::lang.email_templates.subject
# label: system::lang.email_templates.subject
# context: update
description: description:
label: system::lang.email_templates.description label: system::lang.email_templates.description