Populate ISelectionFactory with value from another block's properties

Vote:
 

Is it possible to connect properties from one block model to be the choices for an ISelectionFactory on another block model? Not sure if I'm wording that correctly, but like this...

ItemsGroupBlock

five unique String properties (String1, String2, String3...)
ItemBlock

one String property, using ISelectionFactory for the choices
so it would read the string values in the ItemsGroupBlock model props and populate the SelectItem() objects' Text and Value with those values.
public class ItemSelectionFactory : ISelectionFactory
{
public IEnumerable GetSelections(ExtendedMetadata metadata)
{
return new ISelectItem[] {
new SelectItem() { Text = "String1", Value = "String1" },
new SelectItem() { Text = "String2", Value = "String2" },
new SelectItem() { Text = "String3", Value = "String3" },
new SelectItem() { Text = "String4", Value = "String4" }
};
}
}

I hope this makes sense what I'm asking but if not, I'll try to clarify further. Is it possible? Thanks!

#172789
Dec 12, 2016 16:35
Vote:
 

Hi,

In your ItemBlock, if you could set a ContentReference property for the ItemsGroupBlock, it would be pretty easy from there to get an IContent from the ContentReference, then cast that as an ItemsGroupBlock. From there, you would have access to all the string properties to populate the ISelectionFactory of your ItemBlock.

Hope that helps.

Thanks!

John

#172798
Dec 12, 2016 19:45
Vote:
 

Hey there, it helps a little. I'm afraid I'm a newbie at this. Would you mind spelling it out a little more? I have added the contentreference property to my item block. Where do I go from here?

#173062
Dec 13, 2016 17:54
Vote:
 

Hi,

So, after thinking about this more and playing around a bit, what I described above will not work, unless you are going to only have 1 ItemsGroupBlock and 1 ItemBlock ever (which really defeats the purpose of blocks in Episerver). The problem is that your ItemSelectionFactory class is completely separate from your block class, and there is no way (that I know of anyway) to get a ContentReference to a specific block inside of it, and also no way to pass that reference into the attribute that decorates your string in the ItemBlock. 

Let's say you would only have 1 instance of either. Then, you could add the ContentReference of the ItemsGroupBlock to the StartPage model. Inside of the ItemSelectionFactory class, you can easily get a reference to the StartPage using IContentLoader and from there, get the reference you your ItemsGroupBlock and it's properties. But like I said, it would only work for 1 instance each of the blocks, which I assume is not what you want. Perhaps someone else has an idea?

-John

#173092
Edited, Dec 14, 2016 1:44
Vote:
 

OK, thank you for the time in answering. I knew it was a stretch. Hopefully there's a solution but if not, oh well, the client will get what they get.

#173108
Dec 14, 2016 14:29
Vote:
 

Hey,

You can fetch the block in the selection factory, try sth like this out:

public class SomeStringsSelectionFactory : ISelectionFactory
{
public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
List<SelectItem> selections;

dynamic contentMetadata = metadata;
var ownerContent = contentMetadata.OwnerContent as IContent;
// fetch from the current block the definition block (as a reference, perhaps, as John suggested or as a property on some page in the particular relation to the block)

// read the five unique properties and pack as ISelectItems

}

}

BR,

Marija

#173135
Dec 14, 2016 23:50
Vote:
 

Hi Marija, I'm sorry but I'm a newbie. Could you spell it out for me a little more? 

#173139
Dec 15, 2016 2:04
Vote:
 

Hi, Nathan,

I'm sending an example block with this functionality. However, I am not sure how you imagined to fetch the other block, so in this example, I just took block's parent and if it's a NewsPage, I take it's content guid and take each character from it, just for fun. But, if you send how your blocks are structured or if you can have the block with string properties as a property on my "NewsPage", then you can simply read that instead of ContentGuid.

[ContentType]
    public class SomeBlock : BlockData
    {
        [UIHint("SomeStringHint")]
        public virtual string SomeString { get; set; }
    }

    [EditorDescriptorRegistration(TargetType = typeof(string), UIHint = "SomeStringHint")]
    public class SomeStringEditorDescriptor : EditorDescriptor
    {
        public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
        {
            SelectionFactoryType = typeof(SomeStringSelectionFactory);
            ClientEditingClass = "epi-cms/contentediting/editors/SelectionEditor";
            base.ModifyMetadata(metadata, attributes);
        }
    }

    public class SomeStringSelectionFactory : ISelectionFactory
    {
        public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
        {
            List<SelectItem> selections;

            dynamic contentMetadata = metadata;
            var ownerContent = contentMetadata.OwnerContent as IContent;
            var parentPage = ownerContent.GetParent();

            if (parentPage is NewsPage)
            {
                selections = parentPage.ContentGuid.ToString()
                        .ToCharArray()
                        .Select(c => new SelectItem { Text = c.ToString(), Value = c }).ToList();
            }
            else
            {
                selections = "abcd".ToCharArray().Select(c => new SelectItem { Text = c.ToString(), Value = c }).ToList();
            }


            return selections;
        }
    }

BR,

Marija

#173150
Dec 15, 2016 10:51
Vote:
 

Heck yeah Marija! So, the piece I was missing was:

 dynamic contentMetadata = metadata;
 var ownerContent = contentMetadata.OwnerContent as IContent;

So, Nathan, I just tested a working sample and here is the code for both blocks and the selection factory:

namespace AlloyTraining.Models.Blocks
{
    [ContentType(DisplayName = "Items Group Block", GUID = "YourGUID", Description = "")]
    public class ItemsGroupBlock : BlockData
    {

        [CultureSpecific]
        [Display(
            Name = "String One",
            Description = "",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        public virtual string String1 { get; set; }

        [CultureSpecific]
        [Display(
            Name = "String Two",
            Description = "",
            GroupName = SystemTabNames.Content,
            Order = 2)]
        public virtual string String2 { get; set; }

        [CultureSpecific]
        [Display(
            Name = "String Three",
            Description = "",
            GroupName = SystemTabNames.Content,
            Order = 3)]
        public virtual string String3 { get; set; }

    }
}

namespace AlloyTraining.Models.Blocks
{
    [ContentType(DisplayName = "ItemBlock", GUID = "YorGUID", Description = "")]
    public class ItemBlock : BlockData
    {
        [CultureSpecific]
        [Display(
            Name = "Items Group Block",
            Description = "",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        [AllowedTypes(typeof(ItemsGroupBlock))]
        public virtual ContentReference ItemsGroupBlock { get; set; }

        [CultureSpecific]
        [Display(
            Name = "Select Items",
            Description = "",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        [SelectOne(SelectionFactoryType = typeof(ItemSelectionFactory))]        
        public virtual string Items { get; set; }

        public class ItemSelectionFactory : ISelectionFactory
        {
            public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
            {
                var loader = ServiceLocator.Current.GetInstance<IContentLoader>();

                var list = new List<SelectItem>();

                dynamic contentMetadata = metadata;
                var ownerContent = contentMetadata.OwnerContent as IContent;

                var block = ownerContent as ItemBlock;

                if (block.ItemsGroupBlock != null)
                {
                    var itemsBlock = loader.Get<ItemsGroupBlock>(block.ItemsGroupBlock);
                    if (itemsBlock.String1 != null)
                    {
                        list.Add(new SelectItem() { Text = itemsBlock.String1, Value = itemsBlock.String1 });                           
                    }
                    if (itemsBlock.String2 != null)
                    {
                        list.Add(new SelectItem() { Text = itemsBlock.String2, Value = itemsBlock.String2 });
                    }
                    if (itemsBlock.String2 != null)
                    {
                        list.Add(new SelectItem() { Text = itemsBlock.String3, Value = itemsBlock.String3 });
                    }
                }

                return list;
            }
        }    
    }
}

I am pretty sure this is what you were after. Basically, the OwnerContent variable gets an IContent of the block that contains the selection factory. I take that and cast it to an ItemBlock. From there, I check if the ContentReference to the ItemGroupBlock exists, and if it does, I use IContentLoader to load an instance of it and read the string properties into the list that gets returned. There may be a more elegant way to do all that, but I wanted to show a quick example of the concept. I tested it locally and it is working as expected.

Thanks!

John

#173187
Dec 15, 2016 20:06
Vote:
 

Now there's another problem, I'm not sure how to buy you each a drink to say thanks! This worked exactly like I was looking. So thank you both for putting in the time to help me. If you're ever in New York City, hit me up! Merry Christmas to you both.

#173205
Dec 16, 2016 1:46
Vote:
 

Haha, deal! Maybe we take a beer in Vegas for Ascend 2017 instead ;)

Cheers both and Merry Christmas!

#173221
Dec 16, 2016 9:47
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.