Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more

ima9ines
ima9ines  -  CMS
Aug 3, 2017
  3089
(0 votes)

Limiting items in a ContentArea

Feature

The ContentArea should

  • Supported to limits a number of block inside it (for individual ContentArea).
  • When total blocks is over max limited size, the action links section should be hidden.
  • When total blocks is over max limited size, editor cannot DnD a block to that ContentArea.

Solution

The attribute

By using attribute, we can set individual limited total items for each ContentArea.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class MaxItemCountAttribute : Attribute
{
    public int Count { get; }

    public MaxItemCountAttribute(int maxItemCount)
    {
        Count = maxItemCount;
    }
}

The editor descriptor

We needed to extend both ContentArea and ContentAreaEditor widgets. Inside the custom editor descriptor, we read the value of attribute and then render it to client side.

/// <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";
    }
}

The custom ContentAreaEditor

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;
        }
    });
});

The custom ContentArea

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;
        }
    });
});

Start page

Add following attributes

[UIHint("ContentAreaWithMaxItem")]
[MaxItemCount(5)]

to MainContentArea property

[Display(
    GroupName = SystemTabNames.Content,
    Order = 320)]
[CultureSpecific]
[UIHint("ContentAreaWithMaxItem")]
[MaxItemCount(5)]
public virtual ContentArea MainContentArea { get; set; }

Result

On-Page Editing mode

When total items inside the ContentArea is 4 (qualified with the max item count), action links section still displayed.

action links displayed when not over max size of limited items

The action links section in ContentArea is hidden when total items inside that ContentArea is 5.

hide action links in ContentArea on-page editing mode

On-Page Editing mode with popup

The action links section in ContentArea is hidden when total items inside that ContentArea are 5

hide action links in popup on-page editing mode

All Properties mode

The action links section in ContentArea is hidden when total items inside that ContentArea are 5

hide action links in ContentArea all-properties mode

Related topics

Aug 03, 2017

Comments

Please login to comment.
Latest blogs
Optimizely Configured Commerce and Spire CMS - Figuring out Handlers

I recently entered the world of Optimizely Configured Commerce and Spire CMS. Intriguing, interesting and challenging at the same time, especially...

Ritu Madan | Mar 12, 2025

Another console app for calling the Optimizely CMS REST API

Introducing a Spectre.Console.Cli app for exploring an Optimizely SaaS CMS instance and to source code control definitions.

Johan Kronberg | Mar 11, 2025 |

Extending UrlResolver to Generate Lowercase Links in Optimizely CMS 12

When working with Optimizely CMS 12, URL consistency is crucial for SEO and usability. By default, Optimizely does not enforce lowercase URLs, whic...

Santiago Morla | Mar 7, 2025 |

Optimizing Experiences with Optimizely: Custom Audience Criteria for Mobile Visitors

In today’s mobile-first world, delivering personalized experiences to visitors using mobile devices is crucial for maximizing engagement and...

Nenad Nicevski | Mar 5, 2025 |

Unable to view Optimizely Forms submissions when some values are too long

I discovered a form where the form submissions could not be viewed in the Optimizely UI, only downloaded. Learn how to fix the issue.

Tomas Hensrud Gulla | Mar 4, 2025 |

CMS 12 DXP Migrations - Time Zones

When it comes to migrating a project from CMS 11 and .NET Framework on the DXP to CMS 12 and .NET Core one thing you need to be aware of is the...

Scott Reed | Mar 4, 2025