Skip to content

Commit

Permalink
Fix the QueriesDocument and the Queries UI (#16402)
Browse files Browse the repository at this point in the history
Co-authored-by: Zoltán Lehóczky <zoltan.lehoczky@lombiq.com>
Co-authored-by: Georg von Kries <gvk@creativbox.net>
  • Loading branch information
3 people authored Jul 19, 2024
1 parent d81512c commit 3623bdb
Show file tree
Hide file tree
Showing 86 changed files with 1,532 additions and 625 deletions.
7 changes: 7 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Email.Smtp", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "src\OrchardCore\OrchardCore.Rules.Core\OrchardCore.Rules.Core.csproj", "{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.Queries.Core", "src\OrchardCore\OrchardCore.Queries.Core\OrchardCore.Queries.Core.csproj", "{61B358F2-702C-40AA-9DF7-7121248FE6DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1383,6 +1385,10 @@ Global
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Release|Any CPU.Build.0 = Release|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1622,6 +1628,7 @@ Global
{C35AB37B-5A09-4896-BEEE-B126B7E7018A} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{E8A1097D-A65A-4B17-A3A2-F50D79552732} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using OrchardCore.Admin;
Expand All @@ -16,25 +17,26 @@
using OrchardCore.Navigation;
using OrchardCore.Queries.ViewModels;
using OrchardCore.Routing;
using YesSql.Services;

namespace OrchardCore.Queries.Controllers
{
[Admin("Queries/{action}/{id?}", "Queries{action}")]
public class AdminController : Controller
public sealed class AdminController : Controller
{
private const string _optionsSearch = "Options.Search";

private readonly IAuthorizationService _authorizationService;
private readonly PagerOptions _pagerOptions;
private readonly INotifier _notifier;
private readonly IServiceProvider _serviceProvider;
private readonly IQueryManager _queryManager;
private readonly IEnumerable<IQuerySource> _querySources;
private readonly IDisplayManager<Query> _displayManager;
private readonly IUpdateModelAccessor _updateModelAccessor;
private readonly IShapeFactory _shapeFactory;

protected readonly IStringLocalizer S;
protected readonly IHtmlLocalizer H;
internal readonly IStringLocalizer S;
internal readonly IHtmlLocalizer H;

public AdminController(
IDisplayManager<Query> displayManager,
Expand All @@ -45,14 +47,14 @@ public AdminController(
IHtmlLocalizer<AdminController> htmlLocalizer,
INotifier notifier,
IQueryManager queryManager,
IEnumerable<IQuerySource> querySources,
IServiceProvider serviceProvider,
IUpdateModelAccessor updateModelAccessor)
{
_displayManager = displayManager;
_authorizationService = authorizationService;
_pagerOptions = pagerOptions.Value;
_queryManager = queryManager;
_querySources = querySources;
_serviceProvider = serviceProvider;
_updateModelAccessor = updateModelAccessor;
_shapeFactory = shapeFactory;
_notifier = notifier;
Expand All @@ -69,19 +71,6 @@ public async Task<IActionResult> Index(ContentOptions options, PagerParameters p

var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize());

var queries = await _queryManager.ListQueriesAsync();
queries = queries.OrderBy(x => x.Name);

if (!string.IsNullOrWhiteSpace(options.Search))
{
queries = queries.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase));
}

var results = queries
.Skip(pager.GetStartIndex())
.Take(pager.PageSize)
.ToList();

// Maintain previous route data when generating page links.
var routeData = new RouteData();

Expand All @@ -90,15 +79,20 @@ public async Task<IActionResult> Index(ContentOptions options, PagerParameters p
routeData.Values.TryAdd(_optionsSearch, options.Search);
}

var result = await _queryManager.PageQueriesAsync(pager.Page, pager.PageSize, new QueryContext()
{
Name = options.Search,
});

var model = new QueriesIndexViewModel
{
Queries = [],
Options = options,
Pager = await _shapeFactory.PagerAsync(pager, queries.Count(), routeData),
QuerySourceNames = _querySources.Select(x => x.Name).ToList()
Pager = await _shapeFactory.PagerAsync(pager, result.Count, routeData),
QuerySourceNames = _serviceProvider.GetServices<IQuerySource>().Select(x => x.Name).ToArray(),
};

foreach (var query in results)
foreach (var query in result.Records)
{
model.Queries.Add(new QueryEntry
{
Expand All @@ -115,7 +109,8 @@ public async Task<IActionResult> Index(ContentOptions options, PagerParameters p
return View(model);
}

[HttpPost, ActionName(nameof(Index))]
[HttpPost]
[ActionName(nameof(Index))]
[FormValueRequired("submit.Filter")]
public ActionResult IndexFilterPOST(QueriesIndexViewModel model)
=> RedirectToAction(nameof(Index), new RouteValueDictionary
Expand All @@ -130,7 +125,7 @@ public async Task<IActionResult> Create(string id)
return Forbid();
}

var query = _querySources.FirstOrDefault(x => x.Name == id)?.Create();
var query = await _queryManager.NewAsync(id);

if (query == null)
{
Expand All @@ -139,39 +134,41 @@ public async Task<IActionResult> Create(string id)

var model = new QueriesCreateViewModel
{
Editor = await _displayManager.BuildEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""),
Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, true),
SourceName = id
};

return View(model);
}

[HttpPost, ActionName(nameof(Create))]
[HttpPost]
[ActionName(nameof(Create))]
public async Task<IActionResult> CreatePost(QueriesCreateViewModel model)
{
if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries))
{
return Forbid();
}

var query = _querySources.FirstOrDefault(x => x.Name == model.SourceName)?.Create();
var query = await _queryManager.NewAsync(model.SourceName);

if (query == null)
{
return NotFound();
}

var editor = await _displayManager.UpdateEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", "");
var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, true);

if (ModelState.IsValid)
{
await _queryManager.SaveQueryAsync(query.Name, query);
await _queryManager.SaveAsync(query);

await _notifier.SuccessAsync(H["Query created successfully."]);

return RedirectToAction(nameof(Index));
}

// If we got this far, something failed, redisplay form
// If we got this far, something failed, redisplay form.
model.Editor = editor;

return View(model);
Expand All @@ -193,43 +190,47 @@ public async Task<IActionResult> Edit(string id)

var model = new QueriesEditViewModel
{
SourceName = query.Source,
Name = query.Name,
Schema = query.Schema,
Editor = await _displayManager.BuildEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "")
Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, false)
};

return View(model);
}

[HttpPost, ActionName(nameof(Edit))]
[HttpPost]
[ActionName(nameof(Edit))]
public async Task<IActionResult> EditPost(QueriesEditViewModel model)
{
if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries))
{
return Forbid();
}

