diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css
index 4bc609f14..18a0bf078 100644
--- a/modules/backend/assets/css/october.css
+++ b/modules/backend/assets/css/october.css
@@ -7077,6 +7077,10 @@ body.display-side-panel #layout-side-panel {
-webkit-box-shadow: 2px 0px 2px 0 rgba(0, 0, 0, 0.3);
box-shadow: 2px 0px 2px 0 rgba(0, 0, 0, 0.3);
}
+body.side-panel-fix-shadow #layout-side-panel {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
.touch #layout-side-panel .fix-button {
display: none;
}
diff --git a/modules/backend/assets/js/october.layout.js b/modules/backend/assets/js/october.layout.js
index 7c97e9191..f671dc6c1 100644
--- a/modules/backend/assets/js/october.layout.js
+++ b/modules/backend/assets/js/october.layout.js
@@ -1,5 +1,16 @@
(function($){
- function updateLayout() {
+ var OctoberLayout = function() {}
+
+ OctoberLayout.prototype.setPageTitle = function(title) {
+ var $title = $('title')
+
+ if (this.pageTitleTemplate === undefined)
+ this.pageTitleTemplate = $title.data('titleTemplate')
+
+ $title.text(this.pageTitleTemplate.replace('%s', title))
+ }
+
+ OctoberLayout.prototype.updateLayout = function(title) {
$('.layout-cell.width-fix').each(function(){
var $el = $(this).children();
if ($el.length > 0) {
@@ -14,6 +25,13 @@
})
}
- $(document).ready(updateLayout)
- $(window).on('resize', updateLayout)
+ if ($.oc === undefined)
+ $.oc = {}
+
+ $.oc.layout = new OctoberLayout()
+
+ $(document).ready($.oc.layout.updateLayout())
+ $(window).on('resize', function() {
+ $.oc.layout.updateLayout()
+ })
})(jQuery);
\ No newline at end of file
diff --git a/modules/backend/assets/js/october.sidepaneltab.js b/modules/backend/assets/js/october.sidepaneltab.js
index 3eb1f08be..989956cb0 100644
--- a/modules/backend/assets/js/october.sidepaneltab.js
+++ b/modules/backend/assets/js/october.sidepaneltab.js
@@ -136,14 +136,22 @@
SidePanelTab.prototype.fixPanel = function() {
$(document.body).toggleClass('side-panel-not-fixed')
- var fixed = this.panelFixed()
+ var self = this
- fixed
- ? this.updateActiveTab()
- : this.hideSidePanel()
+ window.setTimeout(function() {
+ var fixed = self.panelFixed()
- if (typeof(localStorage) !== 'undefined')
- localStorage.ocSidePanelFixed = fixed ? 1 : 0
+ if (fixed) {
+ self.updateActiveTab()
+ $(document.body).addClass('side-panel-fix-shadow')
+ } else {
+ $(document.body).removeClass('side-panel-fix-shadow')
+ self.hideSidePanel()
+ }
+
+ if (typeof(localStorage) !== 'undefined')
+ localStorage.ocSidePanelFixed = fixed ? 1 : 0
+ }, 0)
}
SidePanelTab.DEFAULTS = {
diff --git a/modules/backend/assets/js/october.tab.js b/modules/backend/assets/js/october.tab.js
index 37761cc27..3def74610 100644
--- a/modules/backend/assets/js/october.tab.js
+++ b/modules/backend/assets/js/october.tab.js
@@ -131,7 +131,8 @@
var
$tabs = $('>li', this.$tabsContainer),
tabIndex = $tabs.index(li),
- targetId = this.tabId + '-tab-' + tabIndex,
+ time = new Date().getTime(),
+ targetId = this.tabId + '-tab-' + tabIndex + time,
$a = $('a', li)
$a.attr('data-target', '#'+targetId).attr('data-toggle', 'tab')
diff --git a/modules/backend/assets/less/layout/sidepanel.less b/modules/backend/assets/less/layout/sidepanel.less
index 3ce2a7f70..31fdd9c1d 100644
--- a/modules/backend/assets/less/layout/sidepanel.less
+++ b/modules/backend/assets/less/layout/sidepanel.less
@@ -42,7 +42,13 @@ body.display-side-panel {
position: absolute;
z-index: 500;
width: 300px;
- .box-shadow(2px 0px 2px 0 rgba(0, 0, 0, 0.3))
+ .box-shadow(2px 0px 2px 0 rgba(0, 0, 0, 0.3));
+ }
+}
+
+body.side-panel-fix-shadow {
+ #layout-side-panel {
+ .box-shadow(none);
}
}
diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php
index 6bc7e434a..4594c3d07 100644
--- a/modules/backend/classes/Controller.php
+++ b/modules/backend/classes/Controller.php
@@ -76,6 +76,11 @@ class Controller extends Extendable
*/
public $pageTitle;
+ /**
+ * @var string Page title template
+ */
+ public $pageTitleTemplate;
+
/**
* @var string Body class property used for customising the layout on a controller basis.
*/
diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm
index b8eb382d3..487c74f20 100644
--- a/modules/backend/layouts/_head.htm
+++ b/modules/backend/layouts/_head.htm
@@ -1,6 +1,8 @@
-
= $this->pageTitle ?> | October CMS
+
+ = $this->pageTitle ?> | October CMS
+
diff --git a/modules/cms/assets/css/october.components.css b/modules/cms/assets/css/october.components.css
index 121845084..b4994db8c 100644
--- a/modules/cms/assets/css/october.components.css
+++ b/modules/cms/assets/css/october.components.css
@@ -118,6 +118,15 @@ div.control-componentlist.has-components {
div.control-componentlist div.layout {
width: auto;
}
+div.control-componentlist div.components div.layout-cell.error-component {
+ background: #ab2a1c;
+}
+div.control-componentlist div.components div.layout-cell.error-component > div {
+ color: #ffffff;
+}
+div.control-componentlist div.components div.layout-cell.error-component > div:after {
+ color: #ab2a1c;
+}
div.control-componentlist div.components div.layout-cell:first-child {
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
diff --git a/modules/cms/assets/js/october.cmspage.js b/modules/cms/assets/js/october.cmspage.js
index 2a8fdc04d..bf14df02b 100644
--- a/modules/cms/assets/js/october.cmspage.js
+++ b/modules/cms/assets/js/october.cmspage.js
@@ -40,7 +40,7 @@
success: function(data) {
this.success(data).done(function(){
$.oc.stripeLoadIndicator.hide()
- $('#cms-master-tabs').ocTab('addTab', data.title, data.tab, tabId, $form.data('type-icon'))
+ $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon'))
}).always(function(){
$.oc.stripeLoadIndicator.hide()
})
@@ -82,6 +82,21 @@
*/
$('#cms-master-tabs').on('closed.oc.tab', function(event){
updateModifiedCounter()
+
+ if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0)
+ setPageTitle('')
+ })
+
+ /*
+ * Listen for the onBeforeRequest event
+ */
+ $('#cms-master-tabs').on('oc.beforeRequest', function(event) {
+ var $form = $(event.target)
+
+ if ( $('.components .layout-cell.error-component', $form).length > 0) {
+ if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?'))
+ event.preventDefault()
+ }
})
/*
@@ -92,6 +107,11 @@
return
var dataId = $(event.target).closest('li').attr('data-tab-id')
+
+ var title = $(event.target).attr('title')
+ if (title)
+ setPageTitle(title)
+
$('#cms-side-panel [data-control=filelist]').fileList('markActive', dataId)
$('#cms-side-panel form').trigger('oc.list.setActiveItem', [dataId])
})
@@ -202,8 +222,10 @@
$('[data-control=preview-button]', this).attr('href', data.pageUrl)
}
- if (data.title !== undefined)
- $('#cms-master-tabs').ocTab('updateTitle', $(this).closest('.tab-pane'), data.title)
+ if (data.tabTitle !== undefined) {
+ $('#cms-master-tabs').ocTab('updateTitle', $(this).closest('.tab-pane'), data.tabTitle)
+ setPageTitle(data.tabTitle)
+ }
var tabId = $('input[name=templateType]', this).val() + '-'
+ $('input[name=theme]', this).val() + '-'
@@ -251,8 +273,9 @@
},
success: function(data) {
this.success(data).done(function(){
- $('#cms-master-tabs').ocTab('addTab', data.title, data.tab, tabId, $form.data('type-icon') + ' new-template')
+ $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template')
$('#layout-side-panel').trigger('close.oc.sidePanel')
+ setPageTitle(data.tabTitle)
}).always(function(){
$.oc.stripeLoadIndicator.hide()
})
@@ -446,7 +469,7 @@
success: function(data) {
this.success(data).done(function(){
$.oc.stripeLoadIndicator.hide()
- $('#cms-master-tabs').ocTab('updateTab', tab, data.title, data.tab)
+ $('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab)
$('#cms-master-tabs').ocTab('unmodifyTab', tab)
updateModifiedCounter()
}).always(function(){
@@ -460,6 +483,13 @@
})
}
+ function setPageTitle(title) {
+ if (title.length)
+ $.oc.layout.setPageTitle(title + ' | ')
+ else
+ $.oc.layout.setPageTitle(title)
+ }
+
/*
* Listen for the click event on the components' remove link
*/
diff --git a/modules/cms/assets/less/october.components.less b/modules/cms/assets/less/october.components.less
index 3c8c888ca..40589f41d 100644
--- a/modules/cms/assets/less/october.components.less
+++ b/modules/cms/assets/less/october.components.less
@@ -11,6 +11,8 @@
@color-component-hover-text: #ffffff;
@color-component-placeholder: #e0e0e0;
@color-group-bg: #f1f3f4;
+@color-error-component-bg: #ab2a1c;
+@color-error-component-text: #ffffff;
.component-lego-icon() {
position: absolute;
@@ -117,6 +119,18 @@ div.control-componentlist {
div.components {
div.layout-cell {
+ &.error-component {
+ background: @color-error-component-bg;
+
+ > div {
+ color: @color-error-component-text;
+
+ &:after {
+ color: @color-error-component-bg;
+ }
+ }
+ }
+
&:first-child {
.border-left-radius(3px);
}
diff --git a/modules/cms/classes/CmsCompoundObject.php b/modules/cms/classes/CmsCompoundObject.php
index e5af60ff5..c37c46d7f 100644
--- a/modules/cms/classes/CmsCompoundObject.php
+++ b/modules/cms/classes/CmsCompoundObject.php
@@ -133,8 +133,8 @@ class CmsCompoundObject extends CmsObject
$settingParts = explode(' ', $setting);
$settingName = $settingParts[0];
- if (!$manager->hasComponent($settingName))
- continue;
+ // if (!$manager->hasComponent($settingName))
+ // continue;
$components[$setting] = $value;
unset($this->settings[$setting]);
diff --git a/modules/cms/classes/ComponentBase.php b/modules/cms/classes/ComponentBase.php
index 2a37180d6..7e6d38eed 100644
--- a/modules/cms/classes/ComponentBase.php
+++ b/modules/cms/classes/ComponentBase.php
@@ -39,6 +39,18 @@ abstract class ComponentBase extends Extendable
*/
public $pluginIcon;
+ /**
+ * @var string Component CSS class name for the back-end page/layout component list.
+ * This field is used by the CMS internally.
+ */
+ public $componentCssClass;
+
+ /**
+ * @var boolean Determines whether Inspector can be used with the component.
+ * This field is used by the CMS internally.
+ */
+ public $inspectorEnabled = true;
+
/**
* @var string Specifies the component directory name.
*/
diff --git a/modules/cms/classes/ComponentManager.php b/modules/cms/classes/ComponentManager.php
index 76168cfd8..8674c7a33 100644
--- a/modules/cms/classes/ComponentManager.php
+++ b/modules/cms/classes/ComponentManager.php
@@ -3,6 +3,7 @@
use Str;
use Illuminate\Container\Container;
use System\Classes\PluginManager;
+use System\Classes\SystemException;
/**
* Component manager
@@ -179,10 +180,10 @@ class ComponentManager
{
$className = $this->resolve($name);
if (!$className)
- return null;
+ throw new SystemException(sprintf('Class name is not registered for the component %s. Check the component plugin.', $name));
if (!class_exists($className))
- throw new \Exception('Component class not found '.$className);
+ throw new SystemException(sprintf('Component class not found %s.Check the component plugin.', $className));
$component = new $className($cmsObject, $properties);
$component->name = $name;
diff --git a/modules/cms/classes/UnknownComponent.php b/modules/cms/classes/UnknownComponent.php
new file mode 100644
index 000000000..5d9c9c5ed
--- /dev/null
+++ b/modules/cms/classes/UnknownComponent.php
@@ -0,0 +1,29 @@
+errorMessage = $errorMessage;
+ $this->componentCssClass = 'error-component';
+ $this->inspectorEnabled = false;
+
+ parent::__construct($cmsObject, $properties);
+ }
+
+ public function componentDetails()
+ {
+ return [
+ 'name' => 'Uknown component',
+ 'description' => $this->errorMessage
+ ];
+ }
+}
\ No newline at end of file
diff --git a/modules/cms/controllers/Index.php b/modules/cms/controllers/Index.php
index 91e895dfd..e14845c5d 100644
--- a/modules/cms/controllers/Index.php
+++ b/modules/cms/controllers/Index.php
@@ -90,6 +90,7 @@ class Index extends Controller
$this->bodyClass = 'compact-container side-panel-not-fixed';
$this->pageTitle = Lang::get('cms::lang.cms.menu_label');
+ $this->pageTitleTemplate = '%s CMS | October';
}
//
@@ -116,7 +117,7 @@ class Index extends Controller
}
return [
- 'title' => $this->getTabTitle($type, $template),
+ 'tabTitle' => $this->getTabTitle($type, $template),
'tab' => $this->makePartial('form_page', [
'form' => $widget,
'templateType' => $type,
@@ -162,7 +163,7 @@ class Index extends Controller
$result = [
'templatePath' => $template->fileName,
'templateMtime' => $template->mtime,
- 'title' => $this->getTabTitle($type, $template)
+ 'tabTitle' => $this->getTabTitle($type, $template)
];
if ($type == 'page') {
@@ -192,7 +193,7 @@ class Index extends Controller
$this->vars['templatePath'] = '';
return [
- 'title' => $this->getTabTitle($type, $template),
+ 'tabTitle' => $this->getTabTitle($type, $template),
'tab' => $this->makePartial('form_page', [
'form' => $widget,
'templateType' => $type,
diff --git a/modules/cms/formwidgets/Components.php b/modules/cms/formwidgets/Components.php
index 483831191..b52df6785 100644
--- a/modules/cms/formwidgets/Components.php
+++ b/modules/cms/formwidgets/Components.php
@@ -3,7 +3,9 @@
use Backend\Classes\FormWidgetBase;
use Cms\Classes\ComponentManager;
use Cms\Classes\ComponentHelpers;
+use Cms\Classes\UnknownComponent;
use Lang;
+use Exception;
/**
* Component Builder
@@ -36,15 +38,22 @@ class Components extends FormWidgetBase
foreach ($this->model->settings['components'] as $name=>$properties) {
list($name, $alias) = strpos($name, ' ') ? explode(' ', $name) : [$name, $name];
- $componentObj = $manager->makeComponent($name, null, $properties);
- $componentObj->alias = $alias;
- $componentObj->pluginIcon = 'icon-puzzle-piece';
+ try {
+ $componentObj = $manager->makeComponent($name, null, $properties);
- $plugin = $manager->findComponentPlugin($componentObj);
- if ($plugin) {
- $pluginDetails = $plugin->pluginDetails();
- if (isset($pluginDetails['icon']))
- $componentObj->pluginIcon = $pluginDetails['icon'];
+ $componentObj->alias = $alias;
+ $componentObj->pluginIcon = 'icon-puzzle-piece';
+
+ $plugin = $manager->findComponentPlugin($componentObj);
+ if ($plugin) {
+ $pluginDetails = $plugin->pluginDetails();
+ if (isset($pluginDetails['icon']))
+ $componentObj->pluginIcon = $pluginDetails['icon'];
+ }
+ } catch (Exception $ex) {
+ $componentObj = new UnknownComponent(null, $properties, $ex->getMessage());
+ $componentObj->alias = $alias;
+ $componentObj->pluginIcon = 'icon-bug';
}
$result[] = $componentObj;
diff --git a/modules/cms/formwidgets/components/partials/_formcomponents.htm b/modules/cms/formwidgets/components/partials/_formcomponents.htm
index e065303b1..0c769d9cd 100644
--- a/modules/cms/formwidgets/components/partials/_formcomponents.htm
+++ b/modules/cms/formwidgets/components/partials/_formcomponents.htm
@@ -2,15 +2,21 @@
-
-
-
= $name ?>
-
= $description ?>
-
= e($component->alias) ?>
-
-
-
-
×
+
+
inspectorEnabled): ?>data-inspectable
+ data-inspector-title="= $name = e($this->getComponentName($component)) ?>"
+ data-inspector-description="= $description = e($this->getComponentDescription($component)) ?>"
+ data-inspector-config="= e($this->getComponentsPropertyConfig($component)) ?>"
+ data-inspector-class="= get_class($component) ?>">
+
= $name ?>
+
= $description ?>
+
= e($component->alias) ?>
+
+
+
+
×
diff --git a/modules/system/assets/js/framework.js b/modules/system/assets/js/framework.js
index b9c28a623..3b5e0c4e0 100644
--- a/modules/system/assets/js/framework.js
+++ b/modules/system/assets/js/framework.js
@@ -45,7 +45,9 @@ if (window.jQuery === undefined)
loading = options.loading !== undefined && options.loading.length ? $(options.loading) : null,
isRedirect = options.redirect !== undefined && options.redirect.length
- form.trigger('oc.beforeRequest', context)
+ var _event = jQuery.Event('oc.beforeRequest')
+ form.trigger(_event, context)
+ if (_event.isDefaultPrevented()) return
var data = [form.serialize()]