start new controllers
This commit is contained in:
+3
-3
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
{
|
||||
@@ -0,0 +1,33 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace zero.Backoffice.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the request is not cached by the browser
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
{
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
//using zero.Core.Api;
|
||||
//using zero.Web.Models;
|
||||
|
||||
//namespace zero.Web.Filters
|
||||
//namespace zero.Web.Controllers
|
||||
//{
|
||||
// public class VerifyTokenAttribute : TypeFilterAttribute
|
||||
// {
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
//using zero.Core.Entities;
|
||||
//using zero.Web.Models;
|
||||
|
||||
//namespace zero.Web.Filters
|
||||
//namespace zero.Web.Controllers
|
||||
//{
|
||||
// public class ZeroBackofficeAttribute : TypeFilterAttribute
|
||||
// {
|
||||
@@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace zero.Backoffice.Controllers;
|
||||
|
||||
[ApiController]
|
||||
//[ServiceFilter(typeof(ModelStateValidationFilterAttribute))]
|
||||
//[ServiceFilter(typeof(BackofficeFilterAttribute))]
|
||||
public abstract class ZeroBackofficeApiController : ZeroBackofficeController
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace zero.Backoffice.Controllers;
|
||||
|
||||
[ZeroAuthorize]
|
||||
[DisableBrowserCache]
|
||||
public abstract class ZeroBackofficeController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an edit model with associated metadata and permissions from a model
|
||||
/// </summary>
|
||||
protected EditModel<T> GetEditModel<T>(T model)
|
||||
{
|
||||
return GetEditModel<T, EditModel<T>>(new EditModel<T>() { Entity = model });
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an edit model with associated metadata and permissions from a model
|
||||
/// </summary>
|
||||
protected TWrapper GetEditModel<T, TWrapper>(TWrapper editModel) where TWrapper : EditModel<T>, new()
|
||||
{
|
||||
editModel.Meta = new()
|
||||
{
|
||||
Token = null,
|
||||
IsShared = false
|
||||
};
|
||||
|
||||
editModel.Permissions = new()
|
||||
{
|
||||
CanCreate = true,
|
||||
CanEdit = true,
|
||||
CanDelete = true
|
||||
};
|
||||
|
||||
return editModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using System.IO;
|
||||
|
||||
namespace zero.Backoffice.Controllers;
|
||||
|
||||
public static class ZeroBackofficeControllerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform entities to a preview list
|
||||
/// </summary>
|
||||
public static List<PreviewModel> TransformToPreviewModels<T>(this ZeroBackofficeController controller, Dictionary<string, T> items, Action<T, PreviewModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
List<PreviewModel> 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transform entities to a select list
|
||||
/// </summary>
|
||||
public static List<SelectModel> TransformToSelectModels<T>(this ZeroBackofficeController controller, IEnumerable<T> enumerable, Action<T, SelectModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
List<SelectModel> 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a file stream for download in the browser
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a file stream to the browser
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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]"));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Configure routing model for all backoffice controllers
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IActionResult> Install([FromBody] SetupModel model)
|
||||
{
|
||||
model.ContentRootPath = Env.ContentRootPath;
|
||||
|
||||
EntityResult<SetupModel> 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);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -9,7 +9,7 @@ using zero.Core.Entities;
|
||||
|
||||
namespace zero.Backoffice;
|
||||
|
||||
public abstract class ZeroBackofficeCollectionController<TEntity, TCollection> : ZeroBackofficeController
|
||||
public abstract class _ZeroBackofficeCollectionController<TEntity, TCollection> : ZeroBackofficeController
|
||||
where TEntity : ZeroIdEntity, new()
|
||||
where TCollection : ICollectionOperations<TEntity>
|
||||
{
|
||||
@@ -43,13 +43,13 @@ public abstract class ZeroBackofficeCollectionController<TEntity, TCollection> :
|
||||
public virtual async Task<EditModel<TEntity>> GetEmpty() => Edit(await Collection.Empty());
|
||||
|
||||
|
||||
public virtual async Task<ListResult<TEntity>> GetByQuery([FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
public virtual async Task<Paged<TEntity>> GetByQuery([FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
{
|
||||
return await Collection.Load(query);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<ListResult<Revision>> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
public virtual async Task<Paged<Revision>> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
{
|
||||
return null; // TODO
|
||||
//return await Collection.GetRevisions(id, query.Page, query.PageSize);
|
||||
@@ -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<TEntity, TCollection> : BackofficeController
|
||||
where TEntity : ZeroEntity
|
||||
where TCollection : ICollectionBase<TEntity>
|
||||
{
|
||||
protected TCollection Collection { get; private set; }
|
||||
|
||||
[Obsolete]
|
||||
protected Func<IRavenQueryable<TEntity>, IRavenQueryable<TEntity>> DefaultQuery { get; set; }
|
||||
|
||||
protected Action<TEntity, PreviewModel> PreviewTransform { get; set; }
|
||||
|
||||
protected Action<TEntity, SelectModel> PickerTransform { get; set; }
|
||||
|
||||
|
||||
public BackofficeCollectionController(TCollection collection)
|
||||
{
|
||||
Collection = collection;
|
||||
}
|
||||
|
||||
public override void OnScopeChanged(string scope)
|
||||
{
|
||||
Collection.ApplyScope(scope);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<EditModel<TEntity>> GetById([FromQuery] string id, [FromQuery] string changeVector = null) => Edit(changeVector.IsNullOrEmpty() ? await Collection.GetById(id) : await Collection.GetRevision(changeVector));
|
||||
|
||||
|
||||
public virtual async Task<Dictionary<string, TEntity>> GetByIds([FromQuery] string[] ids) => await Collection.GetByIds(ids);
|
||||
|
||||
|
||||
public virtual EditModel<TEntity> GetEmpty([FromServices] TEntity blueprint) => Edit(blueprint);
|
||||
|
||||
|
||||
public virtual async Task<ListResult<TEntity>> GetByQuery([FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
{
|
||||
return await Collection.GetByQuery(query);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<ListResult<Revision>> GetRevisions([FromQuery] string id, [FromQuery] ListBackofficeQuery<TEntity> query)
|
||||
{
|
||||
return await Collection.GetRevisions(id, query.Page, query.PageSize);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IEnumerable<SelectModel>> GetForPicker() => await SelectList(Collection.Stream(), PickerTransform);
|
||||
|
||||
|
||||
public virtual async Task<IList<PreviewModel>> GetPreviews([FromQuery] List<string> ids) => Previews(await Collection.GetByIds(ids.ToArray()), PreviewTransform);
|
||||
|
||||
|
||||
[HttpPost]
|
||||
public virtual async Task<EntityResult<TEntity>> Save([FromBody] TEntity model) => await Collection.Save(model);
|
||||
|
||||
|
||||
[HttpDelete]
|
||||
public virtual async Task<EntityResult<TEntity>> Delete([FromQuery] string id) => await Collection.DeleteById(id);
|
||||
}
|
||||
}
|
||||
@@ -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<IZeroOptions>());
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is execuated when the scope changes.
|
||||
/// The scope is evaluated by the BackofficeFilterAttribute.
|
||||
/// </summary>
|
||||
public virtual void OnScopeChanged(string scope) { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an edit model with appropriate options and permissions
|
||||
/// </summary>
|
||||
public EditModel<T> Edit<T>(T data, bool typed = true, Action<EditModel<T>> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
//ControllerContext.ActionDescriptor.FilterDescriptors[0].
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
EditModel<T> model = new EditModel<T>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an edit model with appropriate options and permissions
|
||||
/// </summary>
|
||||
public TWrapper Edit<T, TWrapper>(TWrapper data, bool typed = true, Action<EditModel<T>> transform = null)
|
||||
where T : ZeroIdEntity
|
||||
where TWrapper : EditModel<T>, 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<PreviewModel> Previews<T>(Dictionary<string, T> items, Func<T, PreviewModel> transform)
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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<PreviewModel> Previews<T>(Dictionary<string, T> items, Action<T, PreviewModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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<IList<SelectModel>> SelectList<T>(IAsyncEnumerable<T> enumerable, Action<T, SelectModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
List<SelectModel> items = new List<SelectModel>();
|
||||
|
||||
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<IList<PreviewModel>> Previews<T>(Dictionary<string, T> items, Func<T, Task<PreviewModel>> transform)
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IZeroOptions>());
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is execuated when the scope changes.
|
||||
/// The scope is evaluated by the BackofficeFilterAttribute.
|
||||
/// </summary>
|
||||
public virtual void OnScopeChanged(string scope) { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an edit model with appropriate options and permissions
|
||||
/// </summary>
|
||||
public EditModel<T> Edit<T>(T data, bool typed = true, Action<EditModel<T>> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
//ControllerContext.ActionDescriptor.FilterDescriptors[0].
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
EditModel<T> model = new EditModel<T>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an edit model with appropriate options and permissions
|
||||
/// </summary>
|
||||
public TWrapper Edit<T, TWrapper>(TWrapper data, bool typed = true, Action<EditModel<T>> transform = null)
|
||||
where T : ZeroIdEntity
|
||||
where TWrapper : EditModel<T>, 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<PreviewModel> Previews<T>(Dictionary<string, T> items, Func<T, PreviewModel> transform)
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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<PreviewModel> Previews<T>(Dictionary<string, T> items, Action<T, PreviewModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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<IList<SelectModel>> SelectList<T>(IAsyncEnumerable<T> enumerable, Action<T, SelectModel> transform = null) where T : ZeroIdEntity
|
||||
{
|
||||
List<SelectModel> items = new List<SelectModel>();
|
||||
|
||||
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<IList<PreviewModel>> Previews<T>(Dictionary<string, T> items, Func<T, Task<PreviewModel>> transform)
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IActionResult> Install([FromBody] SetupModel model)
|
||||
{
|
||||
model.ContentRootPath = Env.ContentRootPath;
|
||||
|
||||
EntityResult<SetupModel> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,38 @@ public class EditModel<T>
|
||||
/// Meta data
|
||||
/// </summary>
|
||||
public EditMetaModel Meta { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Permissions for this entity
|
||||
/// </summary>
|
||||
public EditPermissionModel Permissions { get; set; } = new();
|
||||
}
|
||||
|
||||
|
||||
public class EditMetaModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Wehther this entity is application aware
|
||||
/// </summary>
|
||||
public bool IsAppAware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this entity can be shared across applications (only for IsAppAware=true)
|
||||
/// </summary>
|
||||
public bool CanBeShared { get; set; }
|
||||
|
||||
public bool IsShared { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
/// </summary>
|
||||
public string Token { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EditPermissionModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether an entity of this type can be created
|
||||
@@ -37,23 +65,4 @@ public class EditMetaModel
|
||||
/// Whether this entity can be deleted
|
||||
/// </summary>
|
||||
public bool CanDelete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wehther this entity is application aware
|
||||
/// </summary>
|
||||
public bool IsAppAware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this entity can be shared across applications (only for IsAppAware=true)
|
||||
/// </summary>
|
||||
public bool CanBeShared { get; set; }
|
||||
|
||||
public bool IsShared { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
/// </summary>
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -74,29 +74,4 @@ public class TreeItem
|
||||
/// Output an additional count value.
|
||||
/// </summary>
|
||||
public int? CountOutput { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The modifier displays a small icon (with hover text) next to the main item icon
|
||||
/// </summary>
|
||||
public class TreeItemModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the modifier
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon to display
|
||||
/// </summary>
|
||||
public string Icon { get; set; }
|
||||
|
||||
public TreeItemModifier() { }
|
||||
|
||||
public TreeItemModifier(string name, string icon)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace zero.Backoffice.Models;
|
||||
|
||||
/// <summary>
|
||||
/// The modifier displays a small icon (with hover text) next to the main item icon
|
||||
/// </summary>
|
||||
public class TreeItemModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the modifier
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon to display
|
||||
/// </summary>
|
||||
public string Icon { get; set; }
|
||||
|
||||
public TreeItemModifier() { }
|
||||
|
||||
public TreeItemModifier(string name, string icon)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace zero.Backoffice.Modules;
|
||||
|
||||
/// <summary>
|
||||
/// | 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}
|
||||
/// </summary>
|
||||
public class CountriesController : ZeroBackofficeApiController
|
||||
{
|
||||
protected ICountriesCollection Collection { get; set; }
|
||||
|
||||
public CountriesController(ICountriesCollection collection)
|
||||
{
|
||||
Collection = collection;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("empty")]
|
||||
public virtual async Task<Country> New()
|
||||
{
|
||||
return await Collection.Empty();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public virtual async Task<Country> Get(string id, string changeVector = null)
|
||||
{
|
||||
return await Collection.Load(id, changeVector);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public virtual async Task<Paged<Country>> Get(ListBackofficeQuery<Country> query)
|
||||
{
|
||||
return await Collection.Load<zero_Backoffice_Countries_Listing>(query.Page, query.PageSize, q => q.OrderByDescending(x => x.CreatedDate));
|
||||
}
|
||||
|
||||
//[HttpGet("{id}/revisions")]
|
||||
//public virtual async Task<List<Revision<Country>>> GetRevisions(string id)
|
||||
//{
|
||||
// return await Collection.GetRevisions(id);
|
||||
//}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
public virtual async Task<EntityResult<Country>> Create(Country country)
|
||||
{
|
||||
return await Collection.Create(country);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public virtual async Task<EntityResult<Country>> Update(string id, Country country)
|
||||
{
|
||||
return await Collection.Update(country);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public virtual async Task<EntityResult<Country>> Delete(string id)
|
||||
{
|
||||
return await Collection.Delete(id);
|
||||
}
|
||||
}
|
||||
@@ -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<Country, ICountriesCollection>
|
||||
{
|
||||
public CountriesController(ICountriesCollection collection) : base(collection)
|
||||
{
|
||||
PreviewTransform = (item, model) => model.Icon = "flag-" + item.Code.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public override Task<ListResult<Country>> GetByQuery([FromQuery] ListBackofficeQuery<Country> query)
|
||||
{
|
||||
query.OrderQuery = q => q.OrderByDescending(x => x.IsPreferred).ThenBy(x => x.Name);
|
||||
return Collection.Load<zero_Backoffice_Countries_Listing>(query);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ namespace zero.Web.Controllers
|
||||
}
|
||||
|
||||
|
||||
public override async Task<ListResult<Language>> GetByQuery([FromQuery] ListBackofficeQuery<Language> query)
|
||||
public override async Task<Paged<Language>> GetByQuery([FromQuery] ListBackofficeQuery<Language> query)
|
||||
{
|
||||
query.OrderQuery = q => q.OrderByDescending(x => x.CreatedDate);
|
||||
return await Collection.Load<zero_Languages>(query);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace zero.Web.Controllers
|
||||
PreviewTransform = (item, model) => model.Icon = "fth-mail";
|
||||
}
|
||||
|
||||
public override async Task<ListResult<MailTemplate>> GetByQuery([FromQuery] ListBackofficeQuery<MailTemplate> query)
|
||||
public override async Task<Paged<MailTemplate>> GetByQuery([FromQuery] ListBackofficeQuery<MailTemplate> query)
|
||||
{
|
||||
query.SearchFor(entity => entity.Name, entity => entity.Key, entity => entity.Subject);
|
||||
return await Collection.Load<zero_MailTemplates>(query);
|
||||
|
||||
@@ -18,7 +18,7 @@ public class BackofficeSearchService : IBackofficeSearchService
|
||||
}
|
||||
|
||||
|
||||
public async Task<ListResult<SearchResult>> Query(string searchTerm)
|
||||
public async Task<Paged<SearchResult>> 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<SearchResult>(items, stats.TotalResults, 1, 20);
|
||||
return new Paged<SearchResult>(items, stats.TotalResults, 1, 20);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,5 +69,5 @@ public class BackofficeSearchService : IBackofficeSearchService
|
||||
|
||||
public interface IBackofficeSearchService
|
||||
{
|
||||
Task<ListResult<SearchResult>> Query(string searchTerm);
|
||||
Task<Paged<SearchResult>> Query(string searchTerm);
|
||||
}
|
||||
@@ -12,5 +12,5 @@ public class SearchController : BackofficeController
|
||||
SearchService = searchService;
|
||||
}
|
||||
|
||||
public async Task<ListResult<SearchResult>> Query([FromQuery] string query) => await SearchService.Query(query);
|
||||
public async Task<Paged<SearchResult>> Query([FromQuery] string query) => await SearchService.Query(query);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace zero.Web.Controllers
|
||||
{
|
||||
public TranslationsController(ITranslationsCollection collection) : base(collection) { }
|
||||
|
||||
public override async Task<ListResult<Translation>> GetByQuery([FromQuery] ListBackofficeQuery<Translation> query)
|
||||
public override async Task<Paged<Translation>> GetByQuery([FromQuery] ListBackofficeQuery<Translation> query)
|
||||
{
|
||||
query.SearchFor(entity => entity.Key, entity => entity.Value);
|
||||
query.OrderQuery = q => q.OrderByDescending(x => x.CreatedDate);
|
||||
|
||||
+10
-12
@@ -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;
|
||||
@@ -14,6 +14,7 @@ class ZeroBackofficeMvcOptions : IConfigureOptions<MvcOptions>
|
||||
|
||||
public void Configure(MvcOptions options)
|
||||
{
|
||||
//options.Conventions.Add(new ZeroBackofficeControllerModelConvention(Options.BackofficePath));
|
||||
string backofficePath = Options.For<BackofficeOptions>().Path;
|
||||
options.Conventions.Add(new ZeroBackofficeControllerModelConvention(backofficePath));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace zero.Web.Controllers
|
||||
public async Task<IList<Application>> GetAll() => await Api.GetAll();
|
||||
|
||||
|
||||
public async Task<ListResult<Application>> GetByQuery([FromQuery] ListBackofficeQuery<Application> query) => await Api.GetByQuery(query);
|
||||
public async Task<Paged<Application>> GetByQuery([FromQuery] ListBackofficeQuery<Application> query) => await Api.GetByQuery(query);
|
||||
|
||||
|
||||
public IReadOnlyCollection<Feature> GetAllFeatures() => Options.Features.GetAllItems();
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace zero.Web.Controllers
|
||||
public async Task<EditModel<Integration>> GetByAlias([FromQuery] string alias) => Edit(await Collection.GetByAlias(alias));
|
||||
|
||||
|
||||
public async Task<ListResult<Integration>> Load([FromQuery] ListQuery<Integration> query) => await Collection.Load(query);
|
||||
public async Task<Paged<Integration>> Load([FromQuery] ListQuery<Integration> query) => await Collection.Load(query);
|
||||
|
||||
|
||||
public async Task<IList<IntegrationTypeWithStatus>> GetTypes() => await Collection.GetTypesWithStatus();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace zero.Web.Controllers
|
||||
}
|
||||
|
||||
|
||||
public async Task<ListResult<MediaListItem>> GetListByQuery([FromQuery] MediaListItemQuery query)
|
||||
public async Task<Paged<MediaListItem>> GetListByQuery([FromQuery] MediaListItemQuery query)
|
||||
{
|
||||
query.IncludeInactive = true;
|
||||
return await Collection.Load(query);
|
||||
@@ -47,7 +47,7 @@ namespace zero.Web.Controllers
|
||||
public async Task<MediaListResultModel> GetAll([FromQuery] MediaListQuery query)
|
||||
{
|
||||
query.IncludeInactive = true;
|
||||
ListResult<MediaListModel> items = (await Collection.Load(query)).MapTo(x => new MediaListModel()
|
||||
Paged<MediaListModel> items = (await Collection.Load(query)).MapTo(x => new MediaListModel()
|
||||
{
|
||||
Id = x.Id,
|
||||
IsFolder = false,
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace zero.Web.Controllers
|
||||
}
|
||||
|
||||
|
||||
public async Task<ListResult<RecycledEntity>> GetByQuery([FromQuery] RecycleBinListQuery query)
|
||||
public async Task<Paged<RecycledEntity>> GetByQuery([FromQuery] RecycleBinListQuery query)
|
||||
{
|
||||
query.IncludeInactive = true;
|
||||
return await Api.GetByQuery(query);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace zero.Web.Controllers
|
||||
public async Task<EditModel<BackofficeUser>> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id));
|
||||
|
||||
|
||||
public async Task<ListResult<BackofficeUser>> GetAll([FromQuery] ListBackofficeQuery<BackofficeUser> query) => await Api.GetByQuery(query);
|
||||
public async Task<Paged<BackofficeUser>> GetAll([FromQuery] ListBackofficeQuery<BackofficeUser> query) => await Api.GetByQuery(query);
|
||||
|
||||
|
||||
public IList<PermissionCollection> GetAllPermissions() => PermissionsApi.GetAll();
|
||||
|
||||
@@ -3,7 +3,7 @@ using zero.Core.Entities;
|
||||
|
||||
namespace zero.Web.Models
|
||||
{
|
||||
public class MediaListResultModel : ListResult<MediaListModel>
|
||||
public class MediaListResultModel : Paged<MediaListModel>
|
||||
{
|
||||
public IEnumerable<MediaListModel> Folders { get; private set; }
|
||||
|
||||
@@ -11,12 +11,12 @@ namespace zero.Web.Models
|
||||
|
||||
public MediaFolder Folder { get; private set; }
|
||||
|
||||
public MediaListResultModel(ListResult<MediaListModel> items, IEnumerable<MediaListModel> folders) : base(items.Items, items.TotalItems, items.Page, items.PageSize)
|
||||
public MediaListResultModel(Paged<MediaListModel> items, IEnumerable<MediaListModel> folders) : base(items.Items, items.TotalItems, items.Page, items.PageSize)
|
||||
{
|
||||
Folders = folders;
|
||||
}
|
||||
|
||||
public MediaListResultModel(ListResult<MediaListModel> items, IEnumerable<MediaListModel> folders, MediaFolder currentFolder, IEnumerable<MediaFolder> hierarchy) : this(items, folders)
|
||||
public MediaListResultModel(Paged<MediaListModel> items, IEnumerable<MediaListModel> folders, MediaFolder currentFolder, IEnumerable<MediaFolder> hierarchy) : this(items, folders)
|
||||
{
|
||||
Folder = currentFolder;
|
||||
FolderHierarchy = hierarchy;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace zero.Web.Models
|
||||
{
|
||||
public PageType PageType { get; set; }
|
||||
|
||||
public ListResult<Revision> Revisions { get; set; }
|
||||
public Paged<Revision> Revisions { get; set; }
|
||||
|
||||
public List<string> Urls { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<ListResult<T>> Load<T>(ListQuery<T> query) where T : ZeroIdEntity, new()
|
||||
public virtual async Task<Paged<T>> Load<T>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where T : ZeroIdEntity, new()
|
||||
{
|
||||
return await Session.Query<T>().FilterAsync(query);
|
||||
IRavenQueryable<T> queryable = Session.Query<T>().Statistics(out QueryStatistics statistics);
|
||||
querySelector ??= x => x;
|
||||
|
||||
List<T> result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync();
|
||||
return new Paged<T>(result, statistics.TotalResults, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<ListResult<T>> Load<T, TIndex>(ListQuery<T> query)
|
||||
public virtual async Task<Paged<T>> Load<T, TIndex>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default)
|
||||
where T : ZeroIdEntity, new()
|
||||
where TIndex : AbstractCommonApiForIndexes, new()
|
||||
{
|
||||
return await Session.Query<T, TIndex>().FilterAsync(query);
|
||||
IRavenQueryable<T> queryable = Session.Query<T, TIndex>().Statistics(out QueryStatistics statistics);
|
||||
querySelector ??= x => x;
|
||||
|
||||
List<T> result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync();
|
||||
return new Paged<T>(result, statistics.TotalResults, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,16 +84,17 @@ public abstract partial class CollectionOperations
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async IAsyncEnumerable<T> Stream<T>(Func<IRavenQueryable<T>, IRavenQueryable<T>> expression) where T : ZeroIdEntity, new()
|
||||
public virtual async IAsyncEnumerable<T> Stream<T>(Func<IRavenQueryable<T>, IQueryable<T>> expression) where T : ZeroIdEntity, new()
|
||||
{
|
||||
IRavenQueryable<T> query = Session.Query<T>();
|
||||
IQueryable<T> 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())
|
||||
{
|
||||
|
||||
@@ -5,7 +5,13 @@ namespace zero.Collections;
|
||||
public abstract partial class CollectionOperations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<EntityResult<T>> Save<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new()
|
||||
public virtual Task<EntityResult<T>> Create<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<EntityResult<T>> Update<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected virtual async Task<EntityResult<T>> Save<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new()
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
|
||||
@@ -150,12 +150,12 @@ public interface ICollectionOperations
|
||||
/// <summary>
|
||||
/// Get entities by query
|
||||
/// </summary>
|
||||
Task<ListResult<T>> Load<T>(ListQuery<T> query) where T : ZeroIdEntity, new();
|
||||
Task<Paged<T>> Load<T>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> expression = default) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Get entities by query (by using the specified index)
|
||||
/// </summary>
|
||||
Task<ListResult<T>> Load<T, TIndex>(ListQuery<T> query) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
|
||||
Task<Paged<T>> Load<T, TIndex>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> expression = default) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
|
||||
|
||||
/// <summary>
|
||||
/// Get all entities from this collection.
|
||||
@@ -171,12 +171,17 @@ public interface ICollectionOperations
|
||||
/// <summary>
|
||||
/// Stream the collection
|
||||
/// </summary>
|
||||
IAsyncEnumerable<T> Stream<T>(Func<IRavenQueryable<T>, IRavenQueryable<T>> expression) where T : ZeroIdEntity, new();
|
||||
IAsyncEnumerable<T> Stream<T>(Func<IRavenQueryable<T>, IQueryable<T>> expression) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Updates or creates an entity with an optional validator
|
||||
/// Creates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<EntityResult<T>> Save<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
Task<EntityResult<T>> Create<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<EntityResult<T>> Update<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an entity
|
||||
|
||||
@@ -43,10 +43,10 @@ public abstract partial class EntityCollection<T> : IEntityCollection<T> where T
|
||||
public virtual Task<Dictionary<string, T>> Load(IEnumerable<string> ids) => Operations.Load<T>(ids);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<ListResult<T>> Load(ListQuery<T> query) => Operations.Load(query);
|
||||
public virtual Task<Paged<T>> Load(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) => Operations.Load(pageNumber, pageSize, querySelector);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<ListResult<T>> Load<TIndex>(ListQuery<T> query) where TIndex : AbstractCommonApiForIndexes, new() => Operations.Load<T, TIndex>(query);
|
||||
public virtual Task<Paged<T>> Load<TIndex>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where TIndex : AbstractCommonApiForIndexes, new() => Operations.Load<T, TIndex>(pageNumber, pageSize, querySelector);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<List<T>> LoadAll() => Operations.LoadAll<T>();
|
||||
@@ -55,10 +55,13 @@ public abstract partial class EntityCollection<T> : IEntityCollection<T> where T
|
||||
public virtual IAsyncEnumerable<T> Stream() => Operations.Stream<T>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IAsyncEnumerable<T> Stream(Func<IRavenQueryable<T>, IRavenQueryable<T>> expression) => Operations.Stream<T>(expression);
|
||||
public virtual IAsyncEnumerable<T> Stream(Func<IRavenQueryable<T>, IQueryable<T>> expression) => Operations.Stream<T>(expression);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<EntityResult<T>> Save(T model) => Operations.Save(model, async m => await Validate(m));
|
||||
public virtual Task<EntityResult<T>> Create(T model) => Operations.Create(model, async m => await Validate(m));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<EntityResult<T>> Update(T model) => Operations.Update(model, async m => await Validate(m));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<EntityResult<T>> Delete(T model) => Operations.Delete(model);
|
||||
@@ -115,12 +118,12 @@ public interface IEntityCollection<T> where T : ZeroIdEntity, new()
|
||||
/// <summary>
|
||||
/// Get entities by query
|
||||
/// </summary>
|
||||
Task<ListResult<T>> Load(ListQuery<T> query);
|
||||
Task<Paged<T>> Load(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get entities by query (by using the specified index)
|
||||
/// </summary>
|
||||
Task<ListResult<T>> Load<TIndex>(ListQuery<T> query) where TIndex : AbstractCommonApiForIndexes, new();
|
||||
Task<Paged<T>> Load<TIndex>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where TIndex : AbstractCommonApiForIndexes, new();
|
||||
|
||||
/// <summary>
|
||||
/// Get all entities from this collection.
|
||||
@@ -136,7 +139,7 @@ public interface IEntityCollection<T> where T : ZeroIdEntity, new()
|
||||
/// <summary>
|
||||
/// Stream the collection
|
||||
/// </summary>
|
||||
IAsyncEnumerable<T> Stream(Func<IRavenQueryable<T>, IRavenQueryable<T>> expression);
|
||||
IAsyncEnumerable<T> Stream(Func<IRavenQueryable<T>, IQueryable<T>> expression);
|
||||
|
||||
/// <summary>
|
||||
/// Validates an entity in this collection
|
||||
@@ -144,9 +147,14 @@ public interface IEntityCollection<T> where T : ZeroIdEntity, new()
|
||||
Task<ValidationResult> Validate(T model);
|
||||
|
||||
/// <summary>
|
||||
/// Updates or creates an entity with an optional validator
|
||||
/// Creates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<EntityResult<T>> Save(T model);
|
||||
Task<EntityResult<T>> Create(T model);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<EntityResult<T>> Update(T model);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an entity
|
||||
|
||||
@@ -5,7 +5,7 @@ public static class EnumerableExtensions
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static ListResult<T> ToQueriedList<T>(this IEnumerable<T> items, ListQuery<T> query) where T : ZeroEntity
|
||||
public static Paged<T> ToQueriedList<T>(this IEnumerable<T> items, ListQuery<T> query) where T : ZeroEntity
|
||||
{
|
||||
//queryable = queryable.Statistics(out QueryStatistics stats);
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class EnumerableExtensions
|
||||
|
||||
List<T> result = items.ToList();
|
||||
|
||||
return new ListResult<T>(result, result.Count, query.Page, query.PageSize);
|
||||
return new Paged<T>(result, result.Count, query.Page, query.PageSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<BackofficeUser>> GetByQuery(ListQuery<BackofficeUser> query)
|
||||
public async Task<Paged<BackofficeUser>> GetByQuery(ListQuery<BackofficeUser> query)
|
||||
{
|
||||
string currentUserId = UserManager.GetUserId(Context.Context.BackofficeUser);
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get all available users (with query)
|
||||
/// </summary>
|
||||
Task<ListResult<BackofficeUser>> GetByQuery(ListQuery<BackofficeUser> query);
|
||||
Task<Paged<BackofficeUser>> GetByQuery(ListQuery<BackofficeUser> query);
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates a user
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
public interface IZeroRouteEntity
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
public class ListQuery<T>
|
||||
{
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using FluentValidation.Results;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
[DataContract(Name = "result", Namespace = "")]
|
||||
public class EntityResult<T>
|
||||
@@ -1,11 +1,35 @@
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a paged result for a model collection
|
||||
/// </summary>
|
||||
public class ListResult<T> : ListResult
|
||||
public class Paged<T> : Paged
|
||||
{
|
||||
public ListResult(long totalItems, long page, long pageSize)
|
||||
public IList<T> Items { get; set; } = new List<T>();
|
||||
|
||||
public Paged(IList<T> items, long totalItems, long pageNumber, long pageSize) : this(totalItems, pageNumber, pageSize)
|
||||
{
|
||||
Items = items;
|
||||
}
|
||||
|
||||
public Paged<TTarget> MapTo<TTarget>(Func<T, TTarget> convertItem)
|
||||
{
|
||||
return new Paged<TTarget>(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<T> : ListResult
|
||||
HasMore = TotalPages > Page;
|
||||
}
|
||||
|
||||
public ListResult(IList<T> items, long totalItems, long pageNumber, long pageSize) : this(totalItems, pageNumber, pageSize)
|
||||
{
|
||||
Items = items;
|
||||
}
|
||||
|
||||
public ListResult<TTarget> MapTo<TTarget>(Func<T, TTarget> convertItem)
|
||||
{
|
||||
return new ListResult<TTarget>(Items.Select(x => convertItem(x)).Where(x => x != null).ToList(), TotalItems, Page, PageSize);
|
||||
}
|
||||
|
||||
public IList<T> Items { get; set; } = new List<T>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the skip size based on the paged parameters specified
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns 0 if the page number or page size is zero
|
||||
/// </remarks>
|
||||
public int GetSkipSize()
|
||||
{
|
||||
if (Page > 0 && PageSize > 0)
|
||||
@@ -50,18 +55,4 @@ public class ListResult<T> : 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; }
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
[RavenCollection("Previews")]
|
||||
public class ZeroEntityPreview : ZeroEntity
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
public class ZeroIdEntity
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace zero;
|
||||
namespace zero.Models;
|
||||
|
||||
public class ZeroReference
|
||||
{
|
||||
@@ -11,7 +11,7 @@ public static class RavenQueryableExtensions
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static async Task<ListResult<T>> FilterAsync<T>(this IRavenQueryable<T> source, ListQuery<T> listQuery)
|
||||
public static async Task<Paged<T>> FilterAsync<T>(this IRavenQueryable<T> source, ListQuery<T> listQuery)
|
||||
{
|
||||
return await source.WithFilter(listQuery).ToListResultAsync(listQuery);
|
||||
}
|
||||
@@ -73,10 +73,10 @@ public static class RavenQueryableExtensions
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static async Task<ListResult<T>> ToListResultAsync<T>(this IQueryable<T> source, ListQuery<T> listQuery, CancellationToken token = default)
|
||||
public static async Task<Paged<T>> ToListResultAsync<T>(this IQueryable<T> source, ListQuery<T> listQuery, CancellationToken token = default)
|
||||
{
|
||||
List<T> results = await source.ToListAsync(token);
|
||||
return new ListResult<T>(results, listQuery.Statistics.TotalResults, listQuery.Page, listQuery.PageSize);
|
||||
return new Paged<T>(results, listQuery.Statistics.TotalResults, listQuery.Page, listQuery.PageSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -22,4 +22,5 @@ global using zero.Rendering;
|
||||
global using zero.Routing;
|
||||
global using zero.Utils;
|
||||
global using zero.Validation;
|
||||
global using zero.Collections;
|
||||
global using zero.Collections;
|
||||
global using zero.Models;
|
||||
@@ -38,7 +38,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<Application>> GetByQuery(ListQuery<Application> query)
|
||||
public async Task<Paged<Application>> GetByQuery(ListQuery<Application> query)
|
||||
{
|
||||
query.SearchFor(entity => entity.Name);
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get all available applications (with query)
|
||||
/// </summary>
|
||||
Task<ListResult<Application>> GetByQuery(ListQuery<Application> query);
|
||||
Task<Paged<Application>> GetByQuery(ListQuery<Application> query);
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates a application
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<RecycledEntity>> GetByQuery(RecycleBinListQuery query)
|
||||
public async Task<Paged<RecycledEntity>> GetByQuery(RecycleBinListQuery query)
|
||||
{
|
||||
query.SearchSelector = x => x.Name;
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get all recycled items
|
||||
/// </summary>
|
||||
Task<ListResult<RecycledEntity>> GetByQuery(RecycleBinListQuery query);
|
||||
Task<Paged<RecycledEntity>> GetByQuery(RecycleBinListQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Get affected entities from a specific operation
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get revision list for an entity
|
||||
/// </summary>
|
||||
public async Task<ListResult<Revision<T>>> GetPagedWithModel<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity
|
||||
public async Task<Paged<Revision<T>>> GetPagedWithModel<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity
|
||||
{
|
||||
// get paged revisions
|
||||
List<T> items = await Session.Advanced.Revisions.GetForAsync<T>(id, (pageNumber - 1) * pageSize, pageSize);
|
||||
@@ -66,16 +66,16 @@ namespace zero.Core.Api
|
||||
revisions.Add(revision);
|
||||
}
|
||||
|
||||
return new ListResult<Revision<T>>(revisions, totalResults, pageNumber, pageSize);
|
||||
return new Paged<Revision<T>>(revisions, totalResults, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get revision list for an entity
|
||||
/// </summary>
|
||||
public async Task<ListResult<Revision>> GetPaged<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity
|
||||
public async Task<Paged<Revision>> GetPaged<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity
|
||||
{
|
||||
ListResult<Revision<T>> items = await GetPagedWithModel<T>(id, pageNumber, pageSize);
|
||||
Paged<Revision<T>> items = await GetPagedWithModel<T>(id, pageNumber, pageSize);
|
||||
return items.MapTo(model => new Revision()
|
||||
{
|
||||
ChangeVector = model.ChangeVector,
|
||||
@@ -92,11 +92,11 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get revision list including models for an entity
|
||||
/// </summary>
|
||||
Task<ListResult<Revision<T>>> GetPagedWithModel<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity;
|
||||
Task<Paged<Revision<T>>> GetPagedWithModel<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get revision list for an entity
|
||||
/// </summary>
|
||||
Task<ListResult<Revision>> GetPaged<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity;
|
||||
Task<Paged<Revision>> GetPaged<T>(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<SpaceContent>> GetListByQuery(string alias, ListQuery<SpaceContent> query)
|
||||
public async Task<Paged<SpaceContent>> GetListByQuery(string alias, ListQuery<SpaceContent> query)
|
||||
{
|
||||
query.SearchSelector = item => item.Name;
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<T>> GetListByQuery<T>(string alias, ListQuery<T> query) where T : SpaceContent
|
||||
public async Task<Paged<T>> GetListByQuery<T>(string alias, ListQuery<T> query) where T : SpaceContent
|
||||
{
|
||||
query.SearchSelector = item => item.Name;
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ListResult<T>> GetListByQuery<T>(ListQuery<T> query) where T : SpaceContent
|
||||
public async Task<Paged<T>> GetListByQuery<T>(ListQuery<T> query) where T : SpaceContent
|
||||
{
|
||||
Space space = GetBy<T>();
|
||||
query.SearchSelector = item => item.Name;
|
||||
@@ -172,17 +172,17 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Get all list items for a space (with query)
|
||||
/// </summary>
|
||||
Task<ListResult<SpaceContent>> GetListByQuery(string alias, ListQuery<SpaceContent> query);
|
||||
Task<Paged<SpaceContent>> GetListByQuery(string alias, ListQuery<SpaceContent> query);
|
||||
|
||||
/// <summary>
|
||||
/// Get all list items for a space (with query)
|
||||
/// </summary>
|
||||
Task<ListResult<T>> GetListByQuery<T>(string alias, ListQuery<T> query) where T : SpaceContent;
|
||||
Task<Paged<T>> GetListByQuery<T>(string alias, ListQuery<T> query) where T : SpaceContent;
|
||||
|
||||
/// <summary>
|
||||
/// Get all list items for a space (with query)
|
||||
/// </summary>
|
||||
Task<ListResult<T>> GetListByQuery<T>(ListQuery<T> query) where T : SpaceContent;
|
||||
Task<Paged<T>> GetListByQuery<T>(ListQuery<T> query) where T : SpaceContent;
|
||||
|
||||
/// <summary>
|
||||
/// Saves a content item in a space
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace zero.Core.Collections
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ListResult<Integration>> Load(ListQuery<Integration> query)
|
||||
public override async Task<Paged<Integration>> Load(ListQuery<Integration> query)
|
||||
{
|
||||
List<Integration> result = new();
|
||||
List<Integration> models = await Session.Query<Integration>().ToListAsync();
|
||||
@@ -257,7 +257,7 @@ namespace zero.Core.Collections
|
||||
/// <summary>
|
||||
/// Get all integrations with the specified query
|
||||
/// </summary>
|
||||
Task<ListResult<Integration>> Load(ListQuery<Integration> query);
|
||||
Task<Paged<Integration>> Load(ListQuery<Integration> query);
|
||||
|
||||
/// <summary>
|
||||
/// Get all integration types with their configuration status
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace zero.Core.Collections
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<ListResult<Media>> Load(MediaListQuery query)
|
||||
public virtual async Task<Paged<Media>> Load(MediaListQuery query)
|
||||
{
|
||||
query.SearchFor(entity => entity.Name);
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace zero.Core.Collections
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<ListResult<MediaListItem>> Load(MediaListItemQuery query)
|
||||
public virtual async Task<Paged<MediaListItem>> 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<MediaListItem> result = await dbQuery.ToQueriedListAsyncX(query);
|
||||
Paged<MediaListItem> 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
|
||||
/// <summary>
|
||||
/// Get all available media items with query
|
||||
/// </summary>
|
||||
Task<ListResult<Media>> Load(MediaListQuery query);
|
||||
Task<Paged<Media>> Load(MediaListQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Get all available media items (including folders) with query
|
||||
/// </summary>
|
||||
Task<ListResult<MediaListItem>> Load(MediaListItemQuery query);
|
||||
Task<Paged<MediaListItem>> Load(MediaListItemQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Move a file to a new parent
|
||||
|
||||
Reference in New Issue
Block a user