Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup IContentManager #16077

Merged
merged 22 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public async Task<IActionResult> Update([FromForm] DashboardPartViewModel[] part
return Unauthorized();
}

var contentItemIds = parts.Select(i => i.ContentItemId).ToList();
var contentItemIds = parts.Select(i => i.ContentItemId).ToArray();

// Load the latest version first if any.
var latestItems = await _contentManager.GetAsync(contentItemIds, VersionOptions.Latest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@ public async Task<IActionResult> SearchContentItems(string part, string field, s
.GetAsync(results.Select(r => r.ContentItemId));

var selectedItems = new List<VueMultiselectItemViewModel>();
var user = _httpContextAccessor.HttpContext?.User;
foreach (var contentItem in contentItems)
{
selectedItems.Add(new VueMultiselectItemViewModel()
{
Id = contentItem.ContentItemId,
DisplayText = contentItem.ToString(),
HasPublished = contentItem.IsPublished(),
IsViewable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User,
CommonPermissions.EditContent, contentItem)
IsViewable = await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, contentItem)
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@model OrchardCore.ContentFields.ViewModels.DisplayContentPickerFieldViewModel
@using OrchardCore.ContentManagement
@using OrchardCore.Mvc.Utilities
@using OrchardCore.ContentManagement.Metadata.Models

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public async ValueTask<FluidValue> ProcessAsync(FluidValue input, FilterArgument
if (input.Type == FluidValues.Array)
{
// List of content item ids
var contentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue()).ToArray();
var contentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue());

return FluidValue.Create(await _contentManager.GetAsync(contentItemIds), ctx.Options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,26 @@ public static async Task<ContentItem> GetContentItemByHandleAsync(this IOrchardH
/// </summary>
/// <param name="orchardHelper">The <see cref="IOrchardHelper"/>.</param>
/// <param name="contentItemId">The content item id to load.</param>
/// <param name="latest">Whether a draft should be loaded if available. <c>false</c> by default.</param>
/// <param name="option">The version to load. By default we load the Published version.</param>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
/// <example>GetContentItemByIdAsync("4xxxxxxxxxxxxxxxx").</example>
/// <returns>A content item with the specific id, or <c>null</c> if it doesn't exist.</returns>
public static Task<ContentItem> GetContentItemByIdAsync(this IOrchardHelper orchardHelper, string contentItemId, bool latest = false)
public static Task<ContentItem> GetContentItemByIdAsync(this IOrchardHelper orchardHelper, string contentItemId, VersionOptions option = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= null, why not the actual default value that is documented? I know this is what will be used internally but that would make more sense. If the other default changes for instance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VersionOptions is a class. How can we provide a default value to the argument?

{
var contentManager = orchardHelper.HttpContext.RequestServices.GetService<IContentManager>();
return contentManager.GetAsync(contentItemId, latest ? VersionOptions.Latest : VersionOptions.Published);
return contentManager.GetAsync(contentItemId, option);
}

/// <summary>
/// Loads a list of content items by their ids.
/// </summary>
/// <param name="orchardHelper">The <see cref="IOrchardHelper"/>.</param>
/// <param name="contentItemIds">The content item ids to load.</param>
/// <param name="latest">Whether a draft should be loaded if available. <c>false</c> by default.</param>
/// <param name="option">The version to load. By default we load the Published version.</param>
/// <returns>A list of content items with the specific ids.</returns>
public static Task<IEnumerable<ContentItem>> GetContentItemsByIdAsync(this IOrchardHelper orchardHelper, IEnumerable<string> contentItemIds, bool latest = false)
public static Task<IEnumerable<ContentItem>> GetContentItemsByIdAsync(this IOrchardHelper orchardHelper, IEnumerable<string> contentItemIds, VersionOptions option = null)
{
var contentManager = orchardHelper.HttpContext.RequestServices.GetService<IContentManager>();
return contentManager.GetAsync(contentItemIds, latest);
return contentManager.GetAsync(contentItemIds, option);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ await shellScope.UsingAsync(async scope =>

var allPublishedContentItems = await contentManager.GetAsync(updatedContentItemIds);
allPublished = allPublishedContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v);
var allLatestContentItems = await contentManager.GetAsync(updatedContentItemIds, latest: true);
var allLatestContentItems = await contentManager.GetAsync(updatedContentItemIds, VersionOptions.Latest);
allLatest = allLatestContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v);

// Group all DocumentIndex by index to batch update them.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using OrchardCore.ContentManagement.Handlers;

namespace OrchardCore.ContentManagement;

public static class ContentManagerExtensions
{
public static Task<TAspect> PopulateAspectAsync<TAspect>(this IContentManager contentManager, IContent content) where TAspect : new()
{
return contentManager.PopulateAspectAsync(content, new TAspect());
}

public static async Task<bool> HasPublishedVersionAsync(this IContentManager contentManager, IContent content)
{
if (content?.ContentItem == null)
{
return false;
}

return content.ContentItem.IsPublished() ||
(await contentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Published) != null);
}

public static Task<ContentItemMetadata> GetContentItemMetadataAsync(this IContentManager contentManager, IContent content)
{
return contentManager.PopulateAspectAsync<ContentItemMetadata>(content);
}

public static async Task<IEnumerable<ContentItem>> LoadAsync(this IContentManager contentManager, IEnumerable<ContentItem> contentItems)
{
ArgumentNullException.ThrowIfNull(contentItems);

var results = new List<ContentItem>(contentItems.Count());

foreach (var contentItem in contentItems)
{
results.Add(await contentManager.LoadAsync(contentItem));
}

return results;
}

public static async IAsyncEnumerable<ContentItem> LoadAsync(this IContentManager contentManager, IAsyncEnumerable<ContentItem> contentItems)
{
ArgumentNullException.ThrowIfNull(contentItems);

await foreach (var contentItem in contentItems)
{
yield return await contentManager.LoadAsync(contentItem);
}
}

public static async Task<ContentValidateResult> UpdateValidateAndCreateAsync(this IContentManager contentManager, ContentItem contentItem, VersionOptions options)
{
await contentManager.UpdateAsync(contentItem);
var result = await contentManager.ValidateAsync(contentItem);

if (result.Succeeded)
{
await contentManager.CreateAsync(contentItem, options);
}

return result;
}

/// <summary>
/// Gets either the container content item with the specified id and version, or if the json path supplied gets the contained content item.
/// </summary>
/// <param name="contentManager">The <see cref="IContentManager"/> instance.</param>
/// <param name="contentItemId">The id content item id to load.</param>
/// <param name="options">The version option.</param>
/// <param name="jsonPath">The json path of the contained content item.</param>
public static async Task<ContentItem> GetAsync(this IContentManager contentManager, string contentItemId, string jsonPath, VersionOptions options = null)
{
var contentItem = await contentManager.GetAsync(contentItemId, options);

// It represents a contained content item.
if (!string.IsNullOrEmpty(jsonPath))
{
var root = (JsonObject)contentItem.Content;
contentItem = root.SelectNode(jsonPath)?.ToObject<ContentItem>();

return contentItem;
}

return contentItem;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using OrchardCore.ContentManagement.Handlers;

Expand Down Expand Up @@ -32,8 +29,7 @@ public interface IContentManager
/// </summary>
/// <param name="contentItem">The content instance filled with all necessary data.</param>
/// <param name="options">The version to create the item with.</param>
Task CreateAsync(ContentItem contentItem, VersionOptions options);

Task CreateAsync(ContentItem contentItem, VersionOptions options = null);

/// <summary>
/// Creates (puts) a new content item and manages removing and updating existing published or draft items.
Expand Down Expand Up @@ -70,29 +66,12 @@ public interface IContentManager
/// <returns>The validation <see cref="ContentValidateResult"/> result.</returns>
Task<ContentValidateResult> RestoreAsync(ContentItem contentItem);

/// <summary>
/// Gets the published content item with the specified id.
/// </summary>
/// <param name="id">The content item id to load.</param>
Task<ContentItem> GetAsync(string id);

/// <summary>
/// Gets the content item with the specified id and version.
/// </summary>
/// <param name="id">The id content item id to load.</param>
/// <param name="contentItemId">The id of the content item to load.</param>
/// <param name="options">The version option.</param>
Task<ContentItem> GetAsync(string id, VersionOptions options);

/// <summary>
/// Gets the published content items with the specified ids.
/// </summary>
/// <param name="contentItemIds">The content item ids to load.</param>
/// <param name="latest">Whether a draft should be loaded if available. <c>false</c> by default.</param>
/// <remarks>
/// This method will always issue a database query.
/// This means that it should be used only to get a list of content items that have not been loaded.
/// </remarks>
Task<IEnumerable<ContentItem>> GetAsync(IEnumerable<string> contentItemIds, bool latest = false);
Task<ContentItem> GetAsync(string contentItemId, VersionOptions options = null);

/// <summary>
/// Gets the published content items with the specified ids.
Expand All @@ -103,7 +82,7 @@ public interface IContentManager
/// This method will always issue a database query.
/// This means that it should be used only to get a list of content items that have not been loaded.
/// </remarks>
Task<IEnumerable<ContentItem>> GetAsync(IEnumerable<string> contentItemIds, VersionOptions options);
Task<IEnumerable<ContentItem>> GetAsync(IEnumerable<string> contentItemIds, VersionOptions options = null);

/// <summary>
/// Gets the content item with the specified version id.
Expand Down Expand Up @@ -138,7 +117,9 @@ public interface IContentManager
Task SaveDraftAsync(ContentItem contentItem);

Task PublishAsync(ContentItem contentItem);

Task UnpublishAsync(ContentItem contentItem);

Task<TAspect> PopulateAspectAsync<TAspect>(IContent content, TAspect aspect);

/// <summary>
Expand All @@ -148,138 +129,4 @@ public interface IContentManager
/// <returns>Clone of the item.</returns>
Task<ContentItem> CloneAsync(ContentItem contentItem);
}

public static class ContentManagerExtensions
{
/// <summary>
/// Creates (persists) a new Published content item.
/// </summary>
/// <param name="contentManager">The <see cref="IContentManager"/> instance.</param>
/// <param name="contentItem">The content instance filled with all necessary data.</param>
public static Task CreateAsync(this IContentManager contentManager, ContentItem contentItem)
{
return contentManager.CreateAsync(contentItem, VersionOptions.Published);
}

public static Task<TAspect> PopulateAspectAsync<TAspect>(this IContentManager contentManager, IContent content) where TAspect : new()
{
return contentManager.PopulateAspectAsync(content, new TAspect());
}

public static async Task<bool> HasPublishedVersionAsync(this IContentManager contentManager, IContent content)
{
if (content.ContentItem == null)
{
return false;
}

return content.ContentItem.IsPublished() || (await contentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Published) != null);
}

public static Task<ContentItemMetadata> GetContentItemMetadataAsync(this IContentManager contentManager, IContent content)
{
return contentManager.PopulateAspectAsync<ContentItemMetadata>(content);
}

public static async Task<IEnumerable<ContentItem>> LoadAsync(this IContentManager contentManager, IEnumerable<ContentItem> contentItems)
{
var results = new List<ContentItem>(contentItems.Count());

foreach (var contentItem in contentItems)
{
results.Add(await contentManager.LoadAsync(contentItem));
}

return results;
}

public static async IAsyncEnumerable<ContentItem> LoadAsync(this IContentManager contentManager, IAsyncEnumerable<ContentItem> contentItems)
{
await foreach (var contentItem in contentItems)
{
yield return await contentManager.LoadAsync(contentItem);
}
}

public static async Task<ContentValidateResult> UpdateValidateAndCreateAsync(this IContentManager contentManager, ContentItem contentItem, VersionOptions options)
{
await contentManager.UpdateAsync(contentItem);
var result = await contentManager.ValidateAsync(contentItem);

if (result.Succeeded)
{
await contentManager.CreateAsync(contentItem, options);
}

return result;
}

/// <summary>
/// Gets either the published container content item with the specified id, or if the json path supplied gets the contained content item.
/// </summary>
/// <param name="contentManager">The <see cref="IContentManager"/> instance.</param>
/// <param name="id">The content item id to load.</param>
/// <param name="jsonPath">The json path of the contained content item.</param>
public static Task<ContentItem> GetAsync(this IContentManager contentManager, string id, string jsonPath)
{
return contentManager.GetAsync(id, jsonPath, VersionOptions.Published);
}

/// <summary>
/// Gets either the container content item with the specified id and version, or if the json path supplied gets the contained content item.
/// </summary>
/// <param name="contentManager">The <see cref="IContentManager"/> instance.</param>
/// <param name="id">The id content item id to load.</param>
/// <param name="options">The version option.</param>
/// <param name="jsonPath">The json path of the contained content item.</param>
public static async Task<ContentItem> GetAsync(this IContentManager contentManager, string id, string jsonPath, VersionOptions options)
{
var contentItem = await contentManager.GetAsync(id, options);

// It represents a contained content item
if (!string.IsNullOrEmpty(jsonPath))
{
var root = (JsonObject)contentItem.Content;
contentItem = root.SelectNode(jsonPath)?.ToObject<ContentItem>();

return contentItem;
}

return contentItem;
}
}

public class VersionOptions
{
/// <summary>
/// Gets the latest version.
/// </summary>
public static VersionOptions Latest { get { return new VersionOptions { IsLatest = true }; } }

/// <summary>
/// Gets the latest published version.
/// </summary>
public static VersionOptions Published { get { return new VersionOptions { IsPublished = true }; } }

/// <summary>
/// Gets the latest draft version.
/// </summary>
public static VersionOptions Draft { get { return new VersionOptions { IsDraft = true }; } }

/// <summary>
/// Gets the latest version and creates a new version draft based on it.
/// </summary>
public static VersionOptions DraftRequired { get { return new VersionOptions { IsDraft = true, IsDraftRequired = true }; } }

/// <summary>
/// Gets all versions.
/// </summary>
public static VersionOptions AllVersions { get { return new VersionOptions { IsAllVersions = true }; } }

public bool IsLatest { get; private set; }
public bool IsPublished { get; private set; }
public bool IsDraft { get; private set; }
public bool IsDraftRequired { get; private set; }
public bool IsAllVersions { get; private set; }
}
}
Loading