Added support for components in partials

This commit is contained in:
alekseybobkov 2014-10-29 22:12:42 -07:00
parent a9eecfecd5
commit 9a05a408c9
12 changed files with 519 additions and 114 deletions

View File

@ -13618,6 +13618,71 @@ html.cssanimations .fancy-layout .form-tabless-fields .loading-indicator-contain
.inspector-fields td select {
width: 90%;
}
.inspector-fields td div.external-param-editor-container {
position: relative;
padding-right: 30px;
}
.inspector-fields td div.external-param-editor-container div.external-editor {
bottom: 0;
margin: -5px -12px;
right: 30px;
left: auto;
top: 0;
position: absolute;
-webkit-transition: left 0.2s;
transition: left 0.2s;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
will-change: transform;
}
.inspector-fields td div.external-param-editor-container div.external-editor div.controls {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
.inspector-fields td div.external-param-editor-container div.external-editor div.controls a {
position: absolute;
left: 0;
top: 0;
display: inline-block;
height: 100%;
width: 30px;
color: #2b3e50;
outline: none;
}
.inspector-fields td div.external-param-editor-container div.external-editor div.controls a i {
display: inline-block;
position: relative;
left: 10px;
top: 4px;
font-size: 15px;
}
.inspector-fields td div.external-param-editor-container div.external-editor div.controls input {
position: absolute;
display: block;
border: none;
width: 100%;
height: 100%;
left: 0;
top: 0;
padding-left: 30px;
padding-right: 12px;
background: transparent;
}
.inspector-fields td div.external-param-editor-container.editor-visible div.external-editor div.controls input {
background: #f2f2f2;
}
.inspector-fields td.active div.external-param-editor-container div.external-editor div.controls input {
background: white;
}
.inspector-fields td.dropdown div.external-param-editor-container div.external-editor {
height: 100%;
margin: 0;
bottom: auto;
}
.inspector-fields th {
font-weight: 500;
}

View File

@ -29,6 +29,7 @@
* - placeholder - placholder text, for text and dropdown properties
* - depends - a list of properties the property depend on, for dropdown lists
* - options - an option list for dropdown lists, optional. If not provided the options are loaded with AJAX.
* - noExternalParameter - disables the external parameter feature for the property.
* Example of the configuration string (a single property):
* [{"property":"max-width","title":"Max width","type":"string"}]
*
@ -36,6 +37,10 @@
* The inspector uses this field to read and write field values. The field value is a JSON string, an object with keys matching property
* names and values matching property values.
*
* Any HTML element that wraps the inspectable element can have the data-inspector-external-parameters property that enables the external
* parameters support. External parameters saved with the special syntax {{paramName}}. The external parameter feature can be disabled
* on per-property basis with the noExternalParameter option.
*
* Events
* - change - the event is triggered on the inspectable element when it's properties are updated.
* - showing.oc.inspector - triggered before the Inspector is displayed. Allows to cancel the action with e.preventDefault()
@ -96,10 +101,10 @@
data-dismiss="popover" \
aria-hidden="true">&times;</button> \
</div> \
<form> \
<form autocomplete="off"> \
<table class="inspector-fields {{#tableClass}}{{/tableClass}}"> \
{{#properties}} \
<tr id="{{#propFormat}}{{property}}{{/propFormat}}" \
<tr id="{{#propFormat}}{{property}}{{/propFormat}}" data-property="{{property}}" \
{{#dataGroupIndex}}{{/dataGroupIndex}} \
class="{{#cellClass}}{{/cellClass}}"> \
<th {{#colspan}}{{/colspan}}><div><div><span class="title-element" title="{{title}}"> \
@ -156,6 +161,9 @@
if (this.itemType == 'property' && this.groupIndex !== undefined)
result += self.groupExpanded(this.group) ? ' expanded' : ' collapsed'
if (this.itemType == 'property' && this.noExternalParameter)
result += ' no-external-parameter'
return result
}
},
@ -211,7 +219,7 @@
width: 400
})
self.$el.on('hiding.oc.popover', function(e){self.onBeforeHide(e)})
self.$el.on('hiding.oc.popover', function(e){return self.onBeforeHide(e)})
self.$el.on('hide.oc.popover', function(){self.cleanup()})
self.$el.addClass('inspector-open')
@ -225,12 +233,15 @@
self.editors[0].focus()
}
if (self.$el.closest('[data-inspector-external-parameters]').length > 0)
self.initExternalParameterEditor(self.$el.data('oc.popover').$container)
$.each(self.editors, function(){
if (this.init !== undefined)
this.init()
})
$('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body'})
$('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body', delay: 500})
var $container = self.$el.data('oc.popover').$container
$container.on('click', 'tr.group', function(){
@ -248,6 +259,145 @@
displayPopover()
}
/*
* Initializes the external parameter editors for properties that support this feature.
*/
Inspector.prototype.initExternalParameterEditor = function($container) {
var self = this
$('table.inspector-fields tr', $container).each(function(){
if (!$(this).hasClass('no-external-parameter')) {
var property = $(this).data('property'),
$td = $('td', this),
$editorContainer = $('<div class="external-param-editor-container"></div>'),
$editor = $(
'<div class="external-editor"> \
<div class="controls"> \
<input type="text" tabindex="-1"/> \
<a href="#" tabindex="-1"> \
<i class="oc-icon-terminal"></i> \
</a> \
</div> \
</div>')
$editorContainer.append($td.children())
$editorContainer.append($editor)
$td.append($editorContainer)
var $editorLink = $('a', $editor)
$editorLink.click(function() {
return self.toggleExternalParameterEditor($(this))
})
.attr('title', 'Click to enter the external parameter name to load the property value from')
.tooltip({'container': 'body', delay: 500})
var $input = $editor.find('input'),
propertyValue = self.propertyValues[property]
$input.on('focus', function(){
var $field = $(this)
$('td', $field.closest('table')).removeClass('active')
$field.closest('td').addClass('active')
})
$input.on('change', function(){
self.markPropertyChanged(property, true)
})
var matches = []
if (matches = propertyValue.match(/^\{\{([^\}]+)\}\}$/)) {
var value = $.trim(matches[1])
if (value.length > 0) {
self.showExternalParameterEditor($editorContainer, $editor, $editorLink, $td, true)
$editor.find('input').val(value)
self.writeProperty(property, null, true)
}
}
}
})
}
Inspector.prototype.showExternalParameterEditor = function($container, $editor, $editorLink, $cell, noAnimation) {
var position = $editor.position()
$('input', $editor).focus()
if (!noAnimation) {
$editor.css({
'left': position.left + 'px',
'right': 0
})
} else
$editor.css('right', 0)
setTimeout(function(){
$editor.css('left', 0)
$cell.scrollTop(0)
}, 0)
$container.addClass('editor-visible')
$editorLink.attr('data-original-title', 'Click to enter the property value')
this.toggleCellEditorVisibility($cell, false)
$editor.find('input').attr('tabindex', 0)
}
Inspector.prototype.toggleExternalParameterEditor = function($editorLink) {
var $container = $editorLink.closest('.external-param-editor-container'),
$editor = $('.external-editor', $container),
$cell = $editorLink.closest('td'),
self = this
$editorLink.tooltip('hide')
if (!$container.hasClass('editor-visible')) {
self.showExternalParameterEditor($container, $editor, $editorLink, $cell)
} else {
var left = $container.width()
$editor.css('left', left + 'px')
setTimeout(function(){
$editor.css({
'left': 'auto',
'right': '30px'
})
$container.removeClass('editor-visible')
$container.closest('td').removeClass('active')
}, 200)
$editorLink.attr('data-original-title', 'Click to enter the external parameter name to load the property value from')
$editor.find('input').attr('tabindex', -1)
self.toggleCellEditorVisibility($cell, true)
}
return false
}
Inspector.prototype.toggleCellEditorVisibility = function($cell, show) {
var $container = $('.external-param-editor-container', $cell)
$container.children().each(function(){
var $el = $(this)
if ($el.hasClass('external-editor'))
return
if (show)
$el.removeClass('hide')
else {
var height = $cell.data('inspector-cell-height')
if (!height) {
height = $cell.height()
$cell.data('inspector-cell-height', height)
}
$container.css('height', height + 'px')
$el.addClass('hide')
}
})
}
/*
* Creates group nodes in the property set
*/
@ -379,17 +529,22 @@
return null
}
Inspector.prototype.writeProperty = function(property, value) {
Inspector.prototype.writeProperty = function(property, value, noChangedStatusUpdate) {
this.propertyValues[property] = value
this.propertyValuesField.val(JSON.stringify(this.propertyValues))
if (this.originalPropertyValues[property] === undefined || this.originalPropertyValues[property] != value) {
this.$el.trigger('change')
this.markPropertyChanged(property, true)
} else
this.markPropertyChanged(property, false)
if (!noChangedStatusUpdate) {
this.$el.trigger('change')
this.markPropertyChanged(property, true)
}
} else {
if (!noChangedStatusUpdate)
this.markPropertyChanged(property, false)
}
this.$el.trigger('propertyChanged.oc.Inspector', [property])
if (!noChangedStatusUpdate)
this.$el.trigger('propertyChanged.oc.Inspector', [property])
return value
}
@ -412,6 +567,7 @@
var editor = new $.oc.inspector.editors[editorClass](editorId, this, data)
this.editors.push(editor)
editor.inspectorCellId = editorId
return editor.renderEditor()
}
@ -429,11 +585,40 @@
}
Inspector.prototype.onBeforeHide = function(e) {
var $container = this.$el.data('inspector-container'),
externalParamErrorFound = false,
self = this
$.each(this.editors, function() {
this.applyValue()
if (!self.editorExternalPropertyEnabled(this))
this.applyValue()
else {
var $cell = $('#'+this.inspectorCellId, $container),
$extPropEditorContainer = $cell.find('.external-param-editor-container'),
$input = $extPropEditorContainer.find('.external-editor input'),
val = $.trim($input.val())
if (val.length == 0) {
alert('Please enter external parameter name for the '+this.fieldDef.title+' property.')
externalParamErrorFound = true
setTimeout(function(){
$input.focus()
}, 0)
return false
}
self.writeProperty(this.fieldDef.property, '{{ '+val+' }}')
}
})
var eH = $.Event('hiding.oc.inspector')
if (externalParamErrorFound) {
e.preventDefault()
return false
}
var eH = $.Event('hiding.oc.inspector'),
ispector = this
this.$el.trigger(eH, [{values: this.propertyValues}])
if (eH.isDefaultPrevented()) {
e.preventDefault()
@ -441,6 +626,9 @@
}
$.each(this.editors, function() {
if (ispector.editorExternalPropertyEnabled(this))
return true
if (this.validate === undefined)
return true
@ -458,10 +646,17 @@
return false
})
var $contianer = this.$el.data('inspector-container')
$('[data-toggle=tooltip]', this.$el.data('oc.popover').$container).tooltip('hide')
}
Inspector.prototype.editorExternalPropertyEnabled = function(editor) {
var $container = this.$el.data('inspector-container'),
$cell = $('#'+editor.inspectorCellId, $container),
$extPropEditorContainer = $cell.find('.external-param-editor-container')
return $extPropEditorContainer.hasClass('editor-visible')
}
//
// EDITOR DEFINITIONS
// ==================
@ -474,6 +669,7 @@
* - validate(), optional, validates the value and returns the validation error message
* - applyValue(), applies the editor value
* - focus(), focuses the editor input element, if applicable
* - init(), sets the initial editor value
*/
// STRING EDITOR
@ -483,7 +679,7 @@
this.inspector = inspector
this.fieldDef = fieldDef
this.editorId = editorId
this.selector = '#'+this.editorId+' input'
this.selector = '#'+this.editorId+' input.string-editor'
var self = this
$(document).on('focus', this.selector, function() {
@ -498,6 +694,11 @@
})
}
InspectorEditorString.prototype.init = function() {
var value = $.trim(this.inspector.readProperty(this.fieldDef.property))
$(this.selector).val(value)
}
InspectorEditorString.prototype.applyValue = function() {
this.inspector.writeProperty(this.fieldDef.property, $.trim($(this.selector).val()))
}
@ -505,11 +706,10 @@
InspectorEditorString.prototype.renderEditor = function() {
var data = {
id: this.editorId,
value: $.trim(this.inspector.readProperty(this.fieldDef.property)),
placeholder: this.fieldDef.placeholder !== undefined ? this.fieldDef.placeholder : ''
}
return Mustache.render('<td class="text" id="{{id}}"><input type="text" value="{{value}}" placeholder="{{placeholder}}"/></td>', data)
return Mustache.render('<td class="text" id="{{id}}"><input type="text" class="string-editor" placeholder="{{placeholder}}"/></td>', data)
}
InspectorEditorString.prototype.validate = function() {
@ -525,6 +725,7 @@
InspectorEditorString.prototype.focus = function() {
$(this.selector).focus()
$(this.selector).closest('td').scrollLeft(0)
}
$.oc.inspector.editors.inspectorEditorString = InspectorEditorString;
@ -550,14 +751,8 @@
}
InspectorEditorCheckbox.prototype.renderEditor = function() {
var isChecked = this.inspector.readProperty(this.fieldDef.property)
if (isChecked == '0' || isChecked == 'false')
isChecked = false
var data = {
id: this.editorId,
checked: isChecked ? 'checked' : null,
cbId: this.editorId + '-cb',
title: this.fieldDef.title
}
@ -565,6 +760,15 @@
return Mustache.render(this.getTemplate(), data)
}
InspectorEditorCheckbox.prototype.init = function() {
var isChecked = this.inspector.readProperty(this.fieldDef.property)
if (isChecked == '0' || isChecked == 'false')
isChecked = false
$(this.selector).prop(checked, isChecked)
}
InspectorEditorCheckbox.prototype.focus = function() {
$(this.selector).closest('div').focus()
}
@ -576,7 +780,7 @@
custom-checkbox nolabel"> \
<input type="checkbox" \
value="1" \
{{checked}} id="{{cbId}}"/> \
id="{{cbId}}"/> \
<label for="{{cbId}}">{{title}}</label> \
</div> \
</td> \
@ -594,6 +798,7 @@
this.editorId = editorId
this.selector = '#'+this.editorId+' select'
this.dynamicOptions = this.fieldDef.options ? false : true
this.initialization = false
var self = this
@ -603,7 +808,7 @@
}
InspectorEditorDropdown.prototype.applyValue = function() {
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val())
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val(), this.initialization)
}
InspectorEditorDropdown.prototype.renderEditor = function() {
@ -678,7 +883,7 @@
this.indicatorContainer.addClass('loading-indicator-container').addClass('size-small').addClass('transparent')
}
this.loadOptions()
this.loadOptions(true)
}
if (this.fieldDef.depends)
@ -721,7 +926,7 @@
this.indicatorContainer.loadIndicator('hide')
}
InspectorEditorDropdown.prototype.loadOptions = function() {
InspectorEditorDropdown.prototype.loadOptions = function(initialization) {
var $form = $(this.selector).closest('form'),
data = this.inspector.propertyValues,
$select = $(this.selector),
@ -755,7 +960,9 @@
else
$('option:first-child', $select).attr("selected", "selected");
self.initialization = initialization
$select.trigger('change')
self.initialization = false
self.hideLoadingIndicator()
},

View File

@ -90,6 +90,92 @@
select {
width: 90%;
}
div.external-param-editor-container {
position: relative;
padding-right: 30px;
div.external-editor {
bottom: 0;
margin: -5px -12px;
right: 30px;
left: auto;
top: 0;
position: absolute;
.transition(left 0.2s);
.transform( ~'translateZ(0)');
will-change: transform;
div.controls {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
a {
position: absolute;
left: 0;
top: 0;
display: inline-block;
height: 100%;
width: 30px;
color: #2b3e50;
outline: none;
i {
display: inline-block;
position: relative;
left: 10px;
top: 4px;
font-size: 15px;
}
}
input {
position: absolute;
display: block;
border: none;
width: 100%;
height: 100%;
left: 0;
top: 0;
padding-left: 30px;
padding-right: 12px;
background: transparent;
}
}
}
&.editor-visible {
div.external-editor {
div.controls {
input {
background: @color-inspector-bg;
}
}
}
}
}
&.active {
div.external-param-editor-container {
div.external-editor {
div.controls {
input {
background: white;
}
}
}
}
}
&.dropdown div.external-param-editor-container div.external-editor {
height: 100%;
margin: 0;
bottom: auto;
}
}
th {

View File

@ -580,7 +580,7 @@
var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout')
if ($componentList.length == 0) {
alert('Components can be added only to pages and layouts.')
alert('Components can be added only to pages, partials and layouts.')
return;
}

View File

@ -183,36 +183,4 @@ abstract class ComponentBase extends Extendable
return $value;
}
/**
* Creates a page link to another page. Allows mapping to the other page's
* component properties for the purpose of extracting URL routing parameters.
* @param string $page Page name or page file name
* @param string $class Component class name
* @param array $mappings ['componentProperty' => 'routed value']
* @return string
*/
// protected function makePageLink($page, $class, $mappings = [])
// {
// if (!isset($this->pageLinkCache[$page.$class])) {
// $this->pageLinkCache[$page.$class] = $this->getOtherPageComponent($page, $class);
// }
// if (!$component = $this->pageLinkCache[$page.$class])
// return null;
// $params = [];
// foreach ($mappings as $property => $value) {
// if (!$param = $component->property($property))
// continue;
// if (substr($param, 0, 1) == ':')
// $param = substr($param, 1);
// $params[$param] = $value;
// }
// return $this->pageUrl($page, $params);
// }
}

View File

@ -20,12 +20,13 @@ class ComponentHelpers
$result = [];
$property = [
'property' => 'oc.alias',
'title' => Lang::get('cms::lang.component.alias'),
'description' => Lang::get('cms::lang.component.alias_description'),
'type' => 'string',
'validationPattern' => '^[a-zA-Z]+[0-9a-z\_]*$',
'validationMessage' => Lang::get('cms::lang.component.validation_message')
'property' => 'oc.alias',
'title' => Lang::get('cms::lang.component.alias'),
'description' => Lang::get('cms::lang.component.alias_description'),
'type' => 'string',
'validationPattern' => '^[a-zA-Z]+[0-9a-z\_]*$',
'validationMessage' => Lang::get('cms::lang.component.validation_message'),
'noExternalParameter' => true
];
$result[] = $property;

View File

@ -100,6 +100,8 @@ class Controller extends BaseController
protected static $instance = null;
protected $partialComponentStack = [];
/**
* Creates the controller.
* @param \Cms\Classes\Theme $theme Specifies the CMS theme.
@ -391,6 +393,8 @@ class Controller extends BaseController
{
$manager = ComponentManager::instance();
$properties = $this->setComponentPropertiesFromParameters($properties, []);
if ($addToLayout) {
if (!$componentObj = $manager->makeComponent($name, $this->layoutObj, $properties)) {
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$name]));
@ -664,6 +668,8 @@ class Controller extends BaseController
*/
public function renderPartial($name, $parameters = [], $throwException = true)
{
$vars = $this->vars;
/*
* Alias @ symbol for ::
*/
@ -748,12 +754,63 @@ class Controller extends BaseController
}
}
/*
* Run partial functions (only for real partials)
*/
if ($partial instanceof \Cms\Classes\Partial) {
$manager = ComponentManager::instance();
foreach ($partial->settings['components'] as $component => $properties) {
list($name, $alias) = strpos($component, ' ') ?
explode(' ', $component) : array($component, $component);
$properties = $this->setComponentPropertiesFromParameters($properties, $parameters, []);
if (!$componentObj = $manager->makeComponent($name, $this->pageObj, $properties))
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$name]));
$componentObj->alias = $alias;
$parameters[$alias] = $partial->components[$alias] = $componentObj;
array_push($this->partialComponentStack, [
'name' => $alias,
'obj' => $componentObj
]);
$componentObj->init();
$componentObj->onInit(); // Deprecated: Remove ithis line if year >= 2015
}
CmsException::mask($this->page, 300);
$parser = new CodeParser($partial);
$partialObj = $parser->source($this->page, $this->layout, $this);
CmsException::unmask();
CmsException::mask($partial, 300);
$partialObj->onStart();
$partial->runComponents();
$partialObj->onEnd();
CmsException::unmask();
}
/*
* Render the parital
*/
CmsException::mask($partial, 400);
$this->loader->setObject($partial);
$template = $this->twig->loadTemplate($partial->getFullPath());
$result = $template->render(array_merge($this->vars, $parameters));
CmsException::unmask();
$result = $template->render(array_merge($this->vars, $parameters));
CmsException::unmask($this->partialComponentStack);
if ($partial instanceof \Cms\Classes\Partial) {
if ($this->partialComponentStack) {
array_pop($this->partialComponentStack);
}
}
$this->vars = $vars;
$this->componentContext = null;
return $result;
}
@ -961,6 +1018,11 @@ class Controller extends BaseController
return $this->layout->components[$name];
}
foreach ($this->partialComponentStack as $componentInfo) {
if ($componentInfo['name'] == $name)
return $componentInfo['obj'];
}
return null;
}
@ -1017,50 +1079,32 @@ class Controller extends BaseController
}
/**
* Creates a basic component object for another page, useful for extracting properties.
* @param string $page Page name or page file name
* @param string $class Component class name
* @return ComponentBase
* Sets component property values from partial parameters.
* The property values should be defined as {{ param }}.
* @param array &$properties Specifies the component properties loaded from the partial.
* @param array $parameters Specifies the partial parameters.
* @return Returns updated properties.
*/
// public function getOtherPageComponent($page, $class)
// {
// $class = Str::normalizeClassName($class);
// $theme = $this->getTheme();
// $manager = ComponentManager::instance();
// $componentObj = new $class;
protected function setComponentPropertiesFromParameters(&$properties, $parameters)
{
$result = [];
// if (($page = Page::loadCached($theme, $page)) && isset($page->settings['components'])) {
// foreach ($page->settings['components'] as $component => $properties) {
// list($name, $alias) = strpos($component, ' ') ?
// explode(' ', $component) :
// array($component, $component)
// ;
// if ($manager->resolve($name) == $class) {
// $componentObj->setProperties($properties);
// $componentObj->alias = $alias;
// return $componentObj;
// }
// }
$routerParameters = $this->router->getParameters();
// if (!isset($page->settings['layout']))
// return null;
foreach ($properties as $propertyName=>$propertyValue) {
$matches = [];
if (preg_match('/^\{\{([^\}]+)\}\}$/', $propertyValue, $matches)) {
$paramName = trim($matches[1]);
// $layout = $page->settings['layout'];
// if (($layout = Layout::loadCached($theme, $layout)) && isset($layout->settings['components'])) {
// foreach ($layout->settings['components'] as $component => $properties) {
// list($name, $alias) = strpos($component, ' ') ?
// explode(' ', $component) :
// array($component, $component)
// ;
// if ($manager->resolve($name) == $class) {
// $componentObj->setProperties($properties);
// $componentObj->alias = $alias;
// return $componentObj;
// }
// }
// }
// }
if ( substr($paramName, 0, 1) == ':' ) {
$paramName = substr($paramName, 1);
$result[$propertyName] = array_key_exists($paramName, $routerParameters) ? $routerParameters[$paramName] : null;
} else
$result[$propertyName] = array_key_exists($paramName, $parameters) ? $parameters[$paramName] : null;
} else
$result[$propertyName] = $propertyValue;
}
// return null;
// }
return $result;
}
}