var query = await _queryManager.LoadQueryAsync(model.Name);
if (string.IsNullOrEmpty(model.Name))
{
return BadRequest();
}

var query = await _queryManager.GetQueryAsync(model.Name);

if (query == null)
{
return NotFound();
}

var editor = await _displayManager.UpdateEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: false, string.Empty, string.Empty);
var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, false);

if (ModelState.IsValid)
{
await _queryManager.SaveQueryAsync(model.Name, query);

await _queryManager.UpdateAsync(query);
await _notifier.SuccessAsync(H["Query updated successfully."]);

return RedirectToAction(nameof(Index));
}

model.Editor = editor;

// If we got this far, something failed, redisplay form
// If we got this far, something failed, redisplay form.
return View(model);
}

Expand All @@ -241,21 +242,18 @@ public async Task<IActionResult> Delete(string id)
return Forbid();
}

var query = await _queryManager.LoadQueryAsync(id);

if (query == null)
if (!await _queryManager.DeleteQueryAsync(id))
{
return NotFound();
}

await _queryManager.DeleteQueryAsync(id);

await _notifier.SuccessAsync(H["Query deleted successfully."]);

return RedirectToAction(nameof(Index));
}

[HttpPost, ActionName(nameof(Index))]
[HttpPost]
[ActionName(nameof(Index))]
[FormValueRequired("submit.BulkAction")]
public async Task<ActionResult> IndexPost(ContentOptions options, IEnumerable<string> itemIds)
{
Expand All @@ -266,21 +264,16 @@ public async Task<ActionResult> IndexPost(ContentOptions options, IEnumerable<st

if (itemIds?.Count() > 0)
{
var queriesList = await _queryManager.ListQueriesAsync();
var checkedContentItems = queriesList.Where(x => itemIds.Contains(x.Name));
switch (options.BulkAction)
{
case ContentsBulkAction.None:
break;
case ContentsBulkAction.Remove:
foreach (var item in checkedContentItems)
{
await _queryManager.DeleteQueryAsync(item.Name);
}
await _queryManager.DeleteQueryAsync(itemIds.ToArray());
await _notifier.SuccessAsync(H["Queries successfully removed."]);
break;
default:
throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action.");
return BadRequest();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ namespace OrchardCore.Queries.Controllers
{
[Route("api/queries")]
[ApiController]
[Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous]
public class QueryApiController : ControllerBase
[Authorize(AuthenticationSchemes = "Api")]
[IgnoreAntiforgeryToken]
[AllowAnonymous]
public sealed class QueryApiController : ControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IQueryManager _queryManager;
Expand Down Expand Up @@ -57,6 +59,7 @@ public async Task<IActionResult> Query(
: [];

var result = await _queryManager.ExecuteQueryAsync(query, queryParameters);

return new ObjectResult(result);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan
return;
}

if (!query.ResultsOfType<ContentItem>())
if (!query.CanReturnContentItems || !query.ReturnContentItems)
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using OrchardCore.ContentManagement;
using OrchardCore.Deployment;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.ModelBinding;
Expand All @@ -15,6 +14,7 @@ namespace OrchardCore.Queries.Deployment
public class QueryBasedContentDeploymentStepDriver : DisplayDriver<DeploymentStep, QueryBasedContentDeploymentStep>
{
private readonly IQueryManager _queryManager;

protected readonly IStringLocalizer S;

public QueryBasedContentDeploymentStepDriver(
Expand All @@ -36,21 +36,22 @@ public override IDisplayResult Display(QueryBasedContentDeploymentStep step)

public override IDisplayResult Edit(QueryBasedContentDeploymentStep step)
{
return Initialize<QueryBasedContentDeploymentStepViewModel>("QueryBasedContentDeploymentStep_Fields_Edit", model =>
return Initialize<QueryBasedContentDeploymentStepViewModel>("QueryBasedContentDeploymentStep_Fields_Edit", async model =>
{
model.QueryName = step.QueryName;
model.QueryParameters = step.QueryParameters;
model.ExportAsSetupRecipe = step.ExportAsSetupRecipe;
model.Queries = await _queryManager.ListQueriesAsync(true);
}).Location("Content");
}

public override async Task<IDisplayResult> UpdateAsync(QueryBasedContentDeploymentStep step, IUpdateModel updater)
{
var queryBasedContentViewModel = new QueryBasedContentDeploymentStepViewModel();
await updater.TryUpdateModelAsync(queryBasedContentViewModel, Prefix, viewModel => viewModel.QueryName, viewModel => viewModel.QueryParameters, viewModel => viewModel.ExportAsSetupRecipe);
var query = await _queryManager.LoadQueryAsync(queryBasedContentViewModel.QueryName);
var query = await _queryManager.GetQueryAsync(queryBasedContentViewModel.QueryName);

if (!query.ResultsOfType<ContentItem>())
if (!query.CanReturnContentItems || !query.ReturnContentItems)
{
updater.ModelState.AddModelError(Prefix, nameof(step.QueryName), S["Your Query is not returning content items."]);
}
Expand Down
Loading

0 comments on commit 3623bdb

Please sign in to comment.