How to add more Content Area Context Menu Item in Optimizely CMS 12
Hey folks, today I will share something related to Context Menu customization in the Content Area of Optimizely CMS.
As you know, the content area is a crucial property in Optimizely CMS. It enables editors to place blocks onto a page, allowing for flexible rendering based on the chosen blocks.
You can see the Content Area Context Menu when editing a page in on-page editing mode or in properties editing mode.
With the Content Area Context Menu, you can see the following available actions:
- Edit: Navigate to the selected block for editing in the current view.
- Quick Edit: Allows editing of the block within a modal popup in the current view.
- Personalize: Enables customization of block visibility, specifying who can view the block's content.
- Move Outside Group: Moves the block out from a personalized group.
- Display Option: This action is displayed if configurations for display tags are available.
- Move Up: Shifts the block upwards.
- Move Down: Shifts the block downwards.
- Remove: Deletes a block from the Content Area.
So question: Is it possible if you want to add more customized action for block in Content Area as known as menu item in context menu?
My answer is: Yes. We could
Today, I will show you my way to do it based on my experience in Dojo framework. It may be not the best solution but it works with me.
Here are all steps that you can do to add more menu item to Content Area Context Menu.
1. First step, you need to do is creating a content area command because each menu item in the context menu is currently matched to a content area command.
Here is the code example for creating a content area command:
define("alloy/contentediting/command/CustomOption", [
// General application modules
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/when",
"epi/dependency",
"epi-cms/contentediting/command/_ContentAreaCommand",
"epi-cms/contentediting/viewmodel/ContentBlockViewModel"
], function (declare, lang, when, dependency, _ContentAreaCommand, ContentBlockViewModel) {
return declare([_ContentAreaCommand], {
// label: [public] String
// The action text of the command to be used in visual elements.
label: "Custom action",
// iconClass: [readonly] String
// The icon class of the command to be used in visual elements.
iconClass: "epi-iconStar",
constructor: function () {
},
_execute: function () {
//Add your logic here when clicking on this action
},
_onModelValueChange: function () {
// summary:
// Updates canExecute after the model value has changed.
// tags:
// protected
var item = this.model;
this.set("isAvailable", true);
this.set("canExecute", false);
if (item && item.contentLink) {
this.set("canExecute", true);
return;
}
}
});
});
2. Next step is creating new dojo component one for ContentAreaCommands. This component is used to declare context menu in on-page editting mode.
define("epi-cms/contentediting/command/ContentAreaCommands", [
"dojo/_base/array",
"dojo/_base/declare",
"dojo/Stateful",
"dojo/when",
"dijit/Destroyable",
"epi-cms/ApplicationSettings",
"epi-cms/contentediting/command/BlockRemove",
"epi-cms/contentediting/command/BlockEdit",
"epi-cms/contentediting/command/ContentAreaItemBlockEdit",
"epi-cms/contentediting/command/BlockInlineEdit",
"epi-cms/contentediting/command/MoveVisibleToPrevious",
"epi-cms/contentediting/command/MoveVisibleToNext",
"epi-cms/contentediting/command/Personalize",
"epi-cms/contentediting/command/SelectDisplayOption",
"epi-cms/contentediting/command/MoveOutsideGroup",
"alloy/contentediting/command/CustomOption"
], function (array, declare, Stateful, when, Destroyable, ApplicationSettings, Remove, Edit, ContentAreaItemBlockEdit, InlineEdit, MoveVisibleToPrevious, MoveVisibleToNext, Personalize, SelectDisplayOption, MoveOutsideGroup, CustomOption) {
return declare([Stateful, Destroyable], {
// tags:
// internal
commands: null,
constructor: function () {
this._commandSpliter = this._commandSpliter || new Stateful({
category: "menuWithSeparator"
});
this.contentAreaItemBlockEdit = new ContentAreaItemBlockEdit({ category: null });
this.blockInlineEdit = new InlineEdit();
this.moveVisibleToPrevious = new MoveVisibleToPrevious();
this.moveVisibleToNext = new MoveVisibleToNext();
this.customOption = new CustomOption();
this.commands = [
new Edit({ category: null }),
this.contentAreaItemBlockEdit,
this.blockInlineEdit,
this.customOption,
this._commandSpliter,
new SelectDisplayOption(),
this.moveVisibleToPrevious,
this.moveVisibleToNext,
new Remove()
];
var sectionsVisibility = Object.assign({}, ApplicationSettings.sectionsVisibility);
// Only add personalize command if the ui is not limited
if (!ApplicationSettings.limitUI && (sectionsVisibility.visitorGroups !== false)) {
this.moveOutsideGroup = new MoveOutsideGroup();
this.personalize = new Personalize({ category: null });
this.commands.splice(5, 0, this.personalize, this.moveOutsideGroup);
}
this.commands.forEach(function (command) {
this.own(command);
}, this);
},
handleDoubleClick: function (itemModel) {
if (itemModel.inlineBlockData) {
when(this.contentAreaItemBlockEdit.updateModel(itemModel)).then(function () {
this.contentAreaItemBlockEdit.execute();
}.bind(this));
} else {
when(this.blockInlineEdit.updateModel(itemModel)).then(function () {
this.blockInlineEdit.execute();
}.bind(this));
}
},
_modelSetter: function (model) {
this.model = model;
array.forEach(this.commands, function (command) {
command.set("model", model);
});
}
});
});
3. Next step is creating new dojo component one for ContentAreaEditor. This component is used to declare the context menu in properties editting mode
require({
cache: {
'url:epi-cms/contentediting/editors/templates/ContentAreaEditor.html': "<div class=\"dijitInline\" tabindex=\"-1\" role=\"presentation\">\r\n <div class=\"epi-content-area-header-block\">\r\n <div data-dojo-type=\"epi-cms/contentediting/AllowedTypesList\"\r\n data-dojo-props=\"allowedTypes: this.allowedTypes, restrictedTypes: this.restrictedTypes\"\r\n data-dojo-attach-point=\"allowedTypesHeader\"></div>\r\n </div>\r\n <div class=\"epi-content-area-editor--wide epi-content-area-editor\">\r\n <div data-dojo-attach-point=\"treeNode\"></div>\r\n <div data-dojo-attach-point=\"actionsContainer\" class=\"epi-content-area-actionscontainer\"></div>\r\n </div>\r\n</div>\r\n"
}
});
define("epi-cms/contentediting/editors/ContentAreaEditor", [
// Dojo
"dojo/_base/declare",
"dojo/aspect",
"dojo/dom-class",
"dojo/dom-style",
"dojo/on",
"dojo/topic",
"dojo/when",
"dojo/Stateful",
//Dijit
"dijit/registry",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_CssStateMixin",
"dijit/_WidgetsInTemplateMixin",
// EPi Framework
"epi/dependency",
"epi/shell/dnd/Target",
"epi/shell/command/_CommandProviderMixin",
"epi/shell/command/_Command",
"epi/shell/applicationSettings",
//EPi CMS
"epi-cms/contentediting/editors/_ContentAreaTree",
"epi-cms/contentediting/editors/_ContentAreaTreeModel",
"epi-cms/contentediting/viewmodel/PersonalizedGroupViewModel",
"epi-cms/_ContentContextMixin",
"epi-cms/ApplicationSettings",
"epi-cms/contentediting/viewmodel/ContentAreaViewModel",
"epi-cms/core/ContentReference",
"epi/shell/widget/ContextMenu",
"epi/shell/widget/_ValueRequiredMixin",
"epi-cms/widget/overlay/Block",
"epi-cms/widget/command/CreateContentFromContentArea",
"epi-cms/widget/command/CreateContentFromSelector",
"epi-cms/widget/_HasChildDialogMixin",
"epi-cms/contentediting/command/BlockRemove",
"epi-cms/contentediting/command/BlockConvert",
"epi-cms/contentediting/command/BlockEdit",
"epi-cms/contentediting/command/ContentAreaItemBlockEdit",
"epi-cms/contentediting/command/BlockInlineEdit",
"epi-cms/contentediting/command/MoveToPrevious",
"epi-cms/contentediting/command/MoveToNext",
"epi-cms/contentediting/command/MoveOutsideGroup",
"epi-cms/contentediting/command/Personalize",
"epi-cms/contentediting/command/SelectDisplayOption",
"alloy/contentediting/command/CustomOption",
"epi-cms/contentediting/AllowedTypesList",
"epi-cms/contentediting/editors/_TextWithActionsMixin",
// Resources
"dojo/text!epi-cms/contentediting/editors/templates/ContentAreaEditor.html",
"epi/i18n!epi/cms/nls/episerver.cms.contentediting.editors.contentarea",
"epi/i18n!epi/cms/nls/episerver.cms.widget.overlay.blockarea"
], function (
// Dojo
declare,
aspect,
domClass,
domStyle,
on,
topic,
when,
Stateful,
// Dijit
registry,
_WidgetBase,
_TemplatedMixin,
_CssStateMixin,
_WidgetsInTemplateMixin,
// EPi Framework
dependency,
Target,
_CommandProviderMixin,
_Command,
shellApplicationSettings,
// CMS
_ContentAreaTree,
_ContentAreaTreeModel,
PersonalizedGroupViewModel,
_ContentContextMixin,
ApplicationSettings,
ContentAreaViewModel,
ContentReference,
ContextMenu,
_ValueRequiredMixin,
BlockOverlay,
CreateContentFromContentArea,
CreateContentFromSelector,
_HasChildDialogMixin,
RemoveCommand,
BlockConvertCommand,
EditCommand,
ContentAreaItemBlockEdit,
BlockInlineEdit,
MoveToPrevious,
MoveToNext,
MoveOutsideGroup,
Personalize,
SelectDisplayOption,
CustomOption,
AllowedTypesList, // used in template
_TextWithActionsMixin,
// Resources
template,
resources,
blockAreaRes
) {
return declare([
_WidgetBase,
_TemplatedMixin,
_WidgetsInTemplateMixin,
_CssStateMixin,
_ValueRequiredMixin,
_ContentContextMixin,
_CommandProviderMixin,
_HasChildDialogMixin,
_TextWithActionsMixin
], {
// summary:
// Editor for ContentArea to be able to edit my content area property in forms mode
// This should be a simple, non WYSIWYG listing of the inner content blocks with possibilities to add, remove and rearrange the content.
//
// tags:
// internal
// baseClass: [public] String
// The widget's base CSS class.
baseClass: "epi-content-area-wrapper",
emptyClass: "epi-content-area-wrapper--empty",
// res: Json object
// Language resource
res: resources,
// templateString: String
// UI template for content area editor
templateString: template,
// value: String
// Value of the content area
value: null,
// multiple: Boolean
// Value must be true, otherwise dijit/Form will trea the value as an object instead of an array
multiple: true,
// parent: Object
// Editor wrapper object containe the editor
parent: null,
// overlayItem: Object
// Source overlay of the content area in on page edit mode
overlayItem: null,
// model: Object
// Content area editor view model
model: null,
// intermediateChanges: Boolean
// Inherited from editor interface
intermediateChanges: true,
// editMode: String
// Flags to detect page edit mode (On page edit or Form edit mode or Create content mode)
editMode: "onpageedit",
// _preventOnBlur: [private] Boolean
// When set, the onBlur event is prevented.
_preventOnBlur: false,
_dndTarget: null,
// allowedTypes: [public] Array
// The types which are allowed. i.e used for filtering based on AllowedTypesAttribute
allowedTypes: null,
// restrictedTypes: [public] Array
// The types which are restricted.
restrictedTypes: null,
// actionsResource: [Object]
// The resource of actions link
actionsResource: blockAreaRes,
// actionsResource: [Object]
// Name of constructor function of ContentAreaTree class
treeClass: _ContentAreaTree,
// allowMultipleItems: [Boolean]
// Allow dnd multiple items at once
allowMultipleItems: true,
constructor: function () {
this.allowedDndTypes = [];
},
onChange: function (value) {
// summary:
// Called when the value in the widget changes.
// tags:
// public callback
},
onForceChange: function (value) {
this.onChange(value);
},
_handleModelChange: function (value) {
// summary:
// Called when the value in the model changes.
// tags:
// public callback
this.validate();
this.onForceChange(value);
this._toggleEmptyClass();
if (this.model.selectedItem) {
this.updateCommandModel(this.model.selectedItem);
}
this._toggleActionsContainer();
},
_toggleClass: function (node, className, condition) {
(node || this.domNode).classList[condition ? "add" : "remove"](className);
},
_toggleEmptyClass: function () {
this._toggleClass(this.domNode, this.emptyClass, !this.value || this.value.length <= 0);
},
_onBlur: function () {
// summary:
// Override base to prevent the onBlur from being called when the _preventOnBlur flag is set.
// tags:
// protected override
if (this._preventOnBlur) {
return;
}
this.inherited(arguments);
},
focus: function () {
// summary:
// Focus the tree if there is a value, else focus the create block text.
// tags:
// public
if (this.tree && this.model.get("value").length > 0) {
this._focusManager.focus(this.tree.domNode);
} else {
if (this.textWithLinks) {
this.textWithLinks.focus();
}
}
},
postMixInProperties: function () {
this.inherited(arguments);
this._commandSpliter = this._commandSpliter || new Stateful({
category: "menuWithSeparator"
});
// NOTE: Check for this._commands to allow for mocking the commands without breaking _CommandProviderMixin.
this.contentAreaItemBlockEdit = new ContentAreaItemBlockEdit({ category: null, isContentAreaReadonly: this.get("readOnly") });
this.blockInlineEdit = new BlockInlineEdit();
this.movePrevious = new MoveToPrevious();
this.moveNext = new MoveToNext();
this.customOption = new CustomOption();
this.commands = this._commands || [
new EditCommand({ category: null }),
this.contentAreaItemBlockEdit,
this.blockInlineEdit,
this.customOption,
this._commandSpliter,
new SelectDisplayOption(),
this.movePrevious,
this.moveNext,
new RemoveCommand(),
new BlockConvertCommand()
];
this.own(on(this.contentAreaItemBlockEdit, "save", function (inlineBlockData, name) {
this._preventOnBlur = false;
var value = [];
(this.model.getChildren() || []).forEach(function (child) {
if (child instanceof PersonalizedGroupViewModel) {
(child.getChildren() || []).forEach(function (child) {
if (child.id === this.model.selectedItem.id) {
child.inlineBlockData = inlineBlockData;
child.name = name;
}
value.push(child);
}.bind(this));
} else {
if (child.id === this.model.selectedItem.id) {
child.inlineBlockData = inlineBlockData;
child.name = name;
}
value.push(child);
}
}.bind(this));
value = value.map(function (v) {
return v.serialize();
});
this.set("value", value);
// In order to be able to add a block when creating it from a floating editor
// we need to set the editing parameter on the editors parent wrapper to true
// since it has been set to false while being suspended when switching to
// the secondaryView.
this.parent = this.parent || this.getParent();
this.parent.set("editing", true);
this.onForceChange(value);
// Now call onBlur since it's been prevented using the _preventOnBlur flag.
this.onBlur();
}.bind(this)));
// Only add personalize command if the ui is not limited
if (this._isPersonalizationEnabled()) {
this.commands.splice(5, 0,
new Personalize({ category: null }),
new MoveOutsideGroup()
);
}
this.commands.forEach(function (command) {
this.own(command);
}, this);
this.own(
//Create the view model
this.model = this.model || new ContentAreaViewModel({
maxLength: this.maxLength,
minLength: this.minLength
}),
this.treeModel = this.treeModel || new _ContentAreaTreeModel({ model: this.model }),
this.model.watch("selectedItem", function (name, oldValue, newValue) {
//Update the commands with the selected block
this.updateCommandModel(newValue);
}.bind(this)),
on(this.model, "changed", function () {
if (!this._started || this._supressValueChanged) {
return;
}
this._set("value", this.model.get("value"));
//Call to the handle model change with the new value
this._handleModelChange(this.value);
}.bind(this))
);
// personalizationarea isn't an actual type so it needs to be hardcoded like in _ContentAreaTree
this.allowedDndTypes.push("personalizationarea");
this.allowedDndTypes.push(this._getInlineBlockDndKey());
if (!this.contentDataStore) {
var registry = dependency.resolve("epi.storeregistry");
this.contentDataStore = registry.get("epi.cms.contentdata");
}
},
buildRendering: function () {
this.inherited(arguments);
this.contextMenu = new ContextMenu();
this.contextMenu.addProvider(this);
this.own(this.contextMenu);
this._setupActions(this.actionsContainer);
this.own(this._dndTarget = new Target(this.actionsContainer, {
accept: this.allowedDndTypes,
reject: this.restrictedDndTypes,
isSource: false,
alwaysCopy: false,
allowMultipleItems: this.allowMultipleItems,
insertNodes: function () { }
}));
this.own(aspect.after(this._dndTarget, "onDropData", function (dndData, source, nodes, copy) {
dndData.forEach(function (dndData) {
this.model.modify(function () {
this.model.addChild(dndData.data);
}.bind(this));
}, this);
if (!this.tree) {
this._createTree();
}
}.bind(this), true));
// Handle focus after dropping on the tree or the drop area. We set focus to ourselves so that
// it is not left where the drag originated.
this.own(aspect.after(this._dndTarget, "onDrop", this.focus.bind(this)));
},
postCreate: function () {
this.inherited(arguments);
this.set("emptyMessage", resources.emptymessage);
this._toggleEmptyClass();
if (this.parent && this.overlayItem) {
this.own(aspect.after(this.parent, "onStartEdit", function () {
this._selectFromOverlay(this.overlayItem.model);
}.bind(this)));
}
this.own(topic.subscribe("/dnd/start", this._startDrag.bind(this)));
},
startup: function () {
if (this._started) {
return;
}
this.inherited(arguments);
this.tree && this.tree.startup();
this.contextMenu.startup();
this._updateStyle();
this.own(
this.allowedTypesHeader.watch("hasRestriction", this._updateStyle.bind(this))
);
},
destroy: function () {
this.tree && this.tree.destroyRecursive();
this.inherited(arguments);
},
isCreateLinkVisible: function () {
// summary:
// Overridden mixin class, depend on currentMode will show/not create link
// tags:
// protected
return this.model.canCreateBlock(this.allowedTypes, this.restrictedTypes);
},
onDialogExecute: function (selectedContent) {
if (selectedContent) {
this._saveValueAndFireOnChange({
contentLink: new ContentReference(selectedContent.contentLink).createVersionUnspecificReference().toString(),
name: selectedContent.name,
typeIdentifier: selectedContent.typeIdentifier
});
}
},
_saveValueAndFireOnChange: function (block) {
this._preventOnBlur = false;
var value = Object.assign([], this.model.get("value"), true);
value.push(block);
this.set("value", value);
// In order to be able to add a block when creating it from a floating editor
// we need to set the editing parameter on the editors parent wrapper to true
// since it has been set to false while being suspended when switching to
// the secondaryView.
this.parent = this.parent || this.getParent();
this.parent.set("editing", true);
this.validate();
this.onForceChange(value);
// Now call onBlur since it's been prevented using the _preventOnBlur flag.
this.onBlur();
},
executeAction: function (actionName) {
// summary:
// Overridden mixin class executing click actions from textWithLinks widget
// actionName: [String]
// Action name of link on content area
// tags:
// public
if (actionName === "createnewblock") {
// HACK: Preventing the onBlur from being executed so the editor wrapper keeps this editor in editing state
this._preventOnBlur = true;
// since we're going to create a block, we need to hide all validation tooltips because onBlur is prevented here
this.validate(false);
var command = shellApplicationSettings.inlineBlocksInContentAreaEnabled ? new CreateContentFromContentArea({
allowedTypes: this.allowedTypes,
restrictedTypes: this.restrictedTypes
}) : new CreateContentFromSelector({
creatingTypeIdentifier: "episerver.core.blockdata",
createAsLocalAsset: true,
isInQuickEditMode: this.isInQuickEditMode,
quickEditBlockId: this.quickEditBlockId,
autoPublish: true,
allowedTypes: this.allowedTypes,
restrictedTypes: this.restrictedTypes
});
command.set("model", {
save: this._saveValueAndFireOnChange.bind(this),
cancel: function () {
this._preventOnBlur = false;
this.onBlur();
}.bind(this)
});
command.execute();
}
},
isValid: function (isFocused) {
// summary:
// Check if widget's value is valid.
// isFocused:
// Indicate that the widget is being focused.
// tags:
// protected
// When create block screen is visible, we need to hide all validation messages since onBlur is prevented.
return (this._preventOnBlur || !this.required || this.model.get("value").length > 0);
},
_setReadOnlyAttr: function (readOnly) {
this._set("readOnly", readOnly);
this._toggleActionsContainer();
if (this._source) {
this._source.isSource = !this.readOnly;
}
if (this.model) {
this.model.set("readOnly", readOnly);
}
this.tree && this.tree.set("readOnly", readOnly);
},
_toggleActionsContainer: function () {
// summary:
// Hide actions when readonly or editor has reached the items limit
// tags:
// private
var visible = !this.model.hasReachedItemsLimit() && !this.get("readOnly");
domStyle.set(this.actionsContainer, "display", visible ? "" : "none");
},
_checkAcceptance: function (source, nodes) {
// summary:
// Customize checkAcceptance func
// source: Object
// The source which provides items
// nodes: Array
// The list of transferred items
return this.readOnly ? false : this._source.defaultCheckAcceptance(source, nodes) && !this.model.hasReachedItemsLimit();
},
_createTree: function () {
// summary:
// Creates the tree widget
// tags:
// private
//Create the tree
this.tree = new this.treeClass({
accept: this.allowedDndTypes,
reject: this.restrictedDndTypes,
contextMenu: this.contextMenu,
model: this.treeModel,
readOnly: this.readOnly,
inlineBlockDndKey: this._getInlineBlockDndKey(),
inlineBlockNameProperties: this.inlineBlockNameProperties
}).placeAt(this.treeNode);
this.tree.own(on(this.tree, "dblclick", function (itemModel) {
if (itemModel.inlineBlockData) {
when(this.contentAreaItemBlockEdit.updateModel(itemModel)).then(function () {
this.contentAreaItemBlockEdit.execute();
}.bind(this));
} else {
when(this.blockInlineEdit.updateModel(itemModel)).then(function () {
this.blockInlineEdit.execute();
}.bind(this));
}
}.bind(this)));
this.tree.own(
aspect.after(this.tree.dndController, "onDndEnd", this.focus.bind(this))
);
},
_selectFromOverlay: function (overlayModel) {
var child = overlayModel && overlayModel.selectedItem && overlayModel.selectedItem.serialize(),
model = this.model,
path = ["root"];
// exit if there is no overlay model to select item
if (!child) {
return;
}
if (child.contentGroup) {
model = model.getChild({ name: child.contentGroup });
path.push(model.id);
}
model = model.getChild(child);
if (!model) {
return;
}
path.push(model.id);
model.set("selected", true);
model.set("ensurePersonalization", overlayModel.selectedItem.ensurePersonalization);
// TODO: move this selection into tree instead
this.tree && this.tree.set("path", path);
},
_startDrag: function (source, nodes, copy) {
var accepted = this._dndTarget.accept && this._dndTarget.checkAcceptance(source, nodes);
domClass.toggle(this.domNode, "dojoDndTargetDisabled", !accepted);
// close the editor when user start draging Block from BlockArea
//TODO: This widget should not call a method on the parent
var widget = registry.getEnclosingWidget(nodes[0]);
if (widget && widget.isInstanceOf(BlockOverlay) && this.parent && this.parent.cancel) {
// We set isModified to false (default value) because always synchronize value
// between OPE and Editor
this.parent.set("isModified", false);
this.parent.cancel();
}
},
_ensureNonBrokenContentAreaItems: function (value) {
var itemsList = value || [];
itemsList.forEach(function (item) {
if (!item || (!item.contentLink && !item.inlineBlockData)) {
item.isBrokenLink = true;
item.name = resources.brokenlink;
}
});
return itemsList;
},
_setValueAttr: function (value) {
value = this._ensureNonBrokenContentAreaItems(value);
// Destroy the tree since that is the fastest way to remove all items
this.tree && this.tree.destroyRecursive();
this._set("value", value);
this._supressValueChanged = true;
this.model.set("value", value);
this._supressValueChanged = false;
// Create the tree again after the value has been set so
// all tree nodes are created in one go
this._createTree();
this._toggleEmptyClass();
this._toggleActionsContainer();
},
_updateStyle: function () {
// summary:
// Handle the widget style depending on the allowedTypesList visibility
if (!this.domNode) {
return;
}
if (this.allowedTypesHeader.get("hasRestriction")) {
domClass.remove(this.domNode, "allowed-types-list-hidden");
} else {
domClass.add(this.domNode, "allowed-types-list-hidden");
}
},
_isPersonalizationEnabled: function () {
var sectionsVisibility = Object.assign({}, ApplicationSettings.sectionsVisibility);
if (sectionsVisibility.visitorGroups === false) {
return false;
}
return !ApplicationSettings.limitUI;
},
_getInlineBlockDndKey: function () {
return this.name + "_contentarea-inline-block";
}
});
});
4. Last one, add overrided dojo components into module config as epi base resource to load customized resources once loading epi base resource
<?xml version="1.0" encoding="utf-8"?>
<module loadFromBin="false" clientResourceRelativePath="" viewEngine="Razor" moduleJsonSerializerType="None" preferredUiJsonSerializerType="Net">
<dojo>
<paths>
<add name="alloy" path="ClientResources/Scripts" />
</paths>
</dojo>
<clientResources>
<add name="epi-cms.widgets.base" path="ClientResources/Scripts/contentediting/command/ContentAreaCommands.js" resourceType="Script" />
<add name="epi-cms.widgets.base" path="ClientResources/Scripts/editors/ContentAreaEditor.js" resourceType="Script" />
</clientResources>
</module>
I hope this article will help some of you somehow. Enjoy reading!
Comments