View File

@ -17,4 +17,13 @@ class Partial extends CmsCompoundObject
{
return 'partials';
}
/**
* Returns name of a PHP class to us a parent for the PHP class created for the object's PHP section.
* @return mixed Returns the class name or null.
*/
public function getCodeClassParent()
{
return '\Cms\Classes\PartialCode';
}
}

View File

@ -0,0 +1,11 @@
<?php namespace Cms\Classes;
/**
* Parent class for PHP classes created for partial PHP sections.
*
* @package october\cms
* @author Alexey Bobkov, Samuel Georges
*/
class PartialCode extends CodeBase
{
}

View File

@ -14,6 +14,8 @@ fields:
path: partial_toolbar
cssClass: collapse-visible
components: Cms\FormWidgets\Components
secondaryTabs:
stretch: true
fields:
@ -21,4 +23,10 @@ secondaryTabs:
tab: cms::lang.editor.markup
stretch: true
type: codeeditor
language: twig
language: twig
code:
tab: cms::lang.editor.code
stretch: true
type: codeeditor
language: php

View File

@ -90,6 +90,8 @@ class Index extends Controller
$this->addJs('/modules/cms/assets/js/october.cmspage.js', 'core');
$this->addJs('/modules/cms/assets/js/october.dragcomponents.js', 'core');
$this->addJs('/modules/cms/assets/js/october.tokenexpander.js', 'core');
$this->addJs('/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js', 'core');
$this->addCss('/modules/cms/assets/css/october.components.css', 'core');
// Preload Ace editor modes explicitly, because they could be changed dynamically

View File

@ -1,8 +1,12 @@
<?= Form::open([
'class' => 'layout',
'data-change-monitor' => 'true',
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close'))
]) ?>
<?php
$formConfig = [
'class' => 'layout',
'data-change-monitor' => 'true',
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
'data-inspector-external-parameters' => true
];
echo Form::open($formConfig) ?>
<?= $form->render() ?>
<input type="hidden" value="<?= ($templateType) ?>" name="templateType" />