diff --git a/zero.Core/Api/RevisionsApi.cs b/zero.Core/Api/RevisionsApi.cs index 05bd0cc1..8a9f4972 100644 --- a/zero.Core/Api/RevisionsApi.cs +++ b/zero.Core/Api/RevisionsApi.cs @@ -1,80 +1,56 @@ -using Newtonsoft.Json; -using Raven.Client.Documents; -using Raven.Client.Documents.Commands; -using Raven.Client.Documents.Operations; -using Raven.Client.Documents.Session; -using Sparrow.Json; -using System; +using Raven.Client.Documents.Session; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using zero.Core.Database; using zero.Core.Entities; using zero.Core.Extensions; -using zero.Core.Options; namespace zero.Core.Api { public class RevisionsApi : IRevisionsApi { - protected IZeroOptions Options { get; set; } + protected IAsyncDocumentSession Session { get; set; } - protected IZeroStore Store { get; set; } - - protected IUserApi UserApi { get; set; } - - - public RevisionsApi(IZeroOptions options, IZeroStore store, IUserApi userApi) + public RevisionsApi(IAsyncDocumentSession session) { - Options = options; - Store = store; - UserApi = userApi; + Session = session; } /// /// Get revision list for an entity /// - public async Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10, bool includeContent = false) where T : ZeroEntity + public async Task>> GetPagedWithModel(string id, int pageNumber = 1, int pageSize = 10) where T : ZeroEntity { - using IAsyncDocumentSession session = Store.OpenAsyncSession(); - // get paged revisions - List items = await session.Advanced.Revisions.GetForAsync(id, (pageNumber - 1) * pageSize, pageSize); - double totalResults = items.Count; - - // get total results in case items count equals page size - if (pageSize <= items.Count || pageNumber > 1) - { - GetRevisionsCommand command = new GetRevisionsCommand(id, 0, 1, true); - session.Advanced.RequestExecutor.Execute(command, session.Advanced.Context); - command.Result.Results.Parent.TryGet("TotalResults", out totalResults); - } + List items = await Session.Advanced.Revisions.GetForAsync(id, (pageNumber - 1) * pageSize, pageSize); + long totalResults = await Session.Advanced.Revisions.GetCountForAsync(id); // add creation for last page - if (!items.Any() && pageNumber >= (int)Math.Ceiling((decimal)totalResults / pageSize)) - { - T currentRevision = await session.LoadAsync(id); - if (currentRevision != null) - { - items.Add(currentRevision); - } - } + //if (!items.Any() && pageNumber >= (int)Math.Ceiling((decimal)totalResults / pageSize)) + //{ + // T currentRevision = await Session.LoadAsync(id); + // if (currentRevision != null) + // { + // items.Add(currentRevision); + // } + //} - List revisions = new List(); + List> revisions = new(); // load affected users as the revisions could have been edited by other users too string[] userIds = items.Select(x => x.LastModifiedById).Distinct().ToArray(); - Dictionary users = await UserApi.GetByIds(userIds); + Dictionary users = await Session.LoadAsync(userIds); // create revision objects foreach (T item in items) { - Revision revision = new Revision() + Revision revision = new() { - ChangeVector = session.Advanced.GetChangeVectorFor(item), - Date = item.LastModifiedDate, - Json = includeContent ? JsonConvert.SerializeObject(item) : null + Model = item, + ModelId = item.Id, + ChangeVector = Session.Advanced.GetChangeVectorFor(item), + Date = item.LastModifiedDate }; if (!item.LastModifiedById.IsNullOrEmpty() && users.TryGetValue(item.LastModifiedById, out BackofficeUser user) && user != null) @@ -90,7 +66,23 @@ namespace zero.Core.Api revisions.Add(revision); } - return new ListResult(revisions, Convert.ToInt64(totalResults), pageNumber, pageSize); + return new ListResult>(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 + { + ListResult> items = await GetPagedWithModel(id, pageNumber, pageSize); + return items.MapTo(model => new Revision() + { + ChangeVector = model.ChangeVector, + Date = model.Date, + ModelId = model.ModelId, + User = model.User + }); } } @@ -98,8 +90,13 @@ namespace zero.Core.Api public interface IRevisionsApi { /// - /// Get revision list (without content JSON) for an entity + /// Get revision list including models for an entity /// - Task> GetPaged(string id, int pageNumber = 1, int pageSize = 10, bool includeContent = false) 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; } } diff --git a/zero.Core/Collections/CollectionBase.cs b/zero.Core/Collections/CollectionBase.cs index 35a48101..ed12405f 100644 --- a/zero.Core/Collections/CollectionBase.cs +++ b/zero.Core/Collections/CollectionBase.cs @@ -20,6 +20,7 @@ namespace zero.Core.Collections public abstract class CollectionBase : ICollectionBase, IDisposable where T : ZeroEntity { private IAsyncDocumentSession _session; + private IRevisionsApi _revisions; private string _database; protected ICollectionInterceptorHandler InterceptorHandler { get; private set; } @@ -54,6 +55,23 @@ namespace zero.Core.Collections /// protected readonly IValidator Validator; + /// + /// Manage revisions + /// + protected IRevisionsApi Revisions + { + get + { + if (_revisions != null) + { + return _revisions; + } + + _revisions = new RevisionsApi(Session); + return _revisions; + } + } + /// /// Create an an async document session /// @@ -108,12 +126,16 @@ namespace zero.Core.Collections /// - public virtual async Task GetById(string id) + public virtual async Task GetById(string id, string changeVector = null) { if (id.IsNullOrWhiteSpace()) { return default; } + if (!changeVector.IsNullOrEmpty()) + { + return WhenActive(await GetRevision(changeVector)); + } return WhenActive(await Session.LoadAsync(id)); } @@ -158,6 +180,25 @@ namespace zero.Core.Collections } + /// + public virtual async Task> GetRevisions(string id, int page = 1, int pageSize = 10) + { + if (id.IsNullOrWhiteSpace()) + { + return default; + } + + return await Revisions.GetPaged(id, page, pageSize); + } + + + /// + public virtual async Task GetRevision(string changeVector) + { + return await Session.Advanced.Revisions.GetAsync(changeVector); + } + + /// public virtual IAsyncEnumerable Stream() => Stream(null); @@ -445,7 +486,7 @@ namespace zero.Core.Collections /// /// Get an entity by Id /// - Task GetById(string id); + Task GetById(string id, string changeVector = null); /// /// Get entities by ids @@ -463,6 +504,16 @@ namespace zero.Core.Collections /// Task> GetAll(); + /// + /// Get page revisions for the specified entity + /// + Task> GetRevisions(string id, int page = 1, int pageSize = 10); + + /// + /// Get a revision by change vector + /// + Task GetRevision(string changeVector); + /// /// Stream the collection /// diff --git a/zero.Core/Collections/Media/MediaCollection.cs b/zero.Core/Collections/Media/MediaCollection.cs index 3a0ba216..d43bfd43 100644 --- a/zero.Core/Collections/Media/MediaCollection.cs +++ b/zero.Core/Collections/Media/MediaCollection.cs @@ -62,7 +62,7 @@ namespace zero.Core.Collections protected IPaths Paths { get; set; } - public override Task GetById(string id) + public override Task GetById(string id, string changeVector = null) { ApplyScopeBasedOnId(id); return base.GetById(id); diff --git a/zero.Core/Database/ZeroDocumentConventionsBuilder.cs b/zero.Core/Database/ZeroDocumentConventionsBuilder.cs index ce856acb..d4171f84 100644 --- a/zero.Core/Database/ZeroDocumentConventionsBuilder.cs +++ b/zero.Core/Database/ZeroDocumentConventionsBuilder.cs @@ -81,7 +81,7 @@ namespace zero.Core.Database }; // get inner type for revisions - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Revision<>)) + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Raven.Client.Documents.Subscriptions.Revision<>)) { type = type.GetGenericArguments().FirstOrDefault(); } diff --git a/zero.Core/Entities/Revision.cs b/zero.Core/Entities/Revision.cs index 884e2fc7..ac0d88fd 100644 --- a/zero.Core/Entities/Revision.cs +++ b/zero.Core/Entities/Revision.cs @@ -2,15 +2,20 @@ namespace zero.Core.Entities { + public class Revision : Revision where T : ZeroEntity + { + public T Model { get; set; } + } + public class Revision { + public string ModelId { get; set; } + public RevisionUser User { get; set; } public DateTimeOffset Date { get; set; } public string ChangeVector { get; set; } - - public string Json { get; set; } } public class RevisionUser diff --git a/zero.Web.UI/App/editor/editor.scss b/zero.Web.UI/App/editor/editor.scss index 49eca368..ced5eaa3 100644 --- a/zero.Web.UI/App/editor/editor.scss +++ b/zero.Web.UI/App/editor/editor.scss @@ -119,4 +119,32 @@ margin-top: 0; padding-top: 0; border-top: none; +} + +.editor-aside-links +{ + display: flex; + width: 100%; + margin-bottom: -5px; +} + +.editor-aside-links .ui-link +{ + color: var(--color-text-dim); + font-size: var(--font-size-s); + text-decoration: none !important; + position: relative; +} + +.editor-aside-links .ui-link + .ui-link +{ + margin-left: 20px; +} + +.editor-aside-links .ui-link + .ui-link:before +{ + content: '·'; + position: absolute; + left: -12px; + top: -1px; } \ No newline at end of file diff --git a/zero.Web.UI/app/utils/request.js b/zero.Web.UI/app/utils/request.js new file mode 100644 index 00000000..1cac91a6 --- /dev/null +++ b/zero.Web.UI/app/utils/request.js @@ -0,0 +1,247 @@ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.download = exports.collection = exports.send = exports.patch = exports.put = exports.del = exports.post = exports.get = void 0; +var axios_1 = require("axios"); +var getConfig = function (config) { + config = config || {}; + if (config.scope) { + config.params = config.params || {}; + config.params.scope = config.scope; + } + return config; +}; +function get(url, config) { + if (config === void 0) { config = null; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, send(__assign({ method: 'get', url: url }, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); +} +exports.get = get; +function post(url, data, config) { + if (data === void 0) { data = null; } + if (config === void 0) { config = null; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, send(__assign({ method: 'post', url: url, data: data }, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); +} +exports.post = post; +function del(url, config) { + if (config === void 0) { config = null; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, send(__assign({ method: 'delete', url: url }, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); +} +exports.del = del; +function put(url, data, config) { + if (data === void 0) { data = null; } + if (config === void 0) { config = null; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, send(__assign({ method: 'put', url: url, data: data }, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); +} +exports.put = put; +function patch(url, data, config) { + if (data === void 0) { data = null; } + if (config === void 0) { config = null; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, send(__assign({ method: 'patch', url: url, data: data }, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); +} +exports.patch = patch; +function send(config) { + return __awaiter(this, void 0, void 0, function () { + var result, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + config = getConfig(config); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, axios_1.default(config)]; + case 2: + result = _a.sent(); + return [2 /*return*/, result.data]; + case 3: + err_1 = _a.sent(); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/]; + } + }); + }); +} +exports.send = send; +function collection(base) { + var _this = this; + return { + getById: function (id, changeVector, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getById', __assign(__assign({}, config), { params: { id: id, changeVector: changeVector } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getByIds: function (ids, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getByIds', __assign(__assign({}, config), { params: { ids: ids } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getEmpty: function (config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getEmpty', __assign({}, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getByQuery: function (query, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getByQuery', __assign(__assign({}, config), { params: { query: query } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getAll: function (config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getAll', __assign({}, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getPreviews: function (ids, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getPreviews', __assign(__assign({}, config), { params: { ids: ids } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getForPicker: function (config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getForPicker', __assign({}, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + getRevisions: function (id, query, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, get(base + 'getRevisions', __assign(__assign({}, config), { params: { id: id, query: query } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + save: function (model, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, post(base + 'save', model, __assign({}, config))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); }, + delete: function (id, config) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, del(base + 'delete', __assign(__assign({}, config), { params: { id: id } }))]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); }); } + }; +} +exports.collection = collection; +function download(response) { + var filename = response.headers["zero-filename"] || 'download.bin'; + // code from: https://github.com/kennethjiang/js-file-download/blob/master/file-download.js + var blob = response.data; + if (typeof window.navigator.msSaveBlob !== 'undefined') { + // IE workaround for "HTML7007: One or more blob URLs were + // revoked by closing the blob for which they were created. + // These URLs will no longer resolve as the data backing + // the URL has been freed." + window.navigator.msSaveBlob(blob, filename); + } + else { + var blobURL = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(blob) : window.webkitURL.createObjectURL(blob); + var tempLink = document.createElement('a'); + tempLink.style.display = 'none'; + tempLink.href = blobURL; + tempLink.setAttribute('download', filename); + // Safari thinks _blank anchor are pop ups. We only want to set _blank + // target if the browser does not support the HTML5 download attribute. + // This allows you to download files in desktop safari if pop up blocking + // is enabled. + if (typeof tempLink.download === 'undefined') { + tempLink.setAttribute('target', '_blank'); + } + document.body.appendChild(tempLink); + tempLink.click(); + // Fixes "webkit blob resource error 1" + setTimeout(function () { + document.body.removeChild(tempLink); + window.URL.revokeObjectURL(blobURL); + }, 200); + } +} +exports.download = download; +; +//# sourceMappingURL=request.js.map \ No newline at end of file diff --git a/zero.Web.UI/app/utils/request.js.map b/zero.Web.UI/app/utils/request.js.map new file mode 100644 index 00000000..e50257cc --- /dev/null +++ b/zero.Web.UI/app/utils/request.js.map @@ -0,0 +1 @@ +{"version":3,"file":"request.js","sourceRoot":"","sources":["request.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/zero.Web.UI/app/utils/request.ts b/zero.Web.UI/app/utils/request.ts index 2399ec29..ecb53b67 100644 --- a/zero.Web.UI/app/utils/request.ts +++ b/zero.Web.UI/app/utils/request.ts @@ -56,13 +56,14 @@ export async function send(config) export function collection(base) { return { - getById: async (id, config) => await get(base + 'getById', { ...config, params: { id } }), + getById: async (id, changeVector, config) => await get(base + 'getById', { ...config, params: { id, changeVector } }), getByIds: async (ids, config) => await get(base + 'getByIds', { ...config, params: { ids } }), getEmpty: async config => await get(base + 'getEmpty', { ...config }), getByQuery: async (query, config) => await get(base + 'getByQuery', { ...config, params: { query } }), getAll: async (config) => await get(base + 'getAll', { ...config }), getPreviews: async (ids, config) => await get(base + 'getPreviews', { ...config, params: { ids } }), getForPicker: async (config) => await get(base + 'getForPicker', { ...config }), + getRevisions: async (id, query, config) => await get(base + 'getRevisions', { ...config, params: { id, query } }), save: async (model, config) => await post(base + 'save', model, { ...config }), delete: async (id, config) => await del(base + 'delete', { ...config, params: { id } }) }; diff --git a/zero.Web/Controllers/BackofficeCollectionController.cs b/zero.Web/Controllers/BackofficeCollectionController.cs index 4d0ae8e3..bce84aad 100644 --- a/zero.Web/Controllers/BackofficeCollectionController.cs +++ b/zero.Web/Controllers/BackofficeCollectionController.cs @@ -35,7 +35,7 @@ namespace zero.Web.Controllers } - public virtual async Task> GetById([FromQuery] string id) => Edit(await Collection.GetById(id)); + 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); @@ -57,6 +57,12 @@ namespace zero.Web.Controllers } + 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); diff --git a/zero.Web/Controllers/PagesController.cs b/zero.Web/Controllers/PagesController.cs index 9863e259..f66e1af0 100644 --- a/zero.Web/Controllers/PagesController.cs +++ b/zero.Web/Controllers/PagesController.cs @@ -16,12 +16,10 @@ namespace zero.Web.Controllers { IPagesApi Api; IZeroStore Store; - IRevisionsApi RevisionsApi; - public PagesController(IPagesApi api, IRevisionsApi revisionsApi, IZeroStore store) + public PagesController(IPagesApi api, IZeroStore store) { Api = api; - RevisionsApi = revisionsApi; Store = store; } @@ -88,7 +86,8 @@ namespace zero.Web.Controllers } - public async Task> GetRevisions([FromQuery] string id, [FromQuery] int page = 1) => await RevisionsApi.GetPaged(id, page); + //public async Task> GetRevisions([FromQuery] string id, [FromQuery] int page = 1) => await RevisionsApi.GetPaged(id, page); + // TODO this endpoint is available when pages controller moved to BackofficeCollectionController public async Task> Save([FromBody] Page model) => await Api.Save(model); diff --git a/zero.Web/Defaults/ZeroBackofficePlugin.cs b/zero.Web/Defaults/ZeroBackofficePlugin.cs index 9ce8df18..ad15cb66 100644 --- a/zero.Web/Defaults/ZeroBackofficePlugin.cs +++ b/zero.Web/Defaults/ZeroBackofficePlugin.cs @@ -99,7 +99,6 @@ namespace zero.Web.Defaults services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient();