Select all page-types inside a specific folder

Hosam Abdeltawab asked on August 31, 2020 20:16

I have a page container folder called CTA-List, and the allowed child types of that folder are of type CTA_item.

I am creating a widget, and I want that widget to be able to select one list of the type CTA-List. For instance, if I have a folder and that folder contains two CTA-list page types (CTA-List-1, CTA-List-2) and each CTA-List contains two CTA-Items (CTA-Item-1, CTA-Item-2); I want the widget to be able to choose either CTA-List-1 or CTA-List-2. Then, I would be able to loop through the items in the selected list with their fields.

How I might be able to do that?

Correct Answer

Liam Goldfinch answered on August 31, 2020 23:19

Hi Hosam,

To get the guids from that list?

You could do:

        // Retrieves the pages that corresponds to the selected node alias path
        var pages = DocumentHelper.GetDocuments()
                        .Path(selectedPagePath, PathTypeEnum.Children)
                        .OnCurrentSite()
                        .Published()
                        .ToList();

        var nodeGuids = pages.Select(x => x.NodeGUID);

It sounds like your CTA_item (Gwic.CallToAction) pages are under a different folder though rather than child pages of the CTA-List page, is that right? If so, you might be better to use a Page Selector (or a custom form component) instead of Path selector, then in the controller you can then use NodeGuid to do a lookup like this:

        var selectedList = CTAListProvider.GetCTALists()
            .WhereEquals(nameof(TreeNode.NodeGUID), nodeGuid)
            .TopN(1)
            .Published()
            .FirstOrDefault();

        if (selectedList != null)
        {
            var childrenGuids = selectedList.CTAItemsNodeGuids.Split(new[] { ';' }, System.StringSplitOptions.RemoveEmptyEntries);

            var ctaItems = CTAItemsProvider.GetCTAItems()
                .WhereIn(nameof(TreeNode.NodeGUID), childrenGuids)
                .ToList();

            // do what you need to do with the CTA Items and create a model to render in the widget.
        }
0 votesVote for this answer Unmark Correct answer

Recent Answers


Liam Goldfinch answered on August 31, 2020 20:45 (last edited on August 31, 2020 20:50)

Hi Hosam,

If I understand you correctly, then your tree will look something like this then?

  • Folder
    • CTA-List (CTA-List-1)
      • CTA_item (CTA-Item-1)
      • CTA_item (CTA-Item-2)
    • CTA-List (CTA-List-2)
      • CTA_item (CTA-Item-1)
      • CTA_item (CTA-Item-2)

And you want your widget to have a property which selects a folder to allow you to grab all the child pages? You could look to use the Kentico 12 Service Pack's Path Selector. Add the following attributes to the property and set the correct RootPath, so this would be /Folder to represent the parent folder.

[EditingComponent(PathSelector.IDENTIFIER)]
[EditingComponentProperty(nameof(PathSelectorProperties.RootPath), "/Folder")]
public IList<PathSelectorItem> PagePaths { get; set; }

This would then return an IList<PathSelectorItem> which contains the NodeAliasPath of the folder chosen. To get the NodeAliasPath from the list, you'd need to do:

// Retrieves the node alias path of the selected page from the 'PagePaths' property
string selectedPagePath = GetProperties().PagePaths?.FirstOrDefault()?.NodeAliasPath;

So if you selected CTA-List-2 folder, then the returned NodeAliasPath would be like /Folder/CTA-List-2, you could then get the child pages by doing this in the controller:

// Retrieves the pages that corresponds to the selected node alias path
var pages = DocumentHelper.GetDocuments()
                .Path(selectedPagePath, PathTypeEnum.Children)
                .OnCurrentSite()
                .Published()
                .ToList();

You could also use the Kentico generated classes/providers here instead and use the same Path query.

0 votesVote for this answer Mark as a Correct answer

Hosam Abdeltawab answered on August 31, 2020 21:04

You are correct about how the tree should look like. Moreover, there is another folder called CTA-Items-Folder. The tree looks like this:

Note: The CTA-Item page class name is Gwic.CallToAction.

  • CTA-Items-Folder
    • CTA-Item-1
    • CTA-Item-2
  • CTA-List-Folder
    • CTA-List-1
    • CTA-List-2

The CTA-List page types contain one field that has Uni Selector as a form control. What should happen is that the CTA-List page type can select any CTA-Item from the CTA-Items-Folder. My configurations for the field are as follows:

  • Object type: CMS.Document
  • Return column name: NodeGuid
  • Object site name: {%DocumentName%}
  • Selection mode: Multiple
  • Where condition: ClassName='Gwic.CallToAction' AND DocumentCulture = '{%EditedObject.DocumentCulture#%}'

Now, when I create a new CTA-List and try to select the CTA-Items, it doesn't show any CTA-Items to select. What do you think this is?

0 votesVote for this answer Mark as a Correct answer

Arjan van Hugten answered on August 31, 2020 21:43 (last edited on August 31, 2020 21:47)

The object type of the uni selector can also be: 'cms.documenttype' or 'cms.document.gwic.calltoaction'. The object site name should be the name of your site as configured in the Kentico CMS, I assume that it is something like 'gwic' because you also used it as the prefix for you page type code name.

0 votesVote for this answer Mark as a Correct answer

Liam Goldfinch answered on August 31, 2020 21:48

Hi Hosam,

{%DocumentName%} should be the Display name format field, not the object site name field.

0 votesVote for this answer Mark as a Correct answer

Hosam Abdeltawab answered on August 31, 2020 22:41

Liam,

In the piece of code you wrote below, how I might be able to get the GUID for each of the pages inside a list?

    var pages = DocumentHelper.GetDocuments()
            .Path(selectedPagePath, PathTypeEnum.Children)
            .OnCurrentSite()
            .Published()
            .ToList();
0 votesVote for this answer Mark as a Correct answer

Hosam Abdeltawab answered on September 1, 2020 17:44 (last edited on September 1, 2020 17:46)

Hi Liam,

When I type the following line, I am not seeing an option for my CTAItemsNodeGuids:

var childrenGuids = selectedList.CTAItemsNodeGuids.Split(new[] { ';' }, System.StringSplitOptions.RemoveEmptyEntries);

Here is the full controller code:

public class CTAListWidgetController : WidgetController<CTAListWidgetProperties>
{
    public ActionResult Index()
    {
        //Set variables
        var listItems = new List<CallToAction>();
        var defaultLayout = "_CTAList_Carousel";
        var layout = defaultLayout;

        //Get properties
        var props = GetProperties();

        //Set view name
        if (!string.IsNullOrEmpty(props.Layout))
        {
            layout = props.Layout;
        }

        var viewName = $"Widgets/CTAListWidget/{layout}";

        if (!this.ViewExists(viewName))
        {
            viewName = $"Widgets/CTAListWidget/{defaultLayout}";
        }

       //Get the selected list and iterate over the CTA Items
        Guid selectedListGuid = props.Pages.FirstOrDefault().NodeGuid;
        var selectedList = CTAListProvider.GetCTALists().WhereEquals(nameof(TreeNode.NodeGUID), selectedListGuid).TopN(1).Published().FirstOrDefault();


        if(selectedList != null)
        {
            //TODO
        }

        }
        }
0 votesVote for this answer Mark as a Correct answer

Liam Goldfinch answered on September 1, 2020 18:47

Hi Hosam,

CTAItemsNodeGuids is just the field name on the CTA List page type for the UniSelector, what did you call yours? You need to change that to be the UniSelector property.

0 votesVote for this answer Mark as a Correct answer

Hosam Abdeltawab answered on September 1, 2020 19:01

So I added everything; however, when I try to add the widget on Kentico's UI is is not showing. In other words, there is no option to select CTAList when I try to add a new widget to the screen. Here is what I have:

