Blog posts by Tuan Do2017-08-03T06:29:03.0000000Z/blogs/tuan-do/Optimizely WorldLimiting items in a ContentArea/blogs/tuan-do/dates/2017/8/limiting-items-in-a-contentarea/2017-08-03T06:29:03.0000000Z<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Feature</h2>
<p>The <em>ContentArea</em> should</p>
<ul>
<li>Supported to limits a number of block inside it (for individual <em>ContentArea</em>).</li>
<li>When total blocks is over max limited size, the action links section should be hidden.</li>
<li>When total blocks is over max limited size, editor cannot DnD a block to that <em>ContentArea</em>.</li>
</ul>
<h2>Solution</h2>
<h3>The attribute</h3>
<p>By using attribute, we can set individual limited total items for each ContentArea.</p>
<pre class="language-csharp"><code>[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class MaxItemCountAttribute : Attribute
{
public int Count { get; }
public MaxItemCountAttribute(int maxItemCount)
{
Count = maxItemCount;
}
}</code></pre>
<h3>The editor descriptor</h3>
<p>We needed to extend both <em>ContentArea</em> and <em>ContentAreaEditor</em> widgets. Inside the custom editor descriptor, we read the value of attribute and then render it to client side.</p>
<pre class="language-csharp"><code>/// <summary>
/// Editor descriptor that extends the <see cref="ContentAreaEditorDescriptor"/> for a <see cref="ContentArea"/>
/// that marked with <see cref="MaxItemCountAttribute"/> in order to limits total items inside it.
/// </summary>
[EditorDescriptorRegistration(TargetType = typeof(ContentArea), UIHint = "ContentAreaWithMaxItem")]
public class CustomContentAreaEditorDescriptor : ContentAreaEditorDescriptor
{
public CustomContentAreaEditorDescriptor()
{
ClientEditingClass = "alloy/contentediting/editors/CustomContentAreaEditor";
}
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
{
const string maxItemCount = "maxItemCount";
var maxItemCountAttribute = metadata.Attributes.OfType<MaxItemCountAttribute>().SingleOrDefault();
if (maxItemCountAttribute != null)
{
metadata.EditorConfiguration[maxItemCount] = maxItemCountAttribute.Count;
metadata.OverlayConfiguration[maxItemCount] = maxItemCountAttribute.Count;
}
base.ModifyMetadata(metadata, attributes);
metadata.OverlayConfiguration["customType"] = "alloy/widget/overlay/CustomContentArea";
}
}</code></pre>
<h3>The custom ContentAreaEditor</h3>
<pre class="language-csharp"><code>define([
// dojo
"dojo/_base/declare",
"dojo/dom-style",
// epi
"epi-cms/contentediting/editors/ContentAreaEditor"
], function (
// dojo
declare,
domStyle,
// epi
ContentAreaEditor
) {
return declare([ContentAreaEditor], {
postCreate: function () {
this.inherited(arguments);
this._toggleDisplayActionLinks();
},
onChange: function (value) {
this._toggleDisplayActionLinks();
},
_setValueAttr: function (value) {
this.inherited(arguments);
this._toggleDisplayActionLinks();
},
_toggleDisplayActionLinks: function () {
var display = this._canAddItem();
this.actionsContainer && domStyle.set(this.actionsContainer, "display", display ? "" : "none");
this.tree && this.tree.set("readOnly", !display);
},
_canAddItem: function () {
// tags:
// private
if (this.maxItemCount == undefined) {
return true;
}
return this.maxItemCount > this.model.get("value").length;
}
});
});</code></pre>
<h3>The custom ContentArea</h3>
<pre class="language-csharp"><code>define([
// dojo
"dojo/_base/declare",
"dojo/dom-style",
// epi
"epi-cms/widget/overlay/ContentArea"
], function (
// dojo
declare,
domStyle,
// epi
ContentArea
) {
return declare([ContentArea], {
updatePosition: function () {
this.inherited(arguments);
this._toggleDisplayActionLinks();
},
executeAction: function (actionName) {
this._toggleDisplayActionLinks();
if (actionName === "createnewblock" && !this._canAddItem()) {
return;
}
this.inherited(arguments);
},
_onDrop: function (data, source, nodes, isCopy) {
// isCopy:
// Flag indicating whether the drag is a copy. False indicates a move.
// tags:
// protected extension
this._toggleDisplayActionLinks();
if (isCopy && !this._canAddItem()) {
return;
}
this.inherited(arguments);
},
_toggleDisplayActionLinks: function () {
this.textWithLinks && domStyle.set(this.textWithLinks.domNode, "display", this._canAddItem() ? "" : "none");
},
_canAddItem: function () {
// tags:
// private
if (this.maxItemCount == undefined) {
return true;
}
return this.maxItemCount > this.getChildren().length;
}
});
});</code></pre>
<h3>Start page</h3>
<p>Add following attributes</p>
<pre class="language-csharp"><code>[UIHint("ContentAreaWithMaxItem")]
[MaxItemCount(5)]</code></pre>
<p>to <em>MainContentArea</em> property</p>
<pre class="language-csharp"><code>[Display(
GroupName = SystemTabNames.Content,
Order = 320)]
[CultureSpecific]
[UIHint("ContentAreaWithMaxItem")]
[MaxItemCount(5)]
public virtual ContentArea MainContentArea { get; set; }</code></pre>
<h3>Result</h3>
<p><span style="text-decoration: underline;"><em>On-Page Editing</em> mode</span></p>
<p>When total items inside the ContentArea is 4 (qualified with the max item count), action links section still displayed.</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/4e/9f/6b/4e9f6bbdc3234348f97e54484258603e.png" width="1199" alt="action links displayed when not over max size of limited items" height="350" /></p>
<p>The action links section in ContentArea is hidden when total items inside that ContentArea is 5.</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/c2/5d/44/c25d4433a62732cb3b729c4f7a2a06fa.png" width="972" alt="hide action links in ContentArea on-page editing mode" height="802" /></p>
<p><em></em></p>
<p><span style="text-decoration: underline;"><em>On-Page Editing </em>mode with popup</span></p>
<p><span>The action links section in ContentArea is hidden when total items inside that ContentArea are 5</span></p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/75/79/75/757975259c60541b30043a64de922824.png" width="807" alt="hide action links in popup on-page editing mode" height="476" /></p>
<p><em></em></p>
<p><span style="text-decoration: underline;"><em>All Properties</em> mode</span></p>
<p><span>The action links section in ContentArea is hidden when total items inside that ContentArea are 5</span></p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/44/6f/78/446f78ec77fdfd07a28c2eab3a529919.png" width="793" alt="hide action links in ContentArea all-properties mode" height="504" /></p>
<h2>Related topics</h2>
<ul>
<li><a href="/link/cd88002025b544f2ade6441440e24911.aspx">Show label for a ContentArea when hovering</a></li>
</ul>
</body>
</html>Show overlay label for ContentArea when hovering in On-Page-Edit mode/blogs/tuan-do/dates/2017/7/show-overlay-label-for-contentarea-when-hovering-in-on-page-edit-mode/2017-07-28T14:29:36.0000000Z<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Following this forum thread - <a href="/link/8cc90d703fb743e1b2805321913b5a86.aspx">Show overlay label for ContentArea when hovering in On-Page-Edit mode</a>, I created a custom content area to resolve it (in Alloy MVC template).</p>
<p>Firstly, to see how a property display its overlay label when hover, inspect its markup code (in this case, I inspected logo on Start page). You will see it used <strong>infomationNode </strong>to showing:</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/f6/7f/b7/f67fb7ffa6fc3a1adf52d1369da22ccc.png" width="1045" alt="informationNode dojo attach point" height="405" /></p>
<p>And then, looking in <strong>epi/shell/widget/overlay/Item</strong> widget source, I found that overlay label value set by following code block:</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/87/d1/3a/87d13a6b60a2a41b53bdf522f557502e.png" width="1211" alt="overlay label code block" height="451" /></p>
<p>Let's take a look in <strong>ContentArea</strong> source code (see how to view source code at <a href="/link/ae233441257c43f899c457011f466fdd.aspx">link</a>), it extended from <strong>epi/shell/widget/overlay/Item</strong></p>
<p><strong><img src="https://s-media-cache-ak0.pinimg.com/originals/5f/fe/a0/5ffea0ed15eb26e959e4ddbd332b60ab.png" width="498" alt="ContentArea source" height="283" /></strong></p>
<p>and also had it own template:</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/83/3c/e9/833ce98cc48b9a8db5ca0a98ca1d7c50.png" width="709" alt="default ContentArea template" height="490" /></p>
<p>The above template missed <strong>informationNode</strong> section, so that, nothing displayed when hover a ContentArea property in Edit mode. Well, to show overlay label for ContentArea, we just needed to add that section to ContentArea's template, like that</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/6a/1c/5e/6a1c5ecc604138f2059eb5063257d67e.png" width="715" alt="ContentArea with template have informationNode" height="458" /></p>
<p><span style="text-decoration: underline;"><strong>Let's do it step by step</strong></span></p>
<p>Create custom content area editor descriptor (under "/Business/EditorDescriptors/" folder)</p>
<pre class="language-csharp"><code>/// <summary>
/// Editor descriptor that extends base editor descriptor for <see cref="ContentArea"/> in order to show overlay label when hovering in On-Page-Edit mode
/// </summary>
[EditorDescriptorRegistration(TargetType = typeof(ContentArea), UIHint = "CustomContentArea")]
public class CustomContentAreaEditorDescriptor : ContentAreaEditorDescriptor
{
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
{
base.ModifyMetadata(metadata, attributes);
metadata.OverlayConfiguration["customType"] = "alloy/widget/overlay/CustomContentArea";
}
}</code></pre>
<p>Create custom content area template (under "<span>/ClientResources/Scripts/widget/templates/</span>" folder), named "CustomContentArea.html"</p>
<pre class="language-html"><code><div class="epi-overlay-blockarea">
<div class="epi-overlay-item-container">
<div class="epi-overlay-bracket"></div>
</div>
<div data-dojo-attach-point="containerNode" class="epi-overlay-blockarea-container">
<span data-dojo-attach-point="informationNode" class="epi-overlay-item-info"></span>
</div>
<div data-dojo-attach-point="actionsNode" class="epi-overlay-blockarea-actionscontainer">
<div data-dojo-attach-point="actionsNodeControls" class="epi-overlay-blockarea-actions"></div>
</div>
</div></code></pre>
<p>Create custom content area widget (under "/ClientResources/Scripts/widget/overlay/" folder), named "CustomContentArea.js"</p>
<pre class="language-csharp"><code>define([
// dojo
"dojo/_base/declare",
// epi
"epi-cms/widget/overlay/ContentArea",
// resources
"dojo/text!../templates/CustomContentArea.html"
], function (
// dojo
declare,
// epi
ContentArea,
// resources
template
) {
return declare("alloy.widget.overlay.CustomContentArea", [ContentArea], {
templateString: template
});
});</code></pre>
<p>For example, add UIHint for MainContentArea property of Start page</p>
<pre class="language-csharp"><code>[Display(
GroupName = SystemTabNames.Content,
Order = 320)]
[CultureSpecific]
[UIHint("CustomContentArea")]
public virtual ContentArea MainContentArea { get; set; }</code></pre>
<p>In additional, we have another way to marked all ContentArea properties to rendered with custom editor (without added [UIHint("CustomContentArea")] attribute for each)</p>
<pre class="language-csharp"><code>/// <summary>
/// Editor descriptor that extends base editor descriptor for <see cref="ContentArea"/> in order to show overlay label when hovering in On-Page-Edit mode
/// </summary>
[EditorDescriptorRegistration(TargetType = typeof(ContentArea), EditorDescriptorBehavior = EditorDescriptorBehavior.OverrideDefault)]
public class CustomContentAreaEditorDescriptor : ContentAreaEditorDescriptor</code></pre>
<p>Finally, here is the result when edit Start page</p>
<p><img src="https://s-media-cache-ak0.pinimg.com/originals/3e/e0/73/3ee0732ea16d0ee5ea62d9a531d5c39a.png" width="637" alt="custom content area show overlay label when hovering in on-page-edit mode" height="447" /></p>
<h2>Related topics</h2>
<ul>
<li><a href="/link/216188c9af974124a6566bc65fd6b597.aspx">Limiting items in a ContentArea</a></li>
</ul>
</body>
</html>