diff --git a/zero.Core/Api/ListsApi.cs b/zero.Core/Api/ListsApi.cs new file mode 100644 index 00000000..6a25b04d --- /dev/null +++ b/zero.Core/Api/ListsApi.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.Options; +using Raven.Client.Documents; +using Raven.Client.Documents.Session; +using System.Collections.Generic; +using System.Threading.Tasks; +using zero.Core.Entities; + +namespace zero.Core.Api +{ + public class ListsApi : IListsApi + { + protected IDocumentStore Raven { get; private set; } + + protected ZeroOptions Options { get; private set; } + + + public ListsApi(IDocumentStore raven, IOptionsMonitor options) + { + Raven = raven; + Options = options.CurrentValue; + } + + + /// + public ListCollections GetCollections() + { + return Options.Lists; + } + + + /// + public async Task> GetAll(string alias) where T : ListItem + { + using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) + { + return await session.Query().ProjectInto().ToListAsync(); + } + } + } + + + public interface IListsApi + { + /// + /// Get all list collections + /// + ListCollections GetCollections(); + + /// + /// Get all list items by a list collection alias + /// + Task> GetAll(string alias) where T : ListItem; + } +} diff --git a/zero.Core/Entities/Lists/ListCollection.cs b/zero.Core/Entities/Lists/ListCollection.cs new file mode 100644 index 00000000..85b37d5f --- /dev/null +++ b/zero.Core/Entities/Lists/ListCollection.cs @@ -0,0 +1,17 @@ +using System; + +namespace zero.Core.Entities +{ + public class ListCollection + { + public string Alias { get; set; } + + public Type Type { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public string Icon { get; set; } + } +} \ No newline at end of file diff --git a/zero.Core/Entities/Lists/ListCollections.cs b/zero.Core/Entities/Lists/ListCollections.cs new file mode 100644 index 00000000..b8c87f71 --- /dev/null +++ b/zero.Core/Entities/Lists/ListCollections.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace zero.Core.Entities +{ + public class ListCollections : List + { + public void Add(string alias, string name, string description, string icon) where T : ListItem, new() + { + Add(new ListCollection() + { + Alias = alias, + Name = name, + Description = description, + Icon = icon, + Type = typeof(T) + }); + } + } +} diff --git a/zero.Core/Entities/Lists/ListItem.cs b/zero.Core/Entities/Lists/ListItem.cs new file mode 100644 index 00000000..e62c2708 --- /dev/null +++ b/zero.Core/Entities/Lists/ListItem.cs @@ -0,0 +1,11 @@ +namespace zero.Core.Entities +{ + /// + /// A list item can consist of unlimited properties and be rendered as you wish + /// The backoffice rendering is done by an IRenderer + /// + public abstract class ListItem : DatabaseEntity + { + + } +} \ No newline at end of file diff --git a/zero.Core/ZeroOptions.cs b/zero.Core/ZeroOptions.cs index 665ad08d..73fa8f49 100644 --- a/zero.Core/ZeroOptions.cs +++ b/zero.Core/ZeroOptions.cs @@ -10,6 +10,8 @@ namespace zero.Core public SectionCollection Sections { get; private set; } = new SectionCollection(); + public ListCollections Lists { get; private set; } = new ListCollections(); + public IList SettingsAreas { get; private set; } = new List(); public ZeroAuthorizationOptions Authorization { get; private set; } = new ZeroAuthorizationOptions(); diff --git a/zero.TestData/Lists/News.cs b/zero.TestData/Lists/News.cs new file mode 100644 index 00000000..6d844197 --- /dev/null +++ b/zero.TestData/Lists/News.cs @@ -0,0 +1,9 @@ +using zero.Core.Entities; + +namespace zero.TestData +{ + public class News : ListItem + { + + } +} diff --git a/zero.TestData/Lists/TeamMember.cs b/zero.TestData/Lists/TeamMember.cs new file mode 100644 index 00000000..7c23f3ea --- /dev/null +++ b/zero.TestData/Lists/TeamMember.cs @@ -0,0 +1,15 @@ +using zero.Core.Entities; + +namespace zero.TestData +{ + public class TeamMember : ListItem + { + public string Position { get; set; } + + public Media Image { get; set; } + + public string Email { get; set; } + + public string VideoUri { get; set; } + } +} diff --git a/zero.Web/Plugins/MyPlugin/myplugin-settings.vue b/zero.TestData/Plugins/MyPlugin/myplugin-settings.vue similarity index 100% rename from zero.Web/Plugins/MyPlugin/myplugin-settings.vue rename to zero.TestData/Plugins/MyPlugin/myplugin-settings.vue diff --git a/zero.Web/Plugins/MyPlugin/myplugin.vue b/zero.TestData/Plugins/MyPlugin/myplugin.vue similarity index 100% rename from zero.Web/Plugins/MyPlugin/myplugin.vue rename to zero.TestData/Plugins/MyPlugin/myplugin.vue diff --git a/zero.Web/Plugins/MyPlugin/routes.js b/zero.TestData/Plugins/MyPlugin/routes.js similarity index 100% rename from zero.Web/Plugins/MyPlugin/routes.js rename to zero.TestData/Plugins/MyPlugin/routes.js diff --git a/zero.TestData/zero.TestData.csproj b/zero.TestData/zero.TestData.csproj new file mode 100644 index 00000000..64f7a098 --- /dev/null +++ b/zero.TestData/zero.TestData.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/zero.Web/App/Components/permissions.vue b/zero.Web/App/Components/permissions.vue index a9d2b175..e382a3d2 100644 --- a/zero.Web/App/Components/permissions.vue +++ b/zero.Web/App/Components/permissions.vue @@ -5,6 +5,7 @@ {{ permissionCollection.label | localize }} {{ permissionCollection.description | localize }} + diff --git a/zero.Web/App/Pages/lists/list.vue b/zero.Web/App/Pages/lists/list.vue new file mode 100644 index 00000000..7ee1bec4 --- /dev/null +++ b/zero.Web/App/Pages/lists/list.vue @@ -0,0 +1,107 @@ + + + + \ No newline at end of file diff --git a/zero.Web/App/Pages/lists/routes.js b/zero.Web/App/Pages/lists/routes.js new file mode 100644 index 00000000..2859d3d7 --- /dev/null +++ b/zero.Web/App/Pages/lists/routes.js @@ -0,0 +1,36 @@ +import { find as _find } from 'underscore'; + +const alias = zero.alias.sections.lists; +const section = _find(zero.sections, section => section.alias === alias); +let routes = []; + +if (section) +{ + routes.push({ + path: 'edit/:id', + props: true, + name: 'listitem', + component: () => import('zero/pages/' + alias + '/item') + }); + + routes.push({ + path: ':alias', + props: true, + name: 'list', + component: () => import('zero/pages/' + alias + '/list') + }); + + //routes.push({ + // path: 'history', + // name: 'history', + // component: () => import('zero/pages/' + alias + '/history'), + // meta: { + // name: '@page.history.name' + // } + //}); +} + +export default { + section: alias, + routes: routes +}; \ No newline at end of file diff --git a/zero.Web/App/Resources/lists.js b/zero.Web/App/Resources/lists.js new file mode 100644 index 00000000..a0f648f4 --- /dev/null +++ b/zero.Web/App/Resources/lists.js @@ -0,0 +1,18 @@ +import Axios from 'axios'; + +export default { + + // get all list collections + getCollections() + { + return Axios.get('lists/getCollections').then(res => Promise.resolve(res.data)); + }, + + // get all list items in a collection + getAll(alias, query) + { + query.alias = alias; + + return Axios.get('lists/getAll', { params: query }).then(res => Promise.resolve(res.data)); + } +}; \ No newline at end of file diff --git a/zero.Web/App/pages/lists/lists.vue b/zero.Web/App/pages/lists/lists.vue index a9c98d0a..9e116657 100644 --- a/zero.Web/App/pages/lists/lists.vue +++ b/zero.Web/App/pages/lists/lists.vue @@ -1,107 +1,229 @@  - \ No newline at end of file + + + + \ No newline at end of file diff --git a/zero.Web/Controllers/ListsController.cs b/zero.Web/Controllers/ListsController.cs new file mode 100644 index 00000000..97ef27fc --- /dev/null +++ b/zero.Web/Controllers/ListsController.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using zero.Core; +using zero.Core.Api; +using zero.Core.Entities; +using zero.Core.Identity; +using zero.Web.Mapper; +using zero.Web.Models; + +namespace zero.Web.Controllers +{ + [ZeroAuthorize(Permissions.Sections.Lists, PermissionsValue.True)] + public class ListsController : BackofficeController + { + private IListsApi Api { get; set; } + + public ListsController(IZeroConfiguration config, IListsApi api, IMapper mapper, IToken token) : base(config, mapper, token) + { + Api = api; + } + + + /// + /// Get all list collections + /// + public IActionResult GetCollections([FromQuery] string id) + { + return Json(Api.GetCollections()); + } + + + /// + /// Get list items in a collection + /// + //public async Task GetAll([FromQuery] string alias, [FromQuery] ListQuery query = null) + //{ + // return await As(await Api.GetAll(alias)); + //} + } +} diff --git a/zero.Web/Startup.cs b/zero.Web/Startup.cs index 606e16cb..92f6cf5d 100644 --- a/zero.Web/Startup.cs +++ b/zero.Web/Startup.cs @@ -15,6 +15,7 @@ using System; using zero.Core; using zero.Core.Api; using zero.Core.Extensions; +using zero.TestData; namespace zero.Web { @@ -75,6 +76,9 @@ namespace zero.Web //services.AddCore(appConfig, env); services.AddZero(opts => { + opts.Lists.Add("team", "Team", "Our team members", "fth-users"); + opts.Lists.Add("news", "News", "Articles about the company", "fth-edit"); + //var commercePermissions = new Core.Identity.PermissionCollection() //{ // Label = "Commerce" @@ -133,6 +137,7 @@ namespace zero.Web services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } /// diff --git a/zero.Web/ZeroBuilder.cs b/zero.Web/ZeroBuilder.cs index 40f48c24..836dd3bd 100644 --- a/zero.Web/ZeroBuilder.cs +++ b/zero.Web/ZeroBuilder.cs @@ -56,7 +56,7 @@ namespace zero.Web //opts.Cookie.Path = // TODO use backoffice path opts.Cookie.Name = Constants.Auth.CookieName; opts.SlidingExpiration = true; - opts.ExpireTimeSpan = TimeSpan.FromMinutes(10); + opts.ExpireTimeSpan = TimeSpan.FromMinutes(60); // override redirect to login page (handled by vue frontend) and return a 401 instead opts.Events.OnRedirectToLogin = (context) => diff --git a/zero.Web/zero.Web.csproj b/zero.Web/zero.Web.csproj index 7d3cf7a4..a1dfc6a5 100644 --- a/zero.Web/zero.Web.csproj +++ b/zero.Web/zero.Web.csproj @@ -44,6 +44,7 @@ + diff --git a/zero.sln b/zero.sln index cf67e8a1..950aaba9 100644 --- a/zero.sln +++ b/zero.sln @@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29709.97 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zero.Web", "zero.Web\zero.Web.csproj", "{8C14E4B1-8C00-4B4B-869B-4D2084ABE1F3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zero.Web", "zero.Web\zero.Web.csproj", "{8C14E4B1-8C00-4B4B-869B-4D2084ABE1F3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zero.Core", "zero.Core\zero.Core.csproj", "{7BFF3DDE-F910-4CE1-9B94-846059CE80DA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zero.Core", "zero.Core\zero.Core.csproj", "{7BFF3DDE-F910-4CE1-9B94-846059CE80DA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zero.TestData", "zero.TestData\zero.TestData.csproj", "{2DF370E9-63E9-4213-8490-97BDA5CB5C01}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {7BFF3DDE-F910-4CE1-9B94-846059CE80DA}.Debug|Any CPU.Build.0 = Debug|Any CPU {7BFF3DDE-F910-4CE1-9B94-846059CE80DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {7BFF3DDE-F910-4CE1-9B94-846059CE80DA}.Release|Any CPU.Build.0 = Release|Any CPU + {2DF370E9-63E9-4213-8490-97BDA5CB5C01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DF370E9-63E9-4213-8490-97BDA5CB5C01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DF370E9-63E9-4213-8490-97BDA5CB5C01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DF370E9-63E9-4213-8490-97BDA5CB5C01}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE