diff --git a/CHANGELOG.md b/CHANGELOG.md index 12255c889..ccb9700bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +* **Build 101** (2014-06-06) + - Added a global traceLog() helper for help with debugging. + - New settings area added to manage Email templates and layouts. + * **Build 99** (2014-06-05) - Plugins can now be removed, refreshed and disabled via the back-end. diff --git a/modules/backend/ServiceProvider.php b/modules/backend/ServiceProvider.php index f4339b364..bed3178ab 100644 --- a/modules/backend/ServiceProvider.php +++ b/modules/backend/ServiceProvider.php @@ -7,6 +7,7 @@ use BackendMenu; use BackendAuth; use Backend\Classes\WidgetManager; use October\Rain\Support\ModuleServiceProvider; +use System\Models\EmailTemplate; class ServiceProvider extends ModuleServiceProvider { @@ -70,6 +71,16 @@ class ServiceProvider extends ModuleServiceProvider 'backend.manage_users' => ['label' => 'Manage other administrators', 'tab' => 'System'], ]); }); + + /* + * Register email templates + */ + EmailTemplate::registerCallback(function($template){ + $template->registerEmailTemplates([ + 'backend::emails.invite' => 'Invitation for newly created administrators.', + 'backend::emails.restore' => 'Password reset instructions for backend-end administrators.', + ]); + }); } /** diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index fecc56e6f..00c53a02e 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -7279,6 +7279,18 @@ label { .form-control.align-right { text-align: right; } +.form-preview { + padding: 15px; + margin-bottom: 20px; + background: white; + border: 1px solid #eee; +} +.form-preview .form-group { + padding-bottom: 10px; +} +.form-preview > .form-group:last-child { + padding-bottom: 0; +} .form-elements:before, .form-tabless-fields:before, .form-elements:after, @@ -7769,6 +7781,15 @@ label { .select2-drop .select2-results > li > div { padding: 5px 7px 5px; } +.list-preview { + padding: 20px 0 0; + margin-bottom: 20px; + background: white; + border: 1px solid #e2e2e2; +} +.list-preview .control-list:last-child > table { + margin-bottom: 0; +} .control-list p.no-data { padding: 18px 20px; margin: 0 20px; @@ -8515,7 +8536,7 @@ table.table.data tr.list-tree-level-25 td.list-data-column-1 { .modal-body { padding-bottom: 0; } -.modal-body p:last-child { +.modal-body > p:last-child { margin-bottom: 20px; } .control-popup.fade .modal-dialog { diff --git a/modules/backend/assets/js/october.inspector.js b/modules/backend/assets/js/october.inspector.js index f1e014f39..c54e0deb9 100644 --- a/modules/backend/assets/js/october.inspector.js +++ b/modules/backend/assets/js/october.inspector.js @@ -194,7 +194,7 @@ var e = $.Event('showing.oc.inspector') this.$el.trigger(e, [{callback: displayPopover}]) - if (e.isDefaultPrevented()) + if (e.isDefaultPrevented()) return if (!e.isPropagationStopped()) diff --git a/modules/backend/assets/js/october.tab.js b/modules/backend/assets/js/october.tab.js index ae1e9c10b..37761cc27 100644 --- a/modules/backend/assets/js/october.tab.js +++ b/modules/backend/assets/js/october.tab.js @@ -15,14 +15,14 @@ * - data-closable - enables the Close Tab feature * - data-pane-classes - a list of CSS classes to apply new pane elements * - * Example with data attributes (data-control="tab"): + * Example with data attributes (data-control="tab"): * *
* *
- *
Home
+ *
Home
*
*
* diff --git a/modules/backend/assets/less/controls/forms.less b/modules/backend/assets/less/controls/forms.less index 781d3f156..a00642820 100644 --- a/modules/backend/assets/less/controls/forms.less +++ b/modules/backend/assets/less/controls/forms.less @@ -22,6 +22,25 @@ label { } } +// +// Form containers +// + +.form-preview { + padding: 15px; + margin-bottom: 20px; + background: white; + border: 1px solid #eee; + + .form-group { + padding-bottom: 10px; + } + + >.form-group:last-child { + padding-bottom: 0; + } +} + // // Nice forms // diff --git a/modules/backend/assets/less/controls/lists.less b/modules/backend/assets/less/controls/lists.less index efb2fc5ef..dd1e59533 100644 --- a/modules/backend/assets/less/controls/lists.less +++ b/modules/backend/assets/less/controls/lists.less @@ -51,6 +51,26 @@ // // // + +// +// List containers +// + +.list-preview { + padding: 20px 0 0; + margin-bottom: 20px; + background: white; + border: 1px solid @color-list-border; + + .control-list:last-child > table { + margin-bottom: 0; + } +} + +// +// List control +// + .control-list { p.no-data { padding: 18px 20px; diff --git a/modules/backend/assets/less/controls/popup.less b/modules/backend/assets/less/controls/popup.less index 91e8c94aa..f9969e44c 100644 --- a/modules/backend/assets/less/controls/popup.less +++ b/modules/backend/assets/less/controls/popup.less @@ -31,7 +31,7 @@ .modal-body { padding-bottom: 0; - p:last-child { + > p:last-child { margin-bottom: 20px; } } diff --git a/modules/backend/behaviors/FormController.php b/modules/backend/behaviors/FormController.php index 3a7b53927..d117cc54a 100644 --- a/modules/backend/behaviors/FormController.php +++ b/modules/backend/behaviors/FormController.php @@ -128,12 +128,13 @@ class FormController extends ControllerBehavior $this->initForm($model); $this->controller->formBeforeSave($model); - $this->controller->formBeforeCreateSave($model); + $this->controller->formBeforeCreate($model); $this->setModelAttributes($model, $this->formWidget->getSaveData()); $model->push($this->formWidget->getSessionKey()); $this->controller->formAfterSave($model); + $this->controller->formAfterCreate($model); Flash::success($this->getLang('create[flash-save]', 'backend::lang.form.create_success')); @@ -177,12 +178,13 @@ class FormController extends ControllerBehavior $this->initForm($model, 'update'); $this->controller->formBeforeSave($model); - $this->controller->formBeforeEditSave($model); + $this->controller->formBeforeUpdate($model); $this->setModelAttributes($model, $this->formWidget->getSaveData()); $model->push($this->formWidget->getSessionKey()); $this->controller->formAfterSave($model); + $this->controller->formAfterUpdate($model); Flash::success($this->getLang('update[flash-save]', 'backend::lang.form.update_success')); @@ -409,16 +411,10 @@ class FormController extends ControllerBehavior // /** - * Called before the creation form is saved. + * Called before the creation or updating form is saved. * @param Model */ - public function formBeforeCreateSave($model) {} - - /** - * Called after the creation form is saved. - * @param Model - */ - public function formAfterCreateSave($model) {} + public function formBeforeSave($model) {} /** * Called after the creation or updating form is saved. @@ -426,23 +422,29 @@ class FormController extends ControllerBehavior */ public function formAfterSave($model) {} + /** + * Called before the creation form is saved. + * @param Model + */ + public function formBeforeCreate($model) {} + + /** + * Called after the creation form is saved. + * @param Model + */ + public function formAfterCreate($model) {} + /** * Called before the updating form is saved. * @param Model */ - public function formBeforeEditSave($model) {} - - /** - * Called before the creation or updating form is saved. - * @param Model - */ - public function formBeforeSave($model) {} + public function formBeforeUpdate($model) {} /** * Called after the updating form is saved. * @param Model */ - public function formAfterEditSave($model) {} + public function formAfterUpdate($model) {} /** * Called after the form model is deleted. diff --git a/modules/backend/classes/NavigationManager.php b/modules/backend/classes/NavigationManager.php index 80238b98b..a9b731582 100644 --- a/modules/backend/classes/NavigationManager.php +++ b/modules/backend/classes/NavigationManager.php @@ -60,6 +60,10 @@ class NavigationManager $this->pluginManager = PluginManager::instance(); } + /** + * Loads the menu items from modules and plugins + * @return void + */ protected function loadItems() { /* diff --git a/modules/backend/controllers/Index.php b/modules/backend/controllers/Index.php index 71b16b19e..db130946c 100644 --- a/modules/backend/controllers/Index.php +++ b/modules/backend/controllers/Index.php @@ -26,6 +26,11 @@ class Index extends Controller BackendMenu::setContextOwner('October.Backend'); new ReportContainer($this); + /* @todo Remove line if year >= 2015 */ if (\Schema::hasColumn('backend_users', 'activated')) \Schema::table('backend_users', function($table) { $table->renameColumn('activated', 'is_activated'); }); + /* @todo Remove line if year >= 2015 */ if (\Schema::hasColumn('backend_user_throttle', 'suspended')) \Schema::table('backend_user_throttle', function($table) { $table->renameColumn('suspended', 'is_suspended'); }); + /* @todo Remove line if year >= 2015 */ if (\Schema::hasColumn('backend_user_throttle', 'banned')) \Schema::table('backend_user_throttle', function($table) { $table->renameColumn('banned', 'is_banned'); }); + /* @todo Remove line if year >= 2015 */ if (\Schema::hasColumn('deferred_bindings', 'bind')) \Schema::table('deferred_bindings', function($table) { $table->renameColumn('bind', 'is_bind'); }); + /* @todo Remove line if year >= 2015 */ if (\Schema::hasColumn('system_files', 'public')) \Schema::table('system_files', function($table) { $table->renameColumn('public', 'is_public'); }); } public function index() diff --git a/modules/backend/database/migrations/2013_10_01_000001_Db_Backend_Users.php b/modules/backend/database/migrations/2013_10_01_000001_Db_Backend_Users.php index acc84da11..3d62ce4a6 100644 --- a/modules/backend/database/migrations/2013_10_01_000001_Db_Backend_Users.php +++ b/modules/backend/database/migrations/2013_10_01_000001_Db_Backend_Users.php @@ -21,7 +21,7 @@ class DbBackendUsers extends Migration $table->string('persist_code')->nullable(); $table->string('reset_password_code')->nullable()->index(); $table->text('permissions')->nullable(); - $table->boolean('activated')->default(0); + $table->boolean('is_activated')->default(0); $table->timestamp('activated_at')->nullable(); $table->timestamp('last_login')->nullable(); $table->timestamps(); diff --git a/modules/backend/database/migrations/2013_10_01_000004_Db_Backend_User_Throttle.php b/modules/backend/database/migrations/2013_10_01_000004_Db_Backend_User_Throttle.php index 3eeb2957a..b7eafa86a 100644 --- a/modules/backend/database/migrations/2013_10_01_000004_Db_Backend_User_Throttle.php +++ b/modules/backend/database/migrations/2013_10_01_000004_Db_Backend_User_Throttle.php @@ -15,10 +15,10 @@ class DbBackendUserThrottle extends Migration $table->integer('user_id')->unsigned(); $table->string('ip_address')->nullable(); $table->integer('attempts')->default(0); - $table->boolean('suspended')->default(0); - $table->boolean('banned')->default(0); $table->timestamp('last_attempt_at')->nullable(); + $table->boolean('is_suspended')->default(0); $table->timestamp('suspended_at')->nullable(); + $table->boolean('is_banned')->default(0); $table->timestamp('banned_at')->nullable(); }); } diff --git a/modules/backend/database/seeds/SeedSetupAdmin.php b/modules/backend/database/seeds/SeedSetupAdmin.php index fca419e07..c571b7ca8 100644 --- a/modules/backend/database/seeds/SeedSetupAdmin.php +++ b/modules/backend/database/seeds/SeedSetupAdmin.php @@ -35,7 +35,7 @@ class SeedSetupAdmin extends Seeder 'first_name' => static::$firstName, 'last_name' => static::$lastName, 'permissions' => ['superuser' => 1], - 'activated' => true + 'is_activated' => true ]); $user->addGroup($group); diff --git a/modules/backend/formwidgets/FileUpload.php b/modules/backend/formwidgets/FileUpload.php index 7850f3671..22c127a17 100644 --- a/modules/backend/formwidgets/FileUpload.php +++ b/modules/backend/formwidgets/FileUpload.php @@ -191,7 +191,7 @@ class FileUpload extends FormWidgetBase $file = new File(); $file->data = $uploadedFile; - $file->public = $fileRelation->isPublic(); + $file->is_public = $fileRelation->isPublic(); $file->save(); $fileRelation->add($file, $this->sessionKey); diff --git a/modules/backend/formwidgets/RichEditor.php b/modules/backend/formwidgets/RichEditor.php index 81dd321de..a590ab482 100644 --- a/modules/backend/formwidgets/RichEditor.php +++ b/modules/backend/formwidgets/RichEditor.php @@ -16,11 +16,18 @@ class RichEditor extends FormWidgetBase */ public $defaultAlias = 'richeditor'; + /** + * @var boolean Determines whether content has HEAD and HTML tags. + */ + public $fullPage = false; + /** * {@inheritDoc} */ public function render() { + $this->fullPage = $this->getConfig('fullPage', $this->fullPage); + $this->prepareVars(); return $this->makePartial('richeditor'); } @@ -30,6 +37,7 @@ class RichEditor extends FormWidgetBase */ public function prepareVars() { + $this->vars['fullPage'] = $this->fullPage; $this->vars['stretch'] = $this->formField->stretch; $this->vars['size'] = $this->formField->size; $this->vars['name'] = $this->formField->getName(); diff --git a/modules/backend/views/emails/invite.htm b/modules/backend/views/emails/invite.htm index 76aed9fbe..ed27dc373 100644 --- a/modules/backend/views/emails/invite.htm +++ b/modules/backend/views/emails/invite.htm @@ -1,4 +1,5 @@ subject = "Welcome to October CMS" +layout = "system" == Hi {{ name }}, diff --git a/modules/backend/views/emails/restore.htm b/modules/backend/views/emails/restore.htm index fa6cea090..3a55f45d3 100644 --- a/modules/backend/views/emails/restore.htm +++ b/modules/backend/views/emails/restore.htm @@ -1,4 +1,5 @@ subject = "Password Reset" +layout = "system" == Hello {{ name }}, diff --git a/modules/cms/classes/CmsPropertyHelper.php b/modules/cms/classes/CmsPropertyHelper.php index ca74252f2..d38154d74 100644 --- a/modules/cms/classes/CmsPropertyHelper.php +++ b/modules/cms/classes/CmsPropertyHelper.php @@ -3,7 +3,9 @@ /** * * DEPRECATED WARNING: This class is deprecated and should be deleted - * if the current year is equal to or greater than 2015 @todo. + * if the current year is equal to or greater than 2015. + * + * @todo Delete this file if year >= 2015. * */ diff --git a/modules/cms/controllers/index/_concurrency_resolve_form.htm b/modules/cms/controllers/index/_concurrency_resolve_form.htm index aa0e00ca4..42873cc7e 100644 --- a/modules/cms/controllers/index/_concurrency_resolve_form.htm +++ b/modules/cms/controllers/index/_concurrency_resolve_form.htm @@ -4,9 +4,7 @@ + + + + + + +

fatalError) ?>

+

Return to template list

+ + \ No newline at end of file diff --git a/modules/system/controllers/emaillayouts/update.htm b/modules/system/controllers/emaillayouts/update.htm new file mode 100644 index 000000000..b15be3c40 --- /dev/null +++ b/modules/system/controllers/emaillayouts/update.htm @@ -0,0 +1,68 @@ + + + + +fatalError): ?> + +
+
+
+

+

code ?>

+
+
+
+ + 'layout-item stretch layout-column']) ?> + + formRender() ?> + +
+
+ + + is_locked): ?> + + + + or Cancel + +
+
+ + + + + +

fatalError) ?>

+

Return to template list

+ + \ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/_list_layouts_toolbar.htm b/modules/system/controllers/emailtemplates/_list_layouts_toolbar.htm new file mode 100644 index 000000000..131b0d20e --- /dev/null +++ b/modules/system/controllers/emailtemplates/_list_layouts_toolbar.htm @@ -0,0 +1,7 @@ +
+ + + +
\ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/_list_templates_toolbar.htm b/modules/system/controllers/emailtemplates/_list_templates_toolbar.htm new file mode 100644 index 000000000..bf6067d5b --- /dev/null +++ b/modules/system/controllers/emailtemplates/_list_templates_toolbar.htm @@ -0,0 +1,7 @@ +
+ + + +
\ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/config_form.yaml b/modules/system/controllers/emailtemplates/config_form.yaml new file mode 100644 index 000000000..b75a2a253 --- /dev/null +++ b/modules/system/controllers/emailtemplates/config_form.yaml @@ -0,0 +1,16 @@ +# =================================== +# Form Behavior Config +# =================================== + +name: system::lang.email_templates.template +form: @/modules/system/models/emailtemplate/fields.yaml +modelClass: System\Models\EmailTemplate +defaultRedirect: system/emailtemplates + +create: + redirect: system/emailtemplates/update/:id + redirectClose: system/emailtemplates + +update: + redirect: system/emailtemplates + redirectClose: system/emailtemplates diff --git a/modules/system/controllers/emailtemplates/config_layouts_list.yaml b/modules/system/controllers/emailtemplates/config_layouts_list.yaml new file mode 100644 index 000000000..53148604e --- /dev/null +++ b/modules/system/controllers/emailtemplates/config_layouts_list.yaml @@ -0,0 +1,15 @@ +# =================================== +# List Behavior Config +# =================================== + +title: system::lang.email_templates.menu_label +list: @/modules/system/models/emaillayout/columns.yaml +modelClass: System\Models\EmailLayout +recordUrl: system/emaillayouts/update/:id +noRecordsMessage: backend::lang.list.no_records +showSetup: false + +toolbar: + buttons: list_layouts_toolbar + search: + prompt: backend::lang.list.search_prompt \ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/config_templates_list.yaml b/modules/system/controllers/emailtemplates/config_templates_list.yaml new file mode 100644 index 000000000..2539c63be --- /dev/null +++ b/modules/system/controllers/emailtemplates/config_templates_list.yaml @@ -0,0 +1,16 @@ +# =================================== +# List Behavior Config +# =================================== + +title: system::lang.email_templates.menu_label +list: @/modules/system/models/emailtemplate/columns.yaml +modelClass: System\Models\EmailTemplate +recordUrl: system/emailtemplates/update/:id +noRecordsMessage: backend::lang.list.no_records +recordsPerPage: 20 +showSetup: false + +toolbar: + buttons: list_templates_toolbar + search: + prompt: backend::lang.list.search_prompt \ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/create.htm b/modules/system/controllers/emailtemplates/create.htm new file mode 100644 index 000000000..3ecee25ed --- /dev/null +++ b/modules/system/controllers/emailtemplates/create.htm @@ -0,0 +1,49 @@ + + + + +fatalError): ?> + + 'layout-item stretch layout-column']) ?> + + formRender() ?> + +
+
+ + + + or Cancel + +
+
+ + + + + +

