diff --git a/zero.Backoffice/Filters/BackofficeFilterAttribute.cs b/zero.Backoffice/Controllers/Filters/BackofficeFilterAttribute.cs similarity index 69% rename from zero.Backoffice/Filters/BackofficeFilterAttribute.cs rename to zero.Backoffice/Controllers/Filters/BackofficeFilterAttribute.cs index ca7efd9a..a4ae8893 100644 --- a/zero.Backoffice/Filters/BackofficeFilterAttribute.cs +++ b/zero.Backoffice/Controllers/Filters/BackofficeFilterAttribute.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc.Filters; -namespace zero.Backoffice.Filters; +namespace zero.Backoffice.Controllers; public class BackofficeFilterAttribute : IActionFilter { @@ -11,11 +11,11 @@ public class BackofficeFilterAttribute : IActionFilter { Type type = context.Controller.GetType(); - if (typeof(BackofficeController).IsAssignableFrom(type)) + if (typeof(ZeroBackofficeController).IsAssignableFrom(type)) { if (context.HttpContext.Request.Query.TryGetValue(SCOPE_KEY, out var scope)) { - (context.Controller as BackofficeController).OnScopeChanged(scope.ToString()); + (context.Controller as ZeroBackofficeController).OnScopeChanged(scope.ToString()); } } } diff --git a/zero.Backoffice/Filters/CanEditAttribute.cs b/zero.Backoffice/Controllers/Filters/CanEditAttribute.cs similarity index 95% rename from zero.Backoffice/Filters/CanEditAttribute.cs rename to zero.Backoffice/Controllers/Filters/CanEditAttribute.cs index cb5d6ab8..df111032 100644 --- a/zero.Backoffice/Filters/CanEditAttribute.cs +++ b/zero.Backoffice/Controllers/Filters/CanEditAttribute.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace zero.Backoffice.Filters; +namespace zero.Backoffice.Controllers; public class CanEditAttribute : TypeFilterAttribute { diff --git a/zero.Backoffice/Controllers/Filters/DisableBrowserCacheFilterAttribute.cs b/zero.Backoffice/Controllers/Filters/DisableBrowserCacheFilterAttribute.cs new file mode 100644 index 00000000..367d4828 --- /dev/null +++ b/zero.Backoffice/Controllers/Filters/DisableBrowserCacheFilterAttribute.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Net.Http.Headers; + +namespace zero.Backoffice.Controllers; + +/// +/// Ensures that the request is not cached by the browser +/// +public class DisableBrowserCacheAttribute : ActionFilterAttribute +{ + public override void OnResultExecuting(ResultExecutingContext context) + { + base.OnResultExecuting(context); + + var httpResponse = context.HttpContext.Response; + + if (httpResponse.StatusCode != 200) return; + + httpResponse.GetTypedHeaders().CacheControl = + new CacheControlHeaderValue() + { + NoCache = true, + MaxAge = TimeSpan.Zero, + MustRevalidate = true, + NoStore = true + }; + + httpResponse.Headers[HeaderNames.LastModified] = DateTime.Now.ToString("R"); // Format RFC1123 + httpResponse.Headers[HeaderNames.Pragma] = "no-cache"; + httpResponse.Headers[HeaderNames.Expires] = new DateTime(1990, 1, 1, 0, 0, 0).ToString("R"); + } +} \ No newline at end of file diff --git a/zero.Backoffice/Filters/ModelStateValidationFilterAttribute.cs b/zero.Backoffice/Controllers/Filters/ModelStateValidationFilterAttribute.cs similarity index 91% rename from zero.Backoffice/Filters/ModelStateValidationFilterAttribute.cs rename to zero.Backoffice/Controllers/Filters/ModelStateValidationFilterAttribute.cs index c50589bc..d8788590 100644 --- a/zero.Backoffice/Filters/ModelStateValidationFilterAttribute.cs +++ b/zero.Backoffice/Controllers/Filters/ModelStateValidationFilterAttribute.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace zero.Backoffice.Filters; +namespace zero.Backoffice.Controllers; public class ModelStateValidationFilterAttribute : IActionFilter { diff --git a/zero.Backoffice/Filters/VerifyTokenAttribute.cs b/zero.Backoffice/Controllers/Filters/VerifyTokenAttribute.cs similarity index 97% rename from zero.Backoffice/Filters/VerifyTokenAttribute.cs rename to zero.Backoffice/Controllers/Filters/VerifyTokenAttribute.cs index f48d6055..ff18ac1c 100644 --- a/zero.Backoffice/Filters/VerifyTokenAttribute.cs +++ b/zero.Backoffice/Controllers/Filters/VerifyTokenAttribute.cs @@ -5,7 +5,7 @@ //using zero.Core.Api; //using zero.Web.Models; -//namespace zero.Web.Filters +//namespace zero.Web.Controllers //{ // public class VerifyTokenAttribute : TypeFilterAttribute // { diff --git a/zero.Backoffice/Filters/ZeroBackofficeAttribute.cs b/zero.Backoffice/Controllers/Filters/ZeroBackofficeAttribute.cs similarity index 97% rename from zero.Backoffice/Filters/ZeroBackofficeAttribute.cs rename to zero.Backoffice/Controllers/Filters/ZeroBackofficeAttribute.cs index 2aed6f38..a7e4e9bd 100644 --- a/zero.Backoffice/Filters/ZeroBackofficeAttribute.cs +++ b/zero.Backoffice/Controllers/Filters/ZeroBackofficeAttribute.cs @@ -6,7 +6,7 @@ //using zero.Core.Entities; //using zero.Web.Models; -//namespace zero.Web.Filters +//namespace zero.Web.Controllers //{ // public class ZeroBackofficeAttribute : TypeFilterAttribute // { diff --git a/zero.Backoffice/Controllers/ZeroBackofficeApiController.cs b/zero.Backoffice/Controllers/ZeroBackofficeApiController.cs new file mode 100644 index 00000000..f6e27b65 --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroBackofficeApiController.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Backoffice.Controllers; + +[ApiController] +//[ServiceFilter(typeof(ModelStateValidationFilterAttribute))] +//[ServiceFilter(typeof(BackofficeFilterAttribute))] +public abstract class ZeroBackofficeApiController : ZeroBackofficeController +{ + +} diff --git a/zero.Backoffice/Controllers/ZeroBackofficeController.cs b/zero.Backoffice/Controllers/ZeroBackofficeController.cs new file mode 100644 index 00000000..7b673474 --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroBackofficeController.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Backoffice.Controllers; + +[ZeroAuthorize] +[DisableBrowserCache] +public abstract class ZeroBackofficeController : ControllerBase +{ + /// + /// Create an edit model with associated metadata and permissions from a model + /// + protected EditModel GetEditModel(T model) + { + return GetEditModel>(new EditModel() { Entity = model }); + } + + + /// + /// Create an edit model with associated metadata and permissions from a model + /// + protected TWrapper GetEditModel(TWrapper editModel) where TWrapper : EditModel, new() + { + editModel.Meta = new() + { + Token = null, + IsShared = false + }; + + editModel.Permissions = new() + { + CanCreate = true, + CanEdit = true, + CanDelete = true + }; + + return editModel; + } +} diff --git a/zero.Backoffice/Controllers/ZeroBackofficeControllerExtensions.cs b/zero.Backoffice/Controllers/ZeroBackofficeControllerExtensions.cs new file mode 100644 index 00000000..948bef26 --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroBackofficeControllerExtensions.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; +using System.IO; + +namespace zero.Backoffice.Controllers; + +public static class ZeroBackofficeControllerExtensions +{ + /// + /// Transform entities to a preview list + /// + public static List TransformToPreviewModels(this ZeroBackofficeController controller, Dictionary items, Action transform = null) where T : ZeroIdEntity + { + List previews = new(); + + foreach (var item in items) + { + if (item.Value == null) + { + previews.Add(new PreviewModel() + { + HasError = true, + Icon = "fth-alert-circle color-red", + Id = item.Key, + Name = "@errors.preview.notfound", + Text = "@errors.preview.notfound_text" + }); + continue; + } + + PreviewModel model = new() { Id = item.Value.Id }; + + if (item.Value is ZeroEntity) + { + model.Name = (item.Value as ZeroEntity).Name; + } + + transform?.Invoke(item.Value, model); + previews.Add(model); + } + + return previews; + } + + + /// + /// Transform entities to a select list + /// + public static List TransformToSelectModels(this ZeroBackofficeController controller, IEnumerable enumerable, Action transform = null) where T : ZeroIdEntity + { + List items = new(); + + foreach (T item in enumerable) + { + SelectModel model = new() + { + Id = item.Id + }; + + if (item is ZeroEntity) + { + model.Name = (item as ZeroEntity).Name; + model.IsActive = (item as ZeroEntity).IsActive; + } + + transform?.Invoke(item, model); + + items.Add(model); + } + + return items; + } + + + /// + /// Provides a file stream for download in the browser + /// + public static IActionResult DownloadFile(this ZeroBackofficeController controller, Stream stream, string filename) + { + if (stream == null) + { + // TODO add success property + return error response + } + + if (filename.Contains("{date}")) + { + filename = filename.Replace("{date}", DateTimeOffset.Now.ToString("yyyy-MM-dd")); + } + + var provider = new FileExtensionContentTypeProvider(); + if (filename == null || !provider.TryGetContentType(Path.GetFileName(filename), out string contentType)) + { + contentType = "application/octet-stream"; + } + + controller.Response.Headers.Add("zero-filename", filename); + + return controller.File(stream, contentType, filename, true); + } + + + /// + /// Provides a file stream to the browser + /// + public static IActionResult File(this ZeroBackofficeController controller, FileStorage.FileResult file) + { + if (file == null) + { + return controller.NotFound(); + } + + FileStream stream = System.IO.File.OpenRead(file.Path); + return controller.File(stream, file.ContentType, file.Filename); + } +} diff --git a/zero.Backoffice/Controllers/ZeroBackofficeControllerModelConvention.cs b/zero.Backoffice/Controllers/ZeroBackofficeControllerModelConvention.cs new file mode 100644 index 00000000..285cd2c8 --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroBackofficeControllerModelConvention.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace zero.Backoffice.Controllers; + +public class ZeroBackofficeControllerModelConvention : IApplicationModelConvention +{ + readonly AttributeRouteModel RouteModel; + + readonly Type BaseClass = typeof(ZeroBackofficeController); + + + public ZeroBackofficeControllerModelConvention(string backofficePath) + { + RouteModel = new AttributeRouteModel(new RouteAttribute(backofficePath.TrimEnd('/') + "/api/[controller]")); + } + + + /// + /// Configure routing model for all backoffice controllers + /// + public void Apply(ApplicationModel application) + { + foreach (var controller in application.Controllers) + { + bool hasAttributeRouteModels = controller.Selectors.Any(selector => selector.AttributeRouteModel != null); + + if (controller.ControllerType.IsSubclassOf(BaseClass)) + { + controller.Selectors[0].AttributeRouteModel = RouteModel; + } + } + } +} \ No newline at end of file diff --git a/zero.Backoffice/Controllers/ZeroSetupController.cs b/zero.Backoffice/Controllers/ZeroSetupController.cs new file mode 100644 index 00000000..58d53c0e --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroSetupController.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using zero.Core.Api; +using zero.Setup; + +namespace zero.Backoffice.Endpoints; + +[ZeroAuthorize(false)] +public class ZeroSetupController : Controller +{ + ISetupApi Api; + IWebHostEnvironment Env; + IZeroOptions Options; + + + public ZeroSetupController(ISetupApi api, IWebHostEnvironment env, IZeroOptions options) + { + Api = api; + Env = env; + Options = options; + } + + + public IActionResult Index() + { + //if (!Options.ZeroVersion.IsNullOrEmpty()) + //{ + // return Redirect(Options.BackofficePath); + //} + + return View("/Views/Zero/Setup.cshtml"); + } + + + [HttpPost] + public async Task Install([FromBody] SetupModel model) + { + model.ContentRootPath = Env.ContentRootPath; + + EntityResult result = await Api.Install(model); + + if (result.IsSuccess) + { + return Json(result); + } + + object value = String.Join("\n\n", result.Errors.Select(error => error.Message + "\n(property: " + error.Property + ")")); + return StatusCode(500, value); + } +} \ No newline at end of file diff --git a/zero.Backoffice/Endpoints/ZeroVueController.cs b/zero.Backoffice/Controllers/ZeroVueController.cs similarity index 100% rename from zero.Backoffice/Endpoints/ZeroVueController.cs rename to zero.Backoffice/Controllers/ZeroVueController.cs diff --git a/zero.Backoffice/Endpoints/ZeroBackofficeCollectionController.cs b/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs similarity index 84% rename from zero.Backoffice/Endpoints/ZeroBackofficeCollectionController.cs rename to zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs index 722c0396..ff413319 100644 --- a/zero.Backoffice/Endpoints/ZeroBackofficeCollectionController.cs +++ b/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs @@ -9,7 +9,7 @@ using zero.Core.Entities; namespace zero.Backoffice; -public abstract class ZeroBackofficeCollectionController : ZeroBackofficeController +public abstract class _ZeroBackofficeCollectionController : ZeroBackofficeController where TEntity : ZeroIdEntity, new() where TCollection : ICollectionOperations { @@ -43,13 +43,13 @@ public abstract class ZeroBackofficeCollectionController : public virtual async Task> GetEmpty() => Edit(await Collection.Empty()); - public virtual async Task> GetByQuery([FromQuery] ListBackofficeQuery query) + public virtual async Task> GetByQuery([FromQuery] ListBackofficeQuery query) { return await Collection.Load(query); } - public virtual async Task> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery query) + public virtual async Task> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery query) { return null; // TODO //return await Collection.GetRevisions(id, query.Page, query.PageSize); diff --git a/zero.Backoffice/Endpoints/BackofficeCollectionController.cs b/zero.Backoffice/Endpoints/BackofficeCollectionController.cs deleted file mode 100644 index 75576eae..00000000 --- a/zero.Backoffice/Endpoints/BackofficeCollectionController.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Raven.Client.Documents.Indexes; -using Raven.Client.Documents.Linq; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using zero.Core.Collections; -using zero.Core.Entities; -using zero.Core.Extensions; -using zero.Web.Models; - - -namespace zero.Web.Controllers -{ - public abstract class BackofficeCollectionController : BackofficeController - where TEntity : ZeroEntity - where TCollection : ICollectionBase - { - protected TCollection Collection { get; private set; } - - [Obsolete] - protected Func, IRavenQueryable> DefaultQuery { get; set; } - - protected Action PreviewTransform { get; set; } - - protected Action PickerTransform { get; set; } - - - public BackofficeCollectionController(TCollection collection) - { - Collection = collection; - } - - public override void OnScopeChanged(string scope) - { - Collection.ApplyScope(scope); - } - - - public virtual async Task> GetById([FromQuery] string id, [FromQuery] string changeVector = null) => Edit(changeVector.IsNullOrEmpty() ? await Collection.GetById(id) : await Collection.GetRevision(changeVector)); - - - public virtual async Task> GetByIds([FromQuery] string[] ids) => await Collection.GetByIds(ids); - - - public virtual EditModel GetEmpty([FromServices] TEntity blueprint) => Edit(blueprint); - - - public virtual async Task> GetByQuery([FromQuery] ListBackofficeQuery query) - { - return await Collection.GetByQuery(query); - } - - - public virtual async Task> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery query) - { - return await Collection.GetRevisions(id, query.Page, query.PageSize); - } - - - public virtual async Task> GetForPicker() => await SelectList(Collection.Stream(), PickerTransform); - - - public virtual async Task> GetPreviews([FromQuery] List ids) => Previews(await Collection.GetByIds(ids.ToArray()), PreviewTransform); - - - [HttpPost] - public virtual async Task> Save([FromBody] TEntity model) => await Collection.Save(model); - - - [HttpDelete] - public virtual async Task> Delete([FromQuery] string id) => await Collection.DeleteById(id); - } -} diff --git a/zero.Backoffice/Endpoints/BackofficeController.cs b/zero.Backoffice/Endpoints/BackofficeController.cs deleted file mode 100644 index 77c780fd..00000000 --- a/zero.Backoffice/Endpoints/BackofficeController.cs +++ /dev/null @@ -1,255 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; -using zero.Core.Identity; -using zero.Core.Options; -using zero.Web.Filters; -using zero.Web.Models; - -namespace zero.Web.Controllers -{ - [ZeroAuthorize] - [ServiceFilter(typeof(ModelStateValidationFilterAttribute))] - [ApiController] - [Route("getsreplaced/[controller]/[action]")] - [ServiceFilter(typeof(BackofficeFilterAttribute))] - public abstract class BackofficeController : ControllerBase - { - IZeroOptions _options; - - public bool IsCoreDatabase { get; protected set; } - - protected IZeroOptions Options => _options ?? (_options = HttpContext?.RequestServices?.GetService()); - - - /// - /// Is execuated when the scope changes. - /// The scope is evaluated by the BackofficeFilterAttribute. - /// - public virtual void OnScopeChanged(string scope) { } - - - /// - /// Creates an edit model with appropriate options and permissions - /// - public EditModel Edit(T data, bool typed = true, Action> transform = null) where T : ZeroIdEntity - { - Type type = typeof(T); - - //ControllerContext.ActionDescriptor.FilterDescriptors[0]. - - if (data == null) - { - return null; - } - - EditModel model = new EditModel(); - model.Entity = data; - model.Meta.Token = null; // Token.Get(data); - //model.Meta.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx - //model.Meta.CanBeShared = canBeShared; - model.Meta.CanCreate = true; - //model.Meta.CanCreateShared = canBeShared; - model.Meta.CanEdit = true; - model.Meta.CanDelete = true; - model.Meta.IsShared = IsCoreDatabase; - - transform?.Invoke(model); - - return model; - } - - - /// - /// Creates an edit model with appropriate options and permissions - /// - public TWrapper Edit(TWrapper data, bool typed = true, Action> transform = null) - where T : ZeroIdEntity - where TWrapper : EditModel, new() - { - Type type = typeof(T); - - //ControllerContext.ActionDescriptor.FilterDescriptors[0]. - - data.Meta.Token = null; // Token.Get(data.Entity); - //data.Meta.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx - //data.Meta.CanBeShared = canBeShared; - data.Meta.CanCreate = true; - //data.Meta.CanCreateShared = canBeShared; - data.Meta.CanEdit = true; - data.Meta.CanDelete = true; - data.Meta.IsShared = IsCoreDatabase; - - transform?.Invoke(data); - - return data; - } - - - public IList Previews(Dictionary items, Func transform) - { - IList previews = new List(); - - foreach (var item in items) - { - bool exists = item.Value != null; - - if (!exists) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - previews.Add(transform(item.Value)); - } - } - - return previews; - } - - - public IList Previews(Dictionary items, Action transform = null) where T : ZeroIdEntity - { - IList previews = new List(); - - foreach (var item in items) - { - if (item.Value == null) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - PreviewModel model = new() - { - Id = item.Value.Id - }; - - if (item.Value is ZeroEntity) - { - model.Name = (item.Value as ZeroEntity).Name; - } - - transform?.Invoke(item.Value, model); - - previews.Add(model); - } - } - - return previews; - } - - - - public async Task> SelectList(IAsyncEnumerable enumerable, Action transform = null) where T : ZeroIdEntity - { - List items = new List(); - - await foreach (T item in enumerable) - { - SelectModel model = new() - { - Id = item.Id - }; - - if (item is ZeroEntity) - { - model.Name = (item as ZeroEntity).Name; - model.IsActive = (item as ZeroEntity).IsActive; - } - - transform?.Invoke(item, model); - - items.Add(model); - } - - return items; - } - - - - public async Task> Previews(Dictionary items, Func> transform) - { - IList previews = new List(); - - foreach (var item in items) - { - bool exists = item.Value != null; - - if (!exists) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - previews.Add(await transform(item.Value)); - } - } - - return previews; - } - - - protected IActionResult Download(Stream stream, string filename) - { - if (stream == null) - { - // TODO add success property + return error response - } - - if (filename.Contains("{date}")) - { - filename = filename.Replace("{date}", DateTimeOffset.Now.ToString("yyyy-MM-dd")); - } - - var provider = new FileExtensionContentTypeProvider(); - if (filename == null || !provider.TryGetContentType(Path.GetFileName(filename), out string contentType)) - { - contentType = "application/octet-stream"; - } - - Response.Headers.Add("zero-filename", filename); - - return base.File(stream, contentType, filename, true); - } - - - protected IActionResult File(Core.Entities.FileResult file) - { - if (file == null) - { - return NotFound(); - } - - FileStream stream = System.IO.File.OpenRead(file.Path); - return File(stream, file.ContentType, file.Filename); - } - } -} diff --git a/zero.Backoffice/Endpoints/ZeroBackofficeController.cs b/zero.Backoffice/Endpoints/ZeroBackofficeController.cs deleted file mode 100644 index b1eedd4c..00000000 --- a/zero.Backoffice/Endpoints/ZeroBackofficeController.cs +++ /dev/null @@ -1,252 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; -using zero.Core.Identity; -using zero.Core.Options; - -namespace zero.Backoffice; - -[ZeroAuthorize] -//[ServiceFilter(typeof(ModelStateValidationFilterAttribute))] -[ApiController] -[Route("getsreplaced/[controller]/[action]")] -//[ServiceFilter(typeof(BackofficeFilterAttribute))] -public abstract class ZeroBackofficeController : ControllerBase -{ - IZeroOptions _options; - - public bool IsCoreDatabase { get; protected set; } - - protected IZeroOptions Options => _options ?? (_options = HttpContext?.RequestServices?.GetService()); - - - /// - /// Is execuated when the scope changes. - /// The scope is evaluated by the BackofficeFilterAttribute. - /// - public virtual void OnScopeChanged(string scope) { } - - - /// - /// Creates an edit model with appropriate options and permissions - /// - public EditModel Edit(T data, bool typed = true, Action> transform = null) where T : ZeroIdEntity - { - Type type = typeof(T); - - //ControllerContext.ActionDescriptor.FilterDescriptors[0]. - - if (data == null) - { - return null; - } - - EditModel model = new EditModel(); - model.Entity = data; - model.Meta.Token = null; // Token.Get(data); - //model.Meta.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx - //model.Meta.CanBeShared = canBeShared; - model.Meta.CanCreate = true; - //model.Meta.CanCreateShared = canBeShared; - model.Meta.CanEdit = true; - model.Meta.CanDelete = true; - model.Meta.IsShared = IsCoreDatabase; - - transform?.Invoke(model); - - return model; - } - - - /// - /// Creates an edit model with appropriate options and permissions - /// - public TWrapper Edit(TWrapper data, bool typed = true, Action> transform = null) - where T : ZeroIdEntity - where TWrapper : EditModel, new() - { - Type type = typeof(T); - - //ControllerContext.ActionDescriptor.FilterDescriptors[0]. - - data.Meta.Token = null; // Token.Get(data.Entity); - //data.Meta.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx - //data.Meta.CanBeShared = canBeShared; - data.Meta.CanCreate = true; - //data.Meta.CanCreateShared = canBeShared; - data.Meta.CanEdit = true; - data.Meta.CanDelete = true; - data.Meta.IsShared = IsCoreDatabase; - - transform?.Invoke(data); - - return data; - } - - - public IList Previews(Dictionary items, Func transform) - { - IList previews = new List(); - - foreach (var item in items) - { - bool exists = item.Value != null; - - if (!exists) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - previews.Add(transform(item.Value)); - } - } - - return previews; - } - - - public IList Previews(Dictionary items, Action transform = null) where T : ZeroIdEntity - { - IList previews = new List(); - - foreach (var item in items) - { - if (item.Value == null) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - PreviewModel model = new() - { - Id = item.Value.Id - }; - - if (item.Value is ZeroEntity) - { - model.Name = (item.Value as ZeroEntity).Name; - } - - transform?.Invoke(item.Value, model); - - previews.Add(model); - } - } - - return previews; - } - - - - public async Task> SelectList(IAsyncEnumerable enumerable, Action transform = null) where T : ZeroIdEntity - { - List items = new List(); - - await foreach (T item in enumerable) - { - SelectModel model = new() - { - Id = item.Id - }; - - if (item is ZeroEntity) - { - model.Name = (item as ZeroEntity).Name; - model.IsActive = (item as ZeroEntity).IsActive; - } - - transform?.Invoke(item, model); - - items.Add(model); - } - - return items; - } - - - - public async Task> Previews(Dictionary items, Func> transform) - { - IList previews = new List(); - - foreach (var item in items) - { - bool exists = item.Value != null; - - if (!exists) - { - previews.Add(new PreviewModel() - { - HasError = true, - Icon = "fth-alert-circle color-red", - Id = item.Key, - Name = "@errors.preview.notfound", - Text = "@errors.preview.notfound_text" - }); - } - else - { - previews.Add(await transform(item.Value)); - } - } - - return previews; - } - - - protected IActionResult Download(Stream stream, string filename) - { - if (stream == null) - { - // TODO add success property + return error response - } - - if (filename.Contains("{date}")) - { - filename = filename.Replace("{date}", DateTimeOffset.Now.ToString("yyyy-MM-dd")); - } - - var provider = new FileExtensionContentTypeProvider(); - if (filename == null || !provider.TryGetContentType(Path.GetFileName(filename), out string contentType)) - { - contentType = "application/octet-stream"; - } - - Response.Headers.Add("zero-filename", filename); - - return base.File(stream, contentType, filename, true); - } - - - protected IActionResult File(Core.Entities.FileResult file) - { - if (file == null) - { - return NotFound(); - } - - FileStream stream = System.IO.File.OpenRead(file.Path); - return File(stream, file.ContentType, file.Filename); - } -} \ No newline at end of file diff --git a/zero.Backoffice/Endpoints/ZeroBackofficeControllerModelConvention.cs b/zero.Backoffice/Endpoints/ZeroBackofficeControllerModelConvention.cs deleted file mode 100644 index 39f6bfb4..00000000 --- a/zero.Backoffice/Endpoints/ZeroBackofficeControllerModelConvention.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ApplicationModels; -using System; - -namespace zero.Backoffice; - -public class ZeroBackofficeControllerModelConvention : IControllerModelConvention -{ - readonly AttributeRouteModel RouteModel; - - readonly Type BaseClass = typeof(ZeroBackofficeController); - - - public ZeroBackofficeControllerModelConvention(string backofficePath) - { - RouteModel = new AttributeRouteModel(new RouteAttribute(backofficePath + "/api/[controller]/[action]")); - } - - - public void Apply(ControllerModel controller) - { - if (!controller.ControllerType.IsSubclassOf(BaseClass)) - { - return; - } - - foreach (var selector in controller.Selectors) - { - selector.AttributeRouteModel = RouteModel; - } - } -} \ No newline at end of file diff --git a/zero.Backoffice/Endpoints/ZeroSetupController.cs b/zero.Backoffice/Endpoints/ZeroSetupController.cs deleted file mode 100644 index 126ebe25..00000000 --- a/zero.Backoffice/Endpoints/ZeroSetupController.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; -using zero.Core.Entities.Setup; -using zero.Core.Extensions; -using zero.Core.Identity; -using zero.Core.Options; - -namespace zero.Web.Setup -{ - [ZeroAuthorize(false)] - public class ZeroSetupController : Controller - { - ISetupApi Api; - IWebHostEnvironment Env; - IZeroOptions Options; - - - public ZeroSetupController(ISetupApi api, IWebHostEnvironment env, IZeroOptions options) - { - Api = api; - Env = env; - Options = options; - } - - - public IActionResult Index() - { - //if (!Options.ZeroVersion.IsNullOrEmpty()) - //{ - // return Redirect(Options.BackofficePath); - //} - - return View("/Views/Zero/Setup.cshtml"); - } - - - [HttpPost] - public async Task Install([FromBody] SetupModel model) - { - model.ContentRootPath = Env.ContentRootPath; - - EntityResult result = await Api.Install(model); - - if (result.IsSuccess) - { - return Json(result); - } - - object value = String.Join("\n\n", result.Errors.Select(error => error.Message + "\n(property: " + error.Property + ")")); - return StatusCode(500, value); - } - } -} diff --git a/zero.Backoffice/Models/EditModel.cs b/zero.Backoffice/Models/EditModel.cs index e19a46a3..0af0f978 100644 --- a/zero.Backoffice/Models/EditModel.cs +++ b/zero.Backoffice/Models/EditModel.cs @@ -13,10 +13,38 @@ public class EditModel /// Meta data /// public EditMetaModel Meta { get; set; } = new(); + + /// + /// Permissions for this entity + /// + public EditPermissionModel Permissions { get; set; } = new(); } public class EditMetaModel +{ + /// + /// Wehther this entity is application aware + /// + public bool IsAppAware { get; set; } + + /// + /// Whether this entity can be shared across applications (only for IsAppAware=true) + /// + public bool CanBeShared { get; set; } + + public bool IsShared { get; set; } + + /// + /// The change token maps to a database entity which holds ID and collection of the model to edit + /// If these values do not match the entity on save it is rejected + /// // TODO expiration expiry session.Advanced.GetMetadataFor(user)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddMinutes(60); + /// + public string Token { get; set; } +} + + +public class EditPermissionModel { /// /// Whether an entity of this type can be created @@ -37,23 +65,4 @@ public class EditMetaModel /// Whether this entity can be deleted /// public bool CanDelete { get; set; } - - /// - /// Wehther this entity is application aware - /// - public bool IsAppAware { get; set; } - - /// - /// Whether this entity can be shared across applications (only for IsAppAware=true) - /// - public bool CanBeShared { get; set; } - - public bool IsShared { get; set; } - - /// - /// The change token maps to a database entity which holds ID and collection of the model to edit - /// If these values do not match the entity on save it is rejected - /// // TODO expiration expiry session.Advanced.GetMetadataFor(user)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddMinutes(60); - /// - public string Token { get; set; } -} +} \ No newline at end of file diff --git a/zero.Backoffice/Models/PreviewModel.cs b/zero.Backoffice/Models/PreviewModel.cs new file mode 100644 index 00000000..5bdb4d8c --- /dev/null +++ b/zero.Backoffice/Models/PreviewModel.cs @@ -0,0 +1,14 @@ +namespace zero.Backoffice.Models; + +public class PreviewModel +{ + public string Id { get; set; } + + public string Icon { get; set; } + + public string Text { get; set; } + + public string Name { get; set; } + + public bool HasError { get; set; } +} \ No newline at end of file diff --git a/zero.Backoffice/Models/TreeItem.cs b/zero.Backoffice/Models/TreeItem.cs index da0c624b..d82c7027 100644 --- a/zero.Backoffice/Models/TreeItem.cs +++ b/zero.Backoffice/Models/TreeItem.cs @@ -74,29 +74,4 @@ public class TreeItem /// Output an additional count value. /// public int? CountOutput { get; set; } -} - - -/// -/// The modifier displays a small icon (with hover text) next to the main item icon -/// -public class TreeItemModifier -{ - /// - /// Name of the modifier - /// - public string Name { get; set; } - - /// - /// Icon to display - /// - public string Icon { get; set; } - - public TreeItemModifier() { } - - public TreeItemModifier(string name, string icon) - { - Name = name; - Icon = icon; - } -} +} \ No newline at end of file diff --git a/zero.Backoffice/Models/TreeItemModifier.cs b/zero.Backoffice/Models/TreeItemModifier.cs new file mode 100644 index 00000000..90b0afec --- /dev/null +++ b/zero.Backoffice/Models/TreeItemModifier.cs @@ -0,0 +1,25 @@ +namespace zero.Backoffice.Models; + +/// +/// The modifier displays a small icon (with hover text) next to the main item icon +/// +public class TreeItemModifier +{ + /// + /// Name of the modifier + /// + public string Name { get; set; } + + /// + /// Icon to display + /// + public string Icon { get; set; } + + public TreeItemModifier() { } + + public TreeItemModifier(string name, string icon) + { + Name = name; + Icon = icon; + } +} diff --git a/zero.Backoffice/Modules/Countries/CountriesController.cs b/zero.Backoffice/Modules/Countries/CountriesController.cs new file mode 100644 index 00000000..9b4bd176 --- /dev/null +++ b/zero.Backoffice/Modules/Countries/CountriesController.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Backoffice.Modules; + +/// +/// | GET /zero/api/countries/empty +/// | GET /zero/api/countries/{id} +/// | GET /zero/api/countries/{id}/revisions +/// | GET /zero/api/countries[type,query,filter,...] +/// | POST /zero/api/countries +/// | PUT /zero/api/countries/{id} +/// | DELETE /zero/api/countries/{id} +/// +public class CountriesController : ZeroBackofficeApiController +{ + protected ICountriesCollection Collection { get; set; } + + public CountriesController(ICountriesCollection collection) + { + Collection = collection; + } + + + [HttpGet("empty")] + public virtual async Task New() + { + return await Collection.Empty(); + } + + [HttpGet("{id}")] + public virtual async Task Get(string id, string changeVector = null) + { + return await Collection.Load(id, changeVector); + } + + [HttpGet] + public virtual async Task> Get(ListBackofficeQuery query) + { + return await Collection.Load(query.Page, query.PageSize, q => q.OrderByDescending(x => x.CreatedDate)); + } + + //[HttpGet("{id}/revisions")] + //public virtual async Task>> GetRevisions(string id) + //{ + // return await Collection.GetRevisions(id); + //} + + + [HttpPost] + public virtual async Task> Create(Country country) + { + return await Collection.Create(country); + } + + [HttpPut("{id}")] + public virtual async Task> Update(string id, Country country) + { + return await Collection.Update(country); + } + + [HttpDelete("{id}")] + public virtual async Task> Delete(string id) + { + return await Collection.Delete(id); + } +} \ No newline at end of file diff --git a/zero.Backoffice/Modules/Countries/CountriesEndpoint.cs b/zero.Backoffice/Modules/Countries/CountriesEndpoint.cs deleted file mode 100644 index 3cbaa843..00000000 --- a/zero.Backoffice/Modules/Countries/CountriesEndpoint.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Raven.Client.Documents; -using Raven.Client.Documents.Linq; - -namespace zero.Backoffice.Modules; - -[ZeroAuthorize(Permissions.Settings.Countries, PermissionsValue.Read)] -public class CountriesController : ZeroBackofficeCollectionController -{ - public CountriesController(ICountriesCollection collection) : base(collection) - { - PreviewTransform = (item, model) => model.Icon = "flag-" + item.Code.ToLowerInvariant(); - } - - public override Task> GetByQuery([FromQuery] ListBackofficeQuery query) - { - query.OrderQuery = q => q.OrderByDescending(x => x.IsPreferred).ThenBy(x => x.Name); - return Collection.Load(query); - } -} \ No newline at end of file diff --git a/zero.Backoffice/Modules/Languages/LanguagesController.cs b/zero.Backoffice/Modules/Languages/LanguagesController.cs index d924e82a..b61b656e 100644 --- a/zero.Backoffice/Modules/Languages/LanguagesController.cs +++ b/zero.Backoffice/Modules/Languages/LanguagesController.cs @@ -31,7 +31,7 @@ namespace zero.Web.Controllers } - public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) + public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) { query.OrderQuery = q => q.OrderByDescending(x => x.CreatedDate); return await Collection.Load(query); diff --git a/zero.Backoffice/Modules/Mails/MailTemplatesController.cs b/zero.Backoffice/Modules/Mails/MailTemplatesController.cs index dd9cccdb..218ba21b 100644 --- a/zero.Backoffice/Modules/Mails/MailTemplatesController.cs +++ b/zero.Backoffice/Modules/Mails/MailTemplatesController.cs @@ -15,7 +15,7 @@ namespace zero.Web.Controllers PreviewTransform = (item, model) => model.Icon = "fth-mail"; } - public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) + public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) { query.SearchFor(entity => entity.Name, entity => entity.Key, entity => entity.Subject); return await Collection.Load(query); diff --git a/zero.Backoffice/Modules/Search/BackofficeSearchService.cs b/zero.Backoffice/Modules/Search/BackofficeSearchService.cs index 3f62f376..191757ca 100644 --- a/zero.Backoffice/Modules/Search/BackofficeSearchService.cs +++ b/zero.Backoffice/Modules/Search/BackofficeSearchService.cs @@ -18,7 +18,7 @@ public class BackofficeSearchService : IBackofficeSearchService } - public async Task> Query(string searchTerm) + public async Task> Query(string searchTerm) { string[] searchParts = searchTerm.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(x => { @@ -57,7 +57,7 @@ public class BackofficeSearchService : IBackofficeSearchService items.Add(searchResult); } - return new ListResult(items, stats.TotalResults, 1, 20); + return new Paged(items, stats.TotalResults, 1, 20); } @@ -69,5 +69,5 @@ public class BackofficeSearchService : IBackofficeSearchService public interface IBackofficeSearchService { - Task> Query(string searchTerm); + Task> Query(string searchTerm); } \ No newline at end of file diff --git a/zero.Backoffice/Modules/Search/SearchEndpoint.cs b/zero.Backoffice/Modules/Search/SearchEndpoint.cs index ad3800e3..ce3351c4 100644 --- a/zero.Backoffice/Modules/Search/SearchEndpoint.cs +++ b/zero.Backoffice/Modules/Search/SearchEndpoint.cs @@ -12,5 +12,5 @@ public class SearchController : BackofficeController SearchService = searchService; } - public async Task> Query([FromQuery] string query) => await SearchService.Query(query); + public async Task> Query([FromQuery] string query) => await SearchService.Query(query); } \ No newline at end of file diff --git a/zero.Backoffice/Modules/Translations/TranslationsController.cs b/zero.Backoffice/Modules/Translations/TranslationsController.cs index d284ebcb..c9ac78a4 100644 --- a/zero.Backoffice/Modules/Translations/TranslationsController.cs +++ b/zero.Backoffice/Modules/Translations/TranslationsController.cs @@ -14,7 +14,7 @@ namespace zero.Web.Controllers { public TranslationsController(ITranslationsCollection collection) : base(collection) { } - public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) + public override async Task> GetByQuery([FromQuery] ListBackofficeQuery query) { query.SearchFor(entity => entity.Key, entity => entity.Value); query.OrderQuery = q => q.OrderByDescending(x => x.CreatedDate); diff --git a/zero.Backoffice/Usings.cs b/zero.Backoffice/Usings.cs index d215cdf0..3cf5e6e2 100644 --- a/zero.Backoffice/Usings.cs +++ b/zero.Backoffice/Usings.cs @@ -1,29 +1,27 @@  global using System; -global using System.Collections; global using System.Collections.Generic; global using System.Linq; global using System.Threading; global using System.Threading.Tasks; +global using zero.Applications; global using zero.Architecture; +global using zero.Collections; global using zero.Configuration; +global using zero.Context; global using zero.Extensions; global using zero.Identity; -global using zero.Pages; -global using zero.Validation; -global using zero.Utils; -global using zero.Persistence; global using zero.Localization; -global using zero.Routing; -global using zero.Context; -global using zero.Communication; -global using zero.Rendering; global using zero.Media; -global using zero.Applications; +global using zero.Pages; +global using zero.Persistence; +global using zero.Routing; +global using zero.Utils; +global using zero.Models; -global using zero.Backoffice; +global using zero.Backoffice.Configuration; +global using zero.Backoffice.Controllers; global using zero.Backoffice.Models; global using zero.Backoffice.Modules; -global using zero.Backoffice.Configuration; global using zero.Backoffice.UIComposition; \ No newline at end of file diff --git a/zero.Backoffice/ZeroBackofficeMvcOptions.cs b/zero.Backoffice/ZeroBackofficeMvcOptions.cs index d310df82..c37f2aea 100644 --- a/zero.Backoffice/ZeroBackofficeMvcOptions.cs +++ b/zero.Backoffice/ZeroBackofficeMvcOptions.cs @@ -14,6 +14,7 @@ class ZeroBackofficeMvcOptions : IConfigureOptions public void Configure(MvcOptions options) { - //options.Conventions.Add(new ZeroBackofficeControllerModelConvention(Options.BackofficePath)); + string backofficePath = Options.For().Path; + options.Conventions.Add(new ZeroBackofficeControllerModelConvention(backofficePath)); } } \ No newline at end of file diff --git a/zero.Backoffice/_legacy/Controllers/ApplicationsController.cs b/zero.Backoffice/_legacy/Controllers/ApplicationsController.cs index 79faa72d..1bdf0ab1 100644 --- a/zero.Backoffice/_legacy/Controllers/ApplicationsController.cs +++ b/zero.Backoffice/_legacy/Controllers/ApplicationsController.cs @@ -29,7 +29,7 @@ namespace zero.Web.Controllers public async Task> GetAll() => await Api.GetAll(); - public async Task> GetByQuery([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); + public async Task> GetByQuery([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); public IReadOnlyCollection GetAllFeatures() => Options.Features.GetAllItems(); diff --git a/zero.Backoffice/_legacy/Controllers/IntegrationsController.cs b/zero.Backoffice/_legacy/Controllers/IntegrationsController.cs index f81e8394..3b580cdc 100644 --- a/zero.Backoffice/_legacy/Controllers/IntegrationsController.cs +++ b/zero.Backoffice/_legacy/Controllers/IntegrationsController.cs @@ -25,7 +25,7 @@ namespace zero.Web.Controllers public async Task> GetByAlias([FromQuery] string alias) => Edit(await Collection.GetByAlias(alias)); - public async Task> Load([FromQuery] ListQuery query) => await Collection.Load(query); + public async Task> Load([FromQuery] ListQuery query) => await Collection.Load(query); public async Task> GetTypes() => await Collection.GetTypesWithStatus(); diff --git a/zero.Backoffice/_legacy/Controllers/MediaController.cs b/zero.Backoffice/_legacy/Controllers/MediaController.cs index 34266cb8..f70f198e 100644 --- a/zero.Backoffice/_legacy/Controllers/MediaController.cs +++ b/zero.Backoffice/_legacy/Controllers/MediaController.cs @@ -27,7 +27,7 @@ namespace zero.Web.Controllers } - public async Task> GetListByQuery([FromQuery] MediaListItemQuery query) + public async Task> GetListByQuery([FromQuery] MediaListItemQuery query) { query.IncludeInactive = true; return await Collection.Load(query); @@ -47,7 +47,7 @@ namespace zero.Web.Controllers public async Task GetAll([FromQuery] MediaListQuery query) { query.IncludeInactive = true; - ListResult items = (await Collection.Load(query)).MapTo(x => new MediaListModel() + Paged items = (await Collection.Load(query)).MapTo(x => new MediaListModel() { Id = x.Id, IsFolder = false, diff --git a/zero.Backoffice/_legacy/Controllers/RecycleBinController.cs b/zero.Backoffice/_legacy/Controllers/RecycleBinController.cs index 3c44e316..a853becb 100644 --- a/zero.Backoffice/_legacy/Controllers/RecycleBinController.cs +++ b/zero.Backoffice/_legacy/Controllers/RecycleBinController.cs @@ -15,7 +15,7 @@ namespace zero.Web.Controllers } - public async Task> GetByQuery([FromQuery] RecycleBinListQuery query) + public async Task> GetByQuery([FromQuery] RecycleBinListQuery query) { query.IncludeInactive = true; return await Api.GetByQuery(query); diff --git a/zero.Backoffice/_legacy/Controllers/UsersController.cs b/zero.Backoffice/_legacy/Controllers/UsersController.cs index a603ee83..6cef3e3f 100644 --- a/zero.Backoffice/_legacy/Controllers/UsersController.cs +++ b/zero.Backoffice/_legacy/Controllers/UsersController.cs @@ -29,7 +29,7 @@ namespace zero.Web.Controllers public async Task> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id)); - public async Task> GetAll([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); + public async Task> GetAll([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); public IList GetAllPermissions() => PermissionsApi.GetAll(); diff --git a/zero.Backoffice/_legacy/Models/MediaListResultModel.cs b/zero.Backoffice/_legacy/Models/MediaListResultModel.cs index 2aebe262..aefe58ba 100644 --- a/zero.Backoffice/_legacy/Models/MediaListResultModel.cs +++ b/zero.Backoffice/_legacy/Models/MediaListResultModel.cs @@ -3,7 +3,7 @@ using zero.Core.Entities; namespace zero.Web.Models { - public class MediaListResultModel : ListResult + public class MediaListResultModel : Paged { public IEnumerable Folders { get; private set; } @@ -11,12 +11,12 @@ namespace zero.Web.Models public MediaFolder Folder { get; private set; } - public MediaListResultModel(ListResult items, IEnumerable folders) : base(items.Items, items.TotalItems, items.Page, items.PageSize) + public MediaListResultModel(Paged items, IEnumerable folders) : base(items.Items, items.TotalItems, items.Page, items.PageSize) { Folders = folders; } - public MediaListResultModel(ListResult items, IEnumerable folders, MediaFolder currentFolder, IEnumerable hierarchy) : this(items, folders) + public MediaListResultModel(Paged items, IEnumerable folders, MediaFolder currentFolder, IEnumerable hierarchy) : this(items, folders) { Folder = currentFolder; FolderHierarchy = hierarchy; diff --git a/zero.Backoffice/_legacy/Models/PageEditModel.cs b/zero.Backoffice/_legacy/Models/PageEditModel.cs index c99233ab..b8203d12 100644 --- a/zero.Backoffice/_legacy/Models/PageEditModel.cs +++ b/zero.Backoffice/_legacy/Models/PageEditModel.cs @@ -7,7 +7,7 @@ namespace zero.Web.Models { public PageType PageType { get; set; } - public ListResult Revisions { get; set; } + public Paged Revisions { get; set; } public List Urls { get; set; } = new(); } diff --git a/zero.Core/Collections/CollectionOperations.Read.cs b/zero.Core/Collections/CollectionOperations.Read.cs index 7e4ec485..db2e5550 100644 --- a/zero.Core/Collections/CollectionOperations.Read.cs +++ b/zero.Core/Collections/CollectionOperations.Read.cs @@ -1,5 +1,7 @@ -using Raven.Client.Documents.Indexes; +using Raven.Client.Documents; +using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; +using Raven.Client.Documents.Session; namespace zero.Collections; @@ -40,18 +42,26 @@ public abstract partial class CollectionOperations /// - public virtual async Task> Load(ListQuery query) where T : ZeroIdEntity, new() + public virtual async Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : ZeroIdEntity, new() { - return await Session.Query().FilterAsync(query); + IRavenQueryable queryable = Session.Query().Statistics(out QueryStatistics statistics); + querySelector ??= x => x; + + List result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync(); + return new Paged(result, statistics.TotalResults, pageNumber, pageSize); } /// - public virtual async Task> Load(ListQuery query) + public virtual async Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new() { - return await Session.Query().FilterAsync(query); + IRavenQueryable queryable = Session.Query().Statistics(out QueryStatistics statistics); + querySelector ??= x => x; + + List result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync(); + return new Paged(result, statistics.TotalResults, pageNumber, pageSize); } @@ -74,16 +84,17 @@ public abstract partial class CollectionOperations /// - public virtual async IAsyncEnumerable Stream(Func, IRavenQueryable> expression) where T : ZeroIdEntity, new() + public virtual async IAsyncEnumerable Stream(Func, IQueryable> expression) where T : ZeroIdEntity, new() { IRavenQueryable query = Session.Query(); + IQueryable queryable = query; if (expression != null) { - query = expression(query); + queryable = expression(query); } - var stream = await Session.Advanced.StreamAsync(query); + var stream = await Session.Advanced.StreamAsync(queryable); while (await stream.MoveNextAsync()) { diff --git a/zero.Core/Collections/CollectionOperations.Write.cs b/zero.Core/Collections/CollectionOperations.Write.cs index c02177c3..a116b616 100644 --- a/zero.Core/Collections/CollectionOperations.Write.cs +++ b/zero.Core/Collections/CollectionOperations.Write.cs @@ -5,7 +5,13 @@ namespace zero.Collections; public abstract partial class CollectionOperations { /// - public virtual async Task> Save(T model, Func> validate = null) where T : ZeroIdEntity, new() + public virtual Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new() => Save(model, validate); + + /// + public virtual Task> Update(T model, Func> validate = null) where T : ZeroIdEntity, new() => Save(model, validate); + + /// + protected virtual async Task> Save(T model, Func> validate = null) where T : ZeroIdEntity, new() { if (model == null) { diff --git a/zero.Core/Collections/CollectionOperations.cs b/zero.Core/Collections/CollectionOperations.cs index c8f35e9f..90973696 100644 --- a/zero.Core/Collections/CollectionOperations.cs +++ b/zero.Core/Collections/CollectionOperations.cs @@ -150,12 +150,12 @@ public interface ICollectionOperations /// /// Get entities by query /// - Task> Load(ListQuery query) where T : ZeroIdEntity, new(); + Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : ZeroIdEntity, new(); /// /// Get entities by query (by using the specified index) /// - Task> Load(ListQuery query) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new(); + Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new(); /// /// Get all entities from this collection. @@ -171,12 +171,17 @@ public interface ICollectionOperations /// /// Stream the collection /// - IAsyncEnumerable Stream(Func, IRavenQueryable> expression) where T : ZeroIdEntity, new(); + IAsyncEnumerable Stream(Func, IQueryable> expression) where T : ZeroIdEntity, new(); /// - /// Updates or creates an entity with an optional validator + /// Creates an entity with an optional validator /// - Task> Save(T model, Func> validate = null) where T : ZeroIdEntity, new(); + Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new(); + + /// + /// Updates an entity with an optional validator + /// + Task> Update(T model, Func> validate = null) where T : ZeroIdEntity, new(); /// /// Deletes an entity diff --git a/zero.Core/Collections/EntityCollection.cs b/zero.Core/Collections/EntityCollection.cs index 55fbc7cd..cf407e3e 100644 --- a/zero.Core/Collections/EntityCollection.cs +++ b/zero.Core/Collections/EntityCollection.cs @@ -43,10 +43,10 @@ public abstract partial class EntityCollection : IEntityCollection where T public virtual Task> Load(IEnumerable ids) => Operations.Load(ids); /// - public virtual Task> Load(ListQuery query) => Operations.Load(query); + public virtual Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default) => Operations.Load(pageNumber, pageSize, querySelector); /// - public virtual Task> Load(ListQuery query) where TIndex : AbstractCommonApiForIndexes, new() => Operations.Load(query); + public virtual Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where TIndex : AbstractCommonApiForIndexes, new() => Operations.Load(pageNumber, pageSize, querySelector); /// public virtual Task> LoadAll() => Operations.LoadAll(); @@ -55,10 +55,13 @@ public abstract partial class EntityCollection : IEntityCollection where T public virtual IAsyncEnumerable Stream() => Operations.Stream(); /// - public virtual IAsyncEnumerable Stream(Func, IRavenQueryable> expression) => Operations.Stream(expression); + public virtual IAsyncEnumerable Stream(Func, IQueryable> expression) => Operations.Stream(expression); /// - public virtual Task> Save(T model) => Operations.Save(model, async m => await Validate(m)); + public virtual Task> Create(T model) => Operations.Create(model, async m => await Validate(m)); + + /// + public virtual Task> Update(T model) => Operations.Update(model, async m => await Validate(m)); /// public virtual Task> Delete(T model) => Operations.Delete(model); @@ -115,12 +118,12 @@ public interface IEntityCollection where T : ZeroIdEntity, new() /// /// Get entities by query /// - Task> Load(ListQuery query); + Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default); /// /// Get entities by query (by using the specified index) /// - Task> Load(ListQuery query) where TIndex : AbstractCommonApiForIndexes, new(); + Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where TIndex : AbstractCommonApiForIndexes, new(); /// /// Get all entities from this collection. @@ -136,7 +139,7 @@ public interface IEntityCollection where T : ZeroIdEntity, new() /// /// Stream the collection /// - IAsyncEnumerable Stream(Func, IRavenQueryable> expression); + IAsyncEnumerable Stream(Func, IQueryable> expression); /// /// Validates an entity in this collection @@ -144,9 +147,14 @@ public interface IEntityCollection where T : ZeroIdEntity, new() Task Validate(T model); /// - /// Updates or creates an entity with an optional validator + /// Creates an entity with an optional validator /// - Task> Save(T model); + Task> Create(T model); + + /// + /// Updates an entity with an optional validator + /// + Task> Update(T model); /// /// Deletes an entity diff --git a/zero.Core/Extensions/EnumerableExtensions.cs b/zero.Core/Extensions/EnumerableExtensions.cs index dd76d0cb..b527f53d 100644 --- a/zero.Core/Extensions/EnumerableExtensions.cs +++ b/zero.Core/Extensions/EnumerableExtensions.cs @@ -5,7 +5,7 @@ public static class EnumerableExtensions /// /// /// - public static ListResult ToQueriedList(this IEnumerable items, ListQuery query) where T : ZeroEntity + public static Paged ToQueriedList(this IEnumerable items, ListQuery query) where T : ZeroEntity { //queryable = queryable.Statistics(out QueryStatistics stats); @@ -45,7 +45,7 @@ public static class EnumerableExtensions List result = items.ToList(); - return new ListResult(result, result.Count, query.Page, query.PageSize); + return new Paged(result, result.Count, query.Page, query.PageSize); } diff --git a/zero.Core/Identity/Collections/UserApi.cs b/zero.Core/Identity/Collections/UserApi.cs index f8cee7f5..b09e6f0a 100644 --- a/zero.Core/Identity/Collections/UserApi.cs +++ b/zero.Core/Identity/Collections/UserApi.cs @@ -54,7 +54,7 @@ namespace zero.Core.Api /// - public async Task> GetByQuery(ListQuery query) + public async Task> GetByQuery(ListQuery query) { string currentUserId = UserManager.GetUserId(Context.Context.BackofficeUser); @@ -247,7 +247,7 @@ namespace zero.Core.Api /// /// Get all available users (with query) /// - Task> GetByQuery(ListQuery query); + Task> GetByQuery(ListQuery query); /// /// Creates or updates a user diff --git a/zero.Core/BaseEntities/IZeroRouteEntity.cs b/zero.Core/Models/IZeroRouteEntity.cs similarity index 90% rename from zero.Core/BaseEntities/IZeroRouteEntity.cs rename to zero.Core/Models/IZeroRouteEntity.cs index 6528b5ac..f8ac87f2 100644 --- a/zero.Core/BaseEntities/IZeroRouteEntity.cs +++ b/zero.Core/Models/IZeroRouteEntity.cs @@ -1,4 +1,4 @@ -namespace zero; +namespace zero.Models; public interface IZeroRouteEntity { diff --git a/zero.Core/BaseEntities/Queries/ListQuery.cs b/zero.Core/Models/Queries/ListQuery.cs similarity index 98% rename from zero.Core/BaseEntities/Queries/ListQuery.cs rename to zero.Core/Models/Queries/ListQuery.cs index 70e41c48..c2511064 100644 --- a/zero.Core/BaseEntities/Queries/ListQuery.cs +++ b/zero.Core/Models/Queries/ListQuery.cs @@ -2,7 +2,7 @@ using Raven.Client.Documents.Session; using System.Linq.Expressions; -namespace zero; +namespace zero.Models; public class ListQuery { diff --git a/zero.Core/BaseEntities/Results/EntityResult.cs b/zero.Core/Models/Results/EntityResult.cs similarity index 99% rename from zero.Core/BaseEntities/Results/EntityResult.cs rename to zero.Core/Models/Results/EntityResult.cs index b683457e..9e37b7fe 100644 --- a/zero.Core/BaseEntities/Results/EntityResult.cs +++ b/zero.Core/Models/Results/EntityResult.cs @@ -1,7 +1,7 @@ using FluentValidation.Results; using System.Runtime.Serialization; -namespace zero; +namespace zero.Models; [DataContract(Name = "result", Namespace = "")] public class EntityResult diff --git a/zero.Core/BaseEntities/Results/ListResult.cs b/zero.Core/Models/Results/Paged.cs similarity index 50% rename from zero.Core/BaseEntities/Results/ListResult.cs rename to zero.Core/Models/Results/Paged.cs index c9cd23da..fb2cc716 100644 --- a/zero.Core/BaseEntities/Results/ListResult.cs +++ b/zero.Core/Models/Results/Paged.cs @@ -1,11 +1,35 @@ -namespace zero; +namespace zero.Models; -/// -/// Represents a paged result for a model collection -/// -public class ListResult : ListResult +public class Paged : Paged { - public ListResult(long totalItems, long page, long pageSize) + public IList Items { get; set; } = new List(); + + public Paged(IList items, long totalItems, long pageNumber, long pageSize) : this(totalItems, pageNumber, pageSize) + { + Items = items; + } + + public Paged MapTo(Func convertItem) + { + return new Paged(Items.Select(x => convertItem(x)).Where(x => x != null).ToList(), TotalItems, Page, PageSize); + } +} + + +public class Paged +{ + public long Page { get; protected set; } + + public long PageSize { get; protected set; } + + public long TotalPages { get; protected set; } + + public long TotalItems { get; protected set; } + + public bool HasMore { get; protected set; } + + + public Paged(long totalItems, long page, long pageSize) { TotalItems = totalItems; Page = page; @@ -23,25 +47,6 @@ public class ListResult : ListResult HasMore = TotalPages > Page; } - public ListResult(IList items, long totalItems, long pageNumber, long pageSize) : this(totalItems, pageNumber, pageSize) - { - Items = items; - } - - public ListResult MapTo(Func convertItem) - { - return new ListResult(Items.Select(x => convertItem(x)).Where(x => x != null).ToList(), TotalItems, Page, PageSize); - } - - public IList Items { get; set; } = new List(); - - - /// - /// Calculates the skip size based on the paged parameters specified - /// - /// - /// Returns 0 if the page number or page size is zero - /// public int GetSkipSize() { if (Page > 0 && PageSize > 0) @@ -50,18 +55,4 @@ public class ListResult : ListResult } return 0; } -} - - -public class ListResult -{ - public long Page { get; protected set; } - - public long PageSize { get; protected set; } - - public long TotalPages { get; protected set; } - - public long TotalItems { get; protected set; } - - public bool HasMore { get; protected set; } } \ No newline at end of file diff --git a/zero.Core/BaseEntities/ZeroEntity.cs b/zero.Core/Models/ZeroEntity.cs similarity index 98% rename from zero.Core/BaseEntities/ZeroEntity.cs rename to zero.Core/Models/ZeroEntity.cs index 63be5d9e..37e863a0 100644 --- a/zero.Core/BaseEntities/ZeroEntity.cs +++ b/zero.Core/Models/ZeroEntity.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using System.Diagnostics; -namespace zero; +namespace zero.Models; [DebuggerDisplay("Id = {Id,nq}, Name = {Name}, Alias = {Alias}")] public class ZeroEntity : ZeroIdEntity, IZeroDbConventions, IZeroRouteEntity diff --git a/zero.Core/BaseEntities/ZeroEntityPreview.cs b/zero.Core/Models/ZeroEntityPreview.cs similarity index 91% rename from zero.Core/BaseEntities/ZeroEntityPreview.cs rename to zero.Core/Models/ZeroEntityPreview.cs index e33e9758..455b26e7 100644 --- a/zero.Core/BaseEntities/ZeroEntityPreview.cs +++ b/zero.Core/Models/ZeroEntityPreview.cs @@ -1,4 +1,4 @@ -namespace zero; +namespace zero.Models; [RavenCollection("Previews")] public class ZeroEntityPreview : ZeroEntity diff --git a/zero.Core/BaseEntities/ZeroIdEntity.cs b/zero.Core/Models/ZeroIdEntity.cs similarity index 82% rename from zero.Core/BaseEntities/ZeroIdEntity.cs rename to zero.Core/Models/ZeroIdEntity.cs index 0efadec4..8dec8272 100644 --- a/zero.Core/BaseEntities/ZeroIdEntity.cs +++ b/zero.Core/Models/ZeroIdEntity.cs @@ -1,4 +1,4 @@ -namespace zero; +namespace zero.Models; public class ZeroIdEntity { diff --git a/zero.Core/BaseEntities/ZeroReference.cs b/zero.Core/Models/ZeroReference.cs similarity index 95% rename from zero.Core/BaseEntities/ZeroReference.cs rename to zero.Core/Models/ZeroReference.cs index 68e64acf..a8808e26 100644 --- a/zero.Core/BaseEntities/ZeroReference.cs +++ b/zero.Core/Models/ZeroReference.cs @@ -1,4 +1,4 @@ -namespace zero; +namespace zero.Models; public class ZeroReference { diff --git a/zero.Core/Persistence/RavenQueryableExtensions.cs b/zero.Core/Persistence/RavenQueryableExtensions.cs index 112bcd70..8ef969d0 100644 --- a/zero.Core/Persistence/RavenQueryableExtensions.cs +++ b/zero.Core/Persistence/RavenQueryableExtensions.cs @@ -11,7 +11,7 @@ public static class RavenQueryableExtensions /// /// /// - public static async Task> FilterAsync(this IRavenQueryable source, ListQuery listQuery) + public static async Task> FilterAsync(this IRavenQueryable source, ListQuery listQuery) { return await source.WithFilter(listQuery).ToListResultAsync(listQuery); } @@ -73,10 +73,10 @@ public static class RavenQueryableExtensions /// /// /// - public static async Task> ToListResultAsync(this IQueryable source, ListQuery listQuery, CancellationToken token = default) + public static async Task> ToListResultAsync(this IQueryable source, ListQuery listQuery, CancellationToken token = default) { List results = await source.ToListAsync(token); - return new ListResult(results, listQuery.Statistics.TotalResults, listQuery.Page, listQuery.PageSize); + return new Paged(results, listQuery.Statistics.TotalResults, listQuery.Page, listQuery.PageSize); } diff --git a/zero.Core/Usings.cs b/zero.Core/Usings.cs index d45620ca..c6934da0 100644 --- a/zero.Core/Usings.cs +++ b/zero.Core/Usings.cs @@ -22,4 +22,5 @@ global using zero.Rendering; global using zero.Routing; global using zero.Utils; global using zero.Validation; -global using zero.Collections; \ No newline at end of file +global using zero.Collections; +global using zero.Models; \ No newline at end of file diff --git a/zero.Core/_legacy/Api/ApplicationsApi.cs b/zero.Core/_legacy/Api/ApplicationsApi.cs index 60634e98..8de5f6fb 100644 --- a/zero.Core/_legacy/Api/ApplicationsApi.cs +++ b/zero.Core/_legacy/Api/ApplicationsApi.cs @@ -38,7 +38,7 @@ namespace zero.Core.Api /// - public async Task> GetByQuery(ListQuery query) + public async Task> GetByQuery(ListQuery query) { query.SearchFor(entity => entity.Name); @@ -76,7 +76,7 @@ namespace zero.Core.Api /// /// Get all available applications (with query) /// - Task> GetByQuery(ListQuery query); + Task> GetByQuery(ListQuery query); /// /// Creates or updates a application diff --git a/zero.Core/_legacy/Api/RecycleBinApi.cs b/zero.Core/_legacy/Api/RecycleBinApi.cs index 0bfd8948..aae67416 100644 --- a/zero.Core/_legacy/Api/RecycleBinApi.cs +++ b/zero.Core/_legacy/Api/RecycleBinApi.cs @@ -65,7 +65,7 @@ namespace zero.Core.Api /// - public async Task> GetByQuery(RecycleBinListQuery query) + public async Task> GetByQuery(RecycleBinListQuery query) { query.SearchSelector = x => x.Name; @@ -145,7 +145,7 @@ namespace zero.Core.Api /// /// Get all recycled items /// - Task> GetByQuery(RecycleBinListQuery query); + Task> GetByQuery(RecycleBinListQuery query); /// /// Get affected entities from a specific operation diff --git a/zero.Core/_legacy/Api/RevisionsApi.cs b/zero.Core/_legacy/Api/RevisionsApi.cs index 8a9f4972..aca17eca 100644 --- a/zero.Core/_legacy/Api/RevisionsApi.cs +++ b/zero.Core/_legacy/Api/RevisionsApi.cs @@ -20,7 +20,7 @@ namespace zero.Core.Api /// /// Get revision list for an entity /// - public async Task>> GetPagedWithModel(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity + public async Task>> GetPagedWithModel(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity { // get paged revisions List items = await Session.Advanced.Revisions.GetForAsync(id, (pageNumber - 1) * pageSize, pageSize); @@ -66,16 +66,16 @@ namespace zero.Core.Api revisions.Add(revision); } - return new ListResult>(revisions, totalResults, pageNumber, pageSize); + return new Paged>(revisions, totalResults, pageNumber, pageSize); } /// /// Get revision list for an entity /// - public async Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity + public async Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity { - ListResult> items = await GetPagedWithModel(id, pageNumber, pageSize); + Paged> items = await GetPagedWithModel(id, pageNumber, pageSize); return items.MapTo(model => new Revision() { ChangeVector = model.ChangeVector, @@ -92,11 +92,11 @@ namespace zero.Core.Api /// /// Get revision list including models for an entity /// - Task>> GetPagedWithModel(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity; + Task>> GetPagedWithModel(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity; /// /// Get revision list for an entity /// - Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity; + Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity; } } diff --git a/zero.Core/_legacy/Api/SetupApi.cs b/zero.Core/_legacy/Api/SetupApi.cs index 7b29bbbe..3be2ba30 100644 --- a/zero.Core/_legacy/Api/SetupApi.cs +++ b/zero.Core/_legacy/Api/SetupApi.cs @@ -2,23 +2,10 @@ using Microsoft.AspNetCore.Identity; using Newtonsoft.Json; using Raven.Client.Documents; -using Raven.Client.Documents.Conventions; using Raven.Client.Documents.Session; -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Security.Cryptography; -using System.Threading.Tasks; -using zero.Core.Database; -using zero.Core.Entities; -using zero.Core.Entities.Setup; -using zero.Core.Extensions; -using zero.Core.Identity; -using zero.Core.Options; -using zero.Core.Utils; -using zero.Core.Validation; - +using zero.Setup; namespace zero.Core.Api { diff --git a/zero.Core/_legacy/Api/SpacesApi.cs b/zero.Core/_legacy/Api/SpacesApi.cs index 6c1c0d23..36a6d870 100644 --- a/zero.Core/_legacy/Api/SpacesApi.cs +++ b/zero.Core/_legacy/Api/SpacesApi.cs @@ -89,7 +89,7 @@ namespace zero.Core.Api /// - public async Task> GetListByQuery(string alias, ListQuery query) + public async Task> GetListByQuery(string alias, ListQuery query) { query.SearchSelector = item => item.Name; @@ -100,7 +100,7 @@ namespace zero.Core.Api /// - public async Task> GetListByQuery(string alias, ListQuery query) where T : SpaceContent + public async Task> GetListByQuery(string alias, ListQuery query) where T : SpaceContent { query.SearchSelector = item => item.Name; @@ -111,7 +111,7 @@ namespace zero.Core.Api /// - public async Task> GetListByQuery(ListQuery query) where T : SpaceContent + public async Task> GetListByQuery(ListQuery query) where T : SpaceContent { Space space = GetBy(); query.SearchSelector = item => item.Name; @@ -172,17 +172,17 @@ namespace zero.Core.Api /// /// Get all list items for a space (with query) /// - Task> GetListByQuery(string alias, ListQuery query); + Task> GetListByQuery(string alias, ListQuery query); /// /// Get all list items for a space (with query) /// - Task> GetListByQuery(string alias, ListQuery query) where T : SpaceContent; + Task> GetListByQuery(string alias, ListQuery query) where T : SpaceContent; /// /// Get all list items for a space (with query) /// - Task> GetListByQuery(ListQuery query) where T : SpaceContent; + Task> GetListByQuery(ListQuery query) where T : SpaceContent; /// /// Saves a content item in a space diff --git a/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs b/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs index 52e10dba..63ef5c6c 100644 --- a/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs +++ b/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs @@ -89,7 +89,7 @@ namespace zero.Core.Collections /// - public override async Task> Load(ListQuery query) + public override async Task> Load(ListQuery query) { List result = new(); List models = await Session.Query().ToListAsync(); @@ -257,7 +257,7 @@ namespace zero.Core.Collections /// /// Get all integrations with the specified query /// - Task> Load(ListQuery query); + Task> Load(ListQuery query); /// /// Get all integration types with their configuration status diff --git a/zero.Core/_legacy/Collections/Media/MediaCollection.cs b/zero.Core/_legacy/Collections/Media/MediaCollection.cs index d4f80a33..b5103d09 100644 --- a/zero.Core/_legacy/Collections/Media/MediaCollection.cs +++ b/zero.Core/_legacy/Collections/Media/MediaCollection.cs @@ -75,7 +75,7 @@ namespace zero.Core.Collections /// - public virtual async Task> Load(MediaListQuery query) + public virtual async Task> Load(MediaListQuery query) { query.SearchFor(entity => entity.Name); @@ -162,7 +162,7 @@ namespace zero.Core.Collections /// - public virtual async Task> Load(MediaListItemQuery query) + public virtual async Task> Load(MediaListItemQuery query) { bool hasSearch = !query.Search.IsNullOrWhiteSpace(); bool isRoot = query.FolderId.IsNullOrWhiteSpace(); @@ -180,7 +180,7 @@ namespace zero.Core.Collections dbQuery = dbQuery.WhereIf(x => x.ParentId == query.FolderId, !query.FolderId.IsNullOrEmpty(), x => x.ParentId == null); } - ListResult result = await dbQuery.ToQueriedListAsyncX(query); + Paged result = await dbQuery.ToQueriedListAsyncX(query); string[] ids = result.Items.Where(x => x.IsFolder).Select(x => x.Id).ToArray(); @@ -256,12 +256,12 @@ namespace zero.Core.Collections /// /// Get all available media items with query /// - Task> Load(MediaListQuery query); + Task> Load(MediaListQuery query); /// /// Get all available media items (including folders) with query /// - Task> Load(MediaListItemQuery query); + Task> Load(MediaListItemQuery query); /// /// Move a file to a new parent