CTAListController.cs

    using AE.Web.Gwic.Controllers.Widgets.CTAListWidget;
    using AE.Web.Gwic.Models.Kentico.Extensions;
    using AE.Web.Gwic.Models.Widgets.CTAListWidget;
    using CMS.DocumentEngine;
    using CMS.Helpers;
    using CMS.DocumentEngine.Types.Gwic;
    using Kentico.PageBuilder.Web.Mvc;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;

    [assembly: RegisterWidget("AE.Gwic.Web.Widgets.CallToActionWidget", typeof(CTAListWidgetController), "Call To Action List", Description = "", IconClass = "icon-picture")]

    namespace AE.Web.Gwic.Controllers.Widgets.CTAListWidget
    {
        public class CTAListWidgetController : WidgetController<CTAListWidgetProperties>
        {
            public ActionResult Index()
            {
                //Set variables
                List<CallToAction> ctaItems = new List<CallToAction>();
                var defaultLayout = "_CTAList_Carousel";
                var layout = defaultLayout;

                //Get properties
                var props = GetProperties();

                //Set view name
                if (!string.IsNullOrEmpty(props.Layout))
                {
                    layout = props.Layout;
                }

                var viewName = $"Widgets/CTAListWidget/{layout}";

                if (!this.ViewExists(viewName))
                {
                    viewName = $"Widgets/CTAListWidget/{defaultLayout}";
                }

                //Get the selected list and iterate over the CTA Items
                Guid selectedListGuid = props.Pages.FirstOrDefault().NodeGuid;
                var selectedList = CTAListProvider.GetCTALists().WhereEquals(nameof(TreeNode.NodeGUID), selectedListGuid).TopN(1).Published().FirstOrDefault();

                if (selectedList != null)
                {
                    var childrenGuids = selectedList.Related_CTAItemList.Split(new[] { ';' }, System.StringSplitOptions.RemoveEmptyEntries);

                    ctaItems = CallToActionProvider.GetCallToActions().WhereIn(nameof(TreeNode.NodeGUID), childrenGuids).ToList();
                }

                //Set the view Model
                var ctaListViewModel = new CTAListWidgetModel
                {
                    Title = selectedList.Title,
                    Image = selectedList.Image,
                    ImageAltText = selectedList.ImageAltText,
                    CallToActionItems = ctaItems
                };

                return PartialView(viewName, ctaListViewModel);
            }
        }
    }

CTAListWidgetProperties.cs

    using Kentico.Components.Web.Mvc.FormComponents;
    using Kentico.Forms.Web.Mvc;
    using Kentico.PageBuilder.Web.Mvc;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    namespace AE.Web.Gwic.Models.Widgets.CTAListWidget
    {
        public class CTAListWidgetProperties : IWidgetProperties
        {
            [EditingComponent(PageSelector.IDENTIFIER, ExplanationText = "Select the CTA list")]
            [EditingComponentProperty(nameof(PageSelectorProperties.RootPath), "/Global-Widget-Content/CTA-Lists")]
            public IList<PageSelectorItem> Pages { get; set; }

            /// <summary>
            /// The layout
            /// </summary>
            [EditingComponent(DropDownComponent.IDENTIFIER, ExplanationText = "Select the layout to be used", Order = 1, Label = "Layout", DefaultValue = "")]
            [EditingComponentProperty(nameof(DropDownProperties.DataSource), LayoutOptions)]
            public string Layout { get; set; }

            public const string LayoutOptions =
               @";Please select a layout;
                   _CTAList_Carousel;CTA Carousel;
                    _HelpTiles;CTA Help Tiles;";
        }
    }

CTAListWidgetModel.cs

    using CMS.DocumentEngine.Types.Gwic;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    namespace AE.Web.Gwic.Models.Widgets.CTAListWidget
    {
        public class CTAListWidgetModel
        {
            public string Title { get; set; }
            public string Image { get; set; }
            public string ImageAltText { get; set; }
            public int SortOrder { get; set; }
            public IEnumerable<CallToAction> CallToActionItems { get; set; }
        }
    }

What might be the issue?

0 votesVote for this answer Mark as a Correct answer

Liam Goldfinch answered on September 1, 2020 19:28

Hi Hosam

So you see the second widget property for selecting layout? But not the first one?

Maybe try adding a label and order property to the attribute?

I'm not near a computer at the moment so if the above doesn't fix it, I can have another look later.

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.