fatalError) ?>

+

Return to template list

+ + \ No newline at end of file diff --git a/modules/system/controllers/emailtemplates/index.htm b/modules/system/controllers/emailtemplates/index.htm new file mode 100644 index 000000000..a114a5d6c --- /dev/null +++ b/modules/system/controllers/emailtemplates/index.htm @@ -0,0 +1,27 @@ + + + + + +
+ +
+
+
+ listRender('templates') ?> +
+
+
+
+ listRender('layouts') ?> +
+
+
+
+ diff --git a/modules/system/controllers/emailtemplates/update.htm b/modules/system/controllers/emailtemplates/update.htm new file mode 100644 index 000000000..e812ea826 --- /dev/null +++ b/modules/system/controllers/emailtemplates/update.htm @@ -0,0 +1,66 @@ + + + + +fatalError): ?> + +
+
+
+

+

code ?>

+
+
+
+ + 'layout-item stretch layout-column']) ?> + + formRender() ?> + +
+
+ + + + + or Cancel + +
+
+ + + + + +

fatalError) ?>

+

Return to template list

+ + \ No newline at end of file diff --git a/modules/system/controllers/updates/_disable_form.htm b/modules/system/controllers/updates/_disable_form.htm index b186db481..4e30301a1 100644 --- a/modules/system/controllers/updates/_disable_form.htm +++ b/modules/system/controllers/updates/_disable_form.htm @@ -11,19 +11,23 @@

count($checked)])) ?>

-
- -
- - -

+
+ +
+ +
+ + +

+
+
diff --git a/modules/system/database/migrations/2013_10_01_000001_Db_Deferred_Bindings.php b/modules/system/database/migrations/2013_10_01_000001_Db_Deferred_Bindings.php index 18773f632..572e4c7c7 100644 --- a/modules/system/database/migrations/2013_10_01_000001_Db_Deferred_Bindings.php +++ b/modules/system/database/migrations/2013_10_01_000001_Db_Deferred_Bindings.php @@ -17,7 +17,7 @@ class DbDeferredBindings extends Migration $table->string('slave_type')->index(); $table->string('slave_id')->index(); $table->string('session_key'); - $table->boolean('bind')->default(true); + $table->boolean('is_bind')->default(true); $table->timestamps(); }); } diff --git a/modules/system/database/migrations/2013_10_01_000002_Db_System_Files.php b/modules/system/database/migrations/2013_10_01_000002_Db_System_Files.php index 84ff6f23a..07c3b1a9c 100644 --- a/modules/system/database/migrations/2013_10_01_000002_Db_System_Files.php +++ b/modules/system/database/migrations/2013_10_01_000002_Db_System_Files.php @@ -21,7 +21,7 @@ class DbSystemFiles extends Migration $table->string('field')->nullable()->index(); $table->string('attachment_id')->index()->nullable(); $table->string('attachment_type')->index()->nullable(); - $table->boolean('public')->default(true); + $table->boolean('is_public')->default(true); $table->integer('sort_order')->nullable(); $table->timestamps(); }); diff --git a/modules/system/database/migrations/2013_10_01_000008_Db_System_Email_Templates.php b/modules/system/database/migrations/2013_10_01_000008_Db_System_Email_Templates.php new file mode 100644 index 000000000..d0a1c1c61 --- /dev/null +++ b/modules/system/database/migrations/2013_10_01_000008_Db_System_Email_Templates.php @@ -0,0 +1,31 @@ +engine = 'InnoDB'; + $table->increments('id'); + $table->string('code')->nullable(); + $table->string('subject')->nullable(); + $table->text('description')->nullable(); + $table->text('content_html')->nullable(); + $table->text('content_text')->nullable(); + $table->integer('layout_id')->index()->nullable(); + $table->boolean('is_custom')->default(0); + $table->timestamps(); + }); + } + + public function down() + { + Schema::drop('system_email_templates'); + } + +} diff --git a/modules/system/database/migrations/2013_10_01_000009_Db_System_Email_Layouts.php b/modules/system/database/migrations/2013_10_01_000009_Db_System_Email_Layouts.php new file mode 100644 index 000000000..a839d4135 --- /dev/null +++ b/modules/system/database/migrations/2013_10_01_000009_Db_System_Email_Layouts.php @@ -0,0 +1,30 @@ +engine = 'InnoDB'; + $table->increments('id'); + $table->string('name')->nullable(); + $table->string('code')->nullable(); + $table->text('content_html')->nullable(); + $table->text('content_text')->nullable(); + $table->text('content_css')->nullable(); + $table->boolean('is_locked')->default(0); + $table->timestamps(); + }); + } + + public function down() + { + Schema::drop('system_email_layouts'); + } + +} diff --git a/modules/system/database/seeds/DatabaseSeeder.php b/modules/system/database/seeds/DatabaseSeeder.php new file mode 100644 index 000000000..8afe4d8d2 --- /dev/null +++ b/modules/system/database/seeds/DatabaseSeeder.php @@ -0,0 +1,21 @@ +call('System\Database\Seeds\SeedSetupEmailLayouts'); + } + +} \ No newline at end of file diff --git a/modules/system/database/seeds/SeedSetupEmailLayouts.php b/modules/system/database/seeds/SeedSetupEmailLayouts.php new file mode 100644 index 000000000..37cf52c0b --- /dev/null +++ b/modules/system/database/seeds/SeedSetupEmailLayouts.php @@ -0,0 +1,79 @@ + + + + + + {{ message }} + +'; + +$text = '{{message}}'; + + EmailLayout::create([ + 'is_locked' => true, + 'name' => 'Default', + 'code' => 'default', + 'content_html' => $html, + 'content_css' => $css, + 'content_text' => $text, + ]); + +$html = ' + + + + + {{ message }} +
+

This is an automatic message. Please do not reply to it.

+ +'; + +$text = '{{message}} + + +--- +This is an automatic message. Please do not reply to it. +'; + + EmailLayout::create([ + 'is_locked' => true, + 'name' => 'System', + 'code' => 'system', + 'content_html' => $html, + 'content_css' => $css, + 'content_text' => $text, + ]); + } + +} \ No newline at end of file diff --git a/modules/system/lang/en/lang.php b/modules/system/lang/en/lang.php index 9db1f9284..e15f46dd8 100644 --- a/modules/system/lang/en/lang.php +++ b/modules/system/lang/en/lang.php @@ -75,6 +75,27 @@ return [ 'sendmail_path' => 'Sendmail Path', 'sendmail_path_comment' => 'Please specify the path of the sendmail program.', ], + 'email_templates' => [ + 'menu_label' => 'Email Templates', + 'menu_description' => 'Modify the email templates that are sent to users and administrators.', + 'new_template' => 'New Template', + 'new_layout' => 'New Layout', + 'template' => 'Template', + 'templates' => 'Templates', + 'menu_layouts_label' => 'Email Layouts', + 'layout' => 'Layout', + 'layouts' => 'Layouts', + 'name' => 'Name', + 'name_comment' => 'Unique name used to refer to this template', + 'code' => 'Code', + 'code_comment' => 'Unique code used to refer to this template', + 'subject' => 'Subject', + 'subject_comment' => 'Email message subject', + 'description' => 'Description', + 'content_html' => 'HTML', + 'content_css' => 'CSS', + 'content_text' => 'Plaintext', + ], 'install' => [ 'project_label' => 'Attach to Project', 'plugin_label' => 'Install Plugin', diff --git a/modules/system/models/EmailLayout.php b/modules/system/models/EmailLayout.php new file mode 100644 index 000000000..9e61c4ee1 --- /dev/null +++ b/modules/system/models/EmailLayout.php @@ -0,0 +1,25 @@ + 'required|unique:system_email_layouts', + 'name' => 'required', + 'content_html' => 'required', + ]; + + public function beforeDelete() + { + if ($this->is_locked) + throw new ApplicationException('Cannot delete this template because it is locked'); + + } +} \ No newline at end of file diff --git a/modules/system/models/EmailTemplate.php b/modules/system/models/EmailTemplate.php new file mode 100644 index 000000000..cd89ad7f7 --- /dev/null +++ b/modules/system/models/EmailTemplate.php @@ -0,0 +1,148 @@ + 'required|unique:system_email_templates', + 'subject' => 'required', + 'description' => 'required', + 'content_html' => 'required', + ]; + + public $belongsTo = [ + 'layout' => ['System\Models\EmailLayout'] + ]; + + /** + * @var array Cache of registration callbacks. + */ + private static $callbacks = []; + + protected static $registeredTemplates; + + public static function syncAll() + { + $templates = self::make()->listRegisteredTemplates(); + $dbTemplates = self::lists('is_custom', 'code'); + $newTemplates = array_diff_key($templates, $dbTemplates); + + /* + * Clean up non-customized templates + */ + foreach ($dbTemplates as $code => $is_custom) { + if ($is_custom) + continue; + + if (!array_key_exists($code, $templates)) + self::whereCode($code)->delete(); + } + + /* + * Create new templates + */ + if (count($newTemplates)) + $categories = EmailLayout::lists('id', 'code'); + + foreach ($newTemplates as $code => $description) { + $sections = self::getTemplateSections($code); + $layoutCode = array_get($sections, 'settings.layout', 'default'); + + $template = self::make(); + $template->code = $code; + $template->description = $description; + $template->is_custom = false; + $template->layout_id = isset($categories[$layoutCode]) ? $categories[$layoutCode] : null; + $template->forceSave(); + } + } + + public function afterFetch() + { + if (!$this->is_custom) { + $sections = self::getTemplateSections($this->code); + $this->content_html = $sections['html']; + $this->content_text = $sections['text']; + $this->subject = array_get($sections, 'settings.subject', 'No subject'); + } + } + + protected static function getTemplateSections($code) + { + return MailParser::parse(File::get(View::make($code)->getPath())); + } + + // + // Registration + // + + /** + * Loads registered email templates from modules and plugins + * @return void + */ + public function loadRegisteredTemplates() + { + foreach (static::$callbacks as $callback) { + $callback($this); + } + + $plugins = PluginManager::instance()->getPlugins(); + foreach ($plugins as $pluginId => $pluginObj) { + $templates = $pluginObj->registerEmailTemplates(); + if (!is_array($templates)) + continue; + + $this->registerEmailTemplates($templates); + } + } + + /** + * Returns a list of the registered templates. + * @return array + */ + public function listRegisteredTemplates() + { + if (self::$registeredTemplates === null) + $this->loadRegisteredTemplates(); + + return self::$registeredTemplates; + } + + /** + * Registers a callback function that defines email templates. + * The callback function should register templates by calling the manager's + * registerEmailTemplates() function. Thi instance is passed to the + * callback function as an argument. Usage: + *
+     *   EmailTemplate::registerCallback(function($template){
+     *       $template->registerEmailTemplates([...]);
+     *   });
+     * 
+ * @param callable $callback A callable function. + */ + public static function registerCallback(callable $callback) + { + self::$callbacks[] = $callback; + } + + /** + * Registers email views and manageable templates. + */ + public function registerEmailTemplates(array $definitions) + { + if (!static::$registeredTemplates) + static::$registeredTemplates = []; + + static::$registeredTemplates = array_merge(static::$registeredTemplates, $definitions); + } +} \ No newline at end of file diff --git a/modules/system/models/emaillayout/columns.yaml b/modules/system/models/emaillayout/columns.yaml new file mode 100644 index 000000000..2e298a31b --- /dev/null +++ b/modules/system/models/emaillayout/columns.yaml @@ -0,0 +1,13 @@ +# =================================== +# Column Definitions +# =================================== + +columns: + + name: + label: Name + searchable: true + + code: + label: Code + searchable: true diff --git a/modules/system/models/emaillayout/fields.yaml b/modules/system/models/emaillayout/fields.yaml new file mode 100644 index 000000000..6e7d22391 --- /dev/null +++ b/modules/system/models/emaillayout/fields.yaml @@ -0,0 +1,43 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + code: + label: system::lang.email_templates.code + comment: system::lang.email_templates.code_comment + span: left + context: create + + name: + label: system::lang.email_templates.name + span: right + # context: create + + # @todo Fix duplicate key problem + # name: + # label: system::lang.email_templates.name + # context: update + +secondaryTabs: + fields: + + content_html: + type: codeeditor + size: giant + tab: system::lang.email_templates.content_html + options: + language: html + + content_css: + type: codeeditor + size: giant + tab: system::lang.email_templates.content_css + options: + language: css + + content_text: + type: textarea + size: giant + tab: system::lang.email_templates.content_text diff --git a/modules/system/models/emailsettings/fields.yaml b/modules/system/models/emailsettings/fields.yaml index 3b7ab5c2e..447bdf74a 100644 --- a/modules/system/models/emailsettings/fields.yaml +++ b/modules/system/models/emailsettings/fields.yaml @@ -3,8 +3,8 @@ # =================================== tabs: - fields: + send_mode: label: system::lang.email.method type: dropdown diff --git a/modules/system/models/emailtemplate/columns.yaml b/modules/system/models/emailtemplate/columns.yaml new file mode 100644 index 000000000..d3637b9af --- /dev/null +++ b/modules/system/models/emailtemplate/columns.yaml @@ -0,0 +1,21 @@ +# =================================== +# Column Definitions +# =================================== + +columns: + + code: + label: Code + searchable: true + + subject: + label: Subject + searchable: true + + description: + label: Description + searchable: true + + layout: + relation: layout + select: @name \ No newline at end of file diff --git a/modules/system/models/emailtemplate/fields.yaml b/modules/system/models/emailtemplate/fields.yaml new file mode 100644 index 000000000..440e81a70 --- /dev/null +++ b/modules/system/models/emailtemplate/fields.yaml @@ -0,0 +1,46 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + layout: + label: system::lang.email_templates.layout + type: relation + options: + emptyOption: -- No layout -- + + code: + label: system::lang.email_templates.code + comment: system::lang.email_templates.code_comment + span: left + context: create + + subject: + label: system::lang.email_templates.subject + comment: system::lang.email_templates.subject_comment + span: right + # context: create + + # @todo Fix duplicate key problem + # subject: + # label: system::lang.email_templates.subject + # context: update + + description: + label: system::lang.email_templates.description + type: textarea + size: tiny + +secondaryTabs: + fields: + + content_html: + type: richeditor + size: huge + tab: system::lang.email_templates.content_html + + content_text: + type: textarea + size: huge + tab: system::lang.email_templates.content_text \ No newline at end of file diff --git a/modules/system/traits/PropertyContainer.php b/modules/system/traits/PropertyContainer.php index fdc92fd8b..5e9af376f 100644 --- a/modules/system/traits/PropertyContainer.php +++ b/modules/system/traits/PropertyContainer.php @@ -76,8 +76,8 @@ trait PropertyContainer /** * Returns a defined property value or default if one is not set. - * @param $name The property name to look for. - * @param $default A default value to return if no name is found. + * @param string $name The property name to look for. + * @param string $default A default value to return if no name is found. * @return string The property value or the default specified. */ public function property($name, $default = null)