leekelleher / umbraco-contentment

Contentment for Umbraco - a state of happiness and satisfaction
https://marketplace.umbraco.com/package/umbraco.community.contentment
Mozilla Public License 2.0
157 stars 72 forks source link

Data List: Data Source `GetItems` called for every Block List element type (even when not used) #328

Closed Sven883 closed 1 year ago

Sven883 commented 1 year ago

Which Contentment version are you using?

2.2.2

Which Umbraco version are you using? For example: 8.14.1 - don't just write v8

8.14.1

Bug summary

While debugging I noticed that several of our custom datasources are executed when they are not used on a page. As example i've created a empty 'contentpage' in our backoffice:

image image image image

We have multiple custom datasources, but these are the ones that are executed when loading the contentpage in the backoffice.

  1. BlobStorageMedia

    public class BlobStorageMediaItemsDataSource : IDataListSource
    {
        private static readonly string BlobContainerName = "media-files";
        private static readonly string BlobBaseUrl = $"{new Uri(Appsettings.BaseUrl)}";
    
        private const string CacheKey = "blobstorage_media";
    
        public Dictionary<string, object> DefaultValues => new Dictionary<string, object>();
    
        public IEnumerable<ConfigurationField> Fields => Enumerable.Empty<ConfigurationField>();
    
        public string Group => string.Empty;
    
        public OverlaySize OverlaySize => OverlaySize.Small;
    
        public string Name => "Blob storage media files";
    
        public string Description => "Data source for blob storage media files.";
    
        public string Icon => "icon-umb-media";
    
        public IEnumerable<DataListItem> GetItems(Dictionary<string, object> config)
        {
            return GetCachedBlobStorageMedia();
        }
    
        private IEnumerable<DataListItem> GetCachedBlobStorageMedia()
        {
            // Cache this list only 5 minutes.
            return CacheHelper.GetCacheItem(CacheKey, () => GetBlobStorageMedia(), 5);
        }
    
        private IEnumerable<DataListItem> GetBlobStorageMedia()
        {
            var blobStorageClient = DependencyResolver.Current.GetService<IBlobStorageClient>();
    
            if (blobStorageClient == null) return Enumerable.Empty<DataListItem>();
    
            var mediaFiles = blobStorageClient.GetContainerFileListing(BlobContainerName);
    
            if (mediaFiles == null || mediaFiles.Blobs == null || !mediaFiles.Blobs.Any()) return Enumerable.Empty<DataListItem>();
    
            return mediaFiles.Blobs.Where(x => x.Name.EndsWith(".mp4"))?.Select(x => new DataListItem
            {
                Name = x.Name,
                Value = !string.IsNullOrEmpty(x.Url) ? x.Url : $"{BlobBaseUrl}{BlobContainerName.EnsureEndsWith('/')}{x.Name}"
            });
        }
    }
public class ReadingGuidesDataSource : IDataListSource
    {
        public Dictionary<string, object> DefaultValues => new Dictionary<string, object>();

        public IEnumerable<ConfigurationField> Fields => Enumerable.Empty<ConfigurationField>();

        public OverlaySize OverlaySize => OverlaySize.Small;

        public string Name => "Reading Guides";

        public string Description => "Data source for all available reading guides.";

        public string Icon => "icon-book-alt-2";
        public string Group => string.Empty;
        public IEnumerable<DataListItem> GetItems(Dictionary<string, object> config)
        {
            var readingGuides = Current.UmbracoContext.Content.GetByContentType(ReadingGuide.GetModelContentType());

            if (readingGuides.Any() == true)
            {
                return readingGuides.Select(x => new DataListItem
                {
                    Name = x.Name,
                    Value = x.Id.ToString()
                });
            }

            return Enumerable.Empty<DataListItem>();
        }
    }

3.

public class ConstructionDetails3DItemsDataSource : IDataListSource
    {
        private static readonly string BlobContainerName = "constructiondetails-files";

        public Dictionary<string, object> DefaultValues => new Dictionary<string, object>();

        public IEnumerable<ConfigurationField> Fields => Enumerable.Empty<ConfigurationField>();

        public string Group => string.Empty;

        public OverlaySize OverlaySize => OverlaySize.Small;

        public string Name => "Construction Details 3D viewer files";

        public string Description => "Data source for construction Details 3D viewer files.";

        public string Icon => "icon-files";

        public IEnumerable<DataListItem> GetItems(Dictionary<string, object> config)
        {
            var blobStorageClient = DependencyResolver.Current.GetService<IBlobStorageClient>();

            if (blobStorageClient == null) return Enumerable.Empty<DataListItem>();

            var containerListing = blobStorageClient.GetConstructionDetails3DViewerItems(BlobContainerName);

            if (containerListing == null || !containerListing.Any()) return Enumerable.Empty<DataListItem>();

            return containerListing.Select(x => new DataListItem
            {
                Name = x.Key,
                Value = JsonConvert.SerializeObject(x)
            });
        }
    }

Any idea why this code is executed without using any content that uses these custom datasources?

Does contentment pre-load data list items in block list items before even adding them to the page? I noticed that deleting the block list picker from the page 'solves' the problem.

Steps to reproduce

Create umbraco v8.14.1 project with contentment v2.2.2. Create custom datasource as mentioned above & load a clean content page in the backoffice.

Expected result / actual result

The datasources code should only be executed when using a block list item that uses contentment data list with a custom data source.

Do you have Umbraco ModelsBuilder enabled?

What browsers are you seeing the problem on?

Firefox, Chrome, Safari, Microsoft Edge

Sven883 commented 1 year ago

@leekelleher upon further investigion I noticed that all of the custom data sources I've added above are used in umbraco block lists. This empty contentpage has an empty property 'BlockList' where editors can pick block list items that use any of the above custom data source.

should the custom datasource data be fetched if no blocks are added in the blocklist? thnx!

leekelleher commented 1 year ago

Hi @Sven883,

Does contentment pre-load data list items in block list items before even adding them to the page?

That is correct, the Data List items are loaded in with the Content Type (or Element Type) schema/scaffold. I can't recall when exactly the Block List editor loads in the schema/scaffolds for block types, but I know with Contentment's Content Blocks editor it is done on initial page load.

This was done by design, to reduce additional HTTP requests between client and server. If your custom data-source returns many items, or the population is an intensive action, I can appreciate the frustration. (Which is why I have been developing the alternative Data Picker editor, see PR #297, under active development).