diff --git a/modules/backend/ServiceProvider.php b/modules/backend/ServiceProvider.php
index 047f30a545..04489786a4 100644
--- a/modules/backend/ServiceProvider.php
+++ b/modules/backend/ServiceProvider.php
@@ -20,6 +20,7 @@ class ServiceProvider extends ModuleServiceProvider
*/
public function register()
{
+ $this->registerConsole();
$this->registerMailer();
$this->registerAssetBundles();
@@ -45,6 +46,16 @@ public function boot()
parent::boot('backend');
}
+ /**
+ * Register console commands
+ */
+ protected function registerConsole()
+ {
+ $this->registerConsoleCommand('create.controller', \Backend\Console\CreateController::class);
+ $this->registerConsoleCommand('create.formwidget', \Backend\Console\CreateFormWidget::class);
+ $this->registerConsoleCommand('create.reportwidget', \Backend\Console\CreateReportWidget::class);
+ }
+
/**
* Register mail templates
*/
diff --git a/modules/backend/console/CreateController.php b/modules/backend/console/CreateController.php
new file mode 100644
index 0000000000..d256e22ed1
--- /dev/null
+++ b/modules/backend/console/CreateController.php
@@ -0,0 +1,87 @@
+(eg: Winter.Blog)}
+ {controller : The name of the controller to generate. (eg: Posts) }
+ {--force : Overwrite existing files with generated files.}
+ {--model= : Defines the model name to use. If not provided, the singular name of the controller is used.}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Creates a new controller.';
+
+ /**
+ * The type of class being generated.
+ *
+ * @var string
+ */
+ protected $type = 'Controller';
+
+ /**
+ * A mapping of stub to generated file.
+ *
+ * @var array
+ */
+ protected $stubs = [
+ 'scaffold/controller/_list_toolbar.stub' => 'controllers/{{lower_name}}/_list_toolbar.htm',
+ 'scaffold/controller/config_form.stub' => 'controllers/{{lower_name}}/config_form.yaml',
+ 'scaffold/controller/config_list.stub' => 'controllers/{{lower_name}}/config_list.yaml',
+ 'scaffold/controller/create.stub' => 'controllers/{{lower_name}}/create.htm',
+ 'scaffold/controller/index.stub' => 'controllers/{{lower_name}}/index.htm',
+ 'scaffold/controller/preview.stub' => 'controllers/{{lower_name}}/preview.htm',
+ 'scaffold/controller/update.stub' => 'controllers/{{lower_name}}/update.htm',
+ 'scaffold/controller/controller.stub' => 'controllers/{{studly_name}}.php',
+ ];
+
+ /**
+ * Prepare variables for stubs.
+ *
+ * return @array
+ */
+ protected function prepareVars()
+ {
+ $pluginCode = $this->argument('plugin');
+
+ $parts = explode('.', $pluginCode);
+ $plugin = array_pop($parts);
+ $author = array_pop($parts);
+
+ $controller = $this->argument('controller');
+
+ /*
+ * Determine the model name to use,
+ * either supplied or singular from the controller name.
+ */
+ $model = $this->option('model');
+ if (!$model) {
+ $model = Str::singular($controller);
+ }
+
+ return [
+ 'name' => $controller,
+ 'model' => $model,
+ 'author' => $author,
+ 'plugin' => $plugin
+ ];
+ }
+}
diff --git a/modules/backend/console/CreateFormWidget.php b/modules/backend/console/CreateFormWidget.php
new file mode 100644
index 0000000000..17cf2365ef
--- /dev/null
+++ b/modules/backend/console/CreateFormWidget.php
@@ -0,0 +1,71 @@
+(eg: Winter.Blog)}
+ {widget : The name of the form widget to generate. (eg: PostList) }
+ {--force : Overwrite existing files with generated files.}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Creates a new form widget.';
+
+ /**
+ * The type of class being generated.
+ *
+ * @var string
+ */
+ protected $type = 'FormWidget';
+
+ /**
+ * A mapping of stub to generated file.
+ *
+ * @var array
+ */
+ protected $stubs = [
+ 'scaffold/formwidget/formwidget.stub' => 'formwidgets/{{studly_name}}.php',
+ 'scaffold/formwidget/partial.stub' => 'formwidgets/{{lower_name}}/partials/_{{lower_name}}.htm',
+ 'scaffold/formwidget/stylesheet.stub' => 'formwidgets/{{lower_name}}/assets/css/{{lower_name}}.css',
+ 'scaffold/formwidget/javascript.stub' => 'formwidgets/{{lower_name}}/assets/js/{{lower_name}}.js',
+ ];
+
+ /**
+ * Prepare variables for stubs.
+ *
+ * return @array
+ */
+ protected function prepareVars()
+ {
+ $pluginCode = $this->argument('plugin');
+
+ $parts = explode('.', $pluginCode);
+ $plugin = array_pop($parts);
+ $author = array_pop($parts);
+
+ $widget = $this->argument('widget');
+
+ return [
+ 'name' => $widget,
+ 'author' => $author,
+ 'plugin' => $plugin
+ ];
+ }
+}
diff --git a/modules/backend/console/CreateReportWidget.php b/modules/backend/console/CreateReportWidget.php
new file mode 100644
index 0000000000..be6d7f5e26
--- /dev/null
+++ b/modules/backend/console/CreateReportWidget.php
@@ -0,0 +1,69 @@
+(eg: Winter.Blog)}
+ {widget : The name of the report widget to generate. (eg: PostViews) }
+ {--force : Overwrite existing files with generated files.}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Creates a new report widget.';
+
+ /**
+ * The type of class being generated.
+ *
+ * @var string
+ */
+ protected $type = 'ReportWidget';
+
+ /**
+ * A mapping of stub to generated file.
+ *
+ * @var array
+ */
+ protected $stubs = [
+ 'scaffold/reportwidget/reportwidget.stub' => 'reportwidgets/{{studly_name}}.php',
+ 'scaffold/reportwidget/widget.stub' => 'reportwidgets/{{lower_name}}/partials/_{{lower_name}}.htm',
+ ];
+
+ /**
+ * Prepare variables for stubs.
+ *
+ * return @array
+ */
+ protected function prepareVars()
+ {
+ $pluginCode = $this->argument('plugin');
+
+ $parts = explode('.', $pluginCode);
+ $plugin = array_pop($parts);
+ $author = array_pop($parts);
+
+ $widget = $this->argument('widget');
+
+ return [
+ 'name' => $widget,
+ 'author' => $author,
+ 'plugin' => $plugin
+ ];
+ }
+}
diff --git a/modules/backend/console/scaffold/controller/_list_toolbar.stub b/modules/backend/console/scaffold/controller/_list_toolbar.stub
new file mode 100644
index 0000000000..839d456519
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/_list_toolbar.stub
@@ -0,0 +1,21 @@
+
diff --git a/modules/backend/console/scaffold/controller/config_form.stub b/modules/backend/console/scaffold/controller/config_form.stub
new file mode 100644
index 0000000000..f5b0f4fe57
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/config_form.stub
@@ -0,0 +1,31 @@
+# ===================================
+# Form Behavior Config
+# ===================================
+
+# Record name
+name: {{title_singular_name}}
+
+# Model Form Field configuration
+form: $/{{lower_author}}/{{lower_plugin}}/models/{{lower_model}}/fields.yaml
+
+# Model Class name
+modelClass: {{studly_author}}\{{studly_plugin}}\Models\{{studly_model}}
+
+# Default redirect location
+defaultRedirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}}
+
+# Create page
+create:
+ title: backend::lang.form.create_title
+ redirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}}/update/:id
+ redirectClose: {{lower_author}}/{{lower_plugin}}/{{lower_name}}
+
+# Update page
+update:
+ title: backend::lang.form.update_title
+ redirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}}
+ redirectClose: {{lower_author}}/{{lower_plugin}}/{{lower_name}}
+
+# Preview page
+preview:
+ title: backend::lang.form.preview_title
diff --git a/modules/backend/console/scaffold/controller/config_list.stub b/modules/backend/console/scaffold/controller/config_list.stub
new file mode 100644
index 0000000000..6ffce9b3d3
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/config_list.stub
@@ -0,0 +1,50 @@
+# ===================================
+# List Behavior Config
+# ===================================
+
+# Model List Column configuration
+list: $/{{lower_author}}/{{lower_plugin}}/models/{{lower_model}}/columns.yaml
+
+# Model Class name
+modelClass: {{studly_author}}\{{studly_plugin}}\Models\{{studly_model}}
+
+# List Title
+title: Manage {{title_plural_name}}
+
+# Link URL for each record
+recordUrl: {{lower_author}}/{{lower_plugin}}/{{lower_name}}/update/:id
+
+# Message to display if the list is empty
+noRecordsMessage: backend::lang.list.no_records
+
+# Records to display per page
+recordsPerPage: 20
+
+# Options to provide the user when selecting how many records to display per page
+perPageOptions: [20, 40, 80, 100, 120]
+
+# Display page numbers with pagination, disable to improve performance
+showPageNumbers: true
+
+# Displays the list column set up button
+showSetup: true
+
+# Displays the sorting link on each column
+showSorting: true
+
+# Default sorting column
+# defaultSort:
+# column: created_at
+# direction: desc
+
+# Display checkboxes next to each record
+showCheckboxes: true
+
+# Toolbar widget configuration
+toolbar:
+ # Partial for toolbar buttons
+ buttons: list_toolbar
+
+ # Search widget configuration
+ search:
+ prompt: backend::lang.list.search_prompt
diff --git a/modules/backend/console/scaffold/controller/controller.stub b/modules/backend/console/scaffold/controller/controller.stub
new file mode 100644
index 0000000000..6797a76faf
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/controller.stub
@@ -0,0 +1,25 @@
+
+
+
+
+fatalError): ?>
+
+ = Form::open(['class' => 'layout']) ?>
+
+
+ = $this->formRender() ?>
+
+
+
+
+ = Form::close() ?>
+
+
+
+ = e($this->fatalError) ?>
+ Return to {{lower_title_name}} list
+
+
diff --git a/modules/backend/console/scaffold/controller/index.stub b/modules/backend/console/scaffold/controller/index.stub
new file mode 100644
index 0000000000..766877d929
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/index.stub
@@ -0,0 +1,2 @@
+
+= $this->listRender() ?>
diff --git a/modules/backend/console/scaffold/controller/preview.stub b/modules/backend/console/scaffold/controller/preview.stub
new file mode 100644
index 0000000000..df5524cd45
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/preview.stub
@@ -0,0 +1,19 @@
+
+
+
+
+fatalError): ?>
+
+
+ = $this->formRenderPreview() ?>
+
+
+
+
+ = e($this->fatalError) ?>
+ Return to {{lower_title_name}} list
+
+
diff --git a/modules/backend/console/scaffold/controller/update.stub b/modules/backend/console/scaffold/controller/update.stub
new file mode 100644
index 0000000000..7812f2b748
--- /dev/null
+++ b/modules/backend/console/scaffold/controller/update.stub
@@ -0,0 +1,56 @@
+
+
+
+
+fatalError): ?>
+
+ = Form::open(['class' => 'layout']) ?>
+
+
+ = $this->formRender() ?>
+
+
+
+
+ = Form::close() ?>
+
+
+
+ = e($this->fatalError) ?>
+ Return to {{lower_title_name}} list
+
+
diff --git a/modules/backend/console/scaffold/formwidget/formwidget.stub b/modules/backend/console/scaffold/formwidget/formwidget.stub
new file mode 100644
index 0000000000..9fcf5045b0
--- /dev/null
+++ b/modules/backend/console/scaffold/formwidget/formwidget.stub
@@ -0,0 +1,57 @@
+prepareVars();
+ return $this->makePartial('{{lower_name}}');
+ }
+
+ /**
+ * Prepares the form widget view data
+ */
+ public function prepareVars()
+ {
+ $this->vars['name'] = $this->formField->getName();
+ $this->vars['value'] = $this->getLoadValue();
+ $this->vars['model'] = $this->model;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function loadAssets()
+ {
+ $this->addCss('css/{{lower_name}}.css', '{{author}}.{{plugin}}');
+ $this->addJs('js/{{lower_name}}.js', '{{author}}.{{plugin}}');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getSaveValue($value)
+ {
+ return $value;
+ }
+}
diff --git a/modules/backend/console/scaffold/formwidget/javascript.stub b/modules/backend/console/scaffold/formwidget/javascript.stub
new file mode 100644
index 0000000000..d4765f0b0c
--- /dev/null
+++ b/modules/backend/console/scaffold/formwidget/javascript.stub
@@ -0,0 +1,5 @@
+/*
+ * This is a sample JavaScript file used by {{name}}
+ *
+ * You can delete this file if you want
+ */
diff --git a/modules/backend/console/scaffold/formwidget/partial.stub b/modules/backend/console/scaffold/formwidget/partial.stub
new file mode 100644
index 0000000000..f311f175c0
--- /dev/null
+++ b/modules/backend/console/scaffold/formwidget/partial.stub
@@ -0,0 +1,17 @@
+previewMode): ?>
+
+
+ = $value ?>
+
+
+
+
+
+
+
diff --git a/modules/backend/console/scaffold/formwidget/stylesheet.stub b/modules/backend/console/scaffold/formwidget/stylesheet.stub
new file mode 100644
index 0000000000..203c17affa
--- /dev/null
+++ b/modules/backend/console/scaffold/formwidget/stylesheet.stub
@@ -0,0 +1,5 @@
+/*
+ * This is a sample StyleSheet file used by {{name}}
+ *
+ * You can delete this file if you want
+ */
diff --git a/modules/backend/console/scaffold/reportwidget/reportwidget.stub b/modules/backend/console/scaffold/reportwidget/reportwidget.stub
new file mode 100644
index 0000000000..bfe197259e
--- /dev/null
+++ b/modules/backend/console/scaffold/reportwidget/reportwidget.stub
@@ -0,0 +1,63 @@
+ [
+ 'title' => 'backend::lang.dashboard.widget_title_label',
+ 'default' => '{{title_name}} Report Widget',
+ 'type' => 'string',
+ 'validationPattern' => '^.+$',
+ 'validationMessage' => 'backend::lang.dashboard.widget_title_error',
+ ],
+ ];
+ }
+
+ /**
+ * Adds widget specific asset files. Use $this->addJs() and $this->addCss()
+ * to register new assets to include on the page.
+ * @return void
+ */
+ protected function loadAssets()
+ {
+ }
+
+ /**
+ * Renders the widget's primary contents.
+ * @return string HTML markup supplied by this widget.
+ */
+ public function render()
+ {
+ try {
+ $this->prepareVars();
+ } catch (Exception $ex) {
+ $this->vars['error'] = $ex->getMessage();
+ }
+
+ return $this->makePartial('{{lower_name}}');
+ }
+
+ /**
+ * Prepares the report widget view data
+ */
+ public function prepareVars()
+ {
+ }
+}
diff --git a/modules/backend/console/scaffold/reportwidget/widget.stub b/modules/backend/console/scaffold/reportwidget/widget.stub
new file mode 100644
index 0000000000..13f9152e82
--- /dev/null
+++ b/modules/backend/console/scaffold/reportwidget/widget.stub
@@ -0,0 +1,9 @@
+
diff --git a/modules/cms/ServiceProvider.php b/modules/cms/ServiceProvider.php
index d2fa63a562..846a7b790c 100644
--- a/modules/cms/ServiceProvider.php
+++ b/modules/cms/ServiceProvider.php
@@ -78,6 +78,9 @@ public function boot()
*/
protected function registerConsole()
{
+ $this->registerConsoleCommand('create.component', \Cms\Console\CreateComponent::class);
+ $this->registerConsoleCommand('create.theme', \Cms\Console\CreateTheme::class);
+
$this->registerConsoleCommand('theme.install', \Cms\Console\ThemeInstall::class);
$this->registerConsoleCommand('theme.remove', \Cms\Console\ThemeRemove::class);
$this->registerConsoleCommand('theme.list', \Cms\Console\ThemeList::class);
diff --git a/modules/cms/console/CreateComponent.php b/modules/cms/console/CreateComponent.php
new file mode 100644
index 0000000000..d6eaa53d23
--- /dev/null
+++ b/modules/cms/console/CreateComponent.php
@@ -0,0 +1,68 @@
+(eg: Winter.Blog)}
+ {component : The name of the component to generate. (eg: Posts) }
+ {--force : Overwrite existing files with generated files.}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Creates a new plugin component.';
+
+ /**
+ * The type of class being generated.
+ *
+ * @var string
+ */
+ protected $type = 'Component';
+
+ /**
+ * A mapping of stub to generated file.
+ *
+ * @var array
+ */
+ protected $stubs = [
+ 'scaffold/component/component.stub' => 'components/{{studly_name}}.php',
+ 'scaffold/component/default.stub' => 'components/{{lower_name}}/default.htm',
+ ];
+
+ /**
+ * Prepare variables for stubs.
+ *
+ * return @array
+ */
+ protected function prepareVars()
+ {
+ $pluginCode = $this->argument('plugin');
+
+ $parts = explode('.', $pluginCode);
+ $plugin = array_pop($parts);
+ $author = array_pop($parts);
+ $component = $this->argument('component');
+
+ return [
+ 'name' => $component,
+ 'author' => $author,
+ 'plugin' => $plugin
+ ];
+ }
+}
diff --git a/modules/cms/console/CreateTheme.php b/modules/cms/console/CreateTheme.php
new file mode 100644
index 0000000000..fe917827b0
--- /dev/null
+++ b/modules/cms/console/CreateTheme.php
@@ -0,0 +1,121 @@
+(eg: MyTheme)}
+ {--force : Overwrite existing files with generated files.}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Creates a new theme.';
+
+ /**
+ * The type of class being generated.
+ *
+ * @var string
+ */
+ protected $type = 'Theme';
+
+ /**
+ * A mapping of stub to generated file.
+ *
+ * @var array
+ */
+ protected $stubs = [
+ 'scaffold/theme/assets/js/app.stub' => 'assets/js/app.js',
+ 'scaffold/theme/assets/less/theme.stub' => 'assets/less/theme.less',
+ 'scaffold/theme/layouts/default.stub' => 'layouts/default.htm',
+ 'scaffold/theme/pages/404.stub' => 'pages/404.htm',
+ 'scaffold/theme/pages/error.stub' => 'pages/error.htm',
+ 'scaffold/theme/pages/home.stub' => 'pages/home.htm',
+ 'scaffold/theme/partials/meta/seo.stub' => 'partials/meta/seo.htm',
+ 'scaffold/theme/partials/meta/styles.stub' => 'partials/meta/styles.htm',
+ 'scaffold/theme/partials/site/header.stub' => 'partials/site/header.htm',
+ 'scaffold/theme/partials/site/footer.stub' => 'partials/site/footer.htm',
+ 'scaffold/theme/theme.stub' => 'theme.yaml',
+ 'scaffold/theme/version.stub' => 'version.yaml',
+ ];
+
+ /**
+ * Prepare variables for stubs.
+ *
+ * return @array
+ */
+ protected function prepareVars()
+ {
+ /*
+ * Extract the author and name from the plugin code
+ */
+ $code = str_slug($this->argument('theme'));
+
+ return [
+ 'code' => $code,
+ ];
+ }
+
+ /**
+ * Get the plugin path from the input.
+ *
+ * @return string
+ */
+ protected function getDestinationPath()
+ {
+ $code = $this->prepareVars()['code'];
+
+ return themes_path($code);
+ }
+
+ /**
+ * Make a single stub.
+ *
+ * @param string $stubName The source filename for the stub.
+ */
+ public function makeStub($stubName)
+ {
+ if (!isset($this->stubs[$stubName])) {
+ return;
+ }
+
+ $sourceFile = $this->getSourcePath() . '/' . $stubName;
+ $destinationFile = $this->getDestinationPath() . '/' . $this->stubs[$stubName];
+ $destinationContent = $this->files->get($sourceFile);
+
+ /*
+ * Parse each variable in to the destination content and path
+ */
+ foreach ($this->vars as $key => $var) {
+ $destinationContent = str_replace('{{' . $key . '}}', $var, $destinationContent);
+ $destinationFile = str_replace('{{' . $key . '}}', $var, $destinationFile);
+ }
+
+ $this->makeDirectory($destinationFile);
+
+ /*
+ * Make sure this file does not already exist
+ */
+ if ($this->files->exists($destinationFile) && !$this->option('force')) {
+ throw new Exception('Stop everything!!! This file already exists: ' . $destinationFile);
+ }
+
+ $this->files->put($destinationFile, $destinationContent);
+ }
+}
diff --git a/modules/cms/console/scaffold/component/component.stub b/modules/cms/console/scaffold/component/component.stub
new file mode 100644
index 0000000000..5f9bbbb7be
--- /dev/null
+++ b/modules/cms/console/scaffold/component/component.stub
@@ -0,0 +1,19 @@
+ '{{name}} Component',
+ 'description' => 'No description provided yet...'
+ ];
+ }
+
+ public function defineProperties()
+ {
+ return [];
+ }
+}
diff --git a/modules/cms/console/scaffold/component/default.stub b/modules/cms/console/scaffold/component/default.stub
new file mode 100644
index 0000000000..c9069b3759
--- /dev/null
+++ b/modules/cms/console/scaffold/component/default.stub
@@ -0,0 +1,3 @@
+This is the default markup for component {{name}}
+
+You can delete this file if you want
diff --git a/modules/cms/console/scaffold/theme/assets/js/app.stub b/modules/cms/console/scaffold/theme/assets/js/app.stub
new file mode 100644
index 0000000000..235a2e31a0
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/assets/js/app.stub
@@ -0,0 +1,33 @@
+/*
+ * Application
+ */
+(function($) {
+ "use strict";
+
+ jQuery(document).ready(function($) {
+ /*-------------------------------
+ WINTER CMS FLASH MESSAGE HANDLING
+ ---------------------------------*/
+ $(document).on('ajaxSetup', function(event, context) {
+ // Enable AJAX handling of Flash messages on all AJAX requests
+ context.options.flash = true;
+
+ // Enable the StripeLoadIndicator on all AJAX requests
+ context.options.loading = $.oc.stripeLoadIndicator;
+
+ // Handle Flash Messages
+ context.options.handleFlashMessage = function(message, type) {
+ $.oc.flashMsg({ text: message, class: type });
+ };
+
+ // Handle Error Messages
+ context.options.handleErrorMessage = function(message) {
+ $.oc.flashMsg({ text: message, class: 'error' });
+ };
+ });
+ });
+}(jQuery));
+
+if (typeof(gtag) !== 'function') {
+ gtag = function() { console.log('GoogleAnalytics not present.'); }
+}
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/assets/less/theme.stub b/modules/cms/console/scaffold/theme/assets/less/theme.stub
new file mode 100644
index 0000000000..1628f3a057
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/assets/less/theme.stub
@@ -0,0 +1,5 @@
+.content {
+ margin: 2em auto;
+ max-width: 1080px;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/layouts/default.stub b/modules/cms/console/scaffold/theme/layouts/default.stub
new file mode 100644
index 0000000000..060d9ab281
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/layouts/default.stub
@@ -0,0 +1,7 @@
+description = "Default layout"
+==
+{% partial "site/header" %}
+
+ {% page %}
+
+{% partial "site/footer" %}
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/pages/404.stub b/modules/cms/console/scaffold/theme/pages/404.stub
new file mode 100644
index 0000000000..e9c4e27cc0
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/pages/404.stub
@@ -0,0 +1,8 @@
+title = "Page not found (404)"
+url = "/404"
+layout = "default"
+==
+
+
Page not found
+
We're sorry, but the page you requested cannot be found.
+
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/pages/error.stub b/modules/cms/console/scaffold/theme/pages/error.stub
new file mode 100644
index 0000000000..6767bc687b
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/pages/error.stub
@@ -0,0 +1,8 @@
+title = "Error page (500)"
+url = "/error"
+layout = "default"
+==
+
+
Error
+
We're sorry, but something went wrong and the page cannot be displayed.
+
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/pages/home.stub b/modules/cms/console/scaffold/theme/pages/home.stub
new file mode 100644
index 0000000000..ca87de8cbf
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/pages/home.stub
@@ -0,0 +1,7 @@
+title = "Home"
+url = "/"
+layout = "default"
+==
+
+
Home Page
+
\ No newline at end of file
diff --git a/modules/cms/console/scaffold/theme/partials/meta/seo.stub b/modules/cms/console/scaffold/theme/partials/meta/seo.stub
new file mode 100644
index 0000000000..332bb2b08a
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/partials/meta/seo.stub
@@ -0,0 +1,11 @@
+{% if this.theme.googleanalytics_id is not empty %}
+
+
+
+{% endif %}
diff --git a/modules/cms/console/scaffold/theme/partials/meta/styles.stub b/modules/cms/console/scaffold/theme/partials/meta/styles.stub
new file mode 100644
index 0000000000..8c4144b470
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/partials/meta/styles.stub
@@ -0,0 +1,4 @@
+
+
+{% styles %}
+{% placeholder head %}
diff --git a/modules/cms/console/scaffold/theme/partials/site/footer.stub b/modules/cms/console/scaffold/theme/partials/site/footer.stub
new file mode 100644
index 0000000000..565e8bd4e6
--- /dev/null
+++ b/modules/cms/console/scaffold/theme/partials/site/footer.stub
@@ -0,0 +1,17 @@
+
+
+ {% scripts %}
+
+ {% flash %}
+
+ {{ message }}
+
+ {% endflash %}
+