umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
MIT License
4.42k stars 2.67k forks source link

Make it easier to add block list data #10071

Closed bjarnef closed 2 years ago

bjarnef commented 3 years ago

Currently there is this example from documentation to add data programmatically to Block List: https://our.umbraco.com/documentation/getting-started/backoffice/property-editors/built-in-property-editors/Block-List-Editor/#creating-blocklist-programmatically

However it seems like a lot of code to add data to the default Block List editor, and when working with ModelsBuilder, I think this can be done smarter.

The documentation also shows this, but doesn't the Umbraco core contain this base class, that we can re-use instead?

using Newtonsoft.Json;
using System.Collections.Generic;
//this class is used to mock the correct JSON structure when the object is serialized
public class Blocklist
{
    public BlockListUdi layout { get; set; }
    public List<Dictionary<string, string>> contentData { get; set; }
    public List<Dictionary<string, string>> settingsData { get; set; }
}
//this is a subclass which corresponds to the "Umbraco.BlockList" section in JSON
public class BlockListUdi
{
    //we mock the Umbraco.BlockList name with JsonPropertyAttribute to match the requested JSON structure
    [JsonProperty("Umbraco.BlockList")]
    public List<Dictionary<string, string>> contentUdi { get; set; }
    //we do not serialize settingsUdi
    [JsonIgnore]
    public List<Dictionary<string, string>> settingsUdi { get; set; }
    public BlockListUdi(List<Dictionary<string, string>> items, List<Dictionary<string, string>> settings)
    {
        this.contentUdi = items;
        this.settingsUdi = settings;
    }
}

For example when working with ModelsBuilder and having a "Block Link" with Name and Url properties:

private BlockList BuildBlockListLinksModel(List<LinkItem> links, IContentType linkType)
{
    BlockList blocklist = new BlockList();

    var dictionaryUdi = new List<Dictionary<string, string>>();
    var settingUdi = new List<Dictionary<string, string>>();

    var linksList = new List<Dictionary<string, string>>();
    var settingsList = new List<Dictionary<string, string>>();

    foreach (var link in links)
    {
        GuidUdi contentUdi = new GuidUdi("element", Guid.NewGuid());
        //GuidUdi settingsUdi = new GuidUdi("element", Guid.NewGuid());

        linksList.Add(new Dictionary<string, string>
        {
            {"contentTypeKey", linkType.Key.ToString()},
            {"udi", contentUdi.ToString()},
            {"title", link.Name},
            {"link", link.Url}
        });

        dictionaryUdi.Add(new Dictionary<string, string>
        {
            { "contentUdi", contentUdi.ToString() },
            //{ "settingsUdi", settingsUdi.ToString() }
        });
    }

    blocklist.Layout = new BlockListUdi(dictionaryUdi, settingUdi);
    blocklist.ContentData = linksList;
    blocklist.SettingsData = settingsList;

    return blocklist;
}
var contentTypes = _contentTypeService.GetAll();
var linkElementType = contentTypes.FirstOrDefault(n => n.Alias == ContentModels.BlockLink.ModelTypeAlias);
if (linkElementType != null)
{
     // Links
     var linksModel = BuildBlockListLinksModel(model.Links, linkElementType);
     var linksValue = model.Links.Count > 0 ? JsonConvert.SerializeObject(linksModel) : null;
     node.SetValue("links", linksValue);
}

var result = _contentService.SaveAndPublish(node);
if (result.Success)
{
      TempData["Success"] = true;
}

Btw. I noticed here SaveAndPublish() failed if linksModel didn't contain contentData (not sure if this is expected), but set this to null instead seems to work.

I wonder if we could have some utility methods / extensions when working with the standard Block List, e.g:

var blockListItemModel = BuildBlockListModel<T>(...)
var blockListItemsModel = BuildBlockListModel<List<T>>(...)

or

var blockListModel = GetBlockListModel();
var updatedModel = blockListModel.Append<T>();
var json = JsonConvert.SerializeObject(updatedModel);

node.SetValue("propertyAlias", json);
umbrabot commented 2 years ago

Hiya @bjarnef,

Just wanted to let you know that we noticed that this issue got a bit stale and might not be relevant any more.

We will close this issue for now but we're happy to open it up again if you think it's still relevant (for example: it's a feature request that's not yet implemented, or it's a bug that's not yet been fixed).

To open it this issue up again, you can write @umbrabot still relevant in a new comment as the first line. It would be super helpful for us if on the next line you could let us know why you think it's still relevant.

For example:

@umbrabot still relevant This bug can still be reproduced in version x.y.z

This will reopen the issue in the next few hours.

Thanks, from your friendly Umbraco GitHub bot :robot: :slightly_smiling_face: