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

Add CSharpier config #15287

Closed
wants to merge 7 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
Prev Previous commit
Next Next commit
Format all .cs files
with `dotnet csharpier .`
Skrypt committed Feb 16, 2024
commit df7f7edf098ebde9257a254d8ff68b0b5c68bc4e
4 changes: 1 addition & 3 deletions src/OrchardCore.Cms.Web/Program.cs
Original file line number Diff line number Diff line change
@@ -4,9 +4,7 @@

builder.Host.UseNLogHost();

builder.Services
.AddOrchardCms()
.AddSetupFeatures("OrchardCore.AutoSetup");
builder.Services.AddOrchardCms().AddSetupFeatures("OrchardCore.AutoSetup");

var app = builder.Build();

Original file line number Diff line number Diff line change
@@ -20,10 +20,11 @@ public AdminAreaControllerRouteMapper(IOptions<AdminOptions> adminOptions)

public bool TryMapAreaControllerRoute(IEndpointRouteBuilder routes, ControllerActionDescriptor descriptor)
{
if (descriptor.ControllerName == "Admin" ||
descriptor.ControllerTypeInfo.GetCustomAttribute<AdminAttribute>() != null ||
descriptor.MethodInfo.GetCustomAttribute<AdminAttribute>() != null
)
if (
descriptor.ControllerName == "Admin"
|| descriptor.ControllerTypeInfo.GetCustomAttribute<AdminAttribute>() != null
|| descriptor.MethodInfo.GetCustomAttribute<AdminAttribute>() != null
)
{
routes.MapAreaControllerRoute(
name: descriptor.DisplayName,
29 changes: 14 additions & 15 deletions src/OrchardCore.Modules/OrchardCore.Admin/AdminMenu.cs
Original file line number Diff line number Diff line change
@@ -8,11 +8,7 @@ namespace OrchardCore.Admin
{
public class AdminMenu : INavigationProvider
{
private static readonly RouteValueDictionary _routeValues = new()
{
{ "area", "OrchardCore.Settings" },
{ "groupId", AdminSiteSettingsDisplayDriver.GroupId },
};
private static readonly RouteValueDictionary _routeValues = new() { { "area", "OrchardCore.Settings" }, { "groupId", AdminSiteSettingsDisplayDriver.GroupId }, };

protected readonly IStringLocalizer S;

@@ -28,17 +24,20 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
return Task.CompletedTask;
}

builder
.Add(S["Configuration"], configuration => configuration
.Add(S["Settings"], settings => settings
.Add(S["Admin"], S["Admin"].PrefixPosition(), admin => admin
.AddClass("admin").Id("admin")
.Action("Index", "Admin", _routeValues)
.Permission(PermissionsAdminSettings.ManageAdminSettings)
.LocalNav()
)
builder.Add(
S["Configuration"],
configuration =>
configuration.Add(
S["Settings"],
settings =>
settings.Add(
S["Admin"],
S["Admin"].PrefixPosition(),
admin =>
admin.AddClass("admin").Id("admin").Action("Index", "Admin", _routeValues).Permission(PermissionsAdminSettings.ManageAdminSettings).LocalNav()
)
)
);
);

return Task.CompletedTask;
}
10 changes: 2 additions & 8 deletions src/OrchardCore.Modules/OrchardCore.Admin/AdminMenuFilter.cs
Original file line number Diff line number Diff line change
@@ -16,8 +16,7 @@ public class AdminMenuFilter : IAsyncResultFilter
private readonly ILayoutAccessor _layoutAccessor;
private readonly IShapeFactory _shapeFactory;

public AdminMenuFilter(ILayoutAccessor layoutAccessor,
IShapeFactory shapeFactory)
public AdminMenuFilter(ILayoutAccessor layoutAccessor, IShapeFactory shapeFactory)
{
_layoutAccessor = layoutAccessor;
_shapeFactory = shapeFactory;
@@ -55,12 +54,7 @@ public async Task OnResultExecutionAsync(ResultExecutingContext filterContext, R
}

// Populate main nav
var menuShape = await _shapeFactory.CreateAsync("Navigation",
Arguments.From(new
{
MenuName = NavigationConstants.AdminId,
filterContext.RouteData,
}));
var menuShape = await _shapeFactory.CreateAsync("Navigation", Arguments.From(new { MenuName = NavigationConstants.AdminId, filterContext.RouteData, }));

var layout = await _layoutAccessor.GetLayoutAsync();

Original file line number Diff line number Diff line change
@@ -67,9 +67,7 @@ public void OnProvidersExecuting(PageRouteModelProviderContext context)
}
}

public void OnProvidersExecuted(PageRouteModelProviderContext context)
{
}
public void OnProvidersExecuted(PageRouteModelProviderContext context) { }

private static IEnumerable<CompiledViewDescriptor> GetPageDescriptors<T>(ApplicationPartManager applicationManager)
where T : ViewsFeature, new()
@@ -94,10 +92,10 @@ private static IEnumerable<CompiledViewDescriptor> GetPageDescriptors<T>(Applica
}
}

private static bool IsRazorPage(CompiledViewDescriptor viewDescriptor) =>
viewDescriptor.Item?.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind;
private static bool IsRazorPage(CompiledViewDescriptor viewDescriptor) => viewDescriptor.Item?.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind;

private static T GetViewFeature<T>(ApplicationPartManager applicationManager) where T : ViewsFeature, new()
private static T GetViewFeature<T>(ApplicationPartManager applicationManager)
where T : ViewsFeature, new()
{
var viewsFeature = new T();
applicationManager.PopulateFeature(viewsFeature);
11 changes: 2 additions & 9 deletions src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeSelector.cs
Original file line number Diff line number Diff line change
@@ -15,10 +15,7 @@ public class AdminThemeSelector : IThemeSelector
private readonly IAdminThemeService _adminThemeService;
private readonly IHttpContextAccessor _httpContextAccessor;

public AdminThemeSelector(
IAdminThemeService adminThemeService,
IHttpContextAccessor httpContextAccessor
)
public AdminThemeSelector(IAdminThemeService adminThemeService, IHttpContextAccessor httpContextAccessor)
{
_adminThemeService = adminThemeService;
_httpContextAccessor = httpContextAccessor;
@@ -34,11 +31,7 @@ public async Task<ThemeSelectorResult> GetThemeAsync()
return null;
}

return new ThemeSelectorResult
{
Priority = 100,
ThemeName = adminThemeName
};
return new ThemeSelectorResult { Priority = 100, ThemeName = adminThemeName };
}

return null;
Original file line number Diff line number Diff line change
@@ -10,9 +10,7 @@ public class AdminThemeService : IAdminThemeService
private readonly ISiteService _siteService;
private readonly IExtensionManager _extensionManager;

public AdminThemeService(
ISiteService siteService,
IExtensionManager extensionManager)
public AdminThemeService(ISiteService siteService, IExtensionManager extensionManager)
{
_siteService = siteService;
_extensionManager = extensionManager;
Original file line number Diff line number Diff line change
@@ -17,9 +17,7 @@ public class AdminSiteSettingsDisplayDriver : SectionDisplayDriver<ISite, AdminS
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAuthorizationService _authorizationService;

public AdminSiteSettingsDisplayDriver(
IHttpContextAccessor httpContextAccessor,
IAuthorizationService authorizationService)
public AdminSiteSettingsDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService)
{
_httpContextAccessor = httpContextAccessor;
_authorizationService = authorizationService;
@@ -34,13 +32,18 @@ public override async Task<IDisplayResult> EditAsync(AdminSettings settings, Bui
return null;
}

return Initialize<AdminSettingsViewModel>("AdminSettings_Edit", model =>
{
model.DisplayThemeToggler = settings.DisplayThemeToggler;
model.DisplayMenuFilter = settings.DisplayMenuFilter;
model.DisplayNewMenu = settings.DisplayNewMenu;
model.DisplayTitlesInTopbar = settings.DisplayTitlesInTopbar;
}).Location("Content:3").OnGroup(GroupId);
return Initialize<AdminSettingsViewModel>(
"AdminSettings_Edit",
model =>
{
model.DisplayThemeToggler = settings.DisplayThemeToggler;
model.DisplayMenuFilter = settings.DisplayMenuFilter;
model.DisplayNewMenu = settings.DisplayNewMenu;
model.DisplayTitlesInTopbar = settings.DisplayTitlesInTopbar;
}
)
.Location("Content:3")
.OnGroup(GroupId);
}

public override async Task<IDisplayResult> UpdateAsync(AdminSettings settings, BuildEditorContext context)
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ public class VisitSiteNavbarDisplayDriver : DisplayDriver<Navbar>
{
public override IDisplayResult Display(Navbar model)
{
return View("VisitSiteNavbarItem", model)
.Location("DetailAdmin", "Content:20");
return View("VisitSiteNavbarItem", model).Location("DetailAdmin", "Content:20");
}
}
5 changes: 1 addition & 4 deletions src/OrchardCore.Modules/OrchardCore.Admin/Manifest.cs
Original file line number Diff line number Diff line change
@@ -7,8 +7,5 @@
Version = ManifestConstants.OrchardCoreVersion,
Description = "Creates an admin section for the site.",
Category = "Infrastructure",
Dependencies =
[
"OrchardCore.Settings"
]
Dependencies = ["OrchardCore.Settings"]
)]
42 changes: 9 additions & 33 deletions src/OrchardCore.Modules/OrchardCore.Admin/Permissions.cs
Original file line number Diff line number Diff line change
@@ -8,40 +8,16 @@ public class Permissions : IPermissionProvider
{
public static readonly Permission AccessAdminPanel = new("AccessAdminPanel", "Access admin panel");

private readonly IEnumerable<Permission> _allPermissions =
[
AccessAdminPanel,
];
private readonly IEnumerable<Permission> _allPermissions = [AccessAdminPanel,];

public Task<IEnumerable<Permission>> GetPermissionsAsync()
=> Task.FromResult(_allPermissions);
public Task<IEnumerable<Permission>> GetPermissionsAsync() => Task.FromResult(_allPermissions);

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions = _allPermissions,
},
new PermissionStereotype
{
Name = "Editor",
Permissions = _allPermissions,
},
new PermissionStereotype
{
Name = "Moderator",
Permissions = _allPermissions,
},
new PermissionStereotype
{
Name = "Author",
Permissions = _allPermissions,
},
new PermissionStereotype
{
Name = "Contributor",
Permissions = _allPermissions,
},
];
[
new PermissionStereotype { Name = "Administrator", Permissions = _allPermissions, },
new PermissionStereotype { Name = "Editor", Permissions = _allPermissions, },
new PermissionStereotype { Name = "Moderator", Permissions = _allPermissions, },
new PermissionStereotype { Name = "Author", Permissions = _allPermissions, },
new PermissionStereotype { Name = "Contributor", Permissions = _allPermissions, },
];
}
Original file line number Diff line number Diff line change
@@ -8,20 +8,9 @@ public class PermissionsAdminSettings : IPermissionProvider
{
public static readonly Permission ManageAdminSettings = new("ManageAdminSettings", "Manage Admin Settings");

private readonly IEnumerable<Permission> _allPermissions =
[
ManageAdminSettings,
];
private readonly IEnumerable<Permission> _allPermissions = [ManageAdminSettings,];

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions = _allPermissions,
},
];
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() => [new PermissionStereotype { Name = "Administrator", Permissions = _allPermissions, },];

public Task<IEnumerable<Permission>> GetPermissionsAsync()
=> Task.FromResult(_allPermissions);
public Task<IEnumerable<Permission>> GetPermissionsAsync() => Task.FromResult(_allPermissions);
}
28 changes: 16 additions & 12 deletions src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs
Original file line number Diff line number Diff line change
@@ -38,14 +38,16 @@ public override void ConfigureServices(IServiceCollection services)
{
services.AddNavigation();

services.Configure<MvcOptions>((options) =>
{
options.Filters.Add(typeof(AdminFilter));
options.Filters.Add(typeof(AdminMenuFilter));
services.Configure<MvcOptions>(
(options) =>
{
options.Filters.Add(typeof(AdminFilter));
options.Filters.Add(typeof(AdminMenuFilter));

// Ordered to be called before any global filter.
options.Filters.Add(typeof(AdminZoneFilter), -1000);
});
// Ordered to be called before any global filter.
options.Filters.Add(typeof(AdminZoneFilter), -1000);
}
);

services.AddTransient<IAreaControllerRouteMapper, AdminAreaControllerRouteMapper>();
services.AddScoped<IPermissionProvider, Permissions>();
@@ -77,11 +79,13 @@ public class AdminPagesStartup : StartupBase

public override void ConfigureServices(IServiceCollection services)
{
services.Configure<RazorPagesOptions>((options) =>
{
var adminOptions = ShellScope.Services.GetRequiredService<IOptions<AdminOptions>>().Value;
options.Conventions.Add(new AdminPageRouteModelConvention(adminOptions.AdminUrlPrefix));
});
services.Configure<RazorPagesOptions>(
(options) =>
{
var adminOptions = ShellScope.Services.GetRequiredService<IOptions<AdminOptions>>().Value;
options.Conventions.Add(new AdminPageRouteModelConvention(adminOptions.AdminUrlPrefix));
}
);
}
}

Original file line number Diff line number Diff line change
@@ -36,7 +36,8 @@ public DashboardController(
IContentItemDisplayManager contentItemDisplayManager,
IContentDefinitionManager contentDefinitionManager,
IUpdateModelAccessor updateModelAccessor,
YesSql.ISession session)
YesSql.ISession session
)
{
_authorizationService = authorizationService;
_adminDashboardService = adminDashboardService;
@@ -49,10 +50,7 @@ public DashboardController(

public async Task<IActionResult> Index()
{
var model = new AdminDashboardViewModel()
{
CanManageDashboard = await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard),
};
var model = new AdminDashboardViewModel() { CanManageDashboard = await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard), };

if (model.CanManageDashboard || await _authorizationService.AuthorizeAsync(User, Permissions.AccessAdminDashboard))
{
@@ -72,11 +70,13 @@ public async Task<IActionResult> Index()
continue;
}

wrappers.Add(new DashboardWrapper
{
Dashboard = widget,
Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin")
});
wrappers.Add(
new DashboardWrapper
{
Dashboard = widget,
Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin")
}
);
}

model.Dashboards = wrappers.ToArray();
@@ -93,10 +93,7 @@ public async Task<IActionResult> Manage()
}

// Set Manage Dashboard Feature.
Request.HttpContext.Features.Set(new DashboardFeature()
{
IsManageRequest = true
});
Request.HttpContext.Features.Set(new DashboardFeature() { IsManageRequest = true });

var dashboardCreatable = new List<SelectListItem>();
var widgetContentTypes = await GetDashboardWidgetsAsync();
@@ -117,8 +114,10 @@ public async Task<IActionResult> Manage()
var wrappers = new List<DashboardWrapper>();
foreach (var widget in widgets)
{
if (!widgetContentTypes.ContainsKey(widget.ContentType)
|| !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, widget.ContentType, userId))
if (
!widgetContentTypes.ContainsKey(widget.ContentType)
|| !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, widget.ContentType, userId)
)
{
continue;
}
@@ -132,11 +131,7 @@ public async Task<IActionResult> Manage()
wrappers.Add(wrapper);
}

var model = new AdminDashboardViewModel
{
Dashboards = wrappers.ToArray(),
Creatable = dashboardCreatable
};
var model = new AdminDashboardViewModel { Dashboards = wrappers.ToArray(), Creatable = dashboardCreatable };

return View(model);
}
@@ -202,9 +197,7 @@ public async Task<IActionResult> Update([FromForm] DashboardPartViewModel[] part
return RedirectToAction(nameof(Manage));
}

private async Task<Dictionary<string, ContentTypeDefinition>> GetDashboardWidgetsAsync()
=> (await _contentDefinitionManager.ListTypeDefinitionsAsync())
.Where(t => t.StereotypeEquals("DashboardWidget"))
.ToDictionary(ctd => ctd.Name, ctd => ctd);
private async Task<Dictionary<string, ContentTypeDefinition>> GetDashboardWidgetsAsync() =>
(await _contentDefinitionManager.ListTypeDefinitionsAsync()).Where(t => t.StereotypeEquals("DashboardWidget")).ToDictionary(ctd => ctd.Name, ctd => ctd);
}
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;


namespace OrchardCore.AdminDashboard.Drivers
{
public class DashboardContentDisplayDriver : ContentDisplayDriver
@@ -18,23 +17,19 @@ public class DashboardContentDisplayDriver : ContentDisplayDriver
private readonly IAuthorizationService _authorizationService;
private readonly IContentManager _contentManager;


public DashboardContentDisplayDriver(IHttpContextAccessor httpContextAccessor,
IAuthorizationService authorizationService,
IContentManager contentManager)
public DashboardContentDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService, IContentManager contentManager)
{
_httpContextAccessor = httpContextAccessor;
_authorizationService = authorizationService;
_contentManager = contentManager;

}

public override async Task<IDisplayResult> DisplayAsync(ContentItem model, BuildDisplayContext context)
{
var httpContext = _httpContextAccessor.HttpContext;
var dashboardFeature = httpContext.Features.Get<DashboardFeature>();

// Return if it's not Manage dashboard request
// Return if it's not Manage dashboard request
if (dashboardFeature == null || !dashboardFeature.IsManageRequest)
{
return null;
@@ -47,61 +42,89 @@ public override async Task<IDisplayResult> DisplayAsync(ContentItem model, Build
var hasDeletePermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.DeleteContent, model);
var hasPublishPermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.PublishContent, model);

var dragHandle = Initialize<ContentItemViewModel>("Dashboard_DragHandle", m =>
{
m.ContentItem = model;
}).Location("Leading:before");
var dragHandle = Initialize<ContentItemViewModel>(
"Dashboard_DragHandle",
m =>
{
m.ContentItem = model;
}
)
.Location("Leading:before");
results.Add(dragHandle);

if (hasEditPermission)
{
var editButton = Initialize<ContentItemViewModel>("Dashboard_EditButton", m =>
{
m.ContentItem = model;
}).Location("ActionsMenu:after");
var editButton = Initialize<ContentItemViewModel>(
"Dashboard_EditButton",
m =>
{
m.ContentItem = model;
}
)
.Location("ActionsMenu:after");
results.Add(editButton);
}

if (hasDeletePermission)
{
var deleteButton = Initialize<ContentItemViewModel>("Dashboard_DeleteButton", m =>
{
m.ContentItem = model;
}).Location("ActionsMenu:after");
var deleteButton = Initialize<ContentItemViewModel>(
"Dashboard_DeleteButton",
m =>
{
m.ContentItem = model;
}
)
.Location("ActionsMenu:after");
results.Add(deleteButton);
}

if (hasPublished && hasPublishPermission)
{
var unpublishButton = Initialize<ContentItemViewModel>("Dashboard_UnpublishButton", m =>
{
m.ContentItem = model;
}).Location("ActionsMenu:after");
var unpublishButton = Initialize<ContentItemViewModel>(
"Dashboard_UnpublishButton",
m =>
{
m.ContentItem = model;
}
)
.Location("ActionsMenu:after");
results.Add(unpublishButton);
}

if (hasDraft && hasPublishPermission)
{
var publishButton = Initialize<ContentItemViewModel>("Dashboard_PublishButton", m =>
{
m.ContentItem = model;
}).Location("ActionsMenu:after");
var publishButton = Initialize<ContentItemViewModel>(
"Dashboard_PublishButton",
m =>
{
m.ContentItem = model;
}
)
.Location("ActionsMenu:after");
results.Add(publishButton);
}

if (hasDraft && hasEditPermission)
{
var discardDraftButton = Initialize<ContentItemViewModel>("Dashboard_DiscardDraftButton", m =>
{
m.ContentItem = model;
}).Location("ActionsMenu:after");
var discardDraftButton = Initialize<ContentItemViewModel>(
"Dashboard_DiscardDraftButton",
m =>
{
m.ContentItem = model;
}
)
.Location("ActionsMenu:after");
results.Add(discardDraftButton);
}

var shapeTag = Initialize<ContentItemViewModel>("DashboardWidget_DetailAdmin__ContentsTags", m =>
{
m.ContentItem = model;
}).Location("DetailAdmin", "Tags:10");
var shapeTag = Initialize<ContentItemViewModel>(
"DashboardWidget_DetailAdmin__ContentsTags",
m =>
{
m.ContentItem = model;
}
)
.Location("DetailAdmin", "Tags:10");
results.Add(shapeTag);

return Combine(results.ToArray());
Original file line number Diff line number Diff line change
@@ -13,7 +13,8 @@ public class DashboardPartIndexProvider : IndexProvider<ContentItem>
{
public override void Describe(DescribeContext<ContentItem> context)
{
context.For<DashboardPartIndex>()
context
.For<DashboardPartIndex>()
.Map(contentItem =>
{
var dashboardPart = contentItem.As<DashboardPart>();
@@ -26,10 +27,7 @@ public override void Describe(DescribeContext<ContentItem> context)

if (dashboardPart != null)
{
return new DashboardPartIndex
{
Position = dashboardPart.Position,
};
return new DashboardPartIndex { Position = dashboardPart.Position, };
}

return null;
Original file line number Diff line number Diff line change
@@ -6,12 +6,6 @@
Website = ManifestConstants.OrchardCoreWebsite,
Version = ManifestConstants.OrchardCoreVersion,
Description = "Allows to organize widgets in an Admin Dashboard.",
Dependencies =
[
"OrchardCore.Admin",
"OrchardCore.Html",
"OrchardCore.Title",
"OrchardCore.Recipes.Core",
],
Dependencies = ["OrchardCore.Admin", "OrchardCore.Html", "OrchardCore.Title", "OrchardCore.Recipes.Core",],
Category = "Content Management"
)]
24 changes: 7 additions & 17 deletions src/OrchardCore.Modules/OrchardCore.AdminDashboard/Migrations.cs
Original file line number Diff line number Diff line change
@@ -22,20 +22,14 @@ public Migrations(IContentDefinitionManager contentDefinitionManager, IRecipeMig

public async Task<int> CreateAsync()
{
await SchemaBuilder.CreateMapIndexTableAsync<DashboardPartIndex>(table => table
.Column<double>("Position")
);
await SchemaBuilder.CreateMapIndexTableAsync<DashboardPartIndex>(table => table.Column<double>("Position"));

await SchemaBuilder.AlterIndexTableAsync<DashboardPartIndex>(table => table
.CreateIndex("IDX_DashboardPart_DocumentId",
"DocumentId",
"Position")
);
await SchemaBuilder.AlterIndexTableAsync<DashboardPartIndex>(table => table.CreateIndex("IDX_DashboardPart_DocumentId", "DocumentId", "Position"));

await _contentDefinitionManager.AlterPartDefinitionAsync("DashboardPart", builder => builder
.Attachable()
.WithDescription("Provides a way to add widgets to a dashboard.")
);
await _contentDefinitionManager.AlterPartDefinitionAsync(
"DashboardPart",
builder => builder.Attachable().WithDescription("Provides a way to add widgets to a dashboard.")
);

await _recipeMigrator.ExecuteAsync($"dashboard-widgets{RecipesConstants.RecipeExtension}", this);

@@ -53,11 +47,7 @@ public async Task<int> UpdateFrom1Async()
// This code can be removed in a later version.
public async Task<int> UpdateFrom2Async()
{
await SchemaBuilder.AlterIndexTableAsync<DashboardPartIndex>(table => table
.CreateIndex("IDX_DashboardPart_DocumentId",
"DocumentId",
"Position")
);
await SchemaBuilder.AlterIndexTableAsync<DashboardPartIndex>(table => table.CreateIndex("IDX_DashboardPart_DocumentId", "DocumentId", "Position"));

return 3;
}
48 changes: 10 additions & 38 deletions src/OrchardCore.Modules/OrchardCore.AdminDashboard/Permissions.cs
Original file line number Diff line number Diff line change
@@ -9,46 +9,18 @@ public class Permissions : IPermissionProvider
public static readonly Permission ManageAdminDashboard = new("ManageAdminDashboard", "Manage the Admin Dashboard");
public static readonly Permission AccessAdminDashboard = new("AccessAdminDashboard", "Access the Admin Dashboard", new[] { ManageAdminDashboard });

private readonly IEnumerable<Permission> _allPermissions =
[
AccessAdminDashboard,
ManageAdminDashboard,
];
private readonly IEnumerable<Permission> _allPermissions = [AccessAdminDashboard, ManageAdminDashboard,];

private readonly IEnumerable<Permission> _generalPermissions =
[
AccessAdminDashboard,
];
private readonly IEnumerable<Permission> _generalPermissions = [AccessAdminDashboard,];

public Task<IEnumerable<Permission>> GetPermissionsAsync()
=> Task.FromResult(_allPermissions);
public Task<IEnumerable<Permission>> GetPermissionsAsync() => Task.FromResult(_allPermissions);

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions = _allPermissions,
},
new PermissionStereotype
{
Name = "Editor",
Permissions = _generalPermissions,
},
new PermissionStereotype
{
Name = "Moderator",
Permissions = _generalPermissions,
},
new PermissionStereotype
{
Name = "Author",
Permissions = _generalPermissions,
},
new PermissionStereotype
{
Name = "Contributor",
Permissions = _generalPermissions,
},
];
[
new PermissionStereotype { Name = "Administrator", Permissions = _allPermissions, },
new PermissionStereotype { Name = "Editor", Permissions = _generalPermissions, },
new PermissionStereotype { Name = "Moderator", Permissions = _generalPermissions, },
new PermissionStereotype { Name = "Author", Permissions = _generalPermissions, },
new PermissionStereotype { Name = "Contributor", Permissions = _generalPermissions, },
];
}
Original file line number Diff line number Diff line change
@@ -20,11 +20,7 @@ public AdminDashboardService(ISession session)

public async Task<IEnumerable<ContentItem>> GetWidgetsAsync(Expression<Func<ContentItemIndex, bool>> predicate)
{
var widgets = await _session
.Query<ContentItem, DashboardPartIndex>()
.OrderBy(w => w.Position)
.With(predicate)
.ListAsync();
var widgets = await _session.Query<ContentItem, DashboardPartIndex>().OrderBy(w => w.Position).With(predicate).ListAsync();

return widgets;
}
Original file line number Diff line number Diff line change
@@ -37,8 +37,7 @@ public override void ConfigureServices(IServiceCollection services)
services.AddScoped<IAdminDashboardService, AdminDashboardService>();
services.AddIndexProvider<DashboardPartIndexProvider>();

services.AddContentPart<DashboardPart>()
.UseDisplayDriver<DashboardPartDisplayDriver>();
services.AddContentPart<DashboardPart>().UseDisplayDriver<DashboardPartDisplayDriver>();

services.AddScoped<IContentDisplayDriver, DashboardContentDisplayDriver>();

Original file line number Diff line number Diff line change
@@ -6,12 +6,10 @@ namespace OrchardCore.AdminDashboard.ViewModels
{
public class DashboardWrapper : ShapeViewModel
{
public DashboardWrapper() : base("Dashboard_Wrapper")
{
}
public DashboardWrapper()
: base("Dashboard_Wrapper") { }

public ContentItem Dashboard { get; set; }
public IShape Content { get; set; }
}

}
18 changes: 9 additions & 9 deletions src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminMenu.cs
Original file line number Diff line number Diff line change
@@ -10,8 +10,7 @@ public class AdminMenu : INavigationProvider
private readonly AdminMenuNavigationProvidersCoordinator _adminMenuNavigationProvider;
private protected IStringLocalizer S;

public AdminMenu(AdminMenuNavigationProvidersCoordinator adminMenuNavigationProvider,
IStringLocalizer<AdminMenu> localizer)
public AdminMenu(AdminMenuNavigationProvidersCoordinator adminMenuNavigationProvider, IStringLocalizer<AdminMenu> localizer)
{
_adminMenuNavigationProvider = adminMenuNavigationProvider;
S = localizer;
@@ -25,14 +24,15 @@ public async Task BuildNavigationAsync(string name, NavigationBuilder builder)
}

// Configuration and settings menus for the AdminMenu module
builder
.Add(S["Configuration"], configuration => configuration
.Add(S["Admin Menus"], S["Admin Menus"].PrefixPosition(), adminMenu => adminMenu
.Permission(Permissions.ManageAdminMenu)
.Action("List", "Menu", "OrchardCore.AdminMenu")
.LocalNav()
builder.Add(
S["Configuration"],
configuration =>
configuration.Add(
S["Admin Menus"],
S["Admin Menus"].PrefixPosition(),
adminMenu => adminMenu.Permission(Permissions.ManageAdminMenu).Action("List", "Menu", "OrchardCore.AdminMenu").LocalNav()
)
);
);

// This is the entry point for the adminMenu: dynamically generated custom admin menus
await _adminMenuNavigationProvider.BuildNavigationAsync(NavigationConstants.AdminMenuId, builder);
Original file line number Diff line number Diff line change
@@ -28,29 +28,23 @@ public override IDisplayResult Display(LinkAdminNode treeNode)

public override IDisplayResult Edit(LinkAdminNode treeNode)
{
return Initialize<LinkAdminNodeViewModel>("LinkAdminNode_Fields_TreeEdit", async model =>
{
model.LinkText = treeNode.LinkText;
model.LinkUrl = treeNode.LinkUrl;
model.IconClass = treeNode.IconClass;
return Initialize<LinkAdminNodeViewModel>(
"LinkAdminNode_Fields_TreeEdit",
async model =>
{
model.LinkText = treeNode.LinkText;
model.LinkUrl = treeNode.LinkUrl;
model.IconClass = treeNode.IconClass;

var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();

var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name));
var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name));

model.SelectedItems = selectedPermissions
.Select(p => new PermissionViewModel
{
Name = p.Name,
DisplayText = p.Description
}).ToList();
model.AllItems = permissions
.Select(p => new PermissionViewModel
{
Name = p.Name,
DisplayText = p.Description
}).ToList();
}).Location("Content");
model.SelectedItems = selectedPermissions.Select(p => new PermissionViewModel { Name = p.Name, DisplayText = p.Description }).ToList();
model.AllItems = permissions.Select(p => new PermissionViewModel { Name = p.Name, DisplayText = p.Description }).ToList();
}
)
.Location("Content");
}

public override async Task<IDisplayResult> UpdateAsync(LinkAdminNode treeNode, IUpdateModel updater)
@@ -65,9 +59,7 @@ public override async Task<IDisplayResult> UpdateAsync(LinkAdminNode treeNode, I
var selectedPermissions = (model.SelectedPermissionNames == null ? [] : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries));

var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
treeNode.PermissionNames = permissions
.Where(p => selectedPermissions.Contains(p.Name))
.Select(p => p.Name).ToArray();
treeNode.PermissionNames = permissions.Where(p => selectedPermissions.Contains(p.Name)).Select(p => p.Name).ToArray();
}

return Edit(treeNode);
Original file line number Diff line number Diff line change
@@ -17,11 +17,11 @@ public class LinkAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder
private readonly IAdminMenuPermissionService _adminMenuPermissionService;
private readonly AdminOptions _adminOptions;


public LinkAdminNodeNavigationBuilder(
IAdminMenuPermissionService adminMenuPermissionService,
IOptions<AdminOptions> adminOptions,
ILogger<LinkAdminNodeNavigationBuilder> logger)
ILogger<LinkAdminNodeNavigationBuilder> logger
)
{
_adminMenuPermissionService = adminMenuPermissionService;
_adminOptions = adminOptions.Value;
@@ -38,58 +38,62 @@ public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, I
return Task.CompletedTask;
}

return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder =>
{
var nodeLinkUrl = node.LinkUrl;
if (!string.IsNullOrEmpty(nodeLinkUrl) && nodeLinkUrl[0] != '/' && !nodeLinkUrl.Contains("://"))
return builder.AddAsync(
new LocalizedString(node.LinkText, node.LinkText),
async itemBuilder =>
{
if (nodeLinkUrl.StartsWith("~/", StringComparison.Ordinal))
var nodeLinkUrl = node.LinkUrl;
if (!string.IsNullOrEmpty(nodeLinkUrl) && nodeLinkUrl[0] != '/' && !nodeLinkUrl.Contains("://"))
{
nodeLinkUrl = nodeLinkUrl[2..];
}
if (nodeLinkUrl.StartsWith("~/", StringComparison.Ordinal))
{
nodeLinkUrl = nodeLinkUrl[2..];
}

// Check if the first segment of 'nodeLinkUrl' is not equal to the admin prefix.
if (!nodeLinkUrl.StartsWith($"{_adminOptions.AdminUrlPrefix}", StringComparison.OrdinalIgnoreCase) ||
(nodeLinkUrl.Length != _adminOptions.AdminUrlPrefix.Length
&& nodeLinkUrl[_adminOptions.AdminUrlPrefix.Length] != '/'))
{
nodeLinkUrl = $"{_adminOptions.AdminUrlPrefix}/{nodeLinkUrl}";
// Check if the first segment of 'nodeLinkUrl' is not equal to the admin prefix.
if (
!nodeLinkUrl.StartsWith($"{_adminOptions.AdminUrlPrefix}", StringComparison.OrdinalIgnoreCase)
|| (nodeLinkUrl.Length != _adminOptions.AdminUrlPrefix.Length && nodeLinkUrl[_adminOptions.AdminUrlPrefix.Length] != '/')
)
{
nodeLinkUrl = $"{_adminOptions.AdminUrlPrefix}/{nodeLinkUrl}";
}
}
}

// Add the actual link.
itemBuilder.Url(nodeLinkUrl);
itemBuilder.Priority(node.Priority);
itemBuilder.Position(node.Position);
// Add the actual link.
itemBuilder.Url(nodeLinkUrl);
itemBuilder.Priority(node.Priority);
itemBuilder.Position(node.Position);

if (node.PermissionNames.Length > 0)
{
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
if (node.PermissionNames.Length > 0)
{
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();

// Find the actual permissions and apply them to the menu.
var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name));
itemBuilder.Permissions(selectedPermissions);
}
// Find the actual permissions and apply them to the menu.
var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name));
itemBuilder.Permissions(selectedPermissions);
}

// Add adminNode's IconClass property values to menuItem.Classes.
// Add them with a prefix so that later the shape template can extract them to use them on a <i> tag.
node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c));
// Add adminNode's IconClass property values to menuItem.Classes.
// Add them with a prefix so that later the shape template can extract them to use them on a <i> tag.
node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c));

// Let children build themselves inside this MenuItem.
// Todo: This logic can be shared by all TreeNodeNavigationBuilders.
foreach (var childTreeNode in menuItem.Items)
{
try
{
var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name);
await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders);
}
catch (Exception e)
// Let children build themselves inside this MenuItem.
// Todo: This logic can be shared by all TreeNodeNavigationBuilders.
foreach (var childTreeNode in menuItem.Items)
{
_logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name);
try
{
var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name);
await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders);
}
catch (Exception e)
{
_logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name);
}
}
}
});
);
}
}
}
Original file line number Diff line number Diff line change
@@ -28,28 +28,22 @@ public override IDisplayResult Display(PlaceholderAdminNode treeNode)

public override IDisplayResult Edit(PlaceholderAdminNode treeNode)
{
return Initialize<PlaceholderAdminNodeViewModel>("PlaceholderAdminNode_Fields_TreeEdit", async model =>
{
model.LinkText = treeNode.LinkText;
model.IconClass = treeNode.IconClass;
return Initialize<PlaceholderAdminNodeViewModel>(
"PlaceholderAdminNode_Fields_TreeEdit",
async model =>
{
model.LinkText = treeNode.LinkText;
model.IconClass = treeNode.IconClass;

var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();

var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name));
var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name));

model.SelectedItems = selectedPermissions
.Select(p => new PermissionViewModel
{
Name = p.Name,
DisplayText = p.Description
}).ToList();
model.AllItems = permissions
.Select(p => new PermissionViewModel
{
Name = p.Name,
DisplayText = p.Description
}).ToList();
}).Location("Content");
model.SelectedItems = selectedPermissions.Select(p => new PermissionViewModel { Name = p.Name, DisplayText = p.Description }).ToList();
model.AllItems = permissions.Select(p => new PermissionViewModel { Name = p.Name, DisplayText = p.Description }).ToList();
}
)
.Location("Content");
}

public override async Task<IDisplayResult> UpdateAsync(PlaceholderAdminNode treeNode, IUpdateModel updater)
@@ -60,11 +54,11 @@ public override async Task<IDisplayResult> UpdateAsync(PlaceholderAdminNode tree
treeNode.LinkText = model.LinkText;
treeNode.IconClass = model.IconClass;

var selectedPermissions = (model.SelectedPermissionNames == null ? Array.Empty<string>() : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries));
var selectedPermissions = (
model.SelectedPermissionNames == null ? Array.Empty<string>() : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries)
);
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
treeNode.PermissionNames = permissions
.Where(p => selectedPermissions.Contains(p.Name))
.Select(p => p.Name).ToArray();
treeNode.PermissionNames = permissions.Where(p => selectedPermissions.Contains(p.Name)).Select(p => p.Name).ToArray();
}

return Edit(treeNode);
Original file line number Diff line number Diff line change
@@ -31,38 +31,41 @@ public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, I
return Task.CompletedTask;
}

return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder =>
{
itemBuilder.Priority(node.Priority);
itemBuilder.Position(node.Position);

if (node.PermissionNames.Length > 0)
return builder.AddAsync(
new LocalizedString(node.LinkText, node.LinkText),
async itemBuilder =>
{
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
// Find the actual permissions and apply them to the menu.
var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name));
itemBuilder.Permissions(selectedPermissions);
}

// Add adminNode's IconClass property values to menuItem.Classes.
// Add them with a prefix so that later the shape template can extract them to use them on a <i> tag.
node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c));
itemBuilder.Priority(node.Priority);
itemBuilder.Position(node.Position);

// Let children build themselves inside this MenuItem
// todo: this logic can be shared by all TreeNodeNavigationBuilders
foreach (var childTreeNode in menuItem.Items)
{
try
if (node.PermissionNames.Length > 0)
{
var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name);
await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders);
var permissions = await _adminMenuPermissionService.GetPermissionsAsync();
// Find the actual permissions and apply them to the menu.
var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name));
itemBuilder.Permissions(selectedPermissions);
}
catch (Exception e)

// Add adminNode's IconClass property values to menuItem.Classes.
// Add them with a prefix so that later the shape template can extract them to use them on a <i> tag.
node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c));

// Let children build themselves inside this MenuItem
// todo: this logic can be shared by all TreeNodeNavigationBuilders
foreach (var childTreeNode in menuItem.Items)
{
_logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name);
try
{
var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name);
await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders);
}
catch (Exception e)
{
_logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name);
}
}
}
});
);
}
}
}
Original file line number Diff line number Diff line change
@@ -43,7 +43,8 @@ public MenuController(
INotifier notifier,
IStringLocalizer<MenuController> stringLocalizer,
IHtmlLocalizer<MenuController> htmlLocalizer,
ILogger<MenuController> logger)
ILogger<MenuController> logger
)
{
_authorizationService = authorizationService;
_adminMenuService = adminMenuService;
@@ -79,10 +80,7 @@ public async Task<IActionResult> List(ContentOptions options, PagerParameters pa
// load at least the ones without error. Provide a way to delete the ones on error.
try
{
results = adminMenuList
.Skip(startIndex)
.Take(pageSize)
.ToList();
results = adminMenuList.Skip(startIndex).Take(pageSize).ToList();
}
catch (Exception ex)
{
@@ -107,21 +105,14 @@ public async Task<IActionResult> List(ContentOptions options, PagerParameters pa
Pager = pagerShape,
};

model.Options.ContentsBulkAction =
[
new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)),
];
model.Options.ContentsBulkAction = [new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)),];

return View(model);
}

[HttpPost, ActionName(nameof(List))]
[FormValueRequired("submit.Filter")]
public ActionResult IndexFilterPOST(AdminMenuListViewModel model)
=> RedirectToAction(nameof(List), new RouteValueDictionary
{
{_optionsSearch, model.Options.Search }
});
public ActionResult IndexFilterPOST(AdminMenuListViewModel model) => RedirectToAction(nameof(List), new RouteValueDictionary { { _optionsSearch, model.Options.Search } });

public async Task<IActionResult> Create()
{
@@ -170,11 +161,7 @@ public async Task<IActionResult> Edit(string id)
return NotFound();
}

var model = new AdminMenuEditViewModel
{
Id = adminMenu.Id,
Name = adminMenu.Name
};
var model = new AdminMenuEditViewModel { Id = adminMenu.Id, Name = adminMenu.Name };

return View(model);
}
Original file line number Diff line number Diff line change
@@ -25,15 +25,15 @@ public class NodeController : Controller
protected readonly IHtmlLocalizer H;
private readonly IUpdateModelAccessor _updateModelAccessor;


public NodeController(
IAuthorizationService authorizationService,
IDisplayManager<MenuItem> displayManager,
IEnumerable<IAdminNodeProviderFactory> factories,
IAdminMenuService adminMenuService,
IHtmlLocalizer<NodeController> htmlLocalizer,
INotifier notifier,
IUpdateModelAccessor updateModelAccessor)
IUpdateModelAccessor updateModelAccessor
)
{
_displayManager = displayManager;
_factories = factories;
@@ -73,11 +73,7 @@ private async Task<AdminNodeListViewModel> BuildDisplayViewModel(Models.AdminMen
thumbnails.Add(factory.Name, thumbnail);
}

var model = new AdminNodeListViewModel
{
AdminMenu = tree,
Thumbnails = thumbnails,
};
var model = new AdminNodeListViewModel { AdminMenu = tree, Thumbnails = thumbnails, };

return model;
}
@@ -306,8 +302,7 @@ public async Task<IActionResult> Toggle(string id, string treeNodeId)
}

[HttpPost]
public async Task<IActionResult> MoveNode(string treeId, string nodeToMoveId,
string destinationNodeId, int position)
public async Task<IActionResult> MoveNode(string treeId, string nodeToMoveId, string destinationNodeId, int position)
{
if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu))
{
Original file line number Diff line number Diff line change
@@ -24,11 +24,7 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan
}

var data = new JsonArray();
result.Steps.Add(new JsonObject
{
["name"] = "AdminMenu",
["data"] = data,
});
result.Steps.Add(new JsonObject { ["name"] = "AdminMenu", ["data"] = data, });

foreach (var adminMenu in (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu)
{
Original file line number Diff line number Diff line change
@@ -8,11 +8,10 @@ public class AdminMenuDeploymentStepDriver : DisplayDriver<DeploymentStep, Admin
{
public override IDisplayResult Display(AdminMenuDeploymentStep step)
{
return
Combine(
View("AdminMenuDeploymentStep_Fields_Summary", step).Location("Summary", "Content"),
View("AdminMenuDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content")
);
return Combine(
View("AdminMenuDeploymentStep_Fields_Summary", step).Location("Summary", "Content"),
View("AdminMenuDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content")
);
}

public override IDisplayResult Edit(AdminMenuDeploymentStep step)
35 changes: 8 additions & 27 deletions src/OrchardCore.Modules/OrchardCore.AdminMenu/Permissions.cs
Original file line number Diff line number Diff line change
@@ -12,10 +12,7 @@ public class Permissions : IPermissionProvider

private static readonly Permission _viewAdminMenu = new("ViewAdminMenu_{0}", "View Admin Menu - {0}", new[] { ManageAdminMenu, ViewAdminMenuAll });

private readonly IEnumerable<Permission> _generalPermissions =
[
ManageAdminMenu,
];
private readonly IEnumerable<Permission> _generalPermissions = [ManageAdminMenu,];

private readonly IAdminMenuService _adminMenuService;

@@ -28,11 +25,7 @@ public async Task<IEnumerable<Permission>> GetPermissionsAsync()
{
var adminMenuItems = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu;

var permissions = new List<Permission>(adminMenuItems.Count + 2)
{
ViewAdminMenuAll,
ManageAdminMenu,
};
var permissions = new List<Permission>(adminMenuItems.Count + 2) { ViewAdminMenuAll, ManageAdminMenu, };

foreach (var adminMenu in adminMenuItems)
{
@@ -43,23 +36,11 @@ public async Task<IEnumerable<Permission>> GetPermissionsAsync()
}

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions = _generalPermissions,
},
new PermissionStereotype
{
Name = "Editor",
Permissions = _generalPermissions,
},
];
[
new PermissionStereotype { Name = "Administrator", Permissions = _generalPermissions, },
new PermissionStereotype { Name = "Editor", Permissions = _generalPermissions, },
];

public static Permission CreatePermissionForAdminMenu(string name)
=> new(
string.Format(_viewAdminMenu.Name, name),
string.Format(_viewAdminMenu.Description, name),
_viewAdminMenu.ImpliedBy
);
public static Permission CreatePermissionForAdminMenu(string name) =>
new(string.Format(_viewAdminMenu.Name, name), string.Format(_viewAdminMenu.Description, name), _viewAdminMenu.ImpliedBy);
}
Original file line number Diff line number Diff line change
@@ -33,7 +33,9 @@ public async Task ExecuteAsync(RecipeExecutionContext context)

foreach (var token in model.Data.Cast<JsonObject>())
{
var adminMenu = token.ToObject<Models.AdminMenu>(/*serializer*/);
var adminMenu =
token.ToObject<Models.AdminMenu>( /*serializer*/
);

// When the id is not supplied generate an id, otherwise replace the menu if it exists, or create a new menu.
if (string.IsNullOrEmpty(adminMenu.Id))
Original file line number Diff line number Diff line change
@@ -25,7 +25,8 @@ public AdminMenuNavigationProvidersCoordinator(
IAuthorizationService authorizationService,
IHttpContextAccessor httpContextAccessor,
IEnumerable<IAdminNodeNavigationBuilder> nodeBuilders,
ILogger<AdminMenuNavigationProvidersCoordinator> logger)
ILogger<AdminMenuNavigationProvidersCoordinator> logger
)
{
_adminMenuService = adminMenuService;
_authorizationService = authorizationService;
@@ -43,14 +44,11 @@ public async Task BuildNavigationAsync(string name, NavigationBuilder builder)
return;
}

var trees = (await _adminMenuService.GetAdminMenuListAsync())
.AdminMenu.Where(m => m.Enabled && m.MenuItems.Count > 0);
var trees = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu.Where(m => m.Enabled && m.MenuItems.Count > 0);

foreach (var tree in trees)
{
if (await _authorizationService.AuthorizeAsync(
_httpContextAccessor.HttpContext?.User,
Permissions.CreatePermissionForAdminMenu(tree.Name)))
if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, Permissions.CreatePermissionForAdminMenu(tree.Name)))
{
await BuildTreeAsync(tree, builder);
}
Original file line number Diff line number Diff line change
@@ -35,4 +35,3 @@ public async Task<IEnumerable<Permission>> GetPermissionsAsync()
return _permissions;
}
}

Original file line number Diff line number Diff line change
@@ -14,14 +14,10 @@ namespace OrchardCore.Alias.Drivers
{
public class AliasPartDisplayDriver : ContentPartDisplayDriver<AliasPart>
{

private readonly ISession _session;
protected readonly IStringLocalizer S;

public AliasPartDisplayDriver(
ISession session,
IStringLocalizer<AliasPartDisplayDriver> localizer
)
public AliasPartDisplayDriver(ISession session, IStringLocalizer<AliasPartDisplayDriver> localizer)
{
_session = session;
S = localizer;
@@ -51,6 +47,5 @@ private static void BuildViewModel(AliasPartViewModel model, AliasPart part, Ali
model.ContentItem = part.ContentItem;
model.Settings = settings;
}

}
}
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@ public AliasPartHandler(
ITagCache tagCache,
ILiquidTemplateManager liquidTemplateManager,
ISession session,
IStringLocalizer<AliasPartHandler> stringLocalizer)
IStringLocalizer<AliasPartHandler> stringLocalizer
)
{
_contentDefinitionManager = contentDefinitionManager;
_tagCache = tagCache;
@@ -52,7 +53,7 @@ public override async Task ValidatingAsync(ValidateContentContext context, Alias
}
}

public async override Task UpdatedAsync(UpdateContentContext context, AliasPart part)
public override async Task UpdatedAsync(UpdateContentContext context, AliasPart part)
{
// Compute the Alias only if it's empty.
if (!string.IsNullOrEmpty(part.Alias))
@@ -71,8 +72,12 @@ public async override Task UpdatedAsync(UpdateContentContext context, AliasPart
ContentItem = part.ContentItem
};

part.Alias = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model,
new Dictionary<string, FluidValue>() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) });
part.Alias = await _liquidTemplateManager.RenderStringAsync(
pattern,
NullEncoder.Default,
model,
new Dictionary<string, FluidValue>() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) }
);

part.Alias = part.Alias.Replace("\r", string.Empty).Replace("\n", string.Empty);

Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ public override async Task UpdatedAsync(UpdateContentContext context)
var part = context.ContentItem.As<AliasPart>();

// Validate that the content definition contains this part, this prevents indexing parts
// that have been removed from the type definition, but are still present in the elements.
// that have been removed from the type definition, but are still present in the elements.
if (part != null)
{
// Lazy initialization because of ISession cyclic dependency.
@@ -60,7 +60,8 @@ public override async Task UpdatedAsync(UpdateContentContext context)

public void Describe(DescribeContext<ContentItem> context)
{
context.For<AliasPartIndex>()
context
.For<AliasPartIndex>()
.When(contentItem => contentItem.Has<AliasPart>() || _partRemoved.Contains(contentItem.ContentItemId))
.Map(contentItem =>
{
50 changes: 19 additions & 31 deletions src/OrchardCore.Modules/OrchardCore.Alias/Migrations.cs
Original file line number Diff line number Diff line change
@@ -19,24 +19,21 @@ public Migrations(IContentDefinitionManager contentDefinitionManager)

public async Task<int> CreateAsync()
{
await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(AliasPart), builder => builder
.Attachable()
.WithDescription("Provides a way to define custom aliases for content items."));
await _contentDefinitionManager.AlterPartDefinitionAsync(
nameof(AliasPart),
builder => builder.Attachable().WithDescription("Provides a way to define custom aliases for content items.")
);

await SchemaBuilder.CreateMapIndexTableAsync<AliasPartIndex>(table => table
.Column<string>("Alias", col => col.WithLength(AliasPart.MaxAliasLength))
.Column<string>("ContentItemId", c => c.WithLength(26))
.Column<bool>("Latest", c => c.WithDefault(false))
.Column<bool>("Published", c => c.WithDefault(true))
await SchemaBuilder.CreateMapIndexTableAsync<AliasPartIndex>(table =>
table
.Column<string>("Alias", col => col.WithLength(AliasPart.MaxAliasLength))
.Column<string>("ContentItemId", c => c.WithLength(26))
.Column<bool>("Latest", c => c.WithDefault(false))
.Column<bool>("Published", c => c.WithDefault(true))
);

await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
.CreateIndex("IDX_AliasPartIndex_DocumentId",
"DocumentId",
"Alias",
"ContentItemId",
"Published",
"Latest")
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table =>
table.CreateIndex("IDX_AliasPartIndex_DocumentId", "DocumentId", "Alias", "ContentItemId", "Published", "Latest")
);

// Shortcut other migration steps on new content definition schemas.
@@ -46,13 +43,9 @@ await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
// This code can be removed in a later version as Latest and Published are alterations.
public async Task<int> UpdateFrom1Async()
{
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
.AddColumn<bool>("Latest", c => c.WithDefault(false))
);
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table.AddColumn<bool>("Latest", c => c.WithDefault(false)));

await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
.AddColumn<bool>("Published", c => c.WithDefault(true))
);
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table.AddColumn<bool>("Published", c => c.WithDefault(true)));

return 2;
}
@@ -61,17 +54,16 @@ await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
public async Task<int> UpdateFrom2Async()
{
// Can't be fully created on existing databases where the 'Alias' may be of 767 chars.
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table =>
table
// .CreateIndex("IDX_AliasPartIndex_DocumentId",
// "DocumentId",
// "Alias",
// "ContentItemId",
// "Latest",
// "Published")

.CreateIndex("IDX_AliasPartIndex_DocumentId",
"DocumentId",
"Alias")
.CreateIndex("IDX_AliasPartIndex_DocumentId", "DocumentId", "Alias")
);

return 3;
@@ -82,12 +74,8 @@ public async Task<int> UpdateFrom3Async()
{
// In the previous migration step, an index was not fully created,
// but here, we can create a separate one for the missing columns.
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table => table
.CreateIndex("IDX_AliasPartIndex_DocumentId_ContentItemId",
"DocumentId",
"ContentItemId",
"Published",
"Latest")
await SchemaBuilder.AlterIndexTableAsync<AliasPartIndex>(table =>
table.CreateIndex("IDX_AliasPartIndex_DocumentId_ContentItemId", "DocumentId", "ContentItemId", "Published", "Latest")
);

return 4;
Original file line number Diff line number Diff line change
@@ -15,7 +15,10 @@ public static async IAsyncEnumerable<ValidationResult> ValidateAsync(this AliasP
{
if (part.Alias.Length > AliasPart.MaxAliasLength)
{
yield return new ValidationResult(S["Your alias is too long. The alias can only be up to {0} characters. \"{1}\"", AliasPart.MaxAliasLength, part.Alias], new string[] { nameof(part.Alias) });
yield return new ValidationResult(
S["Your alias is too long. The alias can only be up to {0} characters. \"{1}\"", AliasPart.MaxAliasLength, part.Alias],
new string[] { nameof(part.Alias) }
);
}

if (!await IsAliasUniqueAsync(part, session, part.Alias))
@@ -29,6 +32,5 @@ public static async Task<bool> IsAliasUniqueAsync(this AliasPart context, ISessi
{
return (await session.QueryIndex<AliasPartIndex>(o => o.Alias == alias && o.ContentItemId != context.ContentItem.ContentItemId).CountAsync()) == 0;
}

}
}
Original file line number Diff line number Diff line change
@@ -23,14 +23,18 @@ public AliasPartSettingsDisplayDriver(ILiquidTemplateManager templateManager, IS

public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater)
{
return Initialize<AliasPartSettingsViewModel>("AliasPartSettings_Edit", model =>
{
var settings = contentTypePartDefinition.GetSettings<AliasPartSettings>();
return Initialize<AliasPartSettingsViewModel>(
"AliasPartSettings_Edit",
model =>
{
var settings = contentTypePartDefinition.GetSettings<AliasPartSettings>();

model.Pattern = settings.Pattern;
model.Options = settings.Options;
model.AliasPartSettings = settings;
}).Location("Content");
model.Pattern = settings.Pattern;
model.Options = settings.Options;
model.AliasPartSettings = settings;
}
)
.Location("Content");
}

public override async Task<IDisplayResult> UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context)
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ public class AliasPartSettingsViewModel
{
public string Pattern { get; set; }
public AliasPartOptions Options { get; set; }

[BindNever]
public AliasPartSettings AliasPartSettings { get; set; }
}
43 changes: 23 additions & 20 deletions src/OrchardCore.Modules/OrchardCore.Alias/Startup.cs
Original file line number Diff line number Diff line change
@@ -30,30 +30,35 @@ public override void ConfigureServices(IServiceCollection services)
{
o.MemberAccessStrategy.Register<AliasPartViewModel>();

o.MemberAccessStrategy.Register<LiquidContentAccessor, LiquidPropertyAccessor>("Alias", (obj, context) =>
{
var liquidTemplateContext = (LiquidTemplateContext)context;

return new LiquidPropertyAccessor(liquidTemplateContext, async (alias, context) =>
o.MemberAccessStrategy.Register<LiquidContentAccessor, LiquidPropertyAccessor>(
"Alias",
(obj, context) =>
{
var session = context.Services.GetRequiredService<ISession>();
var liquidTemplateContext = (LiquidTemplateContext)context;

return new LiquidPropertyAccessor(
liquidTemplateContext,
async (alias, context) =>
{
var session = context.Services.GetRequiredService<ISession>();

#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
var contentItem = await session.Query<ContentItem, AliasPartIndex>(x =>
x.Published && x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync();
var contentItem = await session.Query<ContentItem, AliasPartIndex>(x => x.Published && x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync();
#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons

if (contentItem == null)
{
return NilValue.Instance;
}
if (contentItem == null)
{
return NilValue.Instance;
}

var contentManager = context.Services.GetRequiredService<IContentManager>();
contentItem = await contentManager.LoadAsync(contentItem);
var contentManager = context.Services.GetRequiredService<IContentManager>();
contentItem = await contentManager.LoadAsync(contentItem);

return new ObjectValue(contentItem);
});
});
return new ObjectValue(contentItem);
}
);
}
);
});

services.AddScoped<AliasPartIndexProvider>();
@@ -64,9 +69,7 @@ public override void ConfigureServices(IServiceCollection services)
services.AddScoped<IContentHandleProvider, AliasPartContentHandleProvider>();

// Identity Part
services.AddContentPart<AliasPart>()
.UseDisplayDriver<AliasPartDisplayDriver>()
.AddHandler<AliasPartHandler>();
services.AddContentPart<AliasPart>().UseDisplayDriver<AliasPartDisplayDriver>().AddHandler<AliasPartHandler>();

services.AddScoped<IContentPartIndexHandler, AliasPartIndexHandler>();
services.AddScoped<IContentTypePartDefinitionDisplayDriver, AliasPartSettingsDisplayDriver>();
15 changes: 8 additions & 7 deletions src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/AdminMenu.cs
Original file line number Diff line number Diff line change
@@ -20,14 +20,15 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
return Task.CompletedTask;
}

builder
.Add(S["Configuration"], configuration => configuration
.Add(S["GraphiQL"], S["GraphiQL"].PrefixPosition(), graphiQL => graphiQL
.Action("Index", "Admin", "OrchardCore.Apis.GraphQL")
.Permission(Permissions.ExecuteGraphQL)
.LocalNav()
builder.Add(
S["Configuration"],
configuration =>
configuration.Add(
S["GraphiQL"],
S["GraphiQL"].PrefixPosition(),
graphiQL => graphiQL.Action("Index", "Admin", "OrchardCore.Apis.GraphQL").Permission(Permissions.ExecuteGraphQL).LocalNav()
)
);
);

return Task.CompletedTask;
}
Original file line number Diff line number Diff line change
@@ -13,10 +13,7 @@ public static async Task WriteErrorAsync(this IGraphQLSerializer graphQLSerializ
{
ArgumentNullException.ThrowIfNull(message);

var errorResult = new ExecutionResult
{
Errors = []
};
var errorResult = new ExecutionResult { Errors = [] };

if (e == null)
{
Original file line number Diff line number Diff line change
@@ -34,15 +34,13 @@ public class GraphQLMiddleware : IMiddleware
private static readonly MediaType _jsonMediaType = new("application/json");
private static readonly MediaType _graphQlMediaType = new("application/graphql");

public GraphQLMiddleware(
IOptions<GraphQLSettings> settingsOption,
IDocumentExecuter executer,
IGraphQLSerializer serializer)
public GraphQLMiddleware(IOptions<GraphQLSettings> settingsOption, IDocumentExecuter executer, IGraphQLSerializer serializer)
{
_settings = settingsOption.Value;
_executer = executer;
_serializer = serializer;
}

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (!IsGraphQLRequest(context))
@@ -70,6 +68,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
}
}
}

private bool IsGraphQLRequest(HttpContext context)
{
return context.Request.Path.StartsWithNormalizedSegments(_settings.Path, StringComparison.OrdinalIgnoreCase);
@@ -89,15 +88,11 @@ private async Task ExecuteAsync(HttpContext context)

if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType))
{

if (mediaType.IsSubsetOf(_graphQlMediaType))
{
using var sr = new StreamReader(context.Request.Body);

request = new GraphQLNamedQueryRequest
{
Query = await sr.ReadToEndAsync()
};
request = new GraphQLNamedQueryRequest { Query = await sr.ReadToEndAsync() };
}
else
{
@@ -131,9 +126,7 @@ private async Task ExecuteAsync(HttpContext context)
{
var namedQueries = context.RequestServices.GetServices<INamedQueryProvider>();

var queries = namedQueries
.SelectMany(dict => dict.Resolve())
.ToDictionary(pair => pair.Key, pair => pair.Value);
var queries = namedQueries.SelectMany(dict => dict.Resolve()).ToDictionary(pair => pair.Key, pair => pair.Value);

queryToExecute = queries[request.NamedQuery];
}
@@ -148,23 +141,29 @@ private async Task ExecuteAsync(HttpContext context)
options.OperationName = request.OperationName;
options.Variables = request.Variables;
options.UserContext = _settings.BuildUserContext?.Invoke(context);
options.ValidationRules = DocumentValidator.CoreRules
.Concat(context.RequestServices.GetServices<IValidationRule>())
.Append(new ComplexityValidationRule(new ComplexityConfiguration
{
MaxDepth = _settings.MaxDepth,
MaxComplexity = _settings.MaxComplexity,
FieldImpact = _settings.FieldImpact
}));
options.ValidationRules = DocumentValidator
.CoreRules.Concat(context.RequestServices.GetServices<IValidationRule>())
.Append(
new ComplexityValidationRule(
new ComplexityConfiguration
{
MaxDepth = _settings.MaxDepth,
MaxComplexity = _settings.MaxComplexity,
FieldImpact = _settings.FieldImpact
}
)
);
options.Listeners.Add(dataLoaderDocumentListener);
options.RequestServices = context.RequestServices;
});

context.Response.StatusCode = (int)(result.Errors == null || result.Errors.Count == 0
? HttpStatusCode.OK
: result.Errors.Any(x => x is ValidationError ve && ve.Number == RequiresPermissionValidationRule.ErrorCode)
? HttpStatusCode.Unauthorized
: HttpStatusCode.BadRequest);
context.Response.StatusCode = (int)(
result.Errors == null || result.Errors.Count == 0
? HttpStatusCode.OK
: result.Errors.Any(x => x is ValidationError ve && ve.Number == RequiresPermissionValidationRule.ErrorCode)
? HttpStatusCode.Unauthorized
: HttpStatusCode.BadRequest
);

context.Response.ContentType = MediaTypeNames.Application.Json;

@@ -183,10 +182,7 @@ private GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext contex
return null;
}

var request = new GraphQLNamedQueryRequest
{
Query = context.Request.Query["query"]
};
var request = new GraphQLNamedQueryRequest { Query = context.Request.Query["query"] };

if (context.Request.Query.ContainsKey("variables"))
{
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

using GraphQL.Transport;

namespace OrchardCore.Apis.GraphQL
21 changes: 3 additions & 18 deletions src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Permissions.cs
Original file line number Diff line number Diff line change
@@ -9,24 +9,9 @@ public class Permissions : IPermissionProvider
public static readonly Permission ExecuteGraphQLMutations = CommonPermissions.ExecuteGraphQLMutations;
public static readonly Permission ExecuteGraphQL = CommonPermissions.ExecuteGraphQL;

private readonly IEnumerable<Permission> _allPermissions =
[
ExecuteGraphQL,
ExecuteGraphQLMutations,
];
private readonly IEnumerable<Permission> _allPermissions = [ExecuteGraphQL, ExecuteGraphQLMutations,];

public Task<IEnumerable<Permission>> GetPermissionsAsync()
=> Task.FromResult(_allPermissions);
public Task<IEnumerable<Permission>> GetPermissionsAsync() => Task.FromResult(_allPermissions);

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions =
[
ExecuteGraphQLMutations,
],
},
];
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() => [new PermissionStereotype { Name = "Administrator", Permissions = [ExecuteGraphQLMutations,], },];
}
Original file line number Diff line number Diff line change
@@ -94,7 +94,6 @@ public async Task<ISchema> GetSchemaAsync()
await builder.BuildAsync(schema);
}


// Clean Query, Mutation and Subscription if they have no fields
// to prevent GraphQL configuration errors.

48 changes: 25 additions & 23 deletions src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs
Original file line number Diff line number Diff line change
@@ -55,32 +55,34 @@ public override void ConfigureServices(IServiceCollection services)
services.AddSingleton<GraphQLMiddleware>();
services.AddGraphQL(builder => builder.AddSystemTextJson());

services.AddOptions<GraphQLSettings>().Configure<IShellConfiguration>((c, configuration) =>
{
var exposeExceptions = configuration.GetValue(
$"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.ExposeExceptions)}",
_hostingEnvironment.IsDevelopment());
services
.AddOptions<GraphQLSettings>()
.Configure<IShellConfiguration>(
(c, configuration) =>
{
var exposeExceptions = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.ExposeExceptions)}", _hostingEnvironment.IsDevelopment());

var maxNumberOfResultsValidationMode = configuration.GetValue<MaxNumberOfResultsValidationMode?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResultsValidationMode)}")
?? MaxNumberOfResultsValidationMode.Default;
var maxNumberOfResultsValidationMode =
configuration.GetValue<MaxNumberOfResultsValidationMode?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResultsValidationMode)}")
?? MaxNumberOfResultsValidationMode.Default;

if (maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Default)
{
maxNumberOfResultsValidationMode = _hostingEnvironment.IsDevelopment() ? MaxNumberOfResultsValidationMode.Enabled : MaxNumberOfResultsValidationMode.Disabled;
}
if (maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Default)
{
maxNumberOfResultsValidationMode = _hostingEnvironment.IsDevelopment()
? MaxNumberOfResultsValidationMode.Enabled
: MaxNumberOfResultsValidationMode.Disabled;
}

c.BuildUserContext = ctx => new GraphQLUserContext
{
User = ctx.User,
};
c.ExposeExceptions = exposeExceptions;
c.MaxDepth = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxDepth)}") ?? 100;
c.MaxComplexity = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxComplexity)}");
c.FieldImpact = configuration.GetValue<double?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.FieldImpact)}");
c.MaxNumberOfResults = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResults)}") ?? 1000;
c.MaxNumberOfResultsValidationMode = maxNumberOfResultsValidationMode;
c.DefaultNumberOfResults = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.DefaultNumberOfResults)}") ?? 100;
});
c.BuildUserContext = ctx => new GraphQLUserContext { User = ctx.User, };
c.ExposeExceptions = exposeExceptions;
c.MaxDepth = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxDepth)}") ?? 100;
c.MaxComplexity = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxComplexity)}");
c.FieldImpact = configuration.GetValue<double?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.FieldImpact)}");
c.MaxNumberOfResults = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResults)}") ?? 1000;
c.MaxNumberOfResultsValidationMode = maxNumberOfResultsValidationMode;
c.DefaultNumberOfResults = configuration.GetValue<int?>($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.DefaultNumberOfResults)}") ?? 100;
}
);
}

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
Original file line number Diff line number Diff line change
@@ -18,7 +18,8 @@ public class MaxNumberOfResultsValidationRule : IValidationRule
public MaxNumberOfResultsValidationRule(
IOptions<GraphQLSettings> options,
IStringLocalizer<MaxNumberOfResultsValidationRule> localizer,
ILogger<MaxNumberOfResultsValidationRule> logger)
ILogger<MaxNumberOfResultsValidationRule> logger
)
{
var settings = options.Value;
_maxNumberOfResults = settings.MaxNumberOfResults;
@@ -29,48 +30,50 @@ public MaxNumberOfResultsValidationRule(

public ValueTask<INodeVisitor> ValidateAsync(ValidationContext validationContext)
{
return ValueTask.FromResult((INodeVisitor)new NodeVisitors(
new MatchingNodeVisitor<GraphQLArgument>((arg, visitorContext) =>
{
if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null)
{
var context = (GraphQLUserContext)validationContext.UserContext;
return ValueTask.FromResult(
(INodeVisitor)
new NodeVisitors(
new MatchingNodeVisitor<GraphQLArgument>(
(arg, visitorContext) =>
{
if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null)
{
var context = (GraphQLUserContext)validationContext.UserContext;

int? value = null;
int? value = null;

if (arg.Value is GraphQLIntValue)
{
value = int.Parse((arg.Value as GraphQLIntValue).Value);
}
else
{
if (validationContext.Variables.TryGetValue(arg.Value.ToString(), out var input))
{
value = (int?)input;
}
}
if (arg.Value is GraphQLIntValue)
{
value = int.Parse((arg.Value as GraphQLIntValue).Value);
}
else
{
if (validationContext.Variables.TryGetValue(arg.Value.ToString(), out var input))
{
value = (int?)input;
}
}

if (value.HasValue && value > _maxNumberOfResults)
{
var errorMessage = S["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults];
if (value.HasValue && value > _maxNumberOfResults)
{
var errorMessage = S["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults];

if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled)
{
validationContext.ReportError(new ValidationError(
validationContext.Document.Source,
"ArgumentInputError",
errorMessage,
arg));
}
else
{
_logger.LogInformation(errorMessage);
if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled)
{
validationContext.ReportError(new ValidationError(validationContext.Document.Source, "ArgumentInputError", errorMessage, arg));
}
else
{
_logger.LogInformation(errorMessage);

arg = new GraphQLArgument(arg.Name, new GraphQLIntValue(_maxNumberOfResults)); // if disabled mode we just log info and override the arg to be maxvalue
}
}
}
})));
arg = new GraphQLArgument(arg.Name, new GraphQLIntValue(_maxNumberOfResults)); // if disabled mode we just log info and override the arg to be maxvalue
}
}
}
}
)
)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -16,9 +16,7 @@ public class RequiresPermissionValidationRule : IValidationRule
private readonly IAuthorizationService _authorizationService;
protected readonly IStringLocalizer S;

public RequiresPermissionValidationRule(
IAuthorizationService authorizationService,
IStringLocalizer<RequiresPermissionValidationRule> localizer)
public RequiresPermissionValidationRule(IAuthorizationService authorizationService, IStringLocalizer<RequiresPermissionValidationRule> localizer)
{
_authorizationService = authorizationService;
S = localizer;
@@ -29,43 +27,61 @@ public async ValueTask<INodeVisitor> ValidateAsync(ValidationContext validationC
// shouldn't we access UserContext from validationcontext inside MatchingNodeVisitor actions?
var userContext = (GraphQLUserContext)validationContext.UserContext;

return await Task.FromResult(new NodeVisitors(
new MatchingNodeVisitor<GraphQLOperationDefinition>(async (operationDefinition, validationContext) =>
{
await AuthorizeOperationAsync(operationDefinition, validationContext, userContext, operationDefinition.Operation, operationDefinition?.Name?.StringValue);
}),
new MatchingNodeVisitor<GraphQLObjectField>(async (objectFieldAst, validationContext) =>
{
if (validationContext.TypeInfo.GetArgument()?.ResolvedType.GetNamedType() is IComplexGraphType argumentType)
{
var fieldType = argumentType.GetField(objectFieldAst.Name);
await AuthorizeNodePermissionAsync(objectFieldAst, fieldType, validationContext, userContext);
}
}),
new MatchingNodeVisitor<GraphQLField>(async (fieldAst, validationContext) =>
{
var fieldDef = validationContext.TypeInfo.GetFieldDef();

if (fieldDef == null)
return;

// check target field
await AuthorizeNodePermissionAsync(fieldAst, fieldDef, validationContext, userContext);
// check returned graph type
// AuthorizeNodePermissionAsync(fieldAst, fieldDef.ResolvedType.GetNamedType(), validationContext, userContext).GetAwaiter().GetResult(); // TODO: need to think of something to avoid this
})
));
return await Task.FromResult(
new NodeVisitors(
new MatchingNodeVisitor<GraphQLOperationDefinition>(
async (operationDefinition, validationContext) =>
{
await AuthorizeOperationAsync(
operationDefinition,
validationContext,
userContext,
operationDefinition.Operation,
operationDefinition?.Name?.StringValue
);
}
),
new MatchingNodeVisitor<GraphQLObjectField>(
async (objectFieldAst, validationContext) =>
{
if (validationContext.TypeInfo.GetArgument()?.ResolvedType.GetNamedType() is IComplexGraphType argumentType)
{
var fieldType = argumentType.GetField(objectFieldAst.Name);
await AuthorizeNodePermissionAsync(objectFieldAst, fieldType, validationContext, userContext);
}
}
),
new MatchingNodeVisitor<GraphQLField>(
async (fieldAst, validationContext) =>
{
var fieldDef = validationContext.TypeInfo.GetFieldDef();

if (fieldDef == null)
return;

// check target field
await AuthorizeNodePermissionAsync(fieldAst, fieldDef, validationContext, userContext);
// check returned graph type
// AuthorizeNodePermissionAsync(fieldAst, fieldDef.ResolvedType.GetNamedType(), validationContext, userContext).GetAwaiter().GetResult(); // TODO: need to think of something to avoid this
}
)
)
);
}

private async Task AuthorizeOperationAsync(ASTNode node, ValidationContext validationContext, GraphQLUserContext userContext, OperationType? operationType, string operationName)
private async Task AuthorizeOperationAsync(
ASTNode node,
ValidationContext validationContext,
GraphQLUserContext userContext,
OperationType? operationType,
string operationName
)
{
if (operationType == OperationType.Mutation && !(await _authorizationService.AuthorizeAsync(userContext.User, Permissions.ExecuteGraphQLMutations)))
{
validationContext.ReportError(new ValidationError(
validationContext.Document.Source,
ErrorCode,
S["Authorization is required to access {0}.", operationName],
node));
validationContext.ReportError(
new ValidationError(validationContext.Document.Source, ErrorCode, S["Authorization is required to access {0}.", operationName], node)
);
}
}

@@ -114,11 +130,9 @@ private async Task AuthorizeNodePermissionAsync(ASTNode node, IFieldType fieldTy

private void AddPermissionValidationError(ValidationContext validationContext, ASTNode node, string nodeName)
{
validationContext.ReportError(new ValidationError(
validationContext.Document.Source,
ErrorCode,
S["Authorization is required to access the node. {0}", nodeName],
node));
validationContext.ReportError(
new ValidationError(validationContext.Document.Source, ErrorCode, S["Authorization is required to access the node. {0}", nodeName], node)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -18,25 +18,18 @@ public class ArchiveLaterPartDisplayDriver : ContentPartDisplayDriver<ArchiveLat
private readonly IAuthorizationService _authorizationService;
private readonly ILocalClock _localClock;

public ArchiveLaterPartDisplayDriver(
IHttpContextAccessor httpContextAccessor,
IAuthorizationService authorizationService,
ILocalClock localClock)
public ArchiveLaterPartDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService, ILocalClock localClock)
{
_httpContextAccessor = httpContextAccessor;
_authorizationService = authorizationService;
_localClock = localClock;
}

public override IDisplayResult Display(ArchiveLaterPart part, BuildPartDisplayContext context)
=> Initialize<ArchiveLaterPartViewModel>(
$"{nameof(ArchiveLaterPart)}_SummaryAdmin",
model => PopulateViewModel(part, model)).Location("SummaryAdmin", "Meta:25");
public override IDisplayResult Display(ArchiveLaterPart part, BuildPartDisplayContext context) =>
Initialize<ArchiveLaterPartViewModel>($"{nameof(ArchiveLaterPart)}_SummaryAdmin", model => PopulateViewModel(part, model)).Location("SummaryAdmin", "Meta:25");

public override IDisplayResult Edit(ArchiveLaterPart part, BuildPartEditorContext context)
=> Initialize<ArchiveLaterPartViewModel>(
GetEditorShapeType(context),
model => PopulateViewModel(part, model)).Location("Actions:10.5");
public override IDisplayResult Edit(ArchiveLaterPart part, BuildPartEditorContext context) =>
Initialize<ArchiveLaterPartViewModel>(GetEditorShapeType(context), model => PopulateViewModel(part, model)).Location("Actions:10.5");

public override async Task<IDisplayResult> UpdateAsync(ArchiveLaterPart part, IUpdateModel updater, UpdatePartEditorContext context)
{
@@ -65,8 +58,6 @@ private async ValueTask PopulateViewModel(ArchiveLaterPart part, ArchiveLaterPar
{
viewModel.ContentItem = part.ContentItem;
viewModel.ScheduledArchiveUtc = part.ScheduledArchiveUtc;
viewModel.ScheduledArchiveLocalDateTime = part.ScheduledArchiveUtc.HasValue
? (await _localClock.ConvertToLocalAsync(part.ScheduledArchiveUtc.Value)).DateTime
: null;
viewModel.ScheduledArchiveLocalDateTime = part.ScheduledArchiveUtc.HasValue ? (await _localClock.ConvertToLocalAsync(part.ScheduledArchiveUtc.Value)).DateTime : null;
}
}
24 changes: 8 additions & 16 deletions src/OrchardCore.Modules/OrchardCore.ArchiveLater/Migrations.cs
Original file line number Diff line number Diff line change
@@ -20,25 +20,17 @@ public Migrations(IContentDefinitionManager contentDefinitionManager)

public async Task<int> CreateAsync()
{
await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(ArchiveLaterPart), builder => builder
.Attachable()
.WithDescription("Adds the ability to schedule content items to be archived at a given future date and time."));
await _contentDefinitionManager.AlterPartDefinitionAsync(
nameof(ArchiveLaterPart),
builder => builder.Attachable().WithDescription("Adds the ability to schedule content items to be archived at a given future date and time.")
);

await SchemaBuilder.CreateMapIndexTableAsync<ArchiveLaterPartIndex>(table => table
.Column<string>("ContentItemId")
.Column<DateTime>("ScheduledArchiveDateTimeUtc")
.Column<bool>("Published")
.Column<bool>("Latest")
await SchemaBuilder.CreateMapIndexTableAsync<ArchiveLaterPartIndex>(table =>
table.Column<string>("ContentItemId").Column<DateTime>("ScheduledArchiveDateTimeUtc").Column<bool>("Published").Column<bool>("Latest")
);

await SchemaBuilder.AlterIndexTableAsync<ArchiveLaterPartIndex>(table => table
.CreateIndex("IDX_ArchiveLaterPartIndex_DocumentId",
"Id",
"DocumentId",
"ContentItemId",
"ScheduledArchiveDateTimeUtc",
"Published",
"Latest")
await SchemaBuilder.AlterIndexTableAsync<ArchiveLaterPartIndex>(table =>
table.CreateIndex("IDX_ArchiveLaterPartIndex_DocumentId", "Id", "DocumentId", "ContentItemId", "ScheduledArchiveDateTimeUtc", "Published", "Latest")
);

return 1;
Original file line number Diff line number Diff line change
@@ -14,10 +14,7 @@

namespace OrchardCore.ArchiveLater.Services;

[BackgroundTask(
Title = "Content Items Archiver",
Schedule = "* * * * *",
Description = "Archives content items when their scheduled archive date time arrives.")]
[BackgroundTask(Title = "Content Items Archiver", Schedule = "* * * * *", Description = "Archives content items when their scheduled archive date time arrives.")]
public class ScheduledArchivingBackgroundTask : IBackgroundTask
{
private readonly ILogger _logger;
5 changes: 1 addition & 4 deletions src/OrchardCore.Modules/OrchardCore.ArchiveLater/Startup.cs
Original file line number Diff line number Diff line change
@@ -17,17 +17,14 @@ namespace OrchardCore.ArchiveLater;

public class Startup : StartupBase
{

public override void ConfigureServices(IServiceCollection services)
{
services.Configure<TemplateOptions>(o =>
{
o.MemberAccessStrategy.Register<ArchiveLaterPartViewModel>();
});

services
.AddContentPart<ArchiveLaterPart>()
.UseDisplayDriver<ArchiveLaterPartDisplayDriver>();
services.AddContentPart<ArchiveLaterPart>().UseDisplayDriver<ArchiveLaterPartDisplayDriver>();

services.AddDataMigration<Migrations>();

Original file line number Diff line number Diff line change
@@ -38,7 +38,8 @@ public AdminController(
IAuditTrailAdminListQueryService auditTrailAdminListQueryService,
IDisplayManager<AuditTrailEvent> displayManager,
IDisplayManager<AuditTrailIndexOptions> auditTrailOptionsDisplayManager,
IStringLocalizer<AdminController> stringLocalizer)
IStringLocalizer<AdminController> stringLocalizer
)
{
_pagerOptions = pagerOptions.Value;
_shapeFactory = shapeFactory;
@@ -51,17 +52,18 @@ public AdminController(
S = stringLocalizer;
}

public async Task<ActionResult> Index([ModelBinder(BinderType = typeof(AuditTrailFilterEngineModelBinder), Name = "q")] QueryFilterResult<AuditTrailEvent> queryFilterResult, PagerParameters pagerParameters, string correlationId = "")
public async Task<ActionResult> Index(
[ModelBinder(BinderType = typeof(AuditTrailFilterEngineModelBinder), Name = "q")] QueryFilterResult<AuditTrailEvent> queryFilterResult,
PagerParameters pagerParameters,
string correlationId = ""
)
{
if (!await _authorizationService.AuthorizeAsync(User, AuditTrailPermissions.ViewAuditTrail))
{
return Forbid();
}

var options = new AuditTrailIndexOptions
{
FilterResult = queryFilterResult
};
var options = new AuditTrailIndexOptions { FilterResult = queryFilterResult };

// This is used by Contents feature for routing so needs to be passed into the options.
if (!string.IsNullOrEmpty(correlationId))
@@ -93,9 +95,7 @@ public async Task<ActionResult> Index([ModelBinder(BinderType = typeof(AuditTrai

foreach (var auditTrailEvent in result.Events)
{
items.Add(
await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "SummaryAdmin")
);
items.Add(await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "SummaryAdmin"));
}

var startIndex = (pager.Page - 1) * pager.PageSize + 1;
@@ -106,13 +106,16 @@ await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAc

var header = await _auditTrailOptionsDisplayManager.BuildEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty);

var shapeViewModel = await _shapeFactory.CreateAsync<AuditTrailListViewModel>("AuditTrailAdminList", viewModel =>
{
viewModel.Events = items;
viewModel.Pager = pagerShape;
viewModel.Options = options;
viewModel.Header = header;
});
var shapeViewModel = await _shapeFactory.CreateAsync<AuditTrailListViewModel>(
"AuditTrailAdminList",
viewModel =>
{
viewModel.Events = items;
viewModel.Pager = pagerShape;
viewModel.Options = options;
viewModel.Header = header;
}
);

return View(shapeViewModel);
}
@@ -150,7 +153,6 @@ public async Task<ActionResult> Display(string auditTrailEventId)
return NotFound();
}


var shape = await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "DetailAdmin");

return View(new AuditTrailItemViewModel { Shape = shape });
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ public AuditTrailSettingsDisplayDriver(
IAuditTrailManager auditTrailManager,
IHttpContextAccessor httpContextAccessor,
IAuthorizationService authorizationService,
IServiceProvider serviceProvider)
IServiceProvider serviceProvider
)
{
_auditTrailManager = auditTrailManager;
_httpContextAccessor = httpContextAccessor;
@@ -39,43 +40,46 @@ public override async Task<IDisplayResult> EditAsync(AuditTrailSettings settings
return null;
}

return Initialize<AuditTrailSettingsViewModel>("AuditTrailSettings_Edit", model =>
{
var categories = _auditTrailManager.DescribeCategories();
return Initialize<AuditTrailSettingsViewModel>(
"AuditTrailSettings_Edit",
model =>
{
var categories = _auditTrailManager.DescribeCategories();

var settingsGroups = settings.Categories
.ToLookup(category => category.Events
.FirstOrDefault()?.Category ?? "");
var settingsGroups = settings.Categories.ToLookup(category => category.Events.FirstOrDefault()?.Category ?? "");

var categoriesViewModel = categories
.Select(category => new AuditTrailCategorySettingsViewModel()
{
Name = category.Name,
LocalizedName = category.LocalizedName(_serviceProvider),
Events = category.Events.Values
.Select(auditTrailEvent =>
var categoriesViewModel = categories
.Select(category => new AuditTrailCategorySettingsViewModel()
{
var settings = settingsGroups[auditTrailEvent.Category]
.FirstOrDefault()?.Events
.FirstOrDefault(settings => settings.Name == auditTrailEvent.Name);
Name = category.Name,
LocalizedName = category.LocalizedName(_serviceProvider),
Events = category
.Events.Values.Select(auditTrailEvent =>
{
var settings = settingsGroups[auditTrailEvent.Category]
.FirstOrDefault()
?.Events.FirstOrDefault(settings => settings.Name == auditTrailEvent.Name);

return new AuditTrailEventSettingsViewModel()
{
Name = auditTrailEvent.Name,
Category = auditTrailEvent.Category,
LocalizedName = auditTrailEvent.LocalizedName(_serviceProvider),
Description = auditTrailEvent.Description(_serviceProvider),
IsEnabled = auditTrailEvent.IsMandatory || (settings?.IsEnabled ?? auditTrailEvent.IsEnabledByDefault),
IsMandatory = auditTrailEvent.IsMandatory
};
return new AuditTrailEventSettingsViewModel()
{
Name = auditTrailEvent.Name,
Category = auditTrailEvent.Category,
LocalizedName = auditTrailEvent.LocalizedName(_serviceProvider),
Description = auditTrailEvent.Description(_serviceProvider),
IsEnabled = auditTrailEvent.IsMandatory || (settings?.IsEnabled ?? auditTrailEvent.IsEnabledByDefault),
IsMandatory = auditTrailEvent.IsMandatory
};
})
.ToArray()
})
.ToArray()
})
.ToArray();
.ToArray();

model.Categories = categoriesViewModel;
model.ClientIpAddressAllowed = settings.ClientIpAddressAllowed;
}).Location("Content:1#Events").OnGroup(AuditTrailSettingsGroup.Id);
model.Categories = categoriesViewModel;
model.ClientIpAddressAllowed = settings.ClientIpAddressAllowed;
}
)
.Location("Content:1#Events")
.OnGroup(AuditTrailSettingsGroup.Id);
}

public override async Task<IDisplayResult> UpdateAsync(AuditTrailSettings settings, BuildEditorContext context)
@@ -90,12 +94,12 @@ public override async Task<IDisplayResult> UpdateAsync(AuditTrailSettings settin
var model = new AuditTrailSettingsViewModel();
await context.Updater.TryUpdateModelAsync(model, Prefix);

settings.Categories = model.Categories
.Select(categorySettings => new AuditTrailCategorySettings()
settings.Categories = model
.Categories.Select(categorySettings => new AuditTrailCategorySettings()
{
Name = categorySettings.Name,
Events = categorySettings.Events
.Select(settings => new AuditTrailEventSettings()
Events = categorySettings
.Events.Select(settings => new AuditTrailEventSettings()
{
Name = settings.Name,
Category = settings.Category,
Original file line number Diff line number Diff line change
@@ -29,12 +29,17 @@ public override async Task<IDisplayResult> EditAsync(AuditTrailTrimmingSettings
return null;
}

return Initialize<AuditTrailTrimmingSettingsViewModel>("AuditTrailTrimmingSettings_Edit", model =>
{
model.RetentionDays = section.RetentionDays;
model.LastRunUtc = section.LastRunUtc;
model.Disabled = section.Disabled;
}).Location("Content:10#Trimming;0").OnGroup(AuditTrailSettingsGroup.Id);
return Initialize<AuditTrailTrimmingSettingsViewModel>(
"AuditTrailTrimmingSettings_Edit",
model =>
{
model.RetentionDays = section.RetentionDays;
model.LastRunUtc = section.LastRunUtc;
model.Disabled = section.Disabled;
}
)
.Location("Content:10#Trimming;0")
.OnGroup(AuditTrailSettingsGroup.Id);
}

public override async Task<IDisplayResult> UpdateAsync(AuditTrailTrimmingSettings section, BuildEditorContext context)
Loading