diff --git a/Piranha.sln b/Piranha.sln index 8461c4da5..6d3f13210 100644 --- a/Piranha.sln +++ b/Piranha.sln @@ -21,7 +21,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piranha.AttributeBuilder.Te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".global", ".global", "{88054E40-5547-4EA5-B75E-D446E8E5A021}" ProjectSection(SolutionItems) = preProject - global.json = global.json LICENSE = LICENSE README.md = README.md EndProjectSection @@ -66,6 +65,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piranha.AspNetCore.Identity EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcWeb", "examples\MvcWeb\MvcWeb.csproj", "{14F9CA8A-6532-40CC-81D6-9763351AFD91}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piranha.Extensions.Sync", "core\Piranha.Extensions.Sync\Piranha.Extensions.Sync.csproj", "{D17485D2-371E-4F7C-952F-57B26606FC6A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data", "data", "{DCC52317-BC88-4F0A-923F-C9D9F6D952ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piranha.Data.EF", "data\Piranha.Data.EF\Piranha.Data.EF.csproj", "{4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -136,6 +141,14 @@ Global {14F9CA8A-6532-40CC-81D6-9763351AFD91}.Debug|Any CPU.Build.0 = Debug|Any CPU {14F9CA8A-6532-40CC-81D6-9763351AFD91}.Release|Any CPU.ActiveCfg = Release|Any CPU {14F9CA8A-6532-40CC-81D6-9763351AFD91}.Release|Any CPU.Build.0 = Release|Any CPU + {D17485D2-371E-4F7C-952F-57B26606FC6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D17485D2-371E-4F7C-952F-57B26606FC6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D17485D2-371E-4F7C-952F-57B26606FC6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D17485D2-371E-4F7C-952F-57B26606FC6A}.Release|Any CPU.Build.0 = Release|Any CPU + {4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -157,6 +170,8 @@ Global {CC742AA4-A544-46D4-901C-7A605B4F132B} = {42A64587-89B5-48F2-84D4-85B97F7847F1} {BB0DADBE-987B-4DE6-8173-49BC8BA139BB} = {42A64587-89B5-48F2-84D4-85B97F7847F1} {14F9CA8A-6532-40CC-81D6-9763351AFD91} = {A82A5922-BF05-4DD7-A2C1-DD22F1AD6A57} + {D17485D2-371E-4F7C-952F-57B26606FC6A} = {42A64587-89B5-48F2-84D4-85B97F7847F1} + {4CCD6AF9-3A1D-41E3-B58F-80E9D6616CA3} = {DCC52317-BC88-4F0A-923F-C9D9F6D952ED} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CF88AA05-D127-4707-8214-A230F0DA569B} diff --git a/appveyor.yml b/appveyor.yml index bf39c29cd..e12270d61 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,5 +14,5 @@ before_build: build_script: - dotnet build -c Debug /p:DebugType=full test_script: - - ps: .\tools\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user -target:".\script\tests.bat" -searchdirs:".\test\Piranha.Tests\bin\Debug\netcoreapp1.1;.\test\Piranha.AttributeBuilder.Tests\bin\Debug\netcoreapp1.1;.\test\Piranha.ImageSharp.Tests\bin\Debug\netcoreapp1.1" -oldstyle -output:coverage.xml -skipautoprops -returntargetcode -filter:"+[Piranha*]* -[*Tests]*" -excludebyattribute:"Piranha.NoCoverageAttribute" + - ps: .\tools\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user -target:".\script\tests.bat" -searchdirs:".\test\Piranha.Tests\bin\Debug\netcoreapp2.1;.\test\Piranha.AttributeBuilder.Tests\bin\Debug\netcoreapp2.1;.\test\Piranha.ImageSharp.Tests\bin\Debug\netcoreapp2.1" -oldstyle -output:coverage.xml -skipautoprops -returntargetcode -filter:"+[Piranha*]* -[*Tests]*" -excludebyattribute:"Piranha.NoCoverageAttribute" - ps: .\tools\coveralls.net.0.7.0\tools\csmacnz.coveralls.exe --opencover -i coverage.xml --repoToken $env:COVERALLS_REPO_TOKEN --useRelativePaths --commitId $env:APPVEYOR_REPO_COMMIT --commitBranch $env:APPVEYOR_REPO_BRANCH --commitAuthor $env:APPVEYOR_REPO_COMMIT_AUTHOR --commitEmail $env:APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL --commitMessage $env:APPVEYOR_REPO_COMMIT_MESSAGE --jobId $env:APPVEYOR_BUILD_NUMBER --serviceName appveyor diff --git a/core/Piranha.AspNetCore/AliasMiddleware.cs b/core/Piranha.AspNetCore/AliasMiddleware.cs index fb3b092e8..8b9c6dadd 100644 --- a/core/Piranha.AspNetCore/AliasMiddleware.cs +++ b/core/Piranha.AspNetCore/AliasMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -38,7 +38,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer { var url = context.Request.Path.HasValue ? context.Request.Path.Value : ""; - var response = AliasRouter.Invoke(api, url, service.Site.Id); + var response = await AliasRouter.InvokeAsync(api, url, service.Site.Id); if (response != null) { _logger?.LogInformation($"Found alias\n Alias: {url}\n Redirect: {response.RedirectUrl}"); diff --git a/core/Piranha.AspNetCore/ApplicationMiddleware.cs b/core/Piranha.AspNetCore/ApplicationMiddleware.cs index 2da542f7b..10d9a034a 100644 --- a/core/Piranha.AspNetCore/ApplicationMiddleware.cs +++ b/core/Piranha.AspNetCore/ApplicationMiddleware.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -37,7 +37,7 @@ public ApplicationMiddleware(RequestDelegate next, ILoggerFactory factory = null /// An async task public override async Task Invoke(HttpContext context, IApi api, IApplicationService service) { - service.Init(context); + await service.InitAsync(context); // Set culture if applicable if (!string.IsNullOrEmpty(service.Site.Culture)) diff --git a/core/Piranha.AspNetCore/ArchiveMiddleware.cs b/core/Piranha.AspNetCore/ArchiveMiddleware.cs index 791ceb508..602714374 100644 --- a/core/Piranha.AspNetCore/ArchiveMiddleware.cs +++ b/core/Piranha.AspNetCore/ArchiveMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -38,7 +38,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer var url = context.Request.Path.HasValue ? context.Request.Path.Value : ""; var siteId = service.Site.Id; - var response = ArchiveRouter.Invoke(api, url, siteId); + var response = await ArchiveRouter.InvokeAsync(api, url, siteId); if (response != null) { _logger?.LogInformation($"Found archive\n Route: {response.Route}\n Params: {response.QueryString}"); diff --git a/core/Piranha.AspNetCore/CoreExtensions.cs b/core/Piranha.AspNetCore/CoreExtensions.cs index 82af8085e..18b71a725 100644 --- a/core/Piranha.AspNetCore/CoreExtensions.cs +++ b/core/Piranha.AspNetCore/CoreExtensions.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Builder; @@ -112,11 +112,11 @@ public static IApplicationBuilder UsePiranhaStartPage(this IApplicationBuilder b /// /// The current application builder /// The builder - [Obsolete("Please replace UsePiranhaSites with UsePiranhaApplication.", false)] + [Obsolete("Please replace UsePiranhaSites with UsePiranhaApplication.", true)] public static IApplicationBuilder UsePiranhaSites(this IApplicationBuilder builder) { return UsePiranhaApplication(builder); - } + } /// /// Uses the piranha sitemap generation middleware. diff --git a/core/Piranha.AspNetCore/PageMiddleware.cs b/core/Piranha.AspNetCore/PageMiddleware.cs index a26f36f5f..46d1e950e 100644 --- a/core/Piranha.AspNetCore/PageMiddleware.cs +++ b/core/Piranha.AspNetCore/PageMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -41,7 +41,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer var siteId = service.Site.Id; var authorized = true; - var response = PageRouter.Invoke(api, url, siteId); + var response = await PageRouter.InvokeAsync(api, url, siteId); if (response != null) { _logger?.LogInformation($"Found page\n Route: {response.Route}\n Params: {response.QueryString}"); diff --git a/core/Piranha.AspNetCore/PostMiddleware.cs b/core/Piranha.AspNetCore/PostMiddleware.cs index 0bea2c7b9..eb4242155 100644 --- a/core/Piranha.AspNetCore/PostMiddleware.cs +++ b/core/Piranha.AspNetCore/PostMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -41,7 +41,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer var siteId = service.Site.Id; var authorized = true; - var response = PostRouter.Invoke(api, url, siteId); + var response = await PostRouter.InvokeAsync(api, url, siteId); if (response != null) { _logger?.LogInformation($"Found post\n Route: {response.Route}\n Params: {response.QueryString}"); diff --git a/core/Piranha.AspNetCore/Services/ApplicationService.cs b/core/Piranha.AspNetCore/Services/ApplicationService.cs index 42fbcb13b..8f397fd3a 100644 --- a/core/Piranha.AspNetCore/Services/ApplicationService.cs +++ b/core/Piranha.AspNetCore/Services/ApplicationService.cs @@ -1,19 +1,20 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; using System; using System.Linq; -using Piranha.Data; +using System.Threading.Tasks; using Piranha.Extend.Fields; using Piranha.Models; +using Piranha.Services; namespace Piranha.AspNetCore.Services { @@ -41,7 +42,7 @@ public class SiteHelper : ISiteHelper /// /// Default internal constructur. /// - internal SiteHelper(IApi api) + internal SiteHelper(IApi api) { _api = api; } @@ -51,11 +52,11 @@ internal SiteHelper(IApi api) /// /// The content type /// The site content model - public T GetContent() where T : SiteContent + public Task GetContentAsync() where T : SiteContent { if (Id != Guid.Empty) { - return _api.Sites.GetContentById(Id); + return _api.Sites.GetContentByIdAsync(Id); } return null; } @@ -68,7 +69,7 @@ public class MediaHelper : IMediaHelper /// /// Default internal constructur. /// - internal MediaHelper(IApi api) + internal MediaHelper(IApi api) { _api = api; } @@ -120,7 +121,7 @@ public string ResizeImage(Media image, int width, int? height = null) /// Gets the media helper. /// public IMediaHelper Media { get; internal set; } - + /// /// Gets the currently requested URL. /// @@ -146,19 +147,19 @@ public ApplicationService(IApi api) /// /// Initializes the service. /// - public void Init(HttpContext context) + public async Task InitAsync(HttpContext context) { // Gets the current site info if (!context.Request.Path.Value.StartsWith("/manager/")) { - Data.Site site = null; + Models.Site site = null; // Try to get the requested site by hostname & prefix var url = context.Request.Path.HasValue ? context.Request.Path.Value : ""; if (!string.IsNullOrEmpty(url) && url.Length > 1) { var segments = url.Substring(1).Split(new char[] { '/' }); - site = Api.Sites.GetByHostname($"{context.Request.Host.Host}/{segments[0]}"); + site = await Api.Sites.GetByHostnameAsync($"{context.Request.Host.Host}/{segments[0]}"); if (site != null) context.Request.Path = "/" + string.Join("/", segments.Skip(1)); @@ -166,18 +167,18 @@ public void Init(HttpContext context) // Try to get the requested site by hostname if (site == null) - site = Api.Sites.GetByHostname(context.Request.Host.Host); + site = await Api.Sites.GetByHostnameAsync(context.Request.Host.Host); // If we didn't find the site, get the default site if (site == null) - site = Api.Sites.GetDefault(); + site = await Api.Sites.GetDefaultAsync(); // Store the current site id & get the sitemap if (site != null) { Site.Id = site.Id; Site.Culture = site.Culture; - Site.Sitemap = Api.Sites.GetSitemap(Site.Id); + Site.Sitemap = await Api.Sites.GetSitemapAsync(Site.Id); } } diff --git a/core/Piranha.AspNetCore/Services/IApplicationService.cs b/core/Piranha.AspNetCore/Services/IApplicationService.cs index 78d8599a0..4f955e751 100644 --- a/core/Piranha.AspNetCore/Services/IApplicationService.cs +++ b/core/Piranha.AspNetCore/Services/IApplicationService.cs @@ -1,15 +1,16 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ -using Microsoft.AspNetCore.Http; using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Piranha.Models; namespace Piranha.AspNetCore.Services @@ -44,6 +45,6 @@ public interface IApplicationService /// /// Initializes the service. /// - void Init(HttpContext context); + Task InitAsync(HttpContext context); } } diff --git a/core/Piranha.AspNetCore/Services/IMediaHelper.cs b/core/Piranha.AspNetCore/Services/IMediaHelper.cs index e799d5c16..00f196f6d 100644 --- a/core/Piranha.AspNetCore/Services/IMediaHelper.cs +++ b/core/Piranha.AspNetCore/Services/IMediaHelper.cs @@ -3,13 +3,12 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; -using Piranha.Data; using Piranha.Extend.Fields; using Piranha.Models; diff --git a/core/Piranha.AspNetCore/Services/ISiteHelper.cs b/core/Piranha.AspNetCore/Services/ISiteHelper.cs index c2a6e89d9..8049b34a0 100644 --- a/core/Piranha.AspNetCore/Services/ISiteHelper.cs +++ b/core/Piranha.AspNetCore/Services/ISiteHelper.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; using Piranha.Models; namespace Piranha.AspNetCore.Services @@ -35,6 +36,6 @@ public interface ISiteHelper /// /// The content type /// The site content model - T GetContent() where T : SiteContent; + Task GetContentAsync() where T : SiteContent; } } \ No newline at end of file diff --git a/core/Piranha.AspNetCore/SitemapMiddleware.cs b/core/Piranha.AspNetCore/SitemapMiddleware.cs index 98651ded6..c16208318 100644 --- a/core/Piranha.AspNetCore/SitemapMiddleware.cs +++ b/core/Piranha.AspNetCore/SitemapMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Piranha.AspNetCore.Services; +using Piranha.Services; using Piranha.Web; using X.Web.Sitemap; @@ -53,14 +54,14 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer var siteId = service.Site.Id; // Get the sitemap for the site - var pages = api.Sites.GetSitemap(siteId); + var pages = await api.Sites.GetSitemapAsync(siteId); // Generate sitemap.xml var sitemap = new Sitemap(); foreach (var page in pages) { - var urls = GetPageUrls(api, page, baseUrl); + var urls = await GetPageUrlsAsync(api, page, baseUrl); if (urls.Count > 0) sitemap.AddRange(urls); @@ -73,7 +74,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer await _next.Invoke(context); } - private List GetPageUrls(IApi api, Models.SitemapItem item, string baseUrl) + private async Task> GetPageUrlsAsync(IApi api, Models.SitemapItem item, string baseUrl) { var urls = new List(); @@ -88,7 +89,7 @@ private List GetPageUrls(IApi api, Models.SitemapItem item, string baseUrl) }); // Get all posts for the blog - var posts = api.Posts.GetAll(item.Id); + var posts = await api.Posts.GetAllAsync(item.Id); foreach (var post in posts) { if (post.Published.HasValue && post.Published.Value <= DateTime.Now) @@ -105,7 +106,7 @@ private List GetPageUrls(IApi api, Models.SitemapItem item, string baseUrl) foreach (var child in item.Items) { - var childUrls = GetPageUrls(api, child, baseUrl); + var childUrls = await GetPageUrlsAsync(api, child, baseUrl); if (childUrls.Count > 0) { diff --git a/core/Piranha.AspNetCore/StartPageMiddleware.cs b/core/Piranha.AspNetCore/StartPageMiddleware.cs index 0a5d3a123..25d958eeb 100644 --- a/core/Piranha.AspNetCore/StartPageMiddleware.cs +++ b/core/Piranha.AspNetCore/StartPageMiddleware.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Http; @@ -41,7 +41,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer var siteId = service.Site.Id; var authorized = true; - var response = StartPageRouter.Invoke(api, url, siteId); + var response = await StartPageRouter.InvokeAsync(api, url, siteId); if (response != null) { _logger?.LogInformation($"Found startpage\n Route: {response.Route}\n Params: {response.QueryString}"); @@ -58,7 +58,7 @@ public override async Task Invoke(HttpContext context, IApi api, IApplicationSer if (authorized) { service.PageId = response.PageId; - + using (var config = new Config(api)) { var headers = context.Response.GetTypedHeaders(); diff --git a/core/Piranha.AttributeBuilder/ContentTypeBuilder.cs b/core/Piranha.AttributeBuilder/ContentTypeBuilder.cs index 60bceb3e8..09b5941c1 100644 --- a/core/Piranha.AttributeBuilder/ContentTypeBuilder.cs +++ b/core/Piranha.AttributeBuilder/ContentTypeBuilder.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Piranha.Extend; @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Reflection; using System.Linq; +using System.Threading.Tasks; namespace Piranha.AttributeBuilder { @@ -37,7 +38,15 @@ public T AddType(Type type) /// /// Builds the page types. /// - public abstract T Build(); + public virtual T Build() + { + return BuildAsync().GetAwaiter().GetResult(); + } + + /// + /// Builds the page types. + /// + public abstract Task BuildAsync(); /// /// Gets the possible content type for the given type. @@ -75,7 +84,7 @@ public T AddType(Type type) if (descAttr != null) { regionType.Description = descAttr.Text; - } + } Type type = null; diff --git a/core/Piranha.AttributeBuilder/PageTypeBuilder.cs b/core/Piranha.AttributeBuilder/PageTypeBuilder.cs index b388f1234..760e59749 100644 --- a/core/Piranha.AttributeBuilder/PageTypeBuilder.cs +++ b/core/Piranha.AttributeBuilder/PageTypeBuilder.cs @@ -1,18 +1,20 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Piranha.Models; +using Piranha.Services; namespace Piranha.AttributeBuilder { @@ -23,7 +25,7 @@ public class PageTypeBuilder : ContentTypeBuilder /// /// Default constructor. /// - /// The current api + /// The current api public PageTypeBuilder(IApi api) { _api = api; @@ -32,7 +34,7 @@ public PageTypeBuilder(IApi api) /// /// Builds the page types. /// - public override PageTypeBuilder Build() + public override async Task BuildAsync() { foreach (var type in _types) { @@ -41,7 +43,7 @@ public override PageTypeBuilder Build() if (pageType != null) { pageType.Ensure(); - _api.PageTypes.Save(pageType); + await _api.PageTypes.SaveAsync(pageType); } } return this; @@ -49,10 +51,20 @@ public override PageTypeBuilder Build() /// /// Deletes all page types in the database that doesn't - /// exist in the database, + /// exist in the database, /// /// The builder public PageTypeBuilder DeleteOrphans() + { + return DeleteOrphansAsync().GetAwaiter().GetResult(); + } + + /// + /// Deletes all page types in the database that doesn't + /// exist in the database, + /// + /// The builder + public async Task DeleteOrphansAsync() { var orphans = new List(); var importTypes = new List(); @@ -67,7 +79,7 @@ public PageTypeBuilder DeleteOrphans() } // Get all previously imported page types. - foreach (var pageType in _api.PageTypes.GetAll()) + foreach (var pageType in await _api.PageTypes.GetAllAsync()) { if (!importTypes.Any(t => t.Id == pageType.Id)) orphans.Add(pageType); @@ -76,7 +88,7 @@ public PageTypeBuilder DeleteOrphans() // Delete all orphans. foreach (var pageType in orphans) { - _api.PageTypes.Delete(pageType); + await _api.PageTypes.DeleteAsync(pageType); } return this; } diff --git a/core/Piranha.AttributeBuilder/PostTypeBuilder.cs b/core/Piranha.AttributeBuilder/PostTypeBuilder.cs index c7e7a24fc..21442f79a 100644 --- a/core/Piranha.AttributeBuilder/PostTypeBuilder.cs +++ b/core/Piranha.AttributeBuilder/PostTypeBuilder.cs @@ -3,16 +3,18 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Piranha.Models; +using Piranha.Services; namespace Piranha.AttributeBuilder { @@ -23,7 +25,7 @@ public class PostTypeBuilder : ContentTypeBuilder /// /// Default constructor. /// - /// The current api + /// The current api public PostTypeBuilder(IApi api) { _api = api; @@ -32,7 +34,7 @@ public PostTypeBuilder(IApi api) /// /// Builds the page types. /// - public override PostTypeBuilder Build() + public override async Task BuildAsync() { foreach (var type in _types) { @@ -41,18 +43,28 @@ public override PostTypeBuilder Build() if (postType != null) { postType.Ensure(); - _api.PostTypes.Save(postType); + await _api.PostTypes.SaveAsync(postType); } } return this; } /// - /// Deletes all page types in the database that doesn't - /// exist in the database, + /// Deletes all post types in the database that doesn't + /// exist in the database, /// /// The builder public PostTypeBuilder DeleteOrphans() + { + return DeleteOrphansAsync().GetAwaiter().GetResult(); + } + + /// + /// Deletes all page types in the database that doesn't + /// exist in the database, + /// + /// The builder + public async Task DeleteOrphansAsync() { var orphans = new List(); var importTypes = new List(); @@ -67,7 +79,7 @@ public PostTypeBuilder DeleteOrphans() } // Get all previously imported page types. - foreach (var postType in _api.PostTypes.GetAll()) + foreach (var postType in await _api.PostTypes.GetAllAsync()) { if (!importTypes.Any(t => t.Id == postType.Id)) orphans.Add(postType); @@ -76,7 +88,7 @@ public PostTypeBuilder DeleteOrphans() // Delete all orphans. foreach (var postType in orphans) { - _api.PostTypes.Delete(postType); + await _api.PostTypes.DeleteAsync(postType); } return this; } diff --git a/core/Piranha.AttributeBuilder/SiteTypeBuilder.cs b/core/Piranha.AttributeBuilder/SiteTypeBuilder.cs index b57cabe13..b4de79c15 100644 --- a/core/Piranha.AttributeBuilder/SiteTypeBuilder.cs +++ b/core/Piranha.AttributeBuilder/SiteTypeBuilder.cs @@ -3,16 +3,18 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Piranha.Models; +using Piranha.Services; namespace Piranha.AttributeBuilder { @@ -23,7 +25,7 @@ public class SiteTypeBuilder : ContentTypeBuilder /// /// Default constructor. /// - /// The current api + /// The current api public SiteTypeBuilder(IApi api) { _api = api; @@ -32,7 +34,7 @@ public SiteTypeBuilder(IApi api) /// /// Builds the site types. /// - public override SiteTypeBuilder Build() + public override async Task BuildAsync() { foreach (var type in _types) { @@ -41,7 +43,7 @@ public override SiteTypeBuilder Build() if (siteType != null) { siteType.Ensure(); - _api.SiteTypes.Save(siteType); + await _api.SiteTypes.SaveAsync(siteType); } } return this; @@ -49,10 +51,20 @@ public override SiteTypeBuilder Build() /// /// Deletes all site types in the database that doesn't - /// exist in the import, + /// exist in the database, /// /// The builder public SiteTypeBuilder DeleteOrphans() + { + return DeleteOrphansAsync().GetAwaiter().GetResult(); + } + + /// + /// Deletes all site types in the database that doesn't + /// exist in the import, + /// + /// The builder + public async Task DeleteOrphansAsync() { var orphans = new List(); var importTypes = new List(); @@ -67,7 +79,7 @@ public SiteTypeBuilder DeleteOrphans() } // Get all previously imported page types. - foreach (var siteType in _api.SiteTypes.GetAll()) + foreach (var siteType in await _api.SiteTypes.GetAllAsync()) { if (!importTypes.Any(t => t.Id == siteType.Id)) orphans.Add(siteType); @@ -76,7 +88,7 @@ public SiteTypeBuilder DeleteOrphans() // Delete all orphans. foreach (var siteType in orphans) { - _api.SiteTypes.Delete(siteType); + await _api.SiteTypes.DeleteAsync(siteType); } return this; } diff --git a/core/Piranha.Extensions.Sync/AliasServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/AliasServiceSyncExtensions.cs new file mode 100644 index 000000000..fe54aba5f --- /dev/null +++ b/core/Piranha.Extensions.Sync/AliasServiceSyncExtensions.cs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class AliasServiceSyncExtensions + { + /// + /// Gets all available models for the specified site. + /// + /// The optional site id + /// The available models + public static IEnumerable GetAll(this AliasService service, Guid? siteId = null) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public static Alias GetById(this AliasService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the given alias url. + /// + /// The unique url + /// The optional site id + /// The model + public static Alias GetByAliasUrl(this AliasService service, string url, Guid? siteId = null) + { + return service.GetByAliasUrlAsync(url, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the given redirect url. + /// + /// The unique url + /// The optional site id + /// The model + public static IEnumerable GetByRedirectUrl(this AliasService service, string url, Guid? siteId = null) + { + return service.GetByRedirectUrlAsync(url, siteId).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this AliasService service, Alias model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this AliasService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this AliasService service, Alias model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/ArchiveServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/ArchiveServiceSyncExtensions.cs new file mode 100644 index 000000000..c375886ac --- /dev/null +++ b/core/Piranha.Extensions.Sync/ArchiveServiceSyncExtensions.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class ArchiveServiceSyncExtensions + { + /// + /// Gets the specified post archive for the specified filter. + /// + /// The archive page id + /// The current page of the archive + /// The optional category id + /// The optional tag id + /// The optional year + /// The optional month + /// The optional page size + /// The archive model type + /// The archive model + public static T GetById(this ArchiveService service, Guid archiveId, int? currentPage = 1, Guid? categoryId = null, + Guid? tagId = null, int? year = null, int? month = null, int? pageSize = null) where T : ArchivePage + { + return service.GetByIdAsync(archiveId, currentPage, categoryId, tagId, year, month, pageSize) + .GetAwaiter() + .GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/MediaServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/MediaServiceSyncExtensions.cs new file mode 100644 index 000000000..f23d4f618 --- /dev/null +++ b/core/Piranha.Extensions.Sync/MediaServiceSyncExtensions.cs @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class MediaServiceSyncExtensions + { + /// + /// Gets all media available in the specified folder. + /// + /// The optional folder id + /// The available media + public static IEnumerable GetAll(this MediaService service, Guid? folderId = null) + { + return service.GetAllAsync(folderId).GetAwaiter().GetResult(); + } + + /// + /// Gets all media folders available in the specified + /// folder. + /// + /// The optional folder id + /// The available media folders + public static IEnumerable GetAllFolders(this MediaService service, Guid? folderId = null) + { + return service.GetAllFoldersAsync(folderId).GetAwaiter().GetResult(); + } + + /// + /// Gets the media with the given id. + /// + /// The unique id + /// The media + public static Media GetById(this MediaService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the media folder with the given id. + /// + /// The unique id + /// The media folder + public static MediaFolder GetFolderById(this MediaService service, Guid id) + { + return service.GetFolderByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the hierachical media structure. + /// + /// The media structure + public static Models.MediaStructure GetStructure(this MediaService service) + { + return service.GetStructureAsync().GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The content to save + public static void Save(this MediaService service, Models.MediaContent content) + { + service.SaveAsync(content).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void SaveFolder(this MediaService service, MediaFolder model) + { + service.SaveFolderAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Moves the media to the folder with the specified id. + /// + /// The media + /// The folder id + public static void Move(this MediaService service, Media model, Guid? folderId) + { + service.MoveAsync(model, folderId).GetAwaiter().GetResult(); + } + + /// + /// Deletes the media with the given id. + /// + /// The unique id + public static void Delete(this MediaService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The media + public static void Delete(this MediaService service, Media model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the media folder with the given id. + /// + /// The unique id + public static void DeleteFolder(this MediaService service, Guid id) + { + service.DeleteFolderAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The media + public static void DeleteFolder(this MediaService service, MediaFolder model) + { + service.DeleteFolderAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/PageServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/PageServiceSyncExtensions.cs new file mode 100644 index 000000000..25b88747c --- /dev/null +++ b/core/Piranha.Extensions.Sync/PageServiceSyncExtensions.cs @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class PageServiceSyncExtensions + { + /// + /// Detaches a copy and initializes it as a standalone page + /// + /// The standalone page + public static void Detach(this PageService service, T model) where T : PageBase + { + service.DetachAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this PageService service, Guid? siteId = null) + { + return service.GetAllAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this PageService service, Guid? siteId = null) where T : PageBase + { + return service.GetAllAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available blog pages for the current site. + /// + /// The optional site id + /// The pages + public static IEnumerable GetAllBlogs(this PageService service, Guid? siteId = null) + { + return service.GetAllBlogsAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available blog pages for the current site. + /// + /// The optional site id + /// The pages + public static IEnumerable GetAllBlogs(this PageService service, Guid? siteId = null) where T : PageBase + { + return service.GetAllBlogsAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the site startpage. + /// + /// The optional site id + /// The page model + public static DynamicPage GetStartpage(this PageService service, Guid? siteId = null) + { + return service.GetStartpageAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the site startpage. + /// + /// The model type + /// The optional site id + /// The page model + public static T GetStartpage(this PageService service, Guid? siteId = null) where T : PageBase + { + return service.GetStartpageAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the page model with the specified id. + /// + /// The unique id + /// The page model + public static DynamicPage GetById(this PageService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public static T GetById(this PageService service, Guid id) where T : PageBase + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the page model with the specified slug. + /// + /// The unique slug + /// The optional site id + /// The page model + public static DynamicPage GetBySlug(this PageService service, string slug, Guid? siteId = null) + { + return service.GetBySlugAsync(slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the page model with the specified slug. + /// + /// The model type + /// The unique slug + /// The optional site id + /// The page model + public static T GetBySlug(this PageService service, string slug, Guid? siteId = null) where T : PageBase + { + return service.GetBySlugAsync(slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the id for the page with the given slug. + /// + /// The unique slug + /// The optional page id + /// The id + public static Guid? GetIdBySlug(this PageService service, string slug, Guid? siteId = null) + { + return service.GetIdBySlugAsync(slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Moves the current page in the structure. + /// + /// The model type + /// The page to move + /// The new parent id + /// The new sort order + public static void Move(this PageService service, T model, Guid? parentId, int sortOrder) where T : PageBase + { + service.MoveAsync(model, parentId, sortOrder).GetAwaiter().GetResult(); + } + + /// + /// Saves the given page model + /// + /// The page model + public static void Save(this PageService service, T model) where T : PageBase + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this PageService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this PageService service, T model) where T : PageBase + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/PageTypeServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/PageTypeServiceSyncExtensions.cs new file mode 100644 index 000000000..d74c2f1dd --- /dev/null +++ b/core/Piranha.Extensions.Sync/PageTypeServiceSyncExtensions.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class PageTypeServiceSyncExtensions + { + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this PageTypeService service) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public static Models.PageType GetById(this PageTypeService service, string id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this PageTypeService service, PageType model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this PageTypeService service, string id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this PageTypeService service, PageType model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/ParamServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/ParamServiceSyncExtensions.cs new file mode 100644 index 000000000..b6c0ecd7b --- /dev/null +++ b/core/Piranha.Extensions.Sync/ParamServiceSyncExtensions.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class ParamServiceSyncExtensions + { + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this ParamService service) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public static Param GetById(this ParamService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the given key. + /// + /// The unique key + /// The model + public static Param GetByKey(this ParamService service, string key) { + return service.GetByKeyAsync(key).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this ParamService service, Param model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this ParamService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this ParamService service, Param model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/Piranha.Extensions.Sync.csproj b/core/Piranha.Extensions.Sync/Piranha.Extensions.Sync.csproj new file mode 100644 index 000000000..b22c547e0 --- /dev/null +++ b/core/Piranha.Extensions.Sync/Piranha.Extensions.Sync.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + 6.0.0-alpha1 + Piranha CMS + + + + + + + diff --git a/core/Piranha.Extensions.Sync/PostServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/PostServiceSyncExtensions.cs new file mode 100644 index 000000000..8efcc999e --- /dev/null +++ b/core/Piranha.Extensions.Sync/PostServiceSyncExtensions.cs @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class PostServiceSyncExtensions + { + /// + /// Gets the available posts for the specified blog. + /// + /// The unique blog id + /// The posts + public static IEnumerable GetAll(this PostService service, Guid blogId) + { + return service.GetAllAsync(blogId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available post items. + /// + /// The unique id + /// The posts + public static IEnumerable GetAll(this PostService service, Guid blogId) where T : PostBase + { + return service.GetAllAsync(blogId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The optional site id + /// The posts + public static IEnumerable GetAllBySiteId(this PostService service, Guid? siteId = null) + { + return service.GetAllBySiteIdAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available post items. + /// + /// The optional site id + /// The posts + public static IEnumerable GetAllBySiteId(this PostService service, Guid? siteId = null) where T : PostBase + { + return service.GetAllBySiteIdAsync(siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The blog slug + /// The optional site id + /// The posts + public static IEnumerable GetAll(this PostService service, string slug, Guid? siteId = null) + { + return service.GetAllAsync(slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The blog slug + /// The optional site id + /// The posts + public static IEnumerable GetAll(this PostService service, string slug, Guid? siteId = null) where T : PostBase + { + return service.GetAllAsync(slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets all available categories for the specified blog. + /// + /// The blog id + /// The available categories + public static IEnumerable GetAllCategories(this PostService service, Guid blogId) + { + return service.GetAllCategoriesAsync(blogId).GetAwaiter().GetResult(); + } + + /// + /// Gets all available tags for the specified blog. + /// + /// The blog id + /// The available tags + public static IEnumerable GetAllTags(this PostService service, Guid blogId) + { + return service.GetAllTagsAsync(blogId).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified id. + /// + /// The unique id + /// The post model + public static DynamicPost GetById(this PostService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified id. + /// + /// The model type + /// The unique id + /// The post model + public static T GetById(this PostService service, Guid id) where T : PostBase + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The unique blog slug + /// The unique slug + /// The optional site id + /// The post model + public static DynamicPost GetBySlug(this PostService service, string blog, string slug, Guid? siteId = null) + { + return service.GetBySlugAsync(blog, slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The model type + /// The unique blog slug + /// The unique slug + /// The optional site id + /// The post model + public static T GetBySlug(this PostService service, string blog, string slug, Guid? siteId = null) where T : PostBase + { + return service.GetBySlugAsync(blog, slug, siteId).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The unique blog slug + /// The unique slug + /// The post model + public static DynamicPost GetBySlug(this PostService service, Guid blogId, string slug) + { + return service.GetBySlugAsync(blogId, slug).GetAwaiter().GetResult(); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The unique blog slug + /// The unique slug + /// The post model + public static T GetBySlug(this PostService service, Guid blogId, string slug) where T : PostBase + { + return service.GetBySlugAsync(blogId, slug).GetAwaiter().GetResult(); + } + + /// + /// Gets the category with the given slug. + /// + /// The blog id + /// The unique slug + /// The model + public static Taxonomy GetCategoryBySlug(this PostService service, Guid blogId, string slug) + { + return service.GetCategoryBySlugAsync(blogId, slug).GetAwaiter().GetResult(); + } + + /// + /// Gets the tag with the given slug. + /// + /// The blog id + /// The unique slug + /// The model + public static Taxonomy GetTagBySlug(this PostService service, Guid blogId, string slug) + { + return service.GetTagBySlugAsync(blogId, slug).GetAwaiter().GetResult(); + } + + /// + /// Saves the given post model + /// + /// The post model + public static void Save(this PostService service, T model) where T : PostBase + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this PostService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this PostService service, T model) where T : PostBase + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/PostTypeServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/PostTypeServiceSyncExtensions.cs new file mode 100644 index 000000000..d7ced053f --- /dev/null +++ b/core/Piranha.Extensions.Sync/PostTypeServiceSyncExtensions.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class PostTypeServiceSyncExtensions + { + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this PostTypeService service) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public static PostType GetById(this PostTypeService service, string id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this PostTypeService service, PostType model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this PostTypeService service, string id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this PostTypeService service, PostType model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/SiteServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/SiteServiceSyncExtensions.cs new file mode 100644 index 000000000..9567a31b7 --- /dev/null +++ b/core/Piranha.Extensions.Sync/SiteServiceSyncExtensions.cs @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class SiteServiceSyncExtensions + { + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this SiteService service) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public static Site GetById(this SiteService service, Guid id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the given internal id. + /// + /// The unique internal i + /// The model + public static Site GetByInternalId(this SiteService service, string internalId) + { + return service.GetByInternalIdAsync(internalId).GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the given hostname. + /// + /// The hostname + /// The model + public static Site GetByHostname(this SiteService service, string hostname) + { + return service.GetByHostnameAsync(hostname).GetAwaiter().GetResult(); + } + + /// + /// Gets the default side. + /// + /// The modell, or NULL if it doesnt exist + public static Site GetDefault(this SiteService service) + { + return service.GetDefaultAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site content model + public static DynamicSiteContent GetContentById(this SiteService service, Guid id) + { + return service.GetContentByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site model type + /// The site content model + public static T GetContentById(this SiteService service, Guid id) where T : SiteContent + { + return service.GetContentByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Gets the hierachical sitemap structure. + /// + /// The optional site id + /// If only published items should be included + /// The sitemap + public static Sitemap GetSitemap(this SiteService service, Guid? id = null, bool onlyPublished = true) + { + return service.GetSitemapAsync(id, onlyPublished).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this SiteService service, Site model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Saves the given site content to the site with the + /// given id. + /// + /// The site id + /// The site content model + /// The site content type + public static void SaveContent(this SiteService service, Guid siteId, T model) where T : SiteContent + { + service.SaveContentAsync(siteId, model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this SiteService service, Guid id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this SiteService service, Site model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Extensions.Sync/SiteTypeServiceSyncExtensions.cs b/core/Piranha.Extensions.Sync/SiteTypeServiceSyncExtensions.cs new file mode 100644 index 000000000..bc7f3abe8 --- /dev/null +++ b/core/Piranha.Extensions.Sync/SiteTypeServiceSyncExtensions.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; + +namespace Piranha.Services +{ + public static class SiteTypeServiceSyncExtensions + { + /// + /// Gets all available models. + /// + /// The available models + public static IEnumerable GetAll(this SiteTypeService service) + { + return service.GetAllAsync().GetAwaiter().GetResult(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public static Models.SiteType GetById(this SiteTypeService service, string id) + { + return service.GetByIdAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public static void Save(this SiteTypeService service, SiteType model) + { + service.SaveAsync(model).GetAwaiter().GetResult(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public static void Delete(this SiteTypeService service, string id) + { + service.DeleteAsync(id).GetAwaiter().GetResult(); + } + + /// + /// Deletes the given model. + /// + /// The model + public static void Delete(this SiteTypeService service, SiteType model) + { + service.DeleteAsync(model).GetAwaiter().GetResult(); + } + } +} diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/AliasController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/AliasController.cs index 359ac13bb..241676e65 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/AliasController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/AliasController.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc; using Piranha.Manager; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Controllers { @@ -59,7 +60,7 @@ public IActionResult Add(Models.AliasEditModel model) { try { - _api.Aliases.Save(new Data.Alias + _api.Aliases.Save(new Alias { SiteId = model.SiteId, AliasUrl = model.AliasUrl, diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/BlockController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/BlockController.cs index 1f5626b84..0c0ab6a10 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/BlockController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/BlockController.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Microsoft.AspNetCore.Mvc; @@ -16,18 +16,16 @@ namespace Piranha.Areas.Manager.Controllers [Area("Manager")] public class BlockController : ManagerAreaControllerBase { - private readonly IContentService _contentService; + private readonly IContentFactory _factory; /// /// Default constructor. /// /// The current api - /// The content service factory - public BlockController(IApi api, IContentServiceFactory factory) : base(api) + /// The content factory + public BlockController(IApi api, IContentFactory factory) : base(api) { - // Block transformation is not dependent on which content - // type is actually selected, so let's create a page service. - _contentService = factory.CreatePageService(); + _factory = factory; } /// @@ -38,7 +36,7 @@ public BlockController(IApi api, IContentServiceFactory factory) : base(api) [Route("manager/block/create")] public IActionResult AddBlock([FromBody]Models.ContentBlockModel model) { - var block = (Extend.Block)_contentService.CreateBlock(model.TypeName); + var block = (Extend.Block)_factory.CreateBlock(model.TypeName); if (block != null) { diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/MediaController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/MediaController.cs index 35dfad594..8e7bc7996 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/MediaController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/MediaController.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc; using Piranha.Manager; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Controllers { @@ -158,7 +159,7 @@ public IActionResult AddFolder(Models.MediaFolderModel model) { if (!string.IsNullOrWhiteSpace(model.Name)) { - _api.Media.SaveFolder(new Data.MediaFolder + _api.Media.SaveFolder(new MediaFolder { ParentId = model.ParentId, Name = model.Name diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/PageController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/PageController.cs index a5792864c..ab3dcb75a 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/PageController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/PageController.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -26,7 +26,7 @@ public class PageController : ManagerAreaControllerBase { private const string COOKIE_SELECTEDSITE = "PiranhaManager_SelectedSite"; private readonly PageEditService _editService; - private readonly IContentService _contentService; + private readonly IContentFactory _factory; private readonly IHubContext _hub; /// @@ -35,10 +35,10 @@ public class PageController : ManagerAreaControllerBase /// The current api /// The current page edit service /// The content service factory - public PageController(IApi api, PageEditService editService, IContentServiceFactory factory, IHubContext hub) : base(api) - { + public PageController(IApi api, PageEditService editService, IContentFactory factory, IHubContext hub) : base(api) + { _editService = editService; - _contentService = factory.CreatePageService(); + _factory = factory; _hub = hub; } @@ -47,15 +47,15 @@ public PageController(IApi api, PageEditService editService, IContentServiceFact /// [Route("manager/pages/{pageId?}")] [Authorize(Policy = Permission.Pages)] - public ViewResult List(string pageId = null) + public ViewResult List(string pageId = null) { // Get the currently selected site from the request cookies var siteId = Request.Cookies[COOKIE_SELECTEDSITE]; Guid? site = null; - if (!string.IsNullOrWhiteSpace(siteId)) - { - site = new Guid(siteId); + if (!string.IsNullOrWhiteSpace(siteId)) + { + site = new Guid(siteId); } return ListSite(site, pageId); } @@ -65,19 +65,19 @@ public ViewResult List(string pageId = null) /// [Route("manager/pages/site/{siteId:Guid?}/{pageId?}")] [Authorize(Policy = Permission.Pages)] - public ViewResult ListSite(Guid? siteId, string pageId = null) + public ViewResult ListSite(Guid? siteId, string pageId = null) { var model = Models.PageListModel.Get(_api, siteId, pageId); // Store a cookie on our currently selected site - if (siteId.HasValue) - { - Response.Cookies.Append(COOKIE_SELECTEDSITE, siteId.ToString()); - } - else - { - Response.Cookies.Delete(COOKIE_SELECTEDSITE); - } + if (siteId.HasValue) + { + Response.Cookies.Append(COOKIE_SELECTEDSITE, siteId.ToString()); + } + else + { + Response.Cookies.Delete(COOKIE_SELECTEDSITE); + } return View("List", model); } @@ -87,7 +87,7 @@ public ViewResult ListSite(Guid? siteId, string pageId = null) /// The page id [Route("manager/page/{id:Guid}")] [Authorize(Policy = Permission.PagesEdit)] - public IActionResult Edit(Guid id) + public IActionResult Edit(Guid id) { return View(_editService.GetById(id)); } @@ -99,7 +99,7 @@ public IActionResult Edit(Guid id) /// The optional site id [Route("manager/page/add/{type}/{siteId:Guid?}")] [Authorize(Policy = Permission.PagesAdd)] - public IActionResult Add(string type, Guid? siteId = null) + public IActionResult Add(string type, Guid? siteId = null) { var sitemap = _api.Sites.GetSitemap(siteId, onlyPublished: false); var model = _editService.Create(type, siteId); @@ -117,7 +117,7 @@ public IActionResult Add(string type, Guid? siteId = null) /// The optional site id [Route("manager/page/add/{type}/{sortOrder:int}/{parentId:Guid?}")] [Authorize(Policy = Permission.PagesAdd)] - public IActionResult AddAt(string type, int sortOrder, Guid? parentId = null) + public IActionResult AddAt(string type, int sortOrder, Guid? parentId = null) { return AddAt(Guid.Empty, type, sortOrder, parentId); } @@ -131,7 +131,7 @@ public IActionResult AddAt(string type, int sortOrder, Guid? parentId = null) /// The optional site id [Route("manager/page/add/site/{siteId:Guid}/{type}/{sortOrder:int}/{parentId:Guid?}")] [Authorize(Policy = Permission.PagesAdd)] - public IActionResult AddAt(Guid siteId, string type, int sortOrder, Guid? parentId = null) + public IActionResult AddAt(Guid siteId, string type, int sortOrder, Guid? parentId = null) { var model = _editService.Create(type, siteId != Guid.Empty ? siteId : (Guid?)null); @@ -139,8 +139,8 @@ public IActionResult AddAt(Guid siteId, string type, int sortOrder, Guid? parent model.SortOrder = sortOrder; return View("Edit", model); - } - + } + /// /// Adds a new copy of the specified page. /// @@ -148,9 +148,9 @@ public IActionResult AddAt(Guid siteId, string type, int sortOrder, Guid? parent /// The optional site id [Route("manager/page/copy/{originalId:Guid}/{siteId:Guid?}")] [Authorize(Policy = Permission.PagesAdd)] - public IActionResult AddCopy(Guid originalId, Guid? siteId = null) + public IActionResult AddCopy(Guid originalId, Guid? siteId = null) { - if (!siteId.HasValue) + if (!siteId.HasValue) { var site = _api.Sites.GetDefault(); siteId = site.Id; @@ -159,7 +159,7 @@ public IActionResult AddCopy(Guid originalId, Guid? siteId = null) var sitemap = _api.Sites.GetSitemap(siteId, onlyPublished: false); var model = _editService.GetById(originalId); - if (model.OriginalPageId.HasValue) + if (model.OriginalPageId.HasValue) { ErrorMessage("Can't create a copy from a copied page."); return RedirectToAction("List", new { siteId = siteId.Value }); @@ -169,7 +169,7 @@ public IActionResult AddCopy(Guid originalId, Guid? siteId = null) model.Id = Guid.NewGuid(); model.SiteId = siteId.Value; model.ParentId = null; - model.SortOrder = sitemap.Count; + model.SortOrder = sitemap.Count; model.Title = "Copy of " + model.Title; model.NavigationTitle = ""; model.Created = model.LastModified = DateTime.MinValue; @@ -181,14 +181,14 @@ public IActionResult AddCopy(Guid originalId, Guid? siteId = null) [Route("manager/page/preview/{id:Guid}")] [Authorize(Policy = Permission.PagesEdit)] - public IActionResult Preview(Guid id) + public IActionResult Preview(Guid id) { var page = _api.Pages.GetById(id); - if (page != null) - { - return View("_Preview", new Models.PreviewModel { Id = id, Permalink = page.Permalink }); - } + if (page != null) + { + return View("_Preview", new Models.PreviewModel { Id = id, Permalink = page.Permalink }); + } return NotFound(); } @@ -198,21 +198,21 @@ public IActionResult Preview(Guid id) /// The page id [Route("manager/page/detach/{id:Guid}")] [Authorize(Policy = Permission.PagesSave)] - public IActionResult Detach(Guid id) + public IActionResult Detach(Guid id) { var page = _api.Pages.GetById(id); - if (page != null && page.OriginalPageId.HasValue) + if (page != null && page.OriginalPageId.HasValue) { _api.Pages.Detach(page); SuccessMessage("The page has been detached from its original."); return RedirectToAction("Edit", new { id }); - } - else + } + else { ErrorMessage("The page could not be be detached."); - return RedirectToAction("Edit", new { id }); + return RedirectToAction("Edit", new { id }); } } @@ -223,10 +223,10 @@ public IActionResult Detach(Guid id) [HttpPost] [Route("manager/page/save")] [Authorize(Policy = Permission.PagesSave)] - public async Task Save(Models.PageEditModel model) + public async Task Save(Models.PageEditModel model) { // Validate - if (string.IsNullOrWhiteSpace(model.Title)) + if (string.IsNullOrWhiteSpace(model.Title)) { return BadRequest(); } @@ -234,13 +234,13 @@ public async Task Save(Models.PageEditModel model) var ret = _editService.Save(model, out var alias); // Save - if (ret) + if (ret) { if (_hub != null) await _hub.Clients.All.SendAsync("Update", model.Id); - if (!string.IsNullOrWhiteSpace(alias)) - { + if (!string.IsNullOrWhiteSpace(alias)) + { return Json(new { Location = Url.Action("Edit", new { id = model.Id }), @@ -251,14 +251,14 @@ public async Task Save(Models.PageEditModel model) SiteId = model.SiteId, PageId = model.Id } - }); - } - else - { - return Json(new { Location = Url.Action("Edit", new { id = model.Id }) }); - } - } - else + }); + } + else + { + return Json(new { Location = Url.Action("Edit", new { id = model.Id }) }); + } + } + else { return StatusCode(500); } @@ -271,24 +271,24 @@ public async Task Save(Models.PageEditModel model) [HttpPost] [Route("manager/page/publish")] [Authorize(Policy = Permission.PagesPublish)] - public IActionResult Publish(Models.PageEditModel model) + public IActionResult Publish(Models.PageEditModel model) { // Validate - if (string.IsNullOrWhiteSpace(model.Title)) + if (string.IsNullOrWhiteSpace(model.Title)) { return BadRequest(); } // Save - if (_editService.Save(model, out var alias, true)) + if (_editService.Save(model, out var alias, true)) { return Json(new { Published = DateTime.Now.ToString("yyyy-MM-dd HH:mm"), Location = Url.Action("Edit", new { id = model.Id }) }); - } - else + } + else { return StatusCode(500); } @@ -301,17 +301,17 @@ public IActionResult Publish(Models.PageEditModel model) [HttpPost] [Route("manager/page/unpublish")] [Authorize(Policy = Permission.PagesPublish)] - public IActionResult UnPublish(Models.PageEditModel model) + public IActionResult UnPublish(Models.PageEditModel model) { - if (_editService.Save(model, out var alias, false)) + if (_editService.Save(model, out var alias, false)) { return Json(new { Unpublished = true, Location = Url.Action("Edit", new { id = model.Id }) }); - } - else + } + else { return StatusCode(500); } @@ -324,7 +324,7 @@ public IActionResult UnPublish(Models.PageEditModel model) [HttpPost] [Route("manager/pages/move")] [Authorize(Policy = Permission.PagesEdit)] - public IActionResult Move([FromBody]Models.PageStructureModel structure) + public IActionResult Move([FromBody]Models.PageStructureModel structure) { Guid? siteId = null; @@ -332,22 +332,22 @@ public IActionResult Move([FromBody]Models.PageStructureModel structure) { var page = _api.Pages.GetById(new Guid(structure.Items[0].Id)); - if (page != null) - { + if (page != null) + { siteId = page.SiteId; - } + } } - for (var n = 0; n < structure.Items.Count; n++) + for (var n = 0; n < structure.Items.Count; n++) { - if (MovePage(structure.Items[n], n)) - { - break; + if (MovePage(structure.Items[n], n)) + { + break; } } - using (var config = new Config(_api)) + using (var config = new Config(_api)) { - return View("Partial/_Sitemap", new Models.SitemapModel + return View("Partial/_Sitemap", new Models.SitemapModel { Sitemap = _api.Sites.GetSitemap(siteId, onlyPublished: false), ExpandedLevels = config.ManagerExpandedSitemapLevels @@ -361,7 +361,7 @@ public IActionResult Move([FromBody]Models.PageStructureModel structure) /// The unique id [Route("manager/page/delete/{id:Guid}")] [Authorize(Policy = Permission.PagesDelete)] - public IActionResult Delete(Guid id) + public IActionResult Delete(Guid id) { _api.Pages.Delete(id); SuccessMessage("The page has been deleted"); @@ -375,19 +375,19 @@ public IActionResult Delete(Guid id) [HttpPost] [Route("manager/page/region")] [Authorize(Policy = Permission.Pages)] - public IActionResult AddRegion([FromBody]Models.PageRegionModel model) + public IActionResult AddRegion([FromBody]Models.PageRegionModel model) { var pageType = _api.PageTypes.GetById(model.PageTypeId); - if (pageType != null) + if (pageType != null) { var regionType = pageType.Regions.SingleOrDefault(r => r.Id == model.RegionTypeId); - if (regionType != null) + if (regionType != null) { - var region = _contentService.CreateDynamicRegion(pageType, model.RegionTypeId); + var region = _factory.CreateDynamicRegion(pageType, model.RegionTypeId); - var editModel = (Models.PageEditRegionCollection)_editService.CreateRegion(regionType, + var editModel = (Models.PageEditRegionCollection)_editService.CreateRegion(regionType, new List { region }); ViewData.TemplateInfo.HtmlFieldPrefix = $"Regions[{model.RegionIndex}].FieldSets[{model.ItemIndex}]"; @@ -400,20 +400,20 @@ public IActionResult AddRegion([FromBody]Models.PageRegionModel model) [HttpPost] [Route("manager/page/alias")] [Authorize(Policy = Permission.PagesEdit)] - public IActionResult AddAlias(Guid siteId, Guid pageId, string alias, string redirect) + public IActionResult AddAlias(Guid siteId, Guid pageId, string alias, string redirect) { // Create alias Piranha.Manager.Utils.CreateAlias(_api, siteId, alias, redirect); // Check if there are posts for this page var posts = _api.Posts.GetAll(pageId); - foreach (var post in posts) + foreach (var post in posts) { // Only create aliases for published posts - if (post.Published.HasValue) - { - Piranha.Manager.Utils.CreateAlias(_api, siteId, $"{alias}/{post.Slug}", $"{redirect}/{post.Slug}"); - } + if (post.Published.HasValue) + { + Piranha.Manager.Utils.CreateAlias(_api, siteId, $"{alias}/{post.Slug}", $"{redirect}/{post.Slug}"); + } } return Json(new @@ -428,26 +428,26 @@ public IActionResult AddAlias(Guid siteId, Guid pageId, string alias, string red /// The site id [Route("manager/page/modal/{siteId:Guid?}")] [Authorize(Policy = Permission.Pages)] - public IActionResult Modal(Guid? siteId = null) + public IActionResult Modal(Guid? siteId = null) { if (!siteId.HasValue) { var site = Request.Cookies[COOKIE_SELECTEDSITE]; - if (!string.IsNullOrEmpty(site)) - { - siteId = new Guid(site); - } + if (!string.IsNullOrEmpty(site)) + { + siteId = new Guid(site); + } } return View(Models.PageModalModel.GetBySiteId(_api, siteId)); - } - - private bool MovePage(Models.PageStructureModel.PageStructureItem page, int sortOrder = 1, Guid? parentId = null) + } + + private bool MovePage(Models.PageStructureModel.PageStructureItem page, int sortOrder = 1, Guid? parentId = null) { var model = _api.Pages.GetById(new Guid(page.Id)); - if (model != null) + if (model != null) { - if (model.ParentId != parentId || model.SortOrder != sortOrder) + if (model.ParentId != parentId || model.SortOrder != sortOrder) { // Move the page in the structure. _api.Pages.Move(model, parentId, sortOrder); @@ -456,11 +456,11 @@ private bool MovePage(Models.PageStructureModel.PageStructureItem page, int sort return true; } - for (var n = 0; n < page.Children.Count; n++) + for (var n = 0; n < page.Children.Count; n++) { - if (MovePage(page.Children[n], n, new Guid(page.Id))) - { - return true; + if (MovePage(page.Children[n], n, new Guid(page.Id))) + { + return true; } } } diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/PostController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/PostController.cs index d74e2f406..356560463 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/PostController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/PostController.cs @@ -26,17 +26,17 @@ public class PostController : ManagerAreaControllerBase { private const string COOKIE_SELECTEDSITE = "PiranhaManager_SelectedSite"; private readonly PostEditService _editService; - private readonly IContentService _contentService; + private readonly IContentFactory _factory; private readonly IHubContext _hub; /// /// Default constructor. /// /// The current api - public PostController(IApi api, PostEditService editService, IContentServiceFactory factory, IHubContext hub) : base(api) + public PostController(IApi api, PostEditService editService, IContentFactory factory, IHubContext hub) : base(api) { _editService = editService; - _contentService = factory.CreatePostService(); + _factory = factory; _hub = hub; } @@ -228,7 +228,7 @@ public IActionResult AddRegion([FromBody]Models.PageRegionModel model) if (regionType != null) { - var region = _contentService.CreateDynamicRegion(postType, model.RegionTypeId); + var region = _factory.CreateDynamicRegion(postType, model.RegionTypeId); var editModel = (Models.PageEditRegionCollection)_editService.CreateRegion(regionType, new List { region }); diff --git a/core/Piranha.Manager/Areas/Manager/Controllers/SiteController.cs b/core/Piranha.Manager/Areas/Manager/Controllers/SiteController.cs index 098fe7e54..857bb2536 100644 --- a/core/Piranha.Manager/Areas/Manager/Controllers/SiteController.cs +++ b/core/Piranha.Manager/Areas/Manager/Controllers/SiteController.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -23,17 +23,17 @@ namespace Piranha.Areas.Manager.Controllers [Area("Manager")] public class SiteController : ManagerAreaControllerBase { - SiteContentEditService _service; - IContentService _contentService; + private readonly SiteContentEditService _service; + private readonly IContentFactory _factory; /// /// Default constructor. /// /// The current api - public SiteController(IApi api, SiteContentEditService service, IContentServiceFactory factory) : base(api) + public SiteController(IApi api, SiteContentEditService service, IContentFactory factory) : base(api) { _service = service; - _contentService = factory.CreateSiteService(); + _factory = factory; } /// @@ -150,7 +150,7 @@ public IActionResult AddRegion([FromBody]PageRegionModel model) if (regionType != null) { - var region = _contentService.CreateDynamicRegion(siteType, model.RegionTypeId); + var region = _factory.CreateDynamicRegion(siteType, model.RegionTypeId); var editModel = (PageEditRegionCollection)_service.CreateRegion(regionType, new List { region }); diff --git a/core/Piranha.Manager/Areas/Manager/Models/AliasListModel.cs b/core/Piranha.Manager/Areas/Manager/Models/AliasListModel.cs index 0899e1ff1..0393a8514 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/AliasListModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/AliasListModel.cs @@ -3,13 +3,15 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; +using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { @@ -18,12 +20,12 @@ public class AliasListModel /// /// Gets/sets the available items. /// - public IEnumerable Items { get; set; } = new List(); + public IEnumerable Items { get; set; } = new List(); /// /// Gets/sets the available sites. /// - public IEnumerable Sites { get; set; } = new List(); + public IEnumerable Sites { get; set; } = new List(); /// /// Gets/sets the id of the currently selected site. @@ -43,7 +45,7 @@ public class AliasListModel /// The model public static AliasListModel Get(IApi api, Guid? siteId = null) { - Data.Site site; + Site site; if (!siteId.HasValue) { diff --git a/core/Piranha.Manager/Areas/Manager/Models/MediaListModel.cs b/core/Piranha.Manager/Areas/Manager/Models/MediaListModel.cs index 7999f61c9..3380f696c 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/MediaListModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/MediaListModel.cs @@ -3,21 +3,22 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { public class MediaListModel { - public IList Media { get; set; } = new List(); + public IList Media { get; set; } = new List(); public MediaStructure Folders { get; set; } = new MediaStructure(); public IList Breadcrumb { get; set; } public Guid? CurrentFolderId { get; set; } diff --git a/core/Piranha.Manager/Areas/Manager/Models/PageListModel.cs b/core/Piranha.Manager/Areas/Manager/Models/PageListModel.cs index fd209fc47..f79665126 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/PageListModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/PageListModel.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { diff --git a/core/Piranha.Manager/Areas/Manager/Models/PageModalModel.cs b/core/Piranha.Manager/Areas/Manager/Models/PageModalModel.cs index 28dda2227..ad00ac0c3 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/PageModalModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/PageModalModel.cs @@ -3,15 +3,16 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { diff --git a/core/Piranha.Manager/Areas/Manager/Models/PostEditModel.cs b/core/Piranha.Manager/Areas/Manager/Models/PostEditModel.cs index 3574ef061..9da3b0956 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/PostEditModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/PostEditModel.cs @@ -3,12 +3,13 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System.Collections.Generic; +using Piranha.Models; namespace Piranha.Areas.Manager.Models { @@ -35,12 +36,12 @@ public class PostEditModel : Piranha.Models.PostBase /// /// Gets/sets the available categories. /// - public IEnumerable AllCategories { get; set; } = new List(); + public IEnumerable AllCategories { get; set; } = new List(); /// /// Gets/sets the available tags. /// - public IEnumerable AllTags { get; set; } = new List(); + public IEnumerable AllTags { get; set; } = new List(); /// /// Gets/sets the currently selected category. diff --git a/core/Piranha.Manager/Areas/Manager/Models/PostListModel.cs b/core/Piranha.Manager/Areas/Manager/Models/PostListModel.cs index 7e81e1b38..dcb760805 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/PostListModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/PostListModel.cs @@ -3,15 +3,16 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Linq; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { @@ -59,7 +60,7 @@ public static PostListModel GetByBlogId(IApi api, Guid blogId) // Get the currently used categories var categoriesId = model.Posts.Select(p => p.CategoryId).Distinct(); - model.CurrentCategories = api.Categories.GetAll(blogId) + model.CurrentCategories = api.Posts.GetAllCategories(blogId) .Where(c => categoriesId.Contains(c.Id)) .Select(c => new Taxonomy { diff --git a/core/Piranha.Manager/Areas/Manager/Models/PostModalModel.cs b/core/Piranha.Manager/Areas/Manager/Models/PostModalModel.cs index e131bb16e..8024699b7 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/PostModalModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/PostModalModel.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -14,6 +14,7 @@ using System.Linq; using Piranha.Manager; using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { diff --git a/core/Piranha.Manager/Areas/Manager/Models/SiteEditModel.cs b/core/Piranha.Manager/Areas/Manager/Models/SiteEditModel.cs index 744bfc162..e4021d92f 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/SiteEditModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/SiteEditModel.cs @@ -3,20 +3,21 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ -using Piranha.Models; using System; using System.Collections.Generic; +using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { public class SiteEditModel { - public Data.Site Site { get; set; } = new Data.Site(); + public Site Site { get; set; } = new Site(); public IEnumerable SiteTypes { get; set; } = new List(); public static SiteEditModel Create(IApi api) diff --git a/core/Piranha.Manager/Areas/Manager/Models/SiteListModel.cs b/core/Piranha.Manager/Areas/Manager/Models/SiteListModel.cs index f8e9ababb..1f4dadefd 100644 --- a/core/Piranha.Manager/Areas/Manager/Models/SiteListModel.cs +++ b/core/Piranha.Manager/Areas/Manager/Models/SiteListModel.cs @@ -3,19 +3,21 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System.Collections.Generic; using System.Linq; +using Piranha.Models; +using Piranha.Services; namespace Piranha.Areas.Manager.Models { public class SiteListModel { - public IList Sites { get; set; } = new List(); + public IList Sites { get; set; } = new List(); public static SiteListModel Get(IApi api) { diff --git a/core/Piranha.Manager/Areas/Manager/Services/PageEditService.cs b/core/Piranha.Manager/Areas/Manager/Services/PageEditService.cs index 3e74801ed..06ba085bb 100644 --- a/core/Piranha.Manager/Areas/Manager/Services/PageEditService.cs +++ b/core/Piranha.Manager/Areas/Manager/Services/PageEditService.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -24,12 +24,12 @@ namespace Piranha.Areas.Manager.Services public class PageEditService { private readonly IApi _api; - private readonly IContentService _service; + private readonly IContentFactory _factory; - public PageEditService(IApi api, IContentServiceFactory factory) + public PageEditService(IApi api, IContentFactory factory) { _api = api; - _service = factory.CreatePageService(); + _factory = factory; } /// @@ -117,7 +117,7 @@ public PageEditModel Refresh(PageEditModel model) /// /// The page type id /// The optional site id - /// The page model + /// The page model public PageEditModel Create(string pageTypeId, Guid? siteId = null) { var type = _api.PageTypes.GetById(pageTypeId); @@ -210,9 +210,9 @@ public PageEditRegionBase CreateRegion(Piranha.Models.RegionType region, object } } - var set = new PageEditFieldSet + var set = new PageEditFieldSet { - new PageEditField + new PageEditField { Id = region.Fields[0].Id, Title = region.Fields[0].Title ?? region.Fields[0].Id, @@ -371,7 +371,7 @@ private void SaveRegions(PageEditModel src, Piranha.Models.DynamicPage dest) { if (!modelRegions.ContainsKey(region.Id)) { - modelRegions[region.Id] = _service.CreateDynamicRegion(type, region.Id); + modelRegions[region.Id] = _factory.CreateDynamicRegion(type, region.Id); } var reg = (PageEditRegion)region; @@ -408,7 +408,7 @@ private void SaveRegions(PageEditModel src, Piranha.Models.DynamicPage dest) } else { - var modelFields = (IDictionary)_service.CreateDynamicRegion(type, region.Id); + var modelFields = (IDictionary)_factory.CreateDynamicRegion(type, region.Id); foreach (var field in set) { diff --git a/core/Piranha.Manager/Areas/Manager/Services/PostEditService.cs b/core/Piranha.Manager/Areas/Manager/Services/PostEditService.cs index 7ba79114f..f270eb40b 100644 --- a/core/Piranha.Manager/Areas/Manager/Services/PostEditService.cs +++ b/core/Piranha.Manager/Areas/Manager/Services/PostEditService.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -24,12 +24,12 @@ namespace Piranha.Areas.Manager.Services public class PostEditService { private readonly IApi _api; - private readonly IContentService _service; + private readonly IContentFactory _factory; - public PostEditService(IApi api, IContentServiceFactory factory) + public PostEditService(IApi api, IContentFactory factory) { _api = api; - _service = factory.CreatePostService(); + _factory = factory; } /// @@ -61,7 +61,7 @@ public bool Save(PostEditModel model, out string alias, bool? publish = null) SaveBlocks(model, post); // Update category - var category = _api.Categories.GetBySlug(model.BlogId, model.SelectedCategory); + var category = _api.Posts.GetCategoryBySlug(model.BlogId, model.SelectedCategory); if (category != null) { post.Category = category; @@ -81,7 +81,7 @@ public bool Save(PostEditModel model, out string alias, bool? publish = null) { if (!post.Tags.Any(t => t.Slug == selectedTag)) { - var tag = _api.Tags.GetBySlug(model.BlogId, selectedTag); + var tag = _api.Posts.GetTagBySlug(model.BlogId, selectedTag); if (tag != null) { @@ -116,10 +116,6 @@ public bool Save(PostEditModel model, out string alias, bool? publish = null) _api.Posts.Save(post); model.Id = post.Id; - // Now tidy up the categories & tags for the blog - _api.Categories.DeleteUnused(post.BlogId); - _api.Tags.DeleteUnused(post.BlogId); - return true; } @@ -137,8 +133,8 @@ public PostEditModel GetById(Guid id) var page = _api.Pages.GetById(post.BlogId); var model = Module.Mapper.Map(post); model.PostType = _api.PostTypes.GetById(model.TypeId); - model.AllCategories = _api.Categories.GetAll(post.BlogId); - model.AllTags = _api.Tags.GetAll(post.BlogId); + model.AllCategories = _api.Posts.GetAllCategories(post.BlogId); + model.AllTags = _api.Posts.GetAllTags(post.BlogId); model.SelectedCategory = post.Category.Slug; model.SelectedTags = post.Tags.Select(t => t.Slug).ToList(); model.BlogSlug = page.Slug; @@ -159,8 +155,8 @@ public PostEditModel Refresh(PostEditModel model) if (!string.IsNullOrWhiteSpace(model.TypeId)) { model.PostType = _api.PostTypes.GetById(model.TypeId); - model.AllCategories = _api.Categories.GetAll(model.BlogId); - model.AllTags = _api.Tags.GetAll(model.BlogId); + model.AllCategories = _api.Posts.GetAllCategories(model.BlogId); + model.AllTags = _api.Posts.GetAllTags(model.BlogId); } return model; } @@ -170,7 +166,7 @@ public PostEditModel Refresh(PostEditModel model) /// /// The post type id /// The blog id - /// The post edit model + /// The post edit model public PostEditModel Create(string postTypeId, Guid blogId) { var type = _api.PostTypes.GetById(postTypeId); @@ -182,8 +178,8 @@ public PostEditModel Create(string postTypeId, Guid blogId) var model = Module.Mapper.Map(post); model.BlogId = blogId; model.PostType = type; - model.AllCategories = _api.Categories.GetAll(blogId); - model.AllTags = _api.Tags.GetAll(blogId); + model.AllCategories = _api.Posts.GetAllCategories(blogId); + model.AllTags = _api.Posts.GetAllTags(blogId); model.BlogSlug = page.Slug; LoadRegions(post, model); @@ -251,9 +247,9 @@ public PageEditRegionBase CreateRegion(Piranha.Models.RegionType region, object } } - var set = new PageEditFieldSet + var set = new PageEditFieldSet { - new PageEditField + new PageEditField { Id = region.Fields[0].Id, Title = region.Fields[0].Title ?? region.Fields[0].Id, @@ -387,7 +383,7 @@ private void SaveRegions(PostEditModel src, Piranha.Models.DynamicPost dest) { if (!modelRegions.ContainsKey(region.Id)) { - modelRegions[region.Id] = _service.CreateDynamicRegion(type, region.Id); + modelRegions[region.Id] = _factory.CreateDynamicRegion(type, region.Id); } var reg = (PageEditRegion)region; @@ -424,7 +420,7 @@ private void SaveRegions(PostEditModel src, Piranha.Models.DynamicPost dest) } else { - var modelFields = (IDictionary)_service.CreateDynamicRegion(type, region.Id); + var modelFields = (IDictionary)_factory.CreateDynamicRegion(type, region.Id); foreach (var field in set) { diff --git a/core/Piranha.Manager/Areas/Manager/Services/SiteContentEditService.cs b/core/Piranha.Manager/Areas/Manager/Services/SiteContentEditService.cs index d807dd9a1..01be9a0d3 100644 --- a/core/Piranha.Manager/Areas/Manager/Services/SiteContentEditService.cs +++ b/core/Piranha.Manager/Areas/Manager/Services/SiteContentEditService.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -23,12 +23,12 @@ namespace Piranha.Areas.Manager.Services /// public class SiteContentEditService { - private readonly IApi api; - private readonly IContentService service; + private readonly IApi _api; + private readonly IContentFactory _factory; - public SiteContentEditService(IApi api, IContentServiceFactory factory) { - this.api = api; - service = factory.CreateSiteService(); + public SiteContentEditService(IApi api, IContentFactory factory) { + _api = api; + _factory = factory; } /// @@ -37,16 +37,16 @@ public SiteContentEditService(IApi api, IContentServiceFactory factory) { /// The site content edit model /// If the model was successfully saved public bool Save(SiteContentEditModel model) { - var site = api.Sites.GetContentById(model.Id); + var site = _api.Sites.GetContentById(model.Id); if (site == null) { - site = Piranha.Models.DynamicSiteContent.Create(api, model.TypeId); + site = Piranha.Models.DynamicSiteContent.Create(_api, model.TypeId); } Module.Mapper.Map(model, site); SaveRegions(model, site); - api.Sites.SaveContent(model.Id, site); + _api.Sites.SaveContent(model.Id, site); model.Id = site.Id; return true; @@ -59,11 +59,11 @@ public bool Save(SiteContentEditModel model) { /// The post id /// The post model public SiteContentEditModel GetById(Guid id) { - var site = api.Sites.GetContentById(id); + var site = _api.Sites.GetContentById(id); if (site == null) { - var siteInfo = api.Sites.GetById(id); - site = Piranha.Models.DynamicSiteContent.Create(api, siteInfo.SiteTypeId); + var siteInfo = _api.Sites.GetById(id); + site = Piranha.Models.DynamicSiteContent.Create(_api, siteInfo.SiteTypeId); site.Id = siteInfo.Id; site.TypeId = siteInfo.SiteTypeId; @@ -72,7 +72,7 @@ public SiteContentEditModel GetById(Guid id) { if (site != null) { var model = Module.Mapper.Map(site); - model.SiteType = api.SiteTypes.GetById(model.TypeId); + model.SiteType = _api.SiteTypes.GetById(model.TypeId); LoadRegions(site, model); @@ -86,7 +86,7 @@ public SiteContentEditModel GetById(Guid id) { /// public SiteContentEditModel Refresh(SiteContentEditModel model) { if (!string.IsNullOrWhiteSpace(model.TypeId)) { - model.SiteType = api.SiteTypes.GetById(model.TypeId); + model.SiteType = _api.SiteTypes.GetById(model.TypeId); } return model; } @@ -95,12 +95,12 @@ public SiteContentEditModel Refresh(SiteContentEditModel model) { /// Creates a new edit model with the given site typeparamref. /// /// The site type id - /// The site content edit model + /// The site content edit model public SiteContentEditModel Create(string siteTypeId) { - var type = api.SiteTypes.GetById(siteTypeId); + var type = _api.SiteTypes.GetById(siteTypeId); if (type != null) { - var site = Piranha.Models.DynamicSiteContent.Create(api, siteTypeId); + var site = Piranha.Models.DynamicSiteContent.Create(_api, siteTypeId); var model = Module.Mapper.Map(site); model.SiteType = type; @@ -177,7 +177,7 @@ public PageEditRegionBase CreateRegion(Piranha.Models.RegionType region, object itemTitle = ((Extend.IField)fieldData[field.Id]).GetTitle(); if (string.IsNullOrWhiteSpace(itemTitle) && !string.IsNullOrWhiteSpace(region.ListTitlePlaceholder)) itemTitle = region.ListTitlePlaceholder; - else if (string.IsNullOrWhiteSpace(itemTitle)) + else if (string.IsNullOrWhiteSpace(itemTitle)) itemTitle = "Item"; fieldSet.ListTitle = itemTitle; @@ -224,13 +224,13 @@ private void LoadRegions(Piranha.Models.DynamicSiteContent src, SiteContentEditM /// The source /// The destination private void SaveRegions(SiteContentEditModel src, Piranha.Models.DynamicSiteContent dest) { - var type = api.SiteTypes.GetById(src.TypeId); + var type = _api.SiteTypes.GetById(src.TypeId); var modelRegions = (IDictionary)dest.Regions; - + foreach (var region in src.Regions) { if (region is PageEditRegion) { if (!modelRegions.ContainsKey(region.Id)) - modelRegions[region.Id] = service.CreateDynamicRegion(type, region.Id); + modelRegions[region.Id] = _factory.CreateDynamicRegion(type, region.Id); var reg = (PageEditRegion)region; @@ -255,7 +255,7 @@ private void SaveRegions(SiteContentEditModel src, Piranha.Models.DynamicSiteCon if (set.Count == 1) { list.Add(set[0].Value); } else { - var modelFields = (IDictionary)service.CreateDynamicRegion(type, region.Id); + var modelFields = (IDictionary)_factory.CreateDynamicRegion(type, region.Id); foreach (var field in set) { modelFields[field.Id] = field.Value; diff --git a/core/Piranha.Manager/Manager/Utils.cs b/core/Piranha.Manager/Manager/Utils.cs index 065b3ba7b..06c7bd20c 100644 --- a/core/Piranha.Manager/Manager/Utils.cs +++ b/core/Piranha.Manager/Manager/Utils.cs @@ -3,13 +3,14 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Linq; +using Piranha.Services; namespace Piranha.Manager { @@ -37,8 +38,8 @@ public static void CreateAlias(IApi api, Guid siteId, string alias, string redir api.Aliases.Save(model); } } - } - + } + // Check for an existing alias var aliasModel = api.Aliases.GetByAliasUrl($"/{alias}", siteId); if (aliasModel != null) { @@ -46,13 +47,13 @@ public static void CreateAlias(IApi api, Guid siteId, string alias, string redir api.Aliases.Save(aliasModel); } else { // Let's create a new alias - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Models.Alias { SiteId = siteId, AliasUrl = alias, RedirectUrl = redirect, Type = Piranha.Models.RedirectType.Permanent }); } - } + } } } \ No newline at end of file diff --git a/core/Piranha.Manager/Piranha.Manager.csproj b/core/Piranha.Manager/Piranha.Manager.csproj index 0e2a820ec..a131af06f 100644 --- a/core/Piranha.Manager/Piranha.Manager.csproj +++ b/core/Piranha.Manager/Piranha.Manager.csproj @@ -15,6 +15,7 @@ + @@ -24,6 +25,7 @@ + diff --git a/core/Piranha/Api.cs b/core/Piranha/Api.cs index 830598ae9..b87a5985a 100644 --- a/core/Piranha/Api.cs +++ b/core/Piranha/Api.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using Piranha.Services; @@ -19,81 +19,61 @@ namespace Piranha /// public sealed class Api : IApi, IDisposable { - /// - /// The private db context. - /// - private readonly IDb _db; - - /// - /// The private storage provider. - /// - private readonly IStorage _storage; - /// /// The private model cache. /// - private ICache _cache; - - /// - /// Gets/sets the alias repository. - /// - public IAliasRepository Aliases { get; private set; } + private readonly ICache _cache; /// - /// Gets/sets the archive repository. + /// Gets/sets the alias service. /// - public IArchiveRepository Archives { get; private set; } + public AliasService Aliases { get; private set; } /// - /// Gets the category repository. + /// Gets/sets the archive service. /// - public ICategoryRepository Categories { get; private set; } + public ArchiveService Archives { get; private set; } /// - /// Gets the media repository. + /// Gets the media service. /// /// - public IMediaRepository Media { get; private set; } + public MediaService Media { get; private set; } /// - /// Gets the page repository. + /// Gets the page service. /// - public IPageRepository Pages { get; private set; } + public PageService Pages { get; private set; } /// - /// Gets the page type repository. + /// Gets the page type service. /// - public IPageTypeRepository PageTypes { get; private set; } + public PageTypeService PageTypes { get; private set; } /// - /// Gets the param repository. + /// Gets the param service. /// - public IParamRepository Params { get; private set; } + public ParamService Params { get; private set; } /// - /// Gets the post repository. + /// Gets the post service. /// - public IPostRepository Posts { get; private set; } + public PostService Posts { get; private set; } /// - /// Gets the post type repository. + /// Gets the post type service. /// - public IPostTypeRepository PostTypes { get; private set; } + public PostTypeService PostTypes { get; private set; } /// - /// Gets the site repository. + /// Gets the site service. /// - public ISiteRepository Sites { get; private set; } + public SiteService Sites { get; private set; } /// - /// Gets the site type repository. + /// Gets the site type service. /// - public ISiteTypeRepository SiteTypes { get; private set; } - - /// - /// Gets the tag repository. - /// - public ITagRepository Tags { get; private set; } + public SiteTypeService SiteTypes { get; private set; } /// /// Gets if the current repository has caching enabled or not. @@ -101,19 +81,41 @@ public sealed class Api : IApi, IDisposable public bool IsCached => _cache != null; /// - /// Default constructor. - /// - /// The current db context - /// The content service factory - /// The current storage - /// The optional model cache - /// The optional image processor - public Api(IDb db, IContentServiceFactory factory, IStorage storage = null, ICache modelCache = null, IImageProcessor imageProcessor = null) + /// Creates a new api from the currently registered + /// repositories. + /// + public Api( + IContentFactory contentFactory, + IAliasRepository aliasRepository, + IArchiveRepository archiveRepository, + IMediaRepository mediaRepository, + IPageRepository pageRepository, + IPageTypeRepository pageTypeRepository, + IParamRepository paramRepository, + IPostRepository postRepository, + IPostTypeRepository postTypeRepository, + ISiteRepository siteRepository, + ISiteTypeRepository siteTypeRepository, + ICache cache = null, + IStorage storage = null, + IImageProcessor processor = null) { - _db = db; - _storage = storage; - - Setup(factory, modelCache, imageProcessor); + // Store the cache + _cache = cache; + + // Create services without dependecies + PageTypes = new PageTypeService(pageTypeRepository, cache); + Params = new ParamService(paramRepository, cache); + PostTypes = new PostTypeService(postTypeRepository, cache); + Sites = new SiteService(siteRepository, contentFactory, cache); + SiteTypes = new SiteTypeService(siteTypeRepository, cache); + + // Create services with dependencies + Aliases = new AliasService(aliasRepository, Sites, cache); + Media = new MediaService(mediaRepository, Params, storage, processor, cache); + Pages = new PageService(pageRepository, contentFactory, Sites, Params, cache); + Posts = new PostService(postRepository, contentFactory, Sites, Pages, cache); + Archives = new ArchiveService(archiveRepository, Pages, Params, Posts); } /// @@ -121,34 +123,6 @@ public Api(IDb db, IContentServiceFactory factory, IStorage storage = null, ICac /// public void Dispose() { - _db.Dispose(); - } - - #region Private methods - /// - /// Configures the api. - /// - /// The optional model cache - /// The optional image processor - private void Setup(IContentServiceFactory factory, ICache modelCache = null, IImageProcessor imageProcessor = null) - { - _cache = modelCache; - - var cacheLevel = (int)App.CacheLevel; - - Aliases = new AliasRepository(this, _db, cacheLevel > 2 ? _cache : null); - Archives = new ArchiveRepository(this, _db); - Categories = new CategoryRepository(this, _db, cacheLevel > 2 ? _cache : null); - Media = new MediaRepository(this, _db, _storage, cacheLevel > 2 ? _cache : null, imageProcessor); - Pages = new PageRepository(this, _db, factory, cacheLevel > 2 ? _cache : null); - PageTypes = new PageTypeRepository(_db); - Params = new ParamRepository(_db, cacheLevel > 0 ? _cache : null); - Posts = new PostRepository(this, _db, factory, cacheLevel > 2 ? _cache : null); - PostTypes = new PostTypeRepository(_db); - Sites = new SiteRepository(this, _db, factory, cacheLevel > 0 ? _cache : null); - SiteTypes = new SiteTypeRepository(_db); - Tags = new TagRepository(_db, cacheLevel > 2 ? _cache : null); } - #endregion } } diff --git a/core/Piranha/App.cs b/core/Piranha/App.cs index dd61f0c75..360db19fe 100644 --- a/core/Piranha/App.cs +++ b/core/Piranha/App.cs @@ -1,21 +1,22 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ -using AutoMapper; +using System; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Piranha.Extend; using Piranha.Extend.Serializers; using Piranha.Runtime; using Piranha.Security; -using System; -using System.Reflection; +using Piranha.Services; namespace Piranha { @@ -27,7 +28,7 @@ public sealed class App /// /// The singleton app instance. /// - private static readonly App Instance = new App(); + private static readonly App Instance; /// /// Mutex for thread safe initialization. @@ -35,7 +36,7 @@ public sealed class App private static readonly object _mutex = new object(); /// - /// The current state of the app. + /// If the app has been initialized. /// private static volatile bool _isInitialized = false; @@ -59,16 +60,6 @@ public sealed class App /// private readonly MediaManager _mediaTypes; - /// - /// The application object mapper. - /// - private IMapper _mapper; - - /// - /// The application markdown converter. - /// - private IMarkdown _markdown; - /// /// The currently registered serializers. /// @@ -94,6 +85,26 @@ public sealed class App /// private Cache.CacheLevel _cacheLevel = Cache.CacheLevel.Full; + /// + /// The application markdown converter. + /// + private IMarkdown _markdown; + + /// + /// The currently available page types. + /// + private readonly ContentTypeList _pageTypes; + + /// + /// The currently available post types. + /// + private readonly ContentTypeList _postTypes; + + /// + /// The currently available post types. + /// + private readonly ContentTypeList _siteTypes; + /// /// Gets the currently registered block types. /// @@ -114,11 +125,6 @@ public sealed class App /// public static MediaManager MediaTypes => Instance._mediaTypes; - /// - /// Gets the application object mapper. - /// - public static IMapper Mapper => Instance._mapper; - /// /// Gets the markdown converter. /// @@ -160,6 +166,93 @@ public static Cache.CacheLevel CacheLevel set { Instance._cacheLevel = value; } } + /// + /// Gets the currently available page types. + /// + public static ContentTypeList PageTypes => Instance._pageTypes; + + /// + /// Gets the currently available page types. + /// + public static ContentTypeList PostTypes => Instance._postTypes; + + /// + /// Gets the currently available page types. + /// + public static ContentTypeList SiteTypes => Instance._siteTypes; + + /// + /// Static constructor. Called once every application + /// lifecycle. + /// + static App() + { + Instance = new App(); + + // Setup media types + Instance._mediaTypes.Documents.Add(".pdf", "application/pdf"); + Instance._mediaTypes.Images.Add(".jpg", "image/jpeg"); + Instance._mediaTypes.Images.Add(".jpeg", "image/jpeg"); + Instance._mediaTypes.Images.Add(".png", "image/png"); + Instance._mediaTypes.Videos.Add(".mp4", "video/mp4"); + + // Compose content types + Instance._contentTypes.Register("Page", "Page"); + Instance._contentTypes.Register("Blog", "Archive", true); + + // Compose field types + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + Instance._fields.Register(); + + // Compose block types + Instance._blocks.Register(); + Instance._blocks.Register(); + Instance._blocks.Register(); + Instance._blocks.Register(); + Instance._blocks.Register(); + + // Compose serializers + Instance._serializers.Register(new CheckBoxFieldSerializer()); + Instance._serializers.Register(new DateFieldSerializer()); + Instance._serializers.Register(new DocumentFieldSerializer()); + Instance._serializers.Register(new StringFieldSerializer()); + Instance._serializers.Register(new StringFieldSerializer()); + Instance._serializers.Register(new MediaFieldSerializer()); + Instance._serializers.Register(new IntegerFieldSerializer()); + Instance._serializers.Register(new PageFieldSerializer()); + Instance._serializers.Register(new PostFieldSerializer()); + Instance._serializers.Register(new StringFieldSerializer()); + Instance._serializers.Register(new StringFieldSerializer()); + Instance._serializers.Register(new ImageFieldSerializer()); + Instance._serializers.Register(new VideoFieldSerializer()); + + // Create markdown converter + Instance._markdown = new DefaultMarkdown(); + + // Register permissions + Instance._permissions["Core"].Add(new PermissionItem + { + Name = Permission.PagePreview, + Title = "Page Preview" + }); + Instance._permissions["Core"].Add(new PermissionItem + { + Name = Permission.PostPreview, + Title = "Post Preview" + }); + } + /// /// Default private constructor. /// @@ -173,23 +266,17 @@ private App() _contentTypes = new ContentTypeManager(); _hooks = new HookManager(); _permissions = new PermissionManager(); + _pageTypes = new ContentTypeList(); + _postTypes = new ContentTypeList(); + _siteTypes = new ContentTypeList(); } /// - /// Initializes the application object. - /// - public static void Init() - { - Instance.Initialize(); - } - - /// - /// Initializes the application object. + /// Initializes the application. /// - [Obsolete("Please refer to App.Init()")] public static void Init(IApi api) { - Instance.Initialize(); + Instance.InitApp(api); } /// @@ -226,7 +313,7 @@ public static object DeserializeObject(string value, Type type) /// Initializes the application object. /// /// The current api - private void Initialize() + private void InitApp(IApi api) { if (!_isInitialized) { @@ -234,144 +321,10 @@ private void Initialize() { if (!_isInitialized) { - // Configure object mapper - var mapperConfig = new MapperConfiguration(cfg => - { - cfg.CreateMap() - .ForMember(a => a.Id, o => o.Ignore()) - .ForMember(a => a.Created, o => o.Ignore()); - cfg.CreateMap() - .ForMember(c => c.Id, o => o.Ignore()) - .ForMember(c => c.Created, o => o.Ignore()); - cfg.CreateMap() - .ForMember(f => f.Id, o => o.Ignore()) - .ForMember(f => f.Created, o => o.Ignore()) - .ForMember(f => f.Media, o => o.Ignore()); - cfg.CreateMap() - .ForMember(f => f.Level, o => o.Ignore()) - .ForMember(f => f.Items, o => o.Ignore()); - cfg.CreateMap() - .ForMember(p => p.TypeId, o => o.MapFrom(m => m.PageTypeId)) - .ForMember(p => p.Permalink, o => o.MapFrom(m => "/" + m.Slug)) - .ForMember(p => p.Blocks, o => o.Ignore()); - cfg.CreateMap() - .ForMember(p => p.PageTypeId, o => o.MapFrom(m => m.TypeId)) - .ForMember(p => p.Blocks, o => o.Ignore()) - .ForMember(p => p.Fields, o => o.Ignore()) - .ForMember(p => p.Created, o => o.Ignore()) - .ForMember(p => p.LastModified, o => o.Ignore()) - .ForMember(p => p.PageType, o => o.Ignore()) - .ForMember(p => p.Site, o => o.Ignore()) - .ForMember(p => p.Parent, o => o.Ignore()); - cfg.CreateMap() - .ForMember(p => p.MenuTitle, o => o.Ignore()) - .ForMember(p => p.Level, o => o.Ignore()) - .ForMember(p => p.Items, o => o.Ignore()) - .ForMember(p => p.PageTypeName, o => o.Ignore()) - .ForMember(p => p.Permalink, o => o.MapFrom(d => !d.ParentId.HasValue && d.SortOrder == 0 ? "/" : "/" + d.Slug)); - cfg.CreateMap() - .ForMember(p => p.Id, o => o.Ignore()) - .ForMember(p => p.Created, o => o.Ignore()); - cfg.CreateMap() - .ForMember(p => p.TypeId, o => o.MapFrom(m => m.PostTypeId)) - .ForMember(p => p.Permalink, o => o.MapFrom(m => "/" + m.Blog.Slug + "/" + m.Slug)) - .ForMember(p => p.Blocks, o => o.Ignore()); - cfg.CreateMap() - .ForMember(p => p.PostTypeId, o => o.MapFrom(m => m.TypeId)) - .ForMember(p => p.CategoryId, o => o.MapFrom(m => m.Category.Id)) - .ForMember(p => p.Blocks, o => o.Ignore()) - .ForMember(p => p.Fields, o => o.Ignore()) - .ForMember(p => p.Created, o => o.Ignore()) - .ForMember(p => p.LastModified, o => o.Ignore()) - .ForMember(p => p.PostType, o => o.Ignore()) - .ForMember(p => p.Blog, o => o.Ignore()) - .ForMember(p => p.Category, o => o.Ignore()) - .ForMember(p => p.Tags, o => o.Ignore()); - cfg.CreateMap() - .ForMember(s => s.Id, o => o.Ignore()) - .ForMember(s => s.Created, o => o.Ignore()); - cfg.CreateMap() - .ForMember(s => s.TypeId, o => o.MapFrom(m => m.SiteTypeId)); - cfg.CreateMap() - .ForMember(s => s.SiteTypeId, o => o.Ignore()) - .ForMember(s => s.InternalId, o => o.Ignore()) - .ForMember(s => s.Description, o => o.Ignore()) - .ForMember(s => s.Hostnames, o => o.Ignore()) - .ForMember(s => s.IsDefault, o => o.Ignore()) - .ForMember(s => s.Culture, o => o.Ignore()) - .ForMember(s => s.Fields, o => o.Ignore()) - .ForMember(s => s.Created, o => o.Ignore()) - .ForMember(s => s.LastModified, o => o.Ignore()) - .ForMember(s => s.ContentLastModified, o => o.Ignore()); - cfg.CreateMap() - .ForMember(t => t.Id, o => o.Ignore()) - .ForMember(t => t.Created, o => o.Ignore()); - }); - mapperConfig.AssertConfigurationIsValid(); - _mapper = mapperConfig.CreateMapper(); - - // Setup media types - _mediaTypes.Documents.Add(".pdf", "application/pdf"); - _mediaTypes.Images.Add(".jpg", "image/jpeg"); - _mediaTypes.Images.Add(".jpeg", "image/jpeg"); - _mediaTypes.Images.Add(".png", "image/png"); - _mediaTypes.Videos.Add(".mp4", "video/mp4"); - - // Compose content types - _contentTypes.Register("Page", "Page"); - _contentTypes.Register("Blog", "Archive", true); - - // Compose field types - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - _fields.Register(); - - // Compose block types - _blocks.Register(); - _blocks.Register(); - _blocks.Register(); - _blocks.Register(); - _blocks.Register(); - - // Compose serializers - _serializers.Register(new CheckBoxFieldSerializer()); - _serializers.Register(new DateFieldSerializer()); - _serializers.Register(new DocumentFieldSerializer()); - _serializers.Register(new StringFieldSerializer()); - _serializers.Register(new StringFieldSerializer()); - _serializers.Register(new MediaFieldSerializer()); - _serializers.Register(new IntegerFieldSerializer()); - _serializers.Register(new PageFieldSerializer()); - _serializers.Register(new PostFieldSerializer()); - _serializers.Register(new StringFieldSerializer()); - _serializers.Register(new StringFieldSerializer()); - _serializers.Register(new ImageFieldSerializer()); - _serializers.Register(new VideoFieldSerializer()); - - // Create markdown converter - _markdown = new DefaultMarkdown(); - - // Register permissions - _permissions["Core"].Add(new PermissionItem - { - Name = Permission.PagePreview, - Title = "Page Preview" - }); - _permissions["Core"].Add(new PermissionItem - { - Name = Permission.PostPreview, - Title = "Post Preview" - }); + // Initialize content types + _pageTypes.Init(api.PageTypes.GetAllAsync().GetAwaiter().GetResult()); + _postTypes.Init(api.PostTypes.GetAllAsync().GetAwaiter().GetResult()); + _siteTypes.Init(api.SiteTypes.GetAllAsync().GetAwaiter().GetResult()); // Initialize all modules foreach (var module in _modules) diff --git a/core/Piranha/Cache/MemoryCache.cs b/core/Piranha/Cache/MemoryCache.cs index d0d90ca82..d860e0329 100644 --- a/core/Piranha/Cache/MemoryCache.cs +++ b/core/Piranha/Cache/MemoryCache.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using Microsoft.Extensions.Caching.Memory; @@ -39,7 +39,7 @@ public MemoryCache(IMemoryCache cache) /// The cached model, null it wasn't found public T Get(string key) { - return _cache.Get(key); + return Utils.DeepClone(_cache.Get(key)); } /// diff --git a/core/Piranha/Cache/SimpleCache.cs b/core/Piranha/Cache/SimpleCache.cs index f084b68c6..7a0efb53f 100644 --- a/core/Piranha/Cache/SimpleCache.cs +++ b/core/Piranha/Cache/SimpleCache.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; @@ -38,7 +38,7 @@ public T Get(string key) if (_cache.TryGetValue(key, out value)) { - return (T)value; + return Utils.DeepClone((T)value); } return default(T); } diff --git a/core/Piranha/Config.cs b/core/Piranha/Config.cs index 9d5661736..f13cd3cb9 100644 --- a/core/Piranha/Config.cs +++ b/core/Piranha/Config.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using Piranha.Models; +using Piranha.Services; namespace Piranha { @@ -18,9 +20,9 @@ namespace Piranha public sealed class Config : IDisposable { /// - /// The private api. + /// The private param service. /// - private readonly IApi _api; + private readonly ParamService _service; /// /// The system config keys. @@ -37,22 +39,22 @@ public sealed class Config : IDisposable /// public int ArchivePageSize { get { - var param = _api.Params.GetByKey(ARCHIVE_PAGE_SIZE); + var param = _service.GetByKeyAsync(ARCHIVE_PAGE_SIZE).GetAwaiter().GetResult(); if (param != null) return Convert.ToInt32(param.Value); return 0; } set { - var param = _api.Params.GetByKey(ARCHIVE_PAGE_SIZE); + var param = _service.GetByKeyAsync(ARCHIVE_PAGE_SIZE).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = ARCHIVE_PAGE_SIZE }; } param.Value = value.ToString(); - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } @@ -62,22 +64,22 @@ public int ArchivePageSize { /// public int CacheExpiresPages { get { - var param = _api.Params.GetByKey(CACHE_EXPIRES_PAGES); + var param = _service.GetByKeyAsync(CACHE_EXPIRES_PAGES).GetAwaiter().GetResult(); if (param != null) return Convert.ToInt32(param.Value); return 0; } set { - var param = _api.Params.GetByKey(CACHE_EXPIRES_PAGES); + var param = _service.GetByKeyAsync(CACHE_EXPIRES_PAGES).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = CACHE_EXPIRES_PAGES }; } param.Value = value.ToString(); - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } @@ -87,22 +89,22 @@ public int CacheExpiresPages { /// public int CacheExpiresPosts { get { - var param = _api.Params.GetByKey(CACHE_EXPIRES_POSTS); + var param = _service.GetByKeyAsync(CACHE_EXPIRES_POSTS).GetAwaiter().GetResult(); if (param != null) return Convert.ToInt32(param.Value); return 0; } set { - var param = _api.Params.GetByKey(CACHE_EXPIRES_POSTS); + var param = _service.GetByKeyAsync(CACHE_EXPIRES_POSTS).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = CACHE_EXPIRES_POSTS }; } param.Value = value.ToString(); - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } @@ -112,22 +114,22 @@ public int CacheExpiresPosts { /// public bool HierarchicalPageSlugs { get { - var param = _api.Params.GetByKey(PAGES_HIERARCHICAL_SLUGS); + var param = _service.GetByKeyAsync(PAGES_HIERARCHICAL_SLUGS).GetAwaiter().GetResult(); if (param != null) return Convert.ToBoolean(param.Value); return true; } set { - var param = _api.Params.GetByKey(PAGES_HIERARCHICAL_SLUGS); + var param = _service.GetByKeyAsync(PAGES_HIERARCHICAL_SLUGS).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = PAGES_HIERARCHICAL_SLUGS }; } param.Value = value.ToString(); - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } @@ -137,22 +139,22 @@ public bool HierarchicalPageSlugs { /// public int ManagerExpandedSitemapLevels { get { - var param = _api.Params.GetByKey(MANAGER_EXPANDED_SITEMAP_LEVELS); + var param = _service.GetByKeyAsync(MANAGER_EXPANDED_SITEMAP_LEVELS).GetAwaiter().GetResult(); if (param != null) return Convert.ToInt32(param.Value); return 0; } set { - var param = _api.Params.GetByKey(MANAGER_EXPANDED_SITEMAP_LEVELS); + var param = _service.GetByKeyAsync(MANAGER_EXPANDED_SITEMAP_LEVELS).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = MANAGER_EXPANDED_SITEMAP_LEVELS }; } param.Value = value.ToString(); - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } @@ -162,16 +164,16 @@ public int ManagerExpandedSitemapLevels { /// public string MediaCDN { get { - var param = _api.Params.GetByKey(MEDIA_CDN_URL); + var param = _service.GetByKeyAsync(MEDIA_CDN_URL).GetAwaiter().GetResult(); if (param != null) return param.Value; return null; } set { - var param = _api.Params.GetByKey(MEDIA_CDN_URL); + var param = _service.GetByKeyAsync(MEDIA_CDN_URL).GetAwaiter().GetResult(); if (param == null) { - param = new Data.Param + param = new Param { Key = MEDIA_CDN_URL }; @@ -182,17 +184,26 @@ public string MediaCDN { value = value + "/"; param.Value = value; - _api.Params.Save(param); + _service.SaveAsync(param).GetAwaiter().GetResult(); } } + /// + /// Default constructor. + /// + /// The current param service + public Config(ParamService paramService) + { + _service = paramService; + } + /// /// Default constructor. /// /// The current api public Config(IApi api) { - _api = api; + _service = api.Params; } /// diff --git a/core/Piranha/Data/ICreated.cs b/core/Piranha/Data/ICreated.cs deleted file mode 100644 index b75d65c0a..000000000 --- a/core/Piranha/Data/ICreated.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; - -namespace Piranha.Data -{ - /// - /// Interface for models tracking created date. - /// - public interface ICreated - { - /// - /// Gets/sets the created date. - /// - DateTime Created { get; set; } - } -} diff --git a/core/Piranha/Data/IModified.cs b/core/Piranha/Data/IModified.cs deleted file mode 100644 index 879592678..000000000 --- a/core/Piranha/Data/IModified.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; - -namespace Piranha.Data -{ - /// - /// Interface for models tracking last modification date. - /// - public interface IModified - { - /// - /// Gets/sets the last modification date. - /// - DateTime LastModified { get; set; } - } -} diff --git a/core/Piranha/Data/PageContentType.cs b/core/Piranha/Data/PageContentType.cs deleted file mode 100644 index d365cd020..000000000 --- a/core/Piranha/Data/PageContentType.cs +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * https://github.com/piranhacms/piranha.core - * - */ - -namespace Piranha.Data -{ - public enum PageContentType - { - Page, - Blog - } -} \ No newline at end of file diff --git a/core/Piranha/Extend/Fields/DocumentField.cs b/core/Piranha/Extend/Fields/DocumentField.cs index 2ed6606ae..c0d956119 100644 --- a/core/Piranha/Extend/Fields/DocumentField.cs +++ b/core/Piranha/Extend/Fields/DocumentField.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using Piranha.Models; namespace Piranha.Extend.Fields { @@ -28,7 +29,7 @@ public static implicit operator DocumentField(Guid guid) /// Implicit operator for converting a media object to a field. /// /// The media object - public static implicit operator DocumentField(Data.Media media) + public static implicit operator DocumentField(Media media) { return new DocumentField { Id = media.Id }; } diff --git a/core/Piranha/Extend/Fields/ImageField.cs b/core/Piranha/Extend/Fields/ImageField.cs index 6e50e1da6..906903e7d 100644 --- a/core/Piranha/Extend/Fields/ImageField.cs +++ b/core/Piranha/Extend/Fields/ImageField.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using Piranha.Models; namespace Piranha.Extend.Fields { @@ -28,7 +29,7 @@ public static implicit operator ImageField(Guid guid) /// Implicit operator for converting a media object to a field. /// /// The media object - public static implicit operator ImageField(Data.Media media) + public static implicit operator ImageField(Media media) { return new ImageField { Id = media.Id }; } diff --git a/core/Piranha/Extend/Fields/MediaField.cs b/core/Piranha/Extend/Fields/MediaField.cs index 8d0cf4364..f7623abb7 100644 --- a/core/Piranha/Extend/Fields/MediaField.cs +++ b/core/Piranha/Extend/Fields/MediaField.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using Piranha.Models; namespace Piranha.Extend.Fields { @@ -28,7 +29,7 @@ public static implicit operator MediaField(Guid guid) /// Implicit operator for converting a media object to a field. /// /// The media object - public static implicit operator MediaField(Data.Media media) + public static implicit operator MediaField(Media media) { return new MediaField { Id = media.Id }; } diff --git a/core/Piranha/Extend/Fields/MediaFieldBase.cs b/core/Piranha/Extend/Fields/MediaFieldBase.cs index 6c807ca90..f6d2d11ca 100644 --- a/core/Piranha/Extend/Fields/MediaFieldBase.cs +++ b/core/Piranha/Extend/Fields/MediaFieldBase.cs @@ -3,13 +3,14 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ -using Newtonsoft.Json; using System; +using System.Threading.Tasks; +using Newtonsoft.Json; namespace Piranha.Extend.Fields { @@ -38,7 +39,7 @@ public virtual string GetTitle() /// Gets/sets the related media object. /// [JsonIgnore] - public Data.Media Media { get; private set; } + public Models.Media Media { get; private set; } /// /// Gets if the field has a media object available. @@ -49,11 +50,11 @@ public virtual string GetTitle() /// Initializes the field for client use. /// /// The current api - public virtual void Init(IApi api) + public virtual async Task Init(IApi api) { if (Id.HasValue) { - Media = api.Media.GetById(Id.Value); + Media = await api.Media.GetByIdAsync(Id.Value); if (Media == null) { diff --git a/core/Piranha/Extend/Fields/PageField.cs b/core/Piranha/Extend/Fields/PageField.cs index c3f9adb53..0905c9809 100644 --- a/core/Piranha/Extend/Fields/PageField.cs +++ b/core/Piranha/Extend/Fields/PageField.cs @@ -3,13 +3,15 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using Newtonsoft.Json; using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Extend.Fields { @@ -48,11 +50,11 @@ public virtual string GetTitle() /// Initializes the field for client use. /// /// The current api - public virtual void Init(IApi api) + public virtual async Task Init(IApi api) { if (Id.HasValue) { - Page = api.Pages.GetById(Id.Value); + Page = await api.Pages.GetByIdAsync(Id.Value); if (Page == null) { @@ -68,10 +70,10 @@ public virtual void Init(IApi api) /// /// The current api /// The referenced page - public virtual T GetPage(IApi api) where T : Models.GenericPage + public virtual Task GetPageAsync(IApi api) where T : Models.GenericPage { if (Id.HasValue) - return api.Pages.GetById(Id.Value); + return api.Pages.GetByIdAsync(Id.Value); return null; } diff --git a/core/Piranha/Extend/Fields/PostField.cs b/core/Piranha/Extend/Fields/PostField.cs index 3ca158952..4d92f26e0 100644 --- a/core/Piranha/Extend/Fields/PostField.cs +++ b/core/Piranha/Extend/Fields/PostField.cs @@ -1,15 +1,17 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ -using Newtonsoft.Json; using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Piranha.Services; namespace Piranha.Extend.Fields { @@ -46,11 +48,11 @@ public virtual string GetTitle() /// Initializes the field for client use. /// /// The current api - public virtual void Init(IApi api) + public virtual async Task Init(IApi api) { if (Id.HasValue) { - Post = api.Posts.GetById(Id.Value); + Post = await api.Posts.GetByIdAsync(Id.Value); if (Post == null) { @@ -66,11 +68,11 @@ public virtual void Init(IApi api) /// /// The current api /// The referenced post - public virtual T GetPost(IApi api) where T : Models.Post + public virtual Task GetPostAsync(IApi api) where T : Models.Post { if (Id.HasValue) { - return api.Posts.GetById(Id.Value); + return api.Posts.GetByIdAsync(Id.Value); } return null; } @@ -81,9 +83,9 @@ public virtual T GetPost(IApi api) where T : Models.Post /// The string value public static implicit operator PostField(Guid guid) { - return new PostField - { - Id = guid + return new PostField + { + Id = guid }; } @@ -93,9 +95,9 @@ public static implicit operator PostField(Guid guid) /// The post object public static implicit operator PostField(Models.PostBase post) { - return new PostField - { - Id = post.Id + return new PostField + { + Id = post.Id }; } diff --git a/core/Piranha/Extend/Fields/VideoField.cs b/core/Piranha/Extend/Fields/VideoField.cs index 7a0c2ac9a..7bddb2956 100644 --- a/core/Piranha/Extend/Fields/VideoField.cs +++ b/core/Piranha/Extend/Fields/VideoField.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using Piranha.Models; namespace Piranha.Extend.Fields { @@ -28,7 +29,7 @@ public static implicit operator VideoField(Guid guid) /// Implicit operator for converting a media object to a field. /// /// The media object - public static implicit operator VideoField(Data.Media media) + public static implicit operator VideoField(Media media) { return new VideoField { Id = media.Id }; } diff --git a/core/Piranha/IApi.cs b/core/Piranha/IApi.cs index 0442985fd..59aadf07f 100644 --- a/core/Piranha/IApi.cs +++ b/core/Piranha/IApi.cs @@ -1,16 +1,16 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Data; -using Piranha.Repositories; using System; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha { @@ -22,61 +22,51 @@ public interface IApi : IDisposable /// /// Gets/sets the alias repository. /// - IAliasRepository Aliases { get; } - - /// - /// Gets/sets the archive repository. - /// - IArchiveRepository Archives { get; } + AliasService Aliases { get; } /// - /// Gets the category repository. + /// Gets/sets the archive service. /// - ICategoryRepository Categories { get; } + ArchiveService Archives { get; } /// - /// Gets the media repository. + /// Gets the media service. /// - IMediaRepository Media { get; } + MediaService Media { get; } /// /// Gets the page repository. /// - IPageRepository Pages { get; } - - /// - /// Gets the page type repository. - /// - IPageTypeRepository PageTypes { get; } + PageService Pages { get; } /// - /// Gets the param repository. + /// Gets the page type service. /// - IParamRepository Params { get; } + PageTypeService PageTypes { get; } /// - /// Gets the post repository. + /// Gets the param service. /// - IPostRepository Posts { get; } + ParamService Params { get; } /// - /// Gets the post type repository. + /// Gets the post service. /// - IPostTypeRepository PostTypes { get; } + PostService Posts { get; } /// - /// Gets the site repository. + /// Gets the post type service. /// - ISiteRepository Sites { get; } + PostTypeService PostTypes { get; } /// - /// Gets the site type repository. + /// Gets the site service. /// - ISiteTypeRepository SiteTypes { get; } + SiteService Sites { get; } /// - /// Gets the tag repository. + /// Gets the site type service. /// - ITagRepository Tags { get; } + SiteTypeService SiteTypes { get; } } } diff --git a/core/Piranha/Data/Alias.cs b/core/Piranha/Models/Alias.cs similarity index 69% rename from core/Piranha/Data/Alias.cs rename to core/Piranha/Models/Alias.cs index 99d2ff8b0..ca4ce4ade 100644 --- a/core/Piranha/Data/Alias.cs +++ b/core/Piranha/Models/Alias.cs @@ -1,45 +1,53 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; +using System.ComponentModel.DataAnnotations; -namespace Piranha.Data +namespace Piranha.Models { [Serializable] - public sealed class Alias : IModel + public sealed class Alias : Alias { } + + [Serializable] + public abstract class Alias { /// /// Gets/sets the unique id. /// - public Guid Id { get; set; } + public TKey Id { get; set; } /// /// Gets/sets the id of the site this alias is for. /// - public Guid SiteId { get; set; } + public TKey SiteId { get; set; } /// /// Gets/sets the alias url. /// + [Required] + [StringLength(256)] public string AliasUrl { get; set; } /// /// Gets/sets the url of the redirect. /// + [Required] + [StringLength(256)] public string RedirectUrl { get; set; } /// /// Gets/sets if this is a permanent or temporary /// redirect. /// - public Models.RedirectType Type { get; set; } = Models.RedirectType.Temporary; + public RedirectType Type { get; set; } = RedirectType.Temporary; /// /// Gets/sets the created date. @@ -50,11 +58,5 @@ public sealed class Alias : IModel /// Gets/sets the last modification date. /// public DateTime LastModified { get; set; } - - /// - /// Gets/sets the site this alias is for. - /// - /// - public Site Site { get; set; } } } \ No newline at end of file diff --git a/core/Piranha/Models/ArchivePage.cs b/core/Piranha/Models/ArchivePage.cs index 942e71819..7dee56c85 100644 --- a/core/Piranha/Models/ArchivePage.cs +++ b/core/Piranha/Models/ArchivePage.cs @@ -12,7 +12,7 @@ namespace Piranha.Models { - [Obsolete("BlogPage has been renamed to ArchivePage, please update your code.", false)] + [Obsolete("BlogPage has been renamed to ArchivePage, please update your code.", true)] public class BlogPage : ArchivePage where T : BlogPage { } /// diff --git a/core/Piranha/Models/Content.cs b/core/Piranha/Models/Content.cs index f8a9ba737..582fb82a7 100644 --- a/core/Piranha/Models/Content.cs +++ b/core/Piranha/Models/Content.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2016 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.ComponentModel.DataAnnotations; namespace Piranha.Models { @@ -25,11 +26,15 @@ public abstract class Content /// /// Gets/sets the content type id. /// + //[Required] + [StringLength(64)] public string TypeId { get; set; } /// /// Gets/sets the title. /// + //[Required] + [StringLength(128)] public string Title { get; set; } /// diff --git a/core/Piranha/Models/ContentType.cs b/core/Piranha/Models/ContentType.cs index c49a8bc7c..7c21048bd 100644 --- a/core/Piranha/Models/ContentType.cs +++ b/core/Piranha/Models/ContentType.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace Piranha.Models { @@ -22,11 +23,14 @@ public abstract class ContentType /// /// Gets/sets the unique id. /// + [Required] + [StringLength(64)] public string Id { get; set; } /// /// Gets/sets the CLR type of the content model. /// + [StringLength(255)] public string CLRType { get; set; } /// diff --git a/core/Piranha/Models/GenericPage.cs b/core/Piranha/Models/GenericPage.cs index 06cf6a884..3d3c55f68 100644 --- a/core/Piranha/Models/GenericPage.cs +++ b/core/Piranha/Models/GenericPage.cs @@ -3,11 +3,13 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ +using Piranha.Services; + namespace Piranha.Models { /// diff --git a/core/Piranha/Models/IBlockModel.cs b/core/Piranha/Models/IBlockModel.cs new file mode 100644 index 000000000..6bb73c831 --- /dev/null +++ b/core/Piranha/Models/IBlockModel.cs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System.Collections.Generic; + +namespace Piranha.Models +{ + public interface IBlockModel + { + /// + /// Gets/sets the blocks. + /// + IList Blocks { get; set; } + } +} diff --git a/core/Piranha/Data/Media.cs b/core/Piranha/Models/Media.cs similarity index 83% rename from core/Piranha/Data/Media.cs rename to core/Piranha/Models/Media.cs index 263aa76b6..5cc431107 100644 --- a/core/Piranha/Data/Media.cs +++ b/core/Piranha/Models/Media.cs @@ -1,35 +1,42 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using System.Collections.Generic; -namespace Piranha.Data +namespace Piranha.Models { - [Serializable] - public sealed class Media : IModel, ICreated, IModified + public sealed class Media : Media { /// - /// Gets/sets the unique id. + /// Gets/sets the optional folder id. /// - public Guid Id { get; set; } + public Guid? FolderId { get; set; } /// - /// Gets/sets the optional folder id. + /// Gets/sets the available versions. /// - public Guid? FolderId { get; set; } + public IList Versions { get; set; } = new List(); + } + + public abstract class Media + { + /// + /// Gets/sets the unique id. + /// + public TKey Id { get; set; } /// /// Gets/sets the media type. /// - public Models.MediaType Type { get; set; } + public MediaType Type { get; set; } /// /// Gets/sets the filename. @@ -73,15 +80,5 @@ public sealed class Media : IModel, ICreated, IModified /// Gets/sets the last modification date. /// public DateTime LastModified { get; set; } - - /// - /// Gets/sets the optional folder. - /// - public MediaFolder Folder { get; set; } - - /// - /// Gets/sets the available versions. - /// - public IList Versions { get; set; } = new List(); } } \ No newline at end of file diff --git a/core/Piranha/Data/MediaFolder.cs b/core/Piranha/Models/MediaFolder.cs similarity index 64% rename from core/Piranha/Data/MediaFolder.cs rename to core/Piranha/Models/MediaFolder.cs index ddad52d44..59de34287 100644 --- a/core/Piranha/Data/MediaFolder.cs +++ b/core/Piranha/Models/MediaFolder.cs @@ -1,44 +1,43 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; -using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; -namespace Piranha.Data +namespace Piranha.Models { - [Serializable] - public sealed class MediaFolder : IModel, ICreated + public sealed class MediaFolder : MediaFolder { /// - /// Gets/sets the unique id. + /// Gets/sets the optional parent id. /// - public Guid Id { get; set; } + public Guid? ParentId { get; set; } + } + public abstract class MediaFolder + { /// - /// Gets/sets the optional parent id. + /// Gets/sets the unique id. /// - public Guid? ParentId { get; set; } + public TKey Id { get; set; } /// /// Gets/sets the folder name. /// + [Required] + [StringLength(128)] public string Name { get; set; } /// /// Gets/sets the created date. /// public DateTime Created { get; set; } - - /// - /// Gets/sets the available media. - /// - public IList Media { get; set; } = new List(); } } \ No newline at end of file diff --git a/core/Piranha/Models/MediaStructureItem.cs b/core/Piranha/Models/MediaStructureItem.cs index b47327511..b3300f1da 100644 --- a/core/Piranha/Models/MediaStructureItem.cs +++ b/core/Piranha/Models/MediaStructureItem.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Models { [Serializable] - public class MediaStructureItem : StructureItem + public class MediaStructureItem : StructureItem { /// /// Gets/sets the folder name. @@ -24,5 +24,13 @@ public class MediaStructureItem : StructureItem /// Gets/sets the created date. /// public DateTime Created { get; set; } + + /// + /// Default constructor. + /// + public MediaStructureItem() + { + Items = new MediaStructure(); + } } } \ No newline at end of file diff --git a/core/Piranha/Data/MediaVersion.cs b/core/Piranha/Models/MediaVersion.cs similarity index 63% rename from core/Piranha/Data/MediaVersion.cs rename to core/Piranha/Models/MediaVersion.cs index 151ac844b..d8093fa5b 100644 --- a/core/Piranha/Data/MediaVersion.cs +++ b/core/Piranha/Models/MediaVersion.cs @@ -1,30 +1,25 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; -namespace Piranha.Data +namespace Piranha.Models { - [Serializable] - public sealed class MediaVersion : IModel + public sealed class MediaVersion : MediaVersion { } + + public abstract class MediaVersion { /// /// Gets/sets the unique id. /// - public Guid Id { get; set; } - - /// - /// Gets/sets the id of the media this is - /// a version of. - /// - public Guid MediaId { get; set; } + public TKey Id { get; set; } /// /// Gets/sets the file size in bytes. @@ -45,10 +40,5 @@ public sealed class MediaVersion : IModel /// Gets/sets the file extension of the generated version. /// public string FileExtension { get; set; } - - /// - /// Gets/sets the media this is a version of. - /// - public Media Media { get; set; } } } \ No newline at end of file diff --git a/core/Piranha/Models/PageBase.cs b/core/Piranha/Models/PageBase.cs index 09e7f91c6..6bf27f43d 100644 --- a/core/Piranha/Models/PageBase.cs +++ b/core/Piranha/Models/PageBase.cs @@ -1,22 +1,23 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace Piranha.Models { /// /// Base class for page models. /// - public abstract class PageBase : RoutedContent, IMeta + public abstract class PageBase : RoutedContent, IBlockModel, IMeta { /// /// Gets/sets the site id. @@ -26,6 +27,7 @@ public abstract class PageBase : RoutedContent, IMeta /// /// Gets/sets the page content type. /// + [StringLength(256)] public string ContentType { get; set; } /// @@ -41,6 +43,7 @@ public abstract class PageBase : RoutedContent, IMeta /// /// Gets/sets the navigation title. /// + [StringLength(128)] public string NavigationTitle { get; set; } /// @@ -51,7 +54,7 @@ public abstract class PageBase : RoutedContent, IMeta /// /// Gets/sets the optional redirect. /// - /// + [StringLength(256)] public string RedirectUrl { get; set; } /// diff --git a/core/Piranha/Data/Param.cs b/core/Piranha/Models/Param.cs similarity index 75% rename from core/Piranha/Data/Param.cs rename to core/Piranha/Models/Param.cs index b92cf346c..a018edd1a 100644 --- a/core/Piranha/Data/Param.cs +++ b/core/Piranha/Models/Param.cs @@ -1,51 +1,58 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; - -namespace Piranha.Data -{ - [Serializable] - /// - /// String parameter. - /// - public sealed class Param : IModel, ICreated, IModified - { - /// - /// Gets/sets the unique id. - /// - public Guid Id { get; set; } - - /// - /// Gets/sets the unique key. - /// - public string Key { get; set; } - - /// - /// Gets/sets the value. - /// - public string Value { get; set; } - - /// - /// Gets/sets the optional description. - /// - public string Description { get; set; } - - /// - /// Gets/sets the created date. - /// - public DateTime Created { get; set; } - - /// - /// Gets/sets the last modification date. - /// - public DateTime LastModified { get; set; } - } -} +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.ComponentModel.DataAnnotations; + +namespace Piranha.Models +{ + [Serializable] + public sealed class Param : Param { } + + [Serializable] + /// + /// String parameter. + /// + public abstract class Param + { + /// + /// Gets/sets the unique id. + /// + public TKey Id { get; set; } + + /// + /// Gets/sets the unique key. + /// + [Required] + [StringLength(64)] + public string Key { get; set; } + + /// + /// Gets/sets the value. + /// + public string Value { get; set; } + + /// + /// Gets/sets the optional description. + /// + [StringLength(255)] + public string Description { get; set; } + + /// + /// Gets/sets the created date. + /// + public DateTime Created { get; set; } + + /// + /// Gets/sets the last modification date. + /// + public DateTime LastModified { get; set; } + } +} diff --git a/core/Piranha/Models/PostBase.cs b/core/Piranha/Models/PostBase.cs index c68a56e0b..8bc5c260f 100644 --- a/core/Piranha/Models/PostBase.cs +++ b/core/Piranha/Models/PostBase.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,7 +16,7 @@ namespace Piranha.Models /// /// Base class for post models. /// - public abstract class PostBase : RoutedContent, IMeta + public abstract class PostBase : RoutedContent, IBlockModel, IMeta { /// /// Gets/sets the blog page id. diff --git a/core/Piranha/Models/RoutedContent.cs b/core/Piranha/Models/RoutedContent.cs index 500c65c6a..3eb887625 100644 --- a/core/Piranha/Models/RoutedContent.cs +++ b/core/Piranha/Models/RoutedContent.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.ComponentModel.DataAnnotations; namespace Piranha.Models { @@ -20,6 +21,7 @@ public abstract class RoutedContent : Content /// /// Gets/sets the unique slug. /// + [StringLength(128)] public string Slug { get; set; } /// @@ -30,16 +32,19 @@ public abstract class RoutedContent : Content /// /// Gets/sets the optional meta keywords. /// + [StringLength(128)] public string MetaKeywords { get; set; } /// /// Gets/sets the optional meta description. /// + [StringLength(256)] public string MetaDescription { get; set; } /// /// Gets/sets the optional route used by the middleware. /// + [StringLength(256)] public string Route { get; set; } /// diff --git a/core/Piranha/Models/Site.cs b/core/Piranha/Models/Site.cs new file mode 100644 index 000000000..c090bef38 --- /dev/null +++ b/core/Piranha/Models/Site.cs @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.ComponentModel.DataAnnotations; + +namespace Piranha.Models +{ + [Serializable] + public sealed class Site + { + /// + /// Gets/sets the unique id. + /// + public Guid Id { get; set; } + + /// + /// Gets/sets the optional site type id. + /// + [StringLength(64)] + public string SiteTypeId { get; set; } + + /// + /// Gets/sets the main title. + /// + [Required] + [StringLength(128)] + public string Title { get; set; } + + /// + /// Gets/sets the internal textual id. + /// + [StringLength(64)] + public string InternalId { get; set; } + + /// + /// Gets/sets the optional description. + /// + [StringLength(256)] + public string Description { get; set; } + + /// + /// Gets/sets the optional hostnames to bind this site for. + /// + [StringLength(256)] + public string Hostnames { get; set; } + + /// + /// Gets/sets if this is the default site. + /// + public bool IsDefault { get; set; } + + /// + /// Gets/sets the optional culture for the site. + /// + [StringLength(6)] + public string Culture { get; set; } + + /// + /// Gets/sets the global last modification date + /// of the site's content. + /// + public DateTime? ContentLastModified { get; set; } + + /// + /// Gets/sets the created date. + /// + public DateTime Created { get; set; } + + /// + /// Gets/sets the last modification date. + /// + public DateTime LastModified { get; set; } + } +} diff --git a/core/Piranha/Models/SitemapItem.cs b/core/Piranha/Models/SitemapItem.cs index 804c65c1f..da76aa473 100644 --- a/core/Piranha/Models/SitemapItem.cs +++ b/core/Piranha/Models/SitemapItem.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Models { [Serializable] - public class SitemapItem : StructureItem + public class SitemapItem : StructureItem { /// /// Gets/sets the optional original id. @@ -44,9 +44,9 @@ public class SitemapItem : StructureItem /// Gets the menu title for the item. The menu title returns /// the navigation title if set, otherwise the main title. /// - public string MenuTitle + public string MenuTitle { - get + get { if (!string.IsNullOrWhiteSpace(NavigationTitle)) { @@ -74,7 +74,7 @@ public string MenuTitle /// /// Gets/sets the published date. - /// + /// public DateTime? Published { get; set; } /// diff --git a/core/Piranha/Models/Structure.cs b/core/Piranha/Models/Structure.cs index 9ded5edc4..5ad039bbc 100644 --- a/core/Piranha/Models/Structure.cs +++ b/core/Piranha/Models/Structure.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,8 +16,8 @@ namespace Piranha.Models /// /// Abstract class for building a hierarchical structure. /// - [Serializable] - public abstract class Structure : List where T : StructureItem where TThis : Structure + [Serializable] + public abstract class Structure : List where T : StructureItem where TThis : Structure { /// /// Gets the partial structure with the items positioned diff --git a/core/Piranha/Models/StructureItem.cs b/core/Piranha/Models/StructureItem.cs index d40cefd25..014bd0b24 100644 --- a/core/Piranha/Models/StructureItem.cs +++ b/core/Piranha/Models/StructureItem.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -17,7 +17,9 @@ namespace Piranha.Models /// Abstract class for an hierarchical item in a structure. /// [Serializable] - public abstract class StructureItem where T : StructureItem + public abstract class StructureItem + where T : StructureItem + where TStructure : Structure { /// /// Gets/sets the unique id. @@ -32,6 +34,6 @@ public abstract class StructureItem where T : StructureItem /// /// Gets/sets the child items. /// - public IList Items { get; set; } = new List(); + public TStructure Items { get; set; } } } \ No newline at end of file diff --git a/core/Piranha/Models/Taxonomy.cs b/core/Piranha/Models/Taxonomy.cs index 622248cdd..9b5e9734e 100644 --- a/core/Piranha/Models/Taxonomy.cs +++ b/core/Piranha/Models/Taxonomy.cs @@ -1,14 +1,15 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.ComponentModel.DataAnnotations; namespace Piranha.Models { @@ -22,11 +23,13 @@ public class Taxonomy /// /// Gets/sets the title. /// + [StringLength(128)] public string Title { get; set; } /// /// Gets/sets the slug. /// + [StringLength(128)] public string Slug { get; set; } /// @@ -40,39 +43,5 @@ public static implicit operator Taxonomy(string str) Title = str }; } - - /// - /// Operator for type casting a category to a taxonomy. - /// - /// The category - public static implicit operator Taxonomy(Data.Category category) - { - if (category != null) - { - return new Taxonomy - { - Id = category.Id, - Title = category.Title, - Slug = category.Slug - }; - } - return null; - } - - /// - /// Operator for type casting a tag to a taxonomy. - /// - /// The tag - public static implicit operator Taxonomy(Data.Tag tag) - { - if (tag != null) - return new Taxonomy - { - Id = tag.Id, - Title = tag.Title, - Slug = tag.Slug - }; - return null; - } } } diff --git a/core/Piranha/Piranha.csproj b/core/Piranha/Piranha.csproj index f795cf9a4..4780e820a 100644 --- a/core/Piranha/Piranha.csproj +++ b/core/Piranha/Piranha.csproj @@ -6,10 +6,11 @@ Piranha - - - + + + + \ No newline at end of file diff --git a/core/Piranha/PiranhaExtensions.cs b/core/Piranha/PiranhaExtensions.cs new file mode 100644 index 000000000..d986dc4f6 --- /dev/null +++ b/core/Piranha/PiranhaExtensions.cs @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using Microsoft.Extensions.DependencyInjection; +using Piranha; +using Piranha.Cache; +using Piranha.Services; + +public static class PiranhaExtensions +{ + public static IServiceCollection AddPiranha(this IServiceCollection services) + { + return services.AddSingleton(); + } + + /// + /// Adds the memory cache service for repository caching. + /// + /// The current service collection + /// The updated service collection + [Obsolete("Please use AddPiranhaSimpleCache instead", true)] + public static IServiceCollection AddPiranhaMemCache(this IServiceCollection services) + { + return services; + } + + /// + /// Adds the distributed cache service for repository caching. + /// + /// The current service collection + /// The updated service collection + public static IServiceCollection AddPiranhaDistributedCache(this IServiceCollection services) + { + return services.AddSingleton(); + } + + /// + /// Adds the memory cache service for repository caching. + /// + /// The current service collection + /// The updated service collection + public static IServiceCollection AddPiranhaMemoryCache(this IServiceCollection services) + { + return services.AddSingleton(); + } + + /// + /// Adds the simple cache service for repository caching. + /// + /// The current service collection + /// The updated service collection + public static IServiceCollection AddPiranhaSimpleCache(this IServiceCollection services) + { + return services.AddSingleton(); + } +} diff --git a/core/Piranha/Repositories/AliasRepository.cs b/core/Piranha/Repositories/AliasRepository.cs deleted file mode 100644 index 65923dd12..000000000 --- a/core/Piranha/Repositories/AliasRepository.cs +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public class AliasRepository : BaseRepository, IAliasRepository - { - private readonly Api _api; - - /// - /// Default constructor. - /// - /// The current db context - /// The optional model cache - public AliasRepository(Api api, IDb db, ICache cache = null) - : base(db, cache) - { - _api = api; - } - - /// - /// Gets all available models for the specified site. - /// - /// The optional site id - /// The available models - public IEnumerable GetAll(Guid? siteId) - { - var models = new List(); - - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - siteId = site.Id; - } - - var aliases = db.Aliases - .AsNoTracking() - .Where(a => a.SiteId == siteId) - .OrderBy(a => a.AliasUrl) - .ThenBy(a => a.RedirectUrl) - .Select(a => a.Id); - - foreach (var a in aliases) - { - var model = GetById(a); - if (model != null) - models.Add(model); - } - return models; - } - - /// - /// Gets the model with the given alias url. - /// - /// The unique url - /// The optional site id - /// The model - public Alias GetByAliasUrl(string url, Guid? siteId = null) - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - siteId = site.Id; - } - - var id = cache?.Get($"AliasId_{siteId}_{url}"); - Alias model = null; - - if (id.HasValue) - { - model = GetById(id.Value); - } - else - { - id = db.Aliases - .AsNoTracking() - .Where(a => a.SiteId == siteId && a.AliasUrl == url) - .Select(a => a.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - model = GetById(id.Value); - } - return model; - } - - /// - /// Gets the model with the given redirect url. - /// - /// The unique url - /// The optional site id - /// The model - public IEnumerable GetByRedirectUrl(string url, Guid? siteId = null) - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - siteId = site.Id; - } - - var models = new List(); - - var aliases = db.Aliases - .AsNoTracking() - .Where(a => a.SiteId == siteId && a.RedirectUrl == url) - .Select(a => a.Id) - .ToList(); - - foreach (var id in aliases) - { - models.Add(GetById(id)); - } - return models; - } - - /// - /// Adds a new model to the database. - /// - /// The model - protected override void Add(Alias model) - { - PrepareInsert(model); - - // Check for alias url - if (string.IsNullOrWhiteSpace(model.AliasUrl)) - throw new ArgumentException("Alias Url cannot be empty"); - - // Check for redirect url - if (string.IsNullOrWhiteSpace(model.RedirectUrl)) - throw new ArgumentException("Redirect Url cannot be empty"); - - // Fix urls - if (!model.AliasUrl.StartsWith("/")) - { - model.AliasUrl = "/" + model.AliasUrl; - } - if (!model.RedirectUrl.StartsWith("/") && !model.RedirectUrl.StartsWith("http://") && !model.RedirectUrl.StartsWith("https://")) - { - model.RedirectUrl = "/" + model.RedirectUrl; - } - - db.Aliases.Add(model); - } - - /// - /// Updates the given model in the database. - /// - /// The model - protected override void Update(Alias model) - { - PrepareUpdate(model); - - // Check for alias url - if (string.IsNullOrWhiteSpace(model.AliasUrl)) - throw new ArgumentException("Alias Url cannot be empty"); - - // Check for redirect url - if (string.IsNullOrWhiteSpace(model.RedirectUrl)) - throw new ArgumentException("Redirect Url cannot be empty"); - - // Fix urls - if (!model.AliasUrl.StartsWith("/")) - { - model.AliasUrl = "/" + model.AliasUrl; - } - if (!model.RedirectUrl.StartsWith("/") && !model.RedirectUrl.StartsWith("http://") && !model.RedirectUrl.StartsWith("https://")) - { - model.RedirectUrl = "/" + model.RedirectUrl; - } - - var alias = db.Aliases.FirstOrDefault(s => s.Id == model.Id); - if (alias != null) - { - App.Mapper.Map(model, alias); - } - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected override void AddToCache(Alias model) - { - cache.Set(model.Id.ToString(), model); - cache.Set($"AliasId_{model.SiteId}_{model.AliasUrl}", model.Id); - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected override void RemoveFromCache(Alias model) - { - cache.Remove($"AliasId_{model.SiteId}_{model.AliasUrl}"); - - base.RemoveFromCache(model); - } - } -} diff --git a/core/Piranha/Repositories/ArchiveRepository.cs b/core/Piranha/Repositories/ArchiveRepository.cs deleted file mode 100644 index 4d36746a1..000000000 --- a/core/Piranha/Repositories/ArchiveRepository.cs +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2016 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * https://github.com/piranhacms/piranha.core - * - */ - -using System; -using System.Linq; - -namespace Piranha.Repositories -{ - public class ArchiveRepository : IArchiveRepository - { - /// - /// The current api. - /// - private readonly Api _api; - - /// - /// The current db context. - /// - private readonly IDb _db; - - /// - /// Default internal constructor. - /// - /// The current db context - internal ArchiveRepository(Api api, IDb db) - { - _api = api; - _db = db; - } - - /// - /// Gets the post archive for the blog with the given id. - /// - /// The unique blog id - /// The optional page - /// The optional category id - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - public T GetById(Guid id, int? page = 1, Guid? categoryId = null, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage - { - return Get(id, page, categoryId, null, year, month, pageSize); - } - - /// - /// Gets the post archive for the specified blog and tag. - /// - /// The unique blog id - /// The optional page - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - public T GetById(Guid id, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage - { - return Get(id, page, null, null, year, month, pageSize); - } - - /// - /// Gets the post archive for the specified blog and tag. - /// - /// The unique blog id - /// The unique category id - /// The optional page - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - public T GetByCategoryId(Guid id, Guid categoryId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage - { - return Get(id, page, categoryId, null, year, month, pageSize); - } - - /// - /// Gets the post archive for the specified blog and tag. - /// - /// The unique blog id - /// The unique tag id - /// The optional page - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - public T GetByTagId(Guid id, Guid tagId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage - { - return Get(id, page, null, tagId, year, month, pageSize); - } - - private T Get(Guid id, int? page = 1, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage - { - // Get the requested blog page - var model = _api.Pages.GetById(id); - - if (model != null) - { - // Set basic fields - model.Archive = new Models.PostArchive(); - - model.Route = model.Route ?? "/archive"; - model.Archive.Year = year; - model.Archive.Month = month; - model.Archive.CurrentPage = Math.Max(1, page.HasValue ? page.Value : 1); - - // Build the query. - var now = DateTime.Now; - var query = _db.Posts - .Where(p => p.BlogId == id && p.Published <= now); - - if (categoryId.HasValue) - { - model.Archive.Category = _api.Categories.GetById(categoryId.Value); - - query = query.Where(p => p.CategoryId == categoryId.Value); - } - if (tagId.HasValue) - { - model.Archive.Tag = _api.Tags.GetById(tagId.Value); - - query = query.Where(p => p.Tags.Any(t => t.TagId == tagId.Value)); - } - - if (year.HasValue) - { - DateTime from; - DateTime to; - - if (month.HasValue) - { - from = new DateTime(year.Value, month.Value, 1); - to = from.AddMonths(1); - } - else - { - from = new DateTime(year.Value, 1, 1); - to = from.AddYears(1); - } - query = query.Where(p => p.Published >= from && p.Published < to); - } - - // Get requested page size - if (!pageSize.HasValue) - { - using (var config = new Config(_api)) - { - pageSize = config.ArchivePageSize; - - if (!pageSize.HasValue || pageSize == 0) - pageSize = 5; - } - } - - // Get the total page count for the archive - model.Archive.TotalPosts = query.Count(); - model.Archive.TotalPages = Math.Max(Convert.ToInt32(Math.Ceiling((double)model.Archive.TotalPosts / pageSize.Value)), 1); - model.Archive.CurrentPage = Math.Min(model.Archive.CurrentPage, model.Archive.TotalPages); - - // Get the posts - var posts = query - .OrderByDescending(p => p.Published) - .Skip((model.Archive.CurrentPage - 1) * pageSize.Value) - .Take(pageSize.Value) - .Select(p => p.Id); - - // Map & add the posts within the requested page - foreach (var post in posts) - { - model.Archive.Posts.Add(_api.Posts.GetById(post)); - } - return model; - } - return null; - } - } -} diff --git a/core/Piranha/Repositories/BaseRepository.cs b/core/Piranha/Repositories/BaseRepository.cs deleted file mode 100644 index 4813977ff..000000000 --- a/core/Piranha/Repositories/BaseRepository.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - /// - /// Abstract base repository. - /// - /// The model type - public abstract class BaseRepository where T : class, IModel - { - protected static readonly bool isCreated = typeof(ICreated).IsAssignableFrom(typeof(T)); - protected static readonly bool isModified = typeof(IModified).IsAssignableFrom(typeof(T)); - protected readonly IDb db; - protected readonly ICache cache; - - /// - /// Default constructor. - /// - /// The current db connection - /// The optional model cache - protected BaseRepository(IDb db, ICache cache = null) - { - this.db = db; - this.cache = cache; - } - - /// - /// Gets the model with the specified id. - /// - /// The unique id - /// The model, or null if it doesn't exist - public virtual T GetById(Guid id) - { - T model = cache != null ? cache.Get(id.ToString()) : null; - - if (model == null) - { - model = db.Set() - .AsNoTracking() - .FirstOrDefault(m => m.Id == id); - - if (model != null) - App.Hooks.OnLoad(model); - - if (cache != null && model != null) - AddToCache(model); - } - return model; - } - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - public virtual void Save(T model) - { - App.Hooks.OnBeforeSave(model); - - if (db.Set().Count(m => m.Id == model.Id) == 0) - Add(model); - else Update(model); - - db.SaveChanges(); - - App.Hooks.OnAfterSave(model); - - if (cache != null) - cache.Remove(model.Id.ToString()); - } - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - public virtual void Delete(Guid id) - { - var model = db.Set().FirstOrDefault(m => m.Id == id); - if (model != null) - { - App.Hooks.OnBeforeDelete(model); - - db.Set().Remove(model); - db.SaveChanges(); - - App.Hooks.OnAfterDelete(model); - } - - if (cache != null) - cache.Remove(id.ToString()); - } - - /// - /// Deletes the given model. - /// - /// The model - public virtual void Delete(T model) - { - Delete(model.Id); - } - - #region Protected methods - /// - /// Adds a new model to the database. - /// - /// The model - protected abstract void Add(T model); - - /// - /// Updates the given model in the database. - /// - /// The model - protected abstract void Update(T model); - - /// - /// Prepares the model for an insert. - /// - /// The model - protected virtual void PrepareInsert(T model) - { - // Prepare id - model.Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(); - - // Prepare created date - if (isCreated) - ((ICreated)model).Created = DateTime.Now; - - // Prepare last modified date - if (isModified) - ((IModified)model).LastModified = DateTime.Now; - } - - /// - /// Perpares the model for an update. - /// - /// The model - protected virtual void PrepareUpdate(T model) - { - // Prepare last modified date - if (isModified) - ((IModified)model).LastModified = DateTime.Now; - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected virtual void AddToCache(T model) - { - cache.Set(model.Id.ToString(), model); - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected virtual void RemoveFromCache(T model) - { - cache.Remove(model.Id.ToString()); - } - #endregion - } -} diff --git a/core/Piranha/Repositories/BaseRepositoryWithAll.cs b/core/Piranha/Repositories/BaseRepositoryWithAll.cs deleted file mode 100644 index 304984e9c..000000000 --- a/core/Piranha/Repositories/BaseRepositoryWithAll.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - /// - /// Abstract base repository. - /// - /// The model type - public abstract class BaseRepositoryWithAll : BaseRepository where T : class, IModel - { - /// - /// Default constructor. - /// - /// The current db connection - /// The optional model cache - protected BaseRepositoryWithAll(IDb db, ICache cache = null) : base(db, cache) { } - - /// - /// Gets all available models. - /// - /// The available models - public virtual IEnumerable GetAll() - { - var models = new List(); - var ids = db.Set() - .AsNoTracking() - .Select(m => m.Id); - - foreach (var id in ids) - { - var model = GetById(id); - if (model != null) - models.Add(model); - } - return models; - } - } -} diff --git a/core/Piranha/Repositories/CategoryRepository.cs b/core/Piranha/Repositories/CategoryRepository.cs deleted file mode 100644 index 230c3671e..000000000 --- a/core/Piranha/Repositories/CategoryRepository.cs +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public class CategoryRepository : BaseRepository, ICategoryRepository - { - private readonly Api api; - - /// - /// Default constructor. - /// - /// The current api - /// The current db context - /// The optional model cache - public CategoryRepository(Api api, IDb db, ICache cache = null) - : base(db, cache) - { - this.api = api; - } - - /// - /// Gets all available models for the specified blog. - /// - /// The blog id - /// The available models - public IEnumerable GetAll(Guid blogId) - { - var models = new List(); - var categories = db.Categories - .AsNoTracking() - .Where(c => c.BlogId == blogId) - .Select(c => c.Id); - - foreach (var c in categories) - { - var model = GetById(c); - if (model != null) - models.Add(model); - } - return models; - } - - /// - /// Gets the model with the given slug. - /// - /// The blog id - /// The unique slug - /// The model - public Category GetBySlug(Guid blogId, string slug) - { - var id = cache != null ? cache.Get($"Category_{blogId}_{slug}") : null; - Category model = null; - - if (id.HasValue) - { - model = GetById(id.Value); - } - else - { - id = db.Categories - .AsNoTracking() - .Where(c => c.BlogId == blogId && c.Slug == slug) - .Select(c => c.Id) - .FirstOrDefault(); - - if (id.HasValue && id != Guid.Empty) - model = GetById(id.Value); - } - return model; - } - - /// - /// Gets the model with the given title - /// - /// The blog id - /// The unique title - /// The model - public Category GetByTitle(Guid blogId, string title) - { - var id = db.Categories - .AsNoTracking() - .Where(c => c.BlogId == blogId && c.Title == title) - .Select(c => c.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - return GetById(id); - return null; - } - - /// - /// Deletes all unused categories for the specified blog. - /// - /// The blog id - public void DeleteUnused(Guid blogId) - { - var used = db.Posts - .Where(p => p.BlogId == blogId) - .Select(p => p.CategoryId) - .Distinct() - .ToArray(); - - var unused = db.Categories - .Where(c => c.BlogId == blogId && !used.Contains(c.Id)) - .ToList(); - - if (unused.Count > 0) - { - db.Categories.RemoveRange(unused); - db.SaveChanges(); - } - } - - #region Protected methods - /// - /// Adds a new model to the database. - /// - /// The model - protected override void Add(Category model) - { - PrepareInsert(model); - - // Check required - if (string.IsNullOrWhiteSpace(model.Title)) - throw new ArgumentException("Title is required for Category"); - - // Ensure slug - if (string.IsNullOrWhiteSpace(model.Slug)) - model.Slug = Utils.GenerateSlug(model.Title, false); - else model.Slug = Utils.GenerateSlug(model.Slug, false); - - db.Categories.Add(model); - } - - /// - /// Updates the given model in the database. - /// - /// The model - protected override void Update(Category model) - { - PrepareUpdate(model); - - // Check required - if (string.IsNullOrWhiteSpace(model.Title)) - throw new ArgumentException("Title is required for Category"); - - // Ensure slug - if (string.IsNullOrWhiteSpace(model.Slug)) - model.Slug = Utils.GenerateSlug(model.Title); - else model.Slug = Utils.GenerateSlug(model.Slug); - - var category = db.Categories.FirstOrDefault(c => c.Id == model.Id); - if (category != null) - { - App.Mapper.Map(model, category); - } - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected override void AddToCache(Category model) - { - cache.Set(model.Id.ToString(), model); - cache.Set($"Category_{model.BlogId}_{model.Slug}", model.Id); - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected override void RemoveFromCache(Category model) - { - cache.Remove(model.Id.ToString()); - cache.Remove($"Category_{model.BlogId}_{model.Slug}"); - } - #endregion - } -} diff --git a/core/Piranha/Repositories/IAliasRepository.cs b/core/Piranha/Repositories/IAliasRepository.cs index 7e4d0fd0a..52eac1e38 100644 --- a/core/Piranha/Repositories/IAliasRepository.cs +++ b/core/Piranha/Repositories/IAliasRepository.cs @@ -1,16 +1,17 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using System.Collections.Generic; -using Piranha.Data; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -21,48 +22,42 @@ public interface IAliasRepository /// /// The optional site id /// The available models - IEnumerable GetAll(Guid? siteId = null); + Task> GetAll(Guid siteId); /// /// Gets the model with the specified id. /// /// The unique id /// The model, or NULL if it doesn't exist - Alias GetById(Guid id); + Task GetById(Guid id); /// /// Gets the model with the given alias url. /// /// The unique url - /// The optional site id + /// The site id /// The model - Alias GetByAliasUrl(string url, Guid? siteId = null); + Task GetByAliasUrl(string url, Guid siteId); /// /// Gets the models with the given redirect url. /// /// The unique url - /// The optional site id + /// The site id /// The models - IEnumerable GetByRedirectUrl(string url, Guid? siteId = null); + Task> GetByRedirectUrl(string url, Guid siteId); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Alias model); + Task Save(Alias model); /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Alias model); + Task Delete(Guid id); } } diff --git a/core/Piranha/Repositories/IArchiveRepository.cs b/core/Piranha/Repositories/IArchiveRepository.cs index 7effd733a..c53f075d9 100644 --- a/core/Piranha/Repositories/IArchiveRepository.cs +++ b/core/Piranha/Repositories/IArchiveRepository.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2016-2017 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Piranha.Models; namespace Piranha.Repositories @@ -16,51 +18,29 @@ namespace Piranha.Repositories public interface IArchiveRepository { /// - /// Gets the post archive for the blog with the given id. + /// Gets the total post count for the specified archive + /// and filter. /// - /// The unique blog id - /// The optional page + /// The archive id /// The optional category id + /// The optional tag id /// The optional year /// The optional month - /// The optional page size - /// The archive model - [Obsolete("Please update your code to use the new GetById, GetByCategoryId & GetByTagId", true)] - T GetById(Guid id, int? page = 1, Guid? categoryId = null, int? year = null, int? month = null, int? pageSize = null) where T : ArchivePage; - - /// - /// Gets the post archive for the blog with the given id. - /// - /// The unique blog id - /// The optional page - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - T GetById(Guid id, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : ArchivePage; + /// The total post count + Task GetPostCount(Guid archiveId, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null); /// - /// Gets the post archive for the blog with the given id. + /// Gets the id of the posts in the current archive + /// matching the specified filter. /// - /// The unique blog id - /// The unique category id - /// The optional page - /// The optional year - /// The optional month - /// The optional page size - /// The archive model - T GetByCategoryId(Guid id, Guid categoryId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : ArchivePage; - - /// - /// Gets the post archive for the blog with the given id. - /// - /// The unique blog id - /// The unique tag id - /// The optional page + /// The archive id + /// The page size + /// The current page + /// The optional category id + /// The optional tag id /// The optional year /// The optional month - /// The optional page size - /// The archive model - T GetByTagId(Guid id, Guid tagId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : ArchivePage; + /// The matching posts + Task> GetPosts(Guid archiveId, int pageSize, int currentPage, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null); } } diff --git a/core/Piranha/Repositories/ICategoryRepository.cs b/core/Piranha/Repositories/ICategoryRepository.cs deleted file mode 100644 index 46db623d9..000000000 --- a/core/Piranha/Repositories/ICategoryRepository.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public interface ICategoryRepository - { - /// - /// Gets all available models. - /// - /// The blog id - /// The available models - IEnumerable GetAll(Guid blogId); - - /// - /// Gets the model with the specified id. - /// - /// The unique id - /// The model, or NULL if it doesn't exist - Category GetById(Guid id); - - /// - /// Gets the model with the given slug - /// - /// The blog id - /// The unique slug - /// The model - Category GetBySlug(Guid blogId, string slug); - - /// - /// Gets the model with the given title - /// - /// The blog id - /// The unique title - /// The model - Category GetByTitle(Guid blogId, string title); - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - void Save(Category model); - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Category model); - - /// - /// Deletes all unused categories for the specified blog. - /// - /// The blog id - void DeleteUnused(Guid blogId); - } -} diff --git a/core/Piranha/Repositories/IMediaRepository.cs b/core/Piranha/Repositories/IMediaRepository.cs index 26c293749..b25ffa98c 100644 --- a/core/Piranha/Repositories/IMediaRepository.cs +++ b/core/Piranha/Repositories/IMediaRepository.cs @@ -1,17 +1,17 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Piranha.Data; +using Piranha.Models; namespace Piranha.Repositories { @@ -22,7 +22,7 @@ public interface IMediaRepository /// /// The optional folder id /// The available media - IEnumerable GetAll(Guid? folderId = null); + Task> GetAll(Guid? folderId = null); /// /// Gets all media folders available in the specified @@ -30,114 +30,59 @@ public interface IMediaRepository /// /// The optional folder id /// The available media folders - IEnumerable GetAllFolders(Guid? folderId = null); + Task> GetAllFolders(Guid? folderId = null); /// /// Gets the media with the given id. /// /// The unique id /// The media - Media GetById(Guid id); + Task GetById(Guid id); /// /// Gets the media folder with the given id. /// /// The unique id /// The media folder - MediaFolder GetFolderById(Guid id); + Task GetFolderById(Guid id); /// /// Gets the hierachical media structure. /// /// The media structure - Models.MediaStructure GetStructure(); + Task GetStructure(); /// - /// Adds or updates the given model in the database depending on its state. - /// Please note that this method is not really synchronous, it's just a - /// wrapper for the async version. + /// Adds or updates the given model in the database. /// - /// The content to save - void Save(Models.MediaContent content); - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The content to save - Task SaveAsync(Models.MediaContent content); + /// The model + Task Save(Media model); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void SaveFolder(MediaFolder model); + Task SaveFolder(MediaFolder model); /// /// Moves the media to the folder with the specified id. /// /// The media /// The folder id - void Move(Media model, Guid? folderId); - - /// - /// Ensures that the image version with the given size exsists - /// and returns its public URL. Please note that this method is - /// not really synchronous, it's just a wrapper for the async version. - /// - /// The unique id - /// The requested width - /// The optionally requested height - /// The public URL - string EnsureVersion(Guid id, int width, int? height = null); - - /// - /// Ensures that the image version with the given size exsists - /// and returns its public URL. - /// - /// The unique id - /// The requested width - /// The optionally requested height - /// The public URL - Task EnsureVersionAsync(Guid id, int width, int? height = null); + Task Move(Media model, Guid? folderId); /// /// Deletes the media with the given id. Please note that this method /// is not really synchronous, it's just a wrapper for the async version. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the media with the given id. - /// - /// The unique id - Task DeleteAsync(Guid id); - - /// - /// Deletes the given model. Please note that this method - /// is not really synchronous, it's just a wrapper for the async version. - /// - /// The media - void Delete(Media model); - - /// - /// Deletes the given model. - /// - /// The media - Task DeleteAsync(Media model); + Task Delete(Guid id); /// /// Deletes the media folder with the given id. /// /// The unique id - void DeleteFolder(Guid id); - - /// - /// Deletes the given model. - /// - /// The media - void DeleteFolder(MediaFolder model); + Task DeleteFolder(Guid id); } } \ No newline at end of file diff --git a/core/Piranha/Repositories/IPageRepository.cs b/core/Piranha/Repositories/IPageRepository.cs index 9668895ce..1c30afc7f 100644 --- a/core/Piranha/Repositories/IPageRepository.cs +++ b/core/Piranha/Repositories/IPageRepository.cs @@ -1,87 +1,43 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { public interface IPageRepository { - /// - /// Creates and initializes a new page of the specified type. - /// - /// The created page - T Create(string typeId = null) where T : Models.PageBase; - - /// - /// Creates and initializes a copy of the given page. - /// - /// The created copy - T Copy(T originalPage) where T : Models.PageBase; - - /// - /// Detaches a copy and initializes it as a standalone page - /// - /// The standalone page - void Detach(T originalPage) where T : Models.PageBase; - /// /// Gets all of the available pages for the current site. /// - /// The optional site id + /// The site id /// The pages - IEnumerable GetAll(Guid? siteId = null); - - /// - /// Gets all of the available pages for the current site. - /// - /// The optional site id - /// The pages - IEnumerable GetAll(Guid? siteId = null) where T : Models.PageBase; + Task> GetAll(Guid siteId); /// /// Gets the available blog pages for the current site. /// - /// The optional site id + /// The site id /// The pages - IEnumerable GetAllBlogs(Guid? siteId = null); - - /// - /// Gets the available blog pages for the current site. - /// - /// The optional site id - /// The pages - IEnumerable GetAllBlogs(Guid? siteId = null) where T : Models.PageBase; - - /// - /// Gets the site startpage. - /// - /// The optional site id - /// The page model - Models.DynamicPage GetStartpage(Guid? siteId = null); + Task> GetAllBlogs(Guid siteId); /// /// Gets the site startpage. /// /// The model type - /// The optional site id + /// The site id /// The page model - T GetStartpage(Guid? siteId = null) where T : Models.PageBase; - - /// - /// Gets the page model with the specified id. - /// - /// The unique id - /// The page model - Models.DynamicPage GetById(Guid id); + Task GetStartpage(Guid siteId) where T : PageBase; /// /// Gets the page model with the specified id. @@ -89,32 +45,16 @@ public interface IPageRepository /// The model type /// The unique id /// The page model - T GetById(Guid id) where T : Models.PageBase; - - /// - /// Gets the page model with the specified slug. - /// - /// The unique slug - /// The optional site id - /// The page model - Models.DynamicPage GetBySlug(string slug, Guid? siteId = null); + Task GetById(Guid id) where T : PageBase; /// /// Gets the page model with the specified slug. /// /// The model type /// The unique slug - /// The optional site id + /// The site id /// The page model - T GetBySlug(string slug, Guid? siteId = null) where T : Models.PageBase; - - /// - /// Gets the id for the page with the given slug. - /// - /// The unique slug - /// The optional page id - /// The id - Guid? GetIdBySlug(string slug, Guid? siteId = null); + Task GetBySlug(string slug, Guid siteId) where T : PageBase; /// /// Moves the current page in the structure. @@ -123,24 +63,21 @@ public interface IPageRepository /// The page to move /// The new parent id /// The new sort order - void Move(T model, Guid? parentId, int sortOrder) where T : Models.PageBase; + /// The other pages that were affected by the move + Task> Move(T model, Guid? parentId, int sortOrder) where T : PageBase; /// /// Saves the given page model /// /// The page model - void Save(T model) where T : Models.PageBase; + /// The other pages that were affected by the move + Task> Save(T model) where T : PageBase; /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(T model) where T : Models.PageBase; + /// The other pages that were affected by the move + Task> Delete(Guid id); } } diff --git a/core/Piranha/Repositories/IPageTypeRepository.cs b/core/Piranha/Repositories/IPageTypeRepository.cs index 3d97491bd..30441f9a2 100644 --- a/core/Piranha/Repositories/IPageTypeRepository.cs +++ b/core/Piranha/Repositories/IPageTypeRepository.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -18,32 +20,26 @@ public interface IPageTypeRepository /// Gets all available models. /// /// The available models - IEnumerable GetAll(); + Task> GetAll(); /// /// Gets the model with the specified id. /// /// The unique i /// - Models.PageType GetById(string id); + Task GetById(string id); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Models.PageType model); + Task Save(PageType model); /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(string id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Models.PageType model); + Task Delete(string id); } } diff --git a/core/Piranha/Repositories/IParamRepository.cs b/core/Piranha/Repositories/IParamRepository.cs index 46625454d..292df1887 100644 --- a/core/Piranha/Repositories/IParamRepository.cs +++ b/core/Piranha/Repositories/IParamRepository.cs @@ -1,16 +1,17 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using System.Collections.Generic; -using Piranha.Data; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -20,39 +21,33 @@ public interface IParamRepository /// Gets all available models. /// /// The available models - IEnumerable GetAll(); + Task> GetAll(); /// /// Gets the model with the specified id. /// /// The unique id /// The model, or NULL if it doesn't exist - Param GetById(Guid id); + Task GetById(Guid id); /// /// Gets the model with the given internal id. /// /// The unique key /// The model - Param GetByKey(string key); + Task GetByKey(string key); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Param model); + Task Save(Param model); /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Param model); + Task Delete(Guid id); } } diff --git a/core/Piranha/Repositories/IPostRepository.cs b/core/Piranha/Repositories/IPostRepository.cs index 483d11842..818868214 100644 --- a/core/Piranha/Repositories/IPostRepository.cs +++ b/core/Piranha/Repositories/IPostRepository.cs @@ -1,74 +1,49 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { public interface IPostRepository { /// - /// Creates and initializes a new post of the specified type. - /// - /// The created post - T Create(string typeId = null) where T : Models.PostBase; - - /// - /// Gets the available post items. - /// - /// The posts - IEnumerable GetAll(); - - /// - /// Gets the available post items. - /// - /// The posts - IEnumerable GetAll() where T : Models.PostBase; - - /// - /// Gets the available post items. + /// Gets the available posts for the specified blog. /// /// The unique id /// The posts - IEnumerable GetAll(Guid blogId); + Task> GetAll(Guid blogId); /// - /// Gets the available post items. + /// Gets the available posts for the specified site. /// - /// The unique id + /// The site id /// The posts - IEnumerable GetAll(Guid blogId) where T : Models.PostBase; + Task> GetAllBySiteId(Guid siteId); /// - /// Gets the available posts for the specified blog. + /// Gets all available categories for the specified blog. /// - /// The blog slug - /// The optional site id - /// The posts - IEnumerable GetAll(string slug, Guid? siteId = null); + /// The blog id + /// The available categories + Task> GetAllCategories(Guid blogId); /// - /// Gets the available posts for the specified blog. + /// Gets all available tags for the specified blog. /// - /// The blog slug - /// The optional site id - /// The posts - IEnumerable GetAll(string slug, Guid? siteId = null) where T : Models.PostBase; - - /// - /// Gets the post model with the specified id. - /// - /// The unique id - /// The post model - Models.DynamicPost GetById(Guid id); + /// The blog id + /// The available tags + Task> GetAllTags(Guid blogId); /// /// Gets the post model with the specified id. @@ -76,60 +51,43 @@ public interface IPostRepository /// The model type /// The unique id /// The post model - T GetById(Guid id) where T : Models.PostBase; - - /// - /// Gets the post model with the specified slug. - /// - /// The unique blog slug - /// The unique slug - /// The post model - Models.DynamicPost GetBySlug(Guid blogId, string slug); + Task GetById(Guid id) where T : PostBase; /// /// Gets the post model with the specified slug. /// /// The model type - /// The unique blog slug + /// The blog id /// The unique slug /// The post model - T GetBySlug(Guid blogId, string slug) where T : Models.PostBase; + Task GetBySlug(Guid blogId, string slug) where T : PostBase; /// - /// Gets the post model with the specified slug. + /// Gets the category with the given slug. /// - /// The unique blog slug + /// The blog id /// The unique slug - /// The optional site id - /// The post model - Models.DynamicPost GetBySlug(string blog, string slug, Guid? siteId = null); + /// The category + Task GetCategoryBySlug(Guid blogId, string slug); /// - /// Gets the post model with the specified slug. + /// Gets the tag with the given slug. /// - /// The model type - /// The unique blog slug + /// The blog id /// The unique slug - /// The optional site id - /// The post model - T GetBySlug(string blog, string slug, Guid? siteId = null) where T : Models.PostBase; + /// The tag + Task GetTagBySlug(Guid blogId, string slug); /// /// Saves the given post model /// /// The post model - void Save(T model) where T : Models.PostBase; + Task Save(T model) where T : PostBase; /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(T model) where T : Models.PostBase; + Task Delete(Guid id); } } diff --git a/core/Piranha/Repositories/IPostTypeRepository.cs b/core/Piranha/Repositories/IPostTypeRepository.cs index d74074df4..0abb426f6 100644 --- a/core/Piranha/Repositories/IPostTypeRepository.cs +++ b/core/Piranha/Repositories/IPostTypeRepository.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -18,32 +20,26 @@ public interface IPostTypeRepository /// Gets all available models. /// /// The available models - IEnumerable GetAll(); + Task> GetAll(); /// /// Gets the model with the specified id. /// /// The unique i /// - Models.PostType GetById(string id); + Task GetById(string id); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Models.PostType model); + Task Save(PostType model); /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(string id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Models.PostType model); + Task Delete(string id); } } diff --git a/core/Piranha/Repositories/ISiteRepository.cs b/core/Piranha/Repositories/ISiteRepository.cs index f8f50286f..34674a362 100644 --- a/core/Piranha/Repositories/ISiteRepository.cs +++ b/core/Piranha/Repositories/ISiteRepository.cs @@ -1,16 +1,17 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using System.Collections.Generic; -using Piranha.Data; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -20,41 +21,34 @@ public interface ISiteRepository /// Gets all available models. /// /// The available models - IEnumerable GetAll(); + Task> GetAll(); /// /// Gets the model with the specified id. /// /// The unique id /// The model, or NULL if it doesn't exist - Site GetById(Guid id); + Task GetById(Guid id); /// /// Gets the model with the given internal id. /// /// The unique internal i /// The model - Site GetByInternalId(string internalId); - - /// - /// Gets the model with the given hostname. - /// - /// The hostname - /// The model - Site GetByHostname(string hostname); + Task GetByInternalId(string internalId); /// /// Gets the default side. /// /// The modell, or NULL if it doesnt exist - Site GetDefault(); + Task GetDefault(); /// /// Gets the site content for given site id. /// /// Site id /// The site content model - Models.DynamicSiteContent GetContentById(Guid id); + Task GetContentById(Guid id); /// /// Gets the site content for given site id. @@ -62,55 +56,36 @@ public interface ISiteRepository /// Site id /// The site model type /// The site content model - T GetContentById(Guid id) where T : Models.SiteContent; + Task GetContentById(Guid id) where T : SiteContent; /// /// Gets the hierachical sitemap structure. /// - /// The optional site id + /// The site id /// If only published items should be included /// The sitemap - Models.Sitemap GetSitemap(Guid? id = null, bool onlyPublished = true); + Task GetSitemap(Guid id, bool onlyPublished = true); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Site model); + Task Save(Site model); /// - /// Saves the given site content to the site with the + /// Saves the given site content to the site with the /// given id. /// /// The site id /// The site content /// The site content type - void SaveContent(Guid siteId, T content) where T : Models.SiteContent; + Task SaveContent(Guid siteId, T content) where T : SiteContent; /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Site model); - - /// - /// Creates and initializes a new site content model of the specified type. - /// - /// The created site content - T CreateContent(string typeId = null) where T : Models.SiteContentBase; - - /// - /// Invalidates the cached version of the sitemap with the - /// given id, if caching is enabled. - /// - /// The site id - void InvalidateSitemap(Guid id); + Task Delete(Guid id); } } diff --git a/core/Piranha/Repositories/ISiteTypeRepository.cs b/core/Piranha/Repositories/ISiteTypeRepository.cs index 9fba56fd3..9159f2237 100644 --- a/core/Piranha/Repositories/ISiteTypeRepository.cs +++ b/core/Piranha/Repositories/ISiteTypeRepository.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System.Collections.Generic; +using System.Threading.Tasks; +using Piranha.Models; namespace Piranha.Repositories { @@ -18,32 +20,26 @@ public interface ISiteTypeRepository /// Gets all available models. /// /// The available models - IEnumerable GetAll(); + Task> GetAll(); /// /// Gets the model with the specified id. /// /// The unique i /// - Models.SiteType GetById(string id); + Task GetById(string id); /// /// Adds or updates the given model in the database /// depending on its state. /// /// The model - void Save(Models.SiteType model); + Task Save(SiteType model); /// /// Deletes the model with the specified id. /// /// The unique id - void Delete(string id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Models.SiteType model); + Task Delete(string id); } } diff --git a/core/Piranha/Repositories/ITagRepository.cs b/core/Piranha/Repositories/ITagRepository.cs deleted file mode 100644 index 4014b550c..000000000 --- a/core/Piranha/Repositories/ITagRepository.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public interface ITagRepository - { - /// - /// Gets all available models. - /// - /// The blog id - /// The available models - IEnumerable GetAll(Guid blogId); - - /// - /// Gets the models for the post with the given id. - /// - /// The post id - /// The model - IEnumerable GetByPostId(Guid postId); - - /// - /// Gets the model with the specified id. - /// - /// The unique id - /// The model, or NULL if it doesn't exist - Tag GetById(Guid id); - - /// - /// Gets the model with the given slug - /// - /// The blog id - /// The unique slug - /// The model - Tag GetBySlug(Guid blogId, string slug); - - /// - /// Gets the model with the given title - /// - /// The blog id - /// The unique title - /// The model - Tag GetByTitle(Guid blogId, string title); - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - void Save(Tag model); - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - void Delete(Guid id); - - /// - /// Deletes the given model. - /// - /// The model - void Delete(Tag model); - - /// - /// Deletes all unused tags for the specified blog. - /// - /// The blog id - void DeleteUnused(Guid blogId); - } -} diff --git a/core/Piranha/Repositories/PageRepository.cs b/core/Piranha/Repositories/PageRepository.cs deleted file mode 100644 index 467b7038b..000000000 --- a/core/Piranha/Repositories/PageRepository.cs +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Copyright (c) 2016-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * https://github.com/piranhacms/piranha.core - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; -using Piranha.Services; - -namespace Piranha.Repositories -{ - public class PageRepository : IPageRepository - { - private readonly IDb _db; - private readonly IApi _api; - private readonly IContentService _contentService; - private readonly ICache _cache; - - /// - /// Default constructor. - /// - /// The current api - /// The current db context - /// The content service factory - /// The optional model cache - public PageRepository(IApi api, IDb db, IContentServiceFactory factory, ICache cache = null) - { - _api = api; - _db = db; - _contentService = factory.CreatePageService(); - _cache = cache; - } - - /// - /// Creates and initializes a new page of the specified type. - /// - /// The created page - public T Create(string typeId = null) where T : Models.PageBase - { - if (string.IsNullOrWhiteSpace(typeId)) - { - typeId = typeof(T).Name; - } - return _contentService.Create(_api.PageTypes.GetById(typeId)); - } - - /// - /// Creates and initializes a copy of the given page. - /// - /// The created copy - public T Copy(T originalPage) where T : Models.PageBase - { - var model = _contentService.Create(_api.PageTypes.GetById(originalPage.TypeId)); - model.OriginalPageId = originalPage.Id; - model.Slug = null; - return model; - } - - /// - /// Detaches a copy and initializes it as a standalone page - /// - /// The standalone page - public void Detach(T page) where T : Models.PageBase - { - if (!page.OriginalPageId.HasValue) - { - throw new InvalidOperationException("Page is not an copy"); - } - - var model = GetById(page.Id); - model.OriginalPageId = null; - - foreach (var pageBlock in model.Blocks) - { - pageBlock.Id = Guid.Empty; - } - - Save(model); - } - - /// - /// Gets all of the available pages for the current site. - /// - /// The optional site id - /// The pages - public IEnumerable GetAll(Guid? siteId = null) - { - return GetAll(siteId); - } - - /// - /// Gets all of the available pages for the current site. - /// - /// The optional site id - /// The pages - public IEnumerable GetAll(Guid? siteId = null) where T : Models.PageBase - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - - if (site != null) - { - siteId = site.Id; - } - } - - var pages = _db.Pages - .AsNoTracking() - .Where(p => p.SiteId == siteId) - .OrderBy(p => p.ParentId) - .ThenBy(p => p.SortOrder) - .Select(p => p.Id); - - var models = new List(); - - foreach (var page in pages) - { - var model = GetById(page); - - if (model != null) - { - models.Add(model); - } - } - return models; - } - - /// - /// Gets the available blog pages for the current site. - /// - /// The optional site id - /// The pages - public IEnumerable GetAllBlogs(Guid? siteId = null) - { - return GetAllBlogs(siteId); - } - - /// - /// Gets the available blog pages for the current site. - /// - /// The optional site id - /// The pages - public IEnumerable GetAllBlogs(Guid? siteId = null) where T : Models.PageBase - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - - if (site != null) - { - siteId = site.Id; - } - } - - var pages = _db.Pages - .AsNoTracking() - .Where(p => p.SiteId == siteId && p.ContentType == "Blog") - .OrderBy(p => p.ParentId) - .ThenBy(p => p.SortOrder) - .Select(p => p.Id); - - var models = new List(); - - foreach (var page in pages) - { - var model = GetById(page); - - if (model != null) - { - models.Add(model); - } - } - return models; - } - - /// - /// Gets the site startpage. - /// - /// The optional site id - /// The page model - public Models.DynamicPage GetStartpage(Guid? siteId = null) - { - return GetStartpage(siteId); - } - - /// - /// Gets the site startpage. - /// - /// The model type - /// The optional site id - /// The page model - public T GetStartpage(Guid? siteId = null) where T : Models.PageBase - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - { - siteId = site.Id; - } - } - - var page = _cache?.Get($"Page_{siteId}"); - - if (page == null) - { - page = GetQuery(out var fullQuery) - .FirstOrDefault(p => p.SiteId == siteId && p.ParentId == null && p.SortOrder == 0); - - if (page != null) - { - if (_cache != null && fullQuery) - { - AddToCache(page); - } - } - } - - if (page != null) - { - if (page.OriginalPageId.HasValue) - { - return MapOriginalPage(page); - } - return _contentService.Transform(page, _api.PageTypes.GetById(page.PageTypeId), Process); - } - return null; - } - - /// - /// Gets the page model with the specified id. - /// - /// The unique id - /// The page model - public Models.DynamicPage GetById(Guid id) - { - return GetById(id); - } - - /// - /// Gets the page model with the specified id. - /// - /// The model type - /// The unique id - /// The page model - public T GetById(Guid id) where T : Models.PageBase - { - var page = _cache?.Get(id.ToString()); - - if (page == null) - { - page = GetQuery(out var fullQuery) - .FirstOrDefault(p => p.Id == id); - - if (page != null) - { - if (_cache != null && fullQuery) - { - AddToCache(page); - } - } - } - - if (page != null) - { - if (page.OriginalPageId.HasValue) - { - return MapOriginalPage(page); - } - return _contentService.Transform(page, _api.PageTypes.GetById(page.PageTypeId), Process); - } - return null; - } - - /// - /// Gets the page model with the specified slug. - /// - /// The unique slug - /// The optional site id - /// The page model - public Models.DynamicPage GetBySlug(string slug, Guid? siteId = null) - { - return GetBySlug(slug, siteId); - } - - /// - /// Gets the page model with the specified slug. - /// - /// The model type - /// The unique slug - /// The optional site id - /// The page model - public T GetBySlug(string slug, Guid? siteId = null) where T : Models.PageBase - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - { - siteId = site.Id; - } - } - - // See if we can get the page id for the slug from cache. - var pageId = _cache?.Get($"PageId_{siteId}_{slug}"); - - if (pageId.HasValue) - { - // Load the page by id instead - return GetById(pageId.Value); - } - else - { - // No cache found, load from database - var page = GetQuery(out var fullQuery) - .FirstOrDefault(p => p.SiteId == siteId && p.Slug == slug); - - if (page != null) - { - if (_cache != null && fullQuery) - { - AddToCache(page); - } - - if (page.OriginalPageId.HasValue) - { - return MapOriginalPage(page); - } - - return _contentService.Transform(page, _api.PageTypes.GetById(page.PageTypeId), Process); - } - return null; - } - } - - /// - /// Gets the id for the page with the given slug. - /// - /// The unique slug - /// The optional page id - /// The id - public Guid? GetIdBySlug(string slug, Guid? siteId = null) - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - { - siteId = site.Id; - } - } - - // See if we can get the page id for the slug from cache. - var pageId = _cache != null ? _cache.Get($"PageId_{siteId}_{slug}") : (Guid?)null; - - if (pageId.HasValue) - { - return pageId; - } - else - { - // No cache found, load from database - var page = _db.Pages - .AsNoTracking() - .FirstOrDefault(p => p.SiteId == siteId && p.Slug == slug); - - if (page != null) - { - return page.Id; - } - return null; - } - } - - /// - /// Moves the current page in the structure. - /// - /// The model type - /// The page to move - /// The new parent id - /// The new sort order - public void Move(T model, Guid? parentId, int sortOrder) where T : Models.PageBase - { - IEnumerable oldSiblings = null; - IEnumerable newSiblings = null; - - // Only get siblings if we need to invalidate from cache - if (_cache != null) - { - oldSiblings = _db.Pages - .Where(p => p.ParentId == model.ParentId && p.Id != model.Id) - .ToList(); - newSiblings = _db.Pages - .Where(p => p.ParentId == parentId) - .ToList(); - } - - // Remove the old position for the page - MovePages(model.Id, model.SiteId, model.ParentId, model.SortOrder + 1, false); - // Add room for the new position of the page - MovePages(model.Id, model.SiteId, parentId, sortOrder, true); - - // Update the position of the current page - var page = _db.Pages - .FirstOrDefault(p => p.Id == model.Id); - var site = _db.Sites - .FirstOrDefault(s => s.Id == page.SiteId); - page.ParentId = parentId; - page.SortOrder = sortOrder; - site.ContentLastModified = DateTime.Now; - - _db.SaveChanges(); - - // Remove all siblings from cache - if (_cache != null) - { - foreach (var sibling in oldSiblings) - { - RemoveFromCache(sibling); - } - - foreach (var sibling in newSiblings) - { - RemoveFromCache(sibling); - } - } - _api.Sites.InvalidateSitemap(model.SiteId); - } - - /// - /// Saves the given page model - /// - /// The page model - public void Save(T model) where T : Models.PageBase - { - var type = _api.PageTypes.GetById(model.TypeId); - var shouldUpdateSiteDate = false; - - if (type != null) - { - // Ensure that we have a slug - if (string.IsNullOrWhiteSpace(model.Slug)) - { - var prefix = ""; - - // Check if we should generate hierarchical slugs - using (var config = new Config(_api)) - { - if (config.HierarchicalPageSlugs && model.ParentId.HasValue) - { - var parentSlug = _db.Pages - .AsNoTracking() - .FirstOrDefault(p => p.Id == model.ParentId)?.Slug; - - if (!string.IsNullOrWhiteSpace(parentSlug)) - { - prefix = parentSlug + "/"; - } - } - model.Slug = prefix + Utils.GenerateSlug(model.NavigationTitle != null ? model.NavigationTitle : model.Title); - } - } - else - { - model.Slug = Utils.GenerateSlug(model.Slug); - } - - // Set content type - model.ContentType = type.ContentTypeId; - - var page = _db.Pages - .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) - .Include(p => p.Fields) - .FirstOrDefault(p => p.Id == model.Id); - var site = _db.Sites - .FirstOrDefault(s => s.Id == model.SiteId); - - if (model.OriginalPageId.HasValue) - { - var originalPageIsCopy = _db.Pages.FirstOrDefault(p => p.Id == model.OriginalPageId)?.OriginalPageId.HasValue ?? false; - if (originalPageIsCopy) - { - throw new InvalidOperationException("Can not set copy of a copy"); - } - - var originalPageType = _db.Pages.FirstOrDefault(p => p.Id == model.OriginalPageId)?.PageTypeId; - if (originalPageType != model.TypeId) - { - throw new InvalidOperationException("Copy can not have a different content type"); - } - - // Transform the model - if (page == null) - { - page = new Page() - { - Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), - }; - - _db.Pages.Add(page); - - // Make room for the new page - MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true); - - // We're adding a page to the site structure, update - // the global last modified date - shouldUpdateSiteDate = true; - } - else - { - // Check if the page has been moved - if (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder) - { - // Remove the old position for the page - MovePages(page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false); - // Add room for the new position of the page - MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true); - - // We've moved pages, update the global last - // modified date - shouldUpdateSiteDate = true; - } - } - - if (page.Title != model.Title || page.NavigationTitle != model.NavigationTitle) - { - // We've changed the title which is reflected in the - // sitemap, update the global last modified date. - shouldUpdateSiteDate = true; - } - - page.PageTypeId = model.TypeId; - page.OriginalPageId = model.OriginalPageId; - page.SiteId = model.SiteId; - page.Title = model.Title; - page.NavigationTitle = model.NavigationTitle; - page.Slug = model.Slug; - page.ParentId = model.ParentId; - page.SortOrder = model.SortOrder; - page.IsHidden = model.IsHidden; - page.Route = model.Route; - page.Published = model.Published; - - if (shouldUpdateSiteDate) - { - site.ContentLastModified = DateTime.Now; - } - - _db.SaveChanges(); - - if (_cache != null) - { - RemoveFromCache(page); - } - - _api.Sites.InvalidateSitemap(model.SiteId); - return; - } - - // Transform the model - if (page == null) - { - page = new Page - { - Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), - ParentId = model.ParentId, - SortOrder = model.SortOrder, - PageTypeId = model.TypeId, - Created = DateTime.Now, - LastModified = DateTime.Now - }; - _db.Pages.Add(page); - model.Id = page.Id; - - // Make room for the new page - MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true); - - // We're adding a page to the site structure, update - // the global last modified date - shouldUpdateSiteDate = true; - } - else - { - // Check if the page has been moved - if (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder) - { - // Remove the old position for the page - MovePages(page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false); - // Add room for the new position of the page - MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true); - - // We've moved pages, update the global last - // modified date - shouldUpdateSiteDate = true; - } - page.LastModified = DateTime.Now; - } - - if (page.Title != model.Title || page.NavigationTitle != model.NavigationTitle) - { - // We've changed the title which is reflected in the - // sitemap, update the global last modified date. - shouldUpdateSiteDate = true; - } - - page = _contentService.Transform(model, type, page); - - // Transform blocks - var blockModels = model.Blocks; - - if (blockModels != null) - { - var pageBlocks = _contentService.TransformBlocks(blockModels); - - var current = pageBlocks.Select(b => b.Block.Id).ToArray(); - - // Delete removed blocks - var removed = page.Blocks - .Where(b => !current.Contains(b.BlockId) && !b.Block.IsReusable) - .Select(b => b.Block); - _db.Blocks.RemoveRange(removed); - - // Delete the old page blocks - page.Blocks.Clear(); - - // Now map the new block - for (var n = 0; n < pageBlocks.Count; n++) - { - var block = _db.Blocks - .Include(b => b.Fields) - .FirstOrDefault(b => b.Id == pageBlocks[n].Block.Id); - if (block == null) - { - block = new Block - { - Id = pageBlocks[n].Block.Id != Guid.Empty ? pageBlocks[n].Block.Id : Guid.NewGuid(), - Created = DateTime.Now - }; - _db.Blocks.Add(block); - } - block.CLRType = pageBlocks[n].Block.CLRType; - block.IsReusable = pageBlocks[n].Block.IsReusable; - block.Title = pageBlocks[n].Block.Title; - block.LastModified = DateTime.Now; - - var currentFields = pageBlocks[n].Block.Fields.Select(f => f.FieldId).Distinct(); - var removedFields = block.Fields.Where(f => !currentFields.Contains(f.FieldId)); - _db.BlockFields.RemoveRange(removedFields); - - foreach (var newField in pageBlocks[n].Block.Fields) - { - var field = block.Fields.FirstOrDefault(f => f.FieldId == newField.FieldId); - if (field == null) - { - field = new BlockField - { - Id = newField.Id != Guid.Empty ? newField.Id : Guid.NewGuid(), - BlockId = block.Id, - FieldId = newField.FieldId - }; - _db.BlockFields.Add(field); - block.Fields.Add(field); - } - field.SortOrder = newField.SortOrder; - field.CLRType = newField.CLRType; - field.Value = newField.Value; - } - - // Create the page block - page.Blocks.Add(new PageBlock - { - Id = pageBlocks[n].Id, - ParentId = pageBlocks[n].ParentId, - BlockId = block.Id, - Block = block, - PageId = page.Id, - SortOrder = n - }); - } - } - - if (shouldUpdateSiteDate) - { - site.ContentLastModified = DateTime.Now; - } - - _db.SaveChanges(); - - if (_cache != null) - { - RemoveFromCache(page); - } - _api.Sites.InvalidateSitemap(model.SiteId); - } - } - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - public virtual void Delete(Guid id) - { - var model = _db.Pages - .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) - .Include(p => p.Fields) - .FirstOrDefault(p => p.Id == id); - - if (model != null) - { - // Make sure this page isn't copied - var copyCount = _db.Pages.Count(p => p.OriginalPageId == model.Id); - if (copyCount > 0) - { - throw new InvalidOperationException("Can not delete page because it has copies"); - } - - // Get the site - var site = _db.Sites.FirstOrDefault(s => s.Id == model.SiteId); - - // Remove all blocks that are not reusable - foreach (var pageBlock in model.Blocks) - { - if (!pageBlock.Block.IsReusable) - { - _db.Blocks.Remove(pageBlock.Block); - } - } - - // Remove the main page. - _db.Pages.Remove(model); - - // Move all remaining pages after this page in the site structure. - MovePages(id, model.SiteId, model.ParentId, model.SortOrder + 1, false); - - // We've removed a page from the site structure, we have to - // update the global last modified date for the site. - site.ContentLastModified = DateTime.Now; - - _db.SaveChanges(); - - // Check if we have the page in cache, and if so remove it - if (_cache != null) - { - var page = _cache.Get(model.Id.ToString()); - if (page != null) - { - RemoveFromCache(page); - } - } - _api.Sites.InvalidateSitemap(model.SiteId); - } - } - - /// - /// Deletes the given model. - /// - /// The model - public virtual void Delete(T model) where T : Models.PageBase - { - Delete(model.Id); - } - - /// - /// Gets the base query for loading pages. - /// - /// If this is a full load or not - /// The requested model type - /// The queryable - private IQueryable GetQuery(out bool fullModel) - { - var loadRelated = !typeof(Models.IContentInfo).IsAssignableFrom(typeof(T)); - - var query = _db.Pages - .AsNoTracking(); - - if (loadRelated) - { - query = query - .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) - .Include(p => p.Fields); - fullModel = true; - } - else - { - fullModel = false; - } - return query; - } - - /// - /// Performs additional processing and loads related models. - /// - /// The source page - /// The targe model - private void Process(Data.Page page, T model) where T : Models.PageBase - { - if (!(model is Models.IContentInfo)) - { - if (page.Blocks.Count > 0) - { - model.Blocks = _contentService.TransformBlocks(page.Blocks.OrderBy(b => b.SortOrder)); - } - } - } - - /// - /// Moves the pages around. This is done when a page is deleted or moved in the structure. - /// - /// The id of the page that is moved - /// The site id - /// The parent id - /// The sort order - /// If sort order should be increase or decreased - private void MovePages(Guid pageId, Guid siteId, Guid? parentId, int sortOrder, bool increase) - { - var pages = _db.Pages - .Where(p => p.SiteId == siteId && p.ParentId == parentId && p.SortOrder >= sortOrder && p.Id != pageId) - .ToList(); - - foreach (var page in pages) - { - page.SortOrder = increase ? page.SortOrder + 1 : page.SortOrder - 1; - } - } - - /// - /// Updates the LastModified date of the pages and - /// removes it from the cache. - /// - /// The id of the pages - internal void Touch(params Guid[] pages) - { - var models = _db.Pages - .Where(p => pages.Contains(p.Id)) - .ToArray(); - - foreach (var page in models) - { - page.LastModified = DateTime.Now; - _db.SaveChanges(); - RemoveFromCache(page); - } - } - - /// - /// Internal method for getting the data page by id. - /// - /// The unique id - /// The page - internal Page GetPageById(Guid id) - { - var page = _cache != null ? _cache.Get(id.ToString()) : null; - - if (page == null) - { - page = _db.Pages - .AsNoTracking() - .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) - .Include(p => p.Fields) - .FirstOrDefault(p => p.Id == id); - - if (page != null) - { - if (_cache != null) - { - AddToCache(page); - } - } - } - return page; - } - - private T MapOriginalPage(Page page) where T : Models.PageBase - { - var originalPage = GetById(page.OriginalPageId.Value); - if (originalPage == null) - { - return null; - } - return SetOriginalPageProperties(originalPage, page); - } - - private T SetOriginalPageProperties(T originalPage, Page page) where T : Models.PageBase - { - originalPage.Id = page.Id; - originalPage.SiteId = page.SiteId; - originalPage.Title = page.Title; - originalPage.NavigationTitle = page.NavigationTitle; - originalPage.Slug = page.Slug; - originalPage.ParentId = page.ParentId; - originalPage.SortOrder = page.SortOrder; - originalPage.IsHidden = page.IsHidden; - originalPage.Route = page.Route; - originalPage.OriginalPageId = page.OriginalPageId; - originalPage.Published = page.Published; - originalPage.Created = page.Created; - originalPage.LastModified = page.LastModified; - return originalPage; - } - - /// - /// Sorts the items. - /// - /// The full page list - /// The current parent id - /// The sitemap - private Models.Sitemap Sort(IEnumerable pages, IEnumerable pageTypes, Guid? parentId = null, int level = 0) - { - var result = new Models.Sitemap(); - - foreach (var page in pages.Where(p => p.ParentId == parentId).OrderBy(p => p.SortOrder)) - { - var item = App.Mapper.Map(page); - - item.Level = level; - item.PageTypeName = pageTypes.First(t => t.Id == page.PageTypeId).Title; - item.Items = Sort(pages, pageTypes, page.Id, level + 1); - - result.Add(item); - } - return result; - } - - /// - /// Adds the given model to cache. - /// - /// The page - private void AddToCache(Page page) - { - _cache.Set(page.Id.ToString(), page); - _cache.Set($"PageId_{page.SiteId}_{page.Slug}", page.Id); - if (!page.ParentId.HasValue && page.SortOrder == 0) - { - _cache.Set($"Page_{page.SiteId}", page); - } - } - - /// - /// Removes the given model from cache. - /// - /// The page - private void RemoveFromCache(Page page) - { - _cache.Remove(page.Id.ToString()); - _cache.Remove($"PageId_{page.SiteId}_{page.Slug}"); - if (!page.ParentId.HasValue && page.SortOrder == 0) - { - _cache.Remove($"Page_{page.SiteId}"); - } - } - } -} diff --git a/core/Piranha/Repositories/PageTypeRepository.cs b/core/Piranha/Repositories/PageTypeRepository.cs deleted file mode 100644 index f077bf2db..000000000 --- a/core/Piranha/Repositories/PageTypeRepository.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; - -namespace Piranha.Repositories -{ - public class PageTypeRepository : IPageTypeRepository - { - private readonly IDb _db; - private static readonly Dictionary _types = new Dictionary(); - private static object _typesMutex = new Object(); - private static volatile bool _isInitialized = false; - - /// - /// Default constructor. - /// - /// The current db connection - public PageTypeRepository(IDb db) - { - _db = db; - - if (!_isInitialized) - { - lock (_typesMutex) - { - if (!_isInitialized) - { - Load(); - - _isInitialized = true; - } - } - } - } - - /// - /// Gets all available models. - /// - /// The available models - public IEnumerable GetAll() - { - return _types.Values.OrderBy(t => t.Id); - } - - /// - /// Gets the model with the specified id. - /// - /// The unique i - /// - public Models.PageType GetById(string id) - { - if (_types.TryGetValue(id, out var type)) - return type; - return null; - } - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - public void Save(Models.PageType model) - { - var type = _db.PageTypes - .FirstOrDefault(t => t.Id == model.Id); - - if (type == null) - { - type = new Data.PageType - { - Id = model.Id, - Created = DateTime.Now - }; - _db.PageTypes.Add(type); - } - type.CLRType = model.CLRType; - type.Body = JsonConvert.SerializeObject(model); - type.LastModified = DateTime.Now; - - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - public void Delete(string id) - { - var type = _db.PageTypes - .FirstOrDefault(t => t.Id == id); - - if (type != null) - { - _db.PageTypes.Remove(type); - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - } - - /// - /// Deletes the given model. - /// - /// The model - public void Delete(Models.PageType model) - { - Delete(model.Id); - } - - /// - /// Reloads the page types from the database. - /// - private void Load() - { - _types.Clear(); - - foreach (var pageType in _db.PageTypes) - { - _types[pageType.Id] = JsonConvert.DeserializeObject(pageType.Body); - } - } - } -} diff --git a/core/Piranha/Repositories/ParamRepository.cs b/core/Piranha/Repositories/ParamRepository.cs deleted file mode 100644 index 2fd140f60..000000000 --- a/core/Piranha/Repositories/ParamRepository.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public class ParamRepository : BaseRepositoryWithAll, IParamRepository - { - /// - /// Default constructor. - /// - /// The current db context - /// The optional model cache - public ParamRepository(IDb db, ICache cache = null) - : base(db, cache) { } - - /// - /// Gets the model with the given key. - /// - /// The unique key - /// The model - public Param GetByKey(string key) { - var id = cache != null ? cache.Get($"ParamKey_{key}") : null; - Param model = null; - - if (id.HasValue) { - model = GetById(id.Value); - } else { - id = db.Params - .AsNoTracking() - .Where(p => p.Key == key) - .Select(p => p.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - model = GetById(id.Value); - } - return model; - } - - #region Protected methods - /// - /// Adds a new model to the database. - /// - /// The model - protected override void Add(Param model) { - PrepareInsert(model); - - db.Params.Add(model); - } - - /// - /// Updates the given model in the database. - /// - /// The model - protected override void Update(Param model) { - PrepareUpdate(model); - - var param = db.Params.FirstOrDefault(p => p.Id == model.Id); - if (param != null) { - App.Mapper.Map(model, param); - } - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected override void AddToCache(Param model) { - cache.Set(model.Id.ToString(), model); - cache.Set($"ParamKey_{model.Key}", model.Id); - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected override void RemoveFromCache(Param model) { - cache.Remove(model.Id.ToString()); - cache.Remove($"ParamKey_{model.Key}"); - } - #endregion - } -} diff --git a/core/Piranha/Repositories/PostTypeRepository.cs b/core/Piranha/Repositories/PostTypeRepository.cs deleted file mode 100644 index 73f1071cc..000000000 --- a/core/Piranha/Repositories/PostTypeRepository.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; - -namespace Piranha.Repositories -{ - public class PostTypeRepository : IPostTypeRepository - { - private readonly IDb _db; - private static readonly Dictionary _types = new Dictionary(); - private static object _typesMutex = new Object(); - private static volatile bool _isInitialized = false; - - /// - /// Default constructor. - /// - /// The current db connection - public PostTypeRepository(IDb db) - { - _db = db; - - if (!_isInitialized) - { - lock (_typesMutex) - { - if (!_isInitialized) - { - Load(); - - _isInitialized = true; - } - } - } - } - - /// - /// Gets all available models. - /// - /// The available models - public IEnumerable GetAll() - { - return _types.Values.OrderBy(t => t.Id); - } - - /// - /// Gets the model with the specified id. - /// - /// The unique id - /// - public Models.PostType GetById(string id) - { - if (_types.TryGetValue(id, out var type)) - return type; - return null; - } - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - public void Save(Models.PostType model) - { - var type = _db.PostTypes - .FirstOrDefault(t => t.Id == model.Id); - - if (type == null) - { - type = new Data.PostType - { - Id = model.Id, - Created = DateTime.Now - }; - _db.PostTypes.Add(type); - } - type.CLRType = model.CLRType; - type.Body = JsonConvert.SerializeObject(model); - type.LastModified = DateTime.Now; - - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - public void Delete(string id) - { - var type = _db.PostTypes - .FirstOrDefault(t => t.Id == id); - - if (type != null) - { - _db.PostTypes.Remove(type); - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - } - - /// - /// Deletes the given model. - /// - /// The model - public void Delete(Models.PostType model) - { - Delete(model.Id); - } - - /// - /// Reloads the page types from the database. - /// - private void Load() - { - _types.Clear(); - - foreach (var postType in _db.PostTypes) - { - _types[postType.Id] = JsonConvert.DeserializeObject(postType.Body); - } - } - } -} diff --git a/core/Piranha/Repositories/SiteRepository.cs b/core/Piranha/Repositories/SiteRepository.cs deleted file mode 100644 index aa5f5c3a8..000000000 --- a/core/Piranha/Repositories/SiteRepository.cs +++ /dev/null @@ -1,467 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; -using Piranha.Services; - -namespace Piranha.Repositories -{ - public class SiteRepository : BaseRepositoryWithAll, ISiteRepository - { - [Serializable] - public class SiteMapping - { - public Guid Id { get; set; } - public string Hostnames { get; set; } - } - - private readonly Api api; - // This is a hack as we don't really want to transform the models, we only want - // to access the create methods. - private readonly IContentService contentService; - private const string SITE_MAPPINGS = "Site_Mappings"; - - /// - /// Default constructor. - /// - /// The current api - /// The current db context - /// The content service factory - /// The optional model cache - public SiteRepository(Api api, IDb db, IContentServiceFactory factory, ICache cache = null) - : base(db, cache) - { - this.api = api; - this.contentService = factory.CreateSiteService(); - } - - /// - /// Gets the model with the given internal id. - /// - /// The unique internal i - /// The model - public Site GetByInternalId(string internalId) - { - var id = cache?.Get($"SiteId_{internalId}"); - Site model = null; - - if (id != null) - { - model = GetById(id.Value); - } - else - { - id = db.Sites - .AsNoTracking() - .Where(s => s.InternalId == internalId) - .Select(s => s.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - model = GetById(id.Value); - } - return model; - } - - /// - /// Gets the model with the given hostname. - /// - /// The hostname - /// The model - public Site GetByHostname(string hostname) - { - IList mappings; - - if (cache != null) - { - mappings = cache.Get>(SITE_MAPPINGS); - - if (mappings == null) - { - mappings = db.Sites - .AsNoTracking() - .Where(s => s.Hostnames != null) - .Select(s => new SiteMapping - { - Id = s.Id, - Hostnames = s.Hostnames - }) - .ToList(); - cache.Set(SITE_MAPPINGS, mappings); - } - } - else - { - mappings = db.Sites - .AsNoTracking() - .Where(s => s.Hostnames.Contains(hostname)) - .Select(s => new SiteMapping - { - Id = s.Id, - Hostnames = s.Hostnames - }) - .ToList(); - } - - foreach (var mapping in mappings) - { - foreach (var host in mapping.Hostnames.Split(new [] { ',' })) - { - if (host.Trim().ToLower() == hostname) - { - return GetById(mapping.Id); - } - } - } - return null; - } - - /// - /// Gets the default side. - /// - /// The modell, or NULL if it doesnt exist - public Site GetDefault() - { - Site model = cache != null ? cache.Get($"Site_{Guid.Empty}") : null; - - if (model == null) - { - var id = db.Sites - .AsNoTracking() - .Where(s => s.IsDefault) - .Select(s => s.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - { - model = GetById(id); - } - } - return model; - } - - /// - /// Gets the site content for given site id. - /// - /// Site id - /// The site content model - public Models.DynamicSiteContent GetContentById(Guid id) - { - return GetContentById(id); - } - - /// - /// Gets the site content for given site id. - /// - /// Site id - /// The site model type - /// The site content model - public T GetContentById(Guid id) where T : Models.SiteContent - { - var site = cache != null ? cache.Get($"SiteContent_{id}") : null; - if (site == null) - { - site = db.Sites - .Include(s => s.Fields) - .Where(s => s.Id == id) - .FirstOrDefault(); - - if (site == null) - { - return null; - } - } - - if (string.IsNullOrEmpty(site.SiteTypeId)) - return null; - - var type = api.SiteTypes.GetById(site.SiteTypeId); - if (type == null) - return null; - - if (cache != null) - cache.Set($"SiteContent_{id}", site); - - return contentService.Transform(site, type); - } - - /// - /// Gets the hierachical sitemap structure. - /// - /// The optional site id - /// If only published items should be included - /// The sitemap - public Models.Sitemap GetSitemap(Guid? id = null, bool onlyPublished = true) - { - if (!id.HasValue) - { - var site = GetDefault(); - - if (site != null) - { - id = site.Id; - } - } - - if (id != null) - { - var sitemap = onlyPublished && cache != null ? cache.Get($"Sitemap_{id}") : null; - - if (sitemap == null) - { - var pages = db.Pages - .AsNoTracking() - .Where(p => p.SiteId == id) - .OrderBy(p => p.ParentId) - .ThenBy(p => p.SortOrder) - .ToList(); - - var pageTypes = api.PageTypes.GetAll(); - - if (onlyPublished) - { - pages = pages.Where(p => p.Published.HasValue).ToList(); - } - sitemap = Sort(pages, pageTypes); - - if (onlyPublished && cache != null) - { - cache.Set($"Sitemap_{id}", sitemap); - } - } - return sitemap; - } - return null; - } - - /// - /// Saves the given site content to the site with the - /// given id. - /// - /// The site id - /// The site content - /// The site content type - public void SaveContent(Guid siteId, T content) where T : Models.SiteContent - { - var site = db.Sites - .Include(s => s.Fields) - .FirstOrDefault(s => s.Id == siteId); - - if (site != null) - { - if (string.IsNullOrEmpty(site.SiteTypeId)) - { - throw new MissingFieldException("Can't save content for a site that doesn't have a Site Type Id."); - } - - var type = api.SiteTypes.GetById(site.SiteTypeId); - if (type == null) - { - throw new MissingFieldException("The specified Site Type is missing. Can't save content."); - } - - content.Id = siteId; - content.TypeId = site.SiteTypeId; - content.Title = site.Title; - - contentService.Transform(content, type, site); - - // Since we've updated global site content, update the - // global last modified date for the site. - site.ContentLastModified = DateTime.Now; - - db.SaveChanges(); - - cache?.Remove($"SiteContent_{siteId}"); - } - } - - /// - /// Creates and initializes a new site content model of the specified type. - /// - /// The created site content - public T CreateContent(string typeId = null) where T : Models.SiteContentBase - { - if (string.IsNullOrWhiteSpace(typeId)) - typeId = typeof(T).Name; - - return contentService.Create(api.SiteTypes.GetById(typeId)); - } - - /// - /// Invalidates the cached version of the sitemap with the - /// given id, if caching is enabled. - /// - /// The site id - public void InvalidateSitemap(Guid id) - { - cache?.Remove($"Sitemap_{id}"); - } - - /// - /// Adds a new model to the database. - /// - /// The model - protected override void Add(Site model) - { - PrepareInsert(model); - - // Check for title - if (string.IsNullOrWhiteSpace(model.Title)) - throw new ArgumentException("Title cannot be empty"); - - // Ensure InternalId - if (string.IsNullOrWhiteSpace(model.InternalId)) - model.InternalId = Utils.GenerateInteralId(model.Title); - - if (model.IsDefault) - { - // Make sure no other site is default first - var def = GetDefault(); - - if (def != null && def.Id != model.Id) - { - def.IsDefault = false; - Update(def, false); - } - } - else - { - // Make sure we have a default site - var count = db.Sites.Count(s => s.IsDefault); - if (count == 0) - model.IsDefault = true; - } - db.Sites.Add(model); - } - - /// - /// Updates the given model in the database. - /// - /// The model - protected override void Update(Site model) - { - Update(model, true); - } - - /// - /// Updates the given model in the database. - /// - /// The model - /// If default site integrity should be validated - protected void Update(Site model, bool checkDefault) - { - PrepareUpdate(model); - - // Check for title - if (string.IsNullOrWhiteSpace(model.Title)) - { - throw new ArgumentException("Title cannot be empty"); - } - - // Ensure InternalId - if (string.IsNullOrWhiteSpace(model.InternalId)) - { - model.InternalId = Utils.GenerateInteralId(model.Title); - } - - if (checkDefault) - { - if (model.IsDefault) - { - // Make sure no other site is default first - var def = GetDefault(); - - if (def != null && def.Id != model.Id) - { - def.IsDefault = false; - Update(def, false); - } - } - else - { - // Make sure we have a default site - var count = db.Sites.Count(s => s.IsDefault && s.Id != model.Id); - if (count == 0) - { - model.IsDefault = true; - } - } - } - var site = db.Sites.FirstOrDefault(s => s.Id == model.Id); - if (site != null) - { - App.Mapper.Map(model, site); - } - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected override void AddToCache(Site model) - { - cache.Set(model.Id.ToString(), model); - cache.Set($"SiteId_{model.InternalId}", model.Id); - if (model.IsDefault) - { - cache.Set($"Site_{Guid.Empty}", model); - } - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected override void RemoveFromCache(Site model) - { - cache.Remove($"SiteId_{model.InternalId}"); - if (model.IsDefault) - { - cache.Remove($"Site_{Guid.Empty}"); - } - cache.Remove(SITE_MAPPINGS); - - base.RemoveFromCache(model); - } - - /// - /// Sorts the items. - /// - /// The full page list - /// The current parent id - /// The sitemap - private Models.Sitemap Sort(IEnumerable pages, IEnumerable pageTypes, Guid? parentId = null, int level = 0) - { - var result = new Models.Sitemap(); - - foreach (var page in pages.Where(p => p.ParentId == parentId).OrderBy(p => p.SortOrder)) - { - var item = App.Mapper.Map(page); - - if (!string.IsNullOrEmpty(page.RedirectUrl)) - { - item.Permalink = page.RedirectUrl; - } - - item.Level = level; - item.PageTypeName = pageTypes.First(t => t.Id == page.PageTypeId).Title; - item.Items = Sort(pages, pageTypes, page.Id, level + 1); - - result.Add(item); - } - return result; - } - } -} diff --git a/core/Piranha/Repositories/SiteTypeRepository.cs b/core/Piranha/Repositories/SiteTypeRepository.cs deleted file mode 100644 index 8da9b30d1..000000000 --- a/core/Piranha/Repositories/SiteTypeRepository.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; - -namespace Piranha.Repositories -{ - public class SiteTypeRepository : ISiteTypeRepository - { - private readonly IDb _db; - private static readonly Dictionary _types = new Dictionary(); - private static object _typesMutex = new Object(); - private static volatile bool _isInitialized = false; - - /// - /// Default constructor. - /// - /// The current db connection - public SiteTypeRepository(IDb db) - { - _db = db; - - if (!_isInitialized) - { - lock (_typesMutex) - { - if (!_isInitialized) - { - Load(); - - _isInitialized = true; - } - } - } - } - - /// - /// Gets all available models. - /// - /// The available models - public IEnumerable GetAll() - { - return _types.Values.OrderBy(t => t.Id); - } - - /// - /// Gets the model with the specified id. - /// - /// The unique i - /// - public Models.SiteType GetById(string id) - { - if (_types.TryGetValue(id, out var type)) - return type; - return null; - } - - /// - /// Adds or updates the given model in the database - /// depending on its state. - /// - /// The model - public void Save(Models.SiteType model) - { - var type = _db.SiteTypes - .FirstOrDefault(t => t.Id == model.Id); - - if (type == null) { - type = new Data.SiteType - { - Id = model.Id, - Created = DateTime.Now - }; - _db.SiteTypes.Add(type); - } - type.CLRType = model.CLRType; - type.Body = JsonConvert.SerializeObject(model); - type.LastModified = DateTime.Now; - - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - - /// - /// Deletes the model with the specified id. - /// - /// The unique id - public void Delete(string id) - { - var type = _db.SiteTypes - .FirstOrDefault(t => t.Id == id); - - if (type != null) - { - _db.SiteTypes.Remove(type); - _db.SaveChanges(); - - lock (_typesMutex) - { - Load(); - } - } - } - - /// - /// Deletes the given model. - /// - /// The model - public void Delete(Models.SiteType model) - { - Delete(model.Id); - } - - /// - /// Reloads the page types from the database. - /// - private void Load() - { - _types.Clear(); - - foreach (var siteType in _db.SiteTypes) - { - _types[siteType.Id] = JsonConvert.DeserializeObject(siteType.Body); - } - } - } -} diff --git a/core/Piranha/Repositories/TagRepository.cs b/core/Piranha/Repositories/TagRepository.cs deleted file mode 100644 index d3a4121fc..000000000 --- a/core/Piranha/Repositories/TagRepository.cs +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2017-2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; - -namespace Piranha.Repositories -{ - public class TagRepository : BaseRepository, ITagRepository - { - /// - /// Default constructor. - /// - /// The current db context - /// The optional model cache - public TagRepository(IDb db, ICache cache = null) - : base(db, cache) { } - - /// - /// Gets all available models for the specified blog. - /// - /// The blog id - /// The available models - public IEnumerable GetAll(Guid blogId) - { - var models = new List(); - var tags = db.Tags - .AsNoTracking() - .Where(t => t.BlogId == blogId) - .Select(t => t.Id); - - foreach (var t in tags) - { - var model = GetById(t); - if (model != null) - models.Add(model); - } - return models; - } - - /// - /// Gets the models for the post with the given id. - /// - /// The post id - /// The model - public IEnumerable GetByPostId(Guid postId) - { - var tags = db.PostTags - .AsNoTracking() - .Where(t => t.PostId == postId) - .Select(t => t.TagId); - - var models = new List(); - - foreach (var tag in tags) - { - var model = GetById(tag); - - if (model != null) - models.Add(model); - } - return models; - } - - /// - /// Gets the model with the given slug. - /// - /// The blog id - /// The unique slug - /// The model - public Tag GetBySlug(Guid blogId, string slug) - { - var id = cache != null ? cache.Get($"Tag_{blogId}_{slug}") : null; - Tag model = null; - - if (id.HasValue) - { - model = GetById(id.Value); - } - else - { - id = db.Tags - .AsNoTracking() - .Where(t => t.BlogId == blogId && t.Slug == slug) - .Select(t => t.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - model = GetById(id.Value); - } - return model; - } - - /// - /// Gets the model with the given title - /// - /// The blog id - /// The unique title - /// The model - public Tag GetByTitle(Guid blogId, string title) - { - var id = db.Tags - .AsNoTracking() - .Where(t => t.BlogId == blogId && t.Title == title) - .Select(t => t.Id) - .FirstOrDefault(); - - if (id != Guid.Empty) - return GetById(id); - return null; - } - - /// - /// Deletes all unused tags for the specified blog. - /// - /// The blog id - public void DeleteUnused(Guid blogId) - { - var used = db.PostTags - .Where(t => t.Post.BlogId == blogId) - .Select(t => t.TagId) - .Distinct() - .ToArray(); - - var unused = db.Tags - .Where(t => t.BlogId == blogId && !used.Contains(t.Id)) - .ToList(); - - if (unused.Count > 0) - { - db.Tags.RemoveRange(unused); - db.SaveChanges(); - } - } - - #region Protected methods - /// - /// Adds a new model to the database. - /// - /// The model - protected override void Add(Tag model) - { - PrepareInsert(model); - - // Check required - if (string.IsNullOrWhiteSpace(model.Title)) - throw new ArgumentException("Title is required for Tag"); - - // Ensure slug - if (string.IsNullOrWhiteSpace(model.Slug)) - model.Slug = Utils.GenerateSlug(model.Title, false); - else model.Slug = Utils.GenerateSlug(model.Slug, false); - - db.Tags.Add(model); - } - - /// - /// Updates the given model in the database. - /// - /// The model - protected override void Update(Tag model) - { - PrepareUpdate(model); - - // Check required - if (string.IsNullOrWhiteSpace(model.Title)) - throw new ArgumentException("Title is required for Tag"); - - // Ensure slug - if (string.IsNullOrWhiteSpace(model.Slug)) - model.Slug = Utils.GenerateSlug(model.Title); - else model.Slug = Utils.GenerateSlug(model.Slug); - - var tag = db.Tags.FirstOrDefault(t => t.Id == model.Id); - if (tag != null) - { - App.Mapper.Map(model, tag); - } - } - - /// - /// Adds the given model to cache. - /// - /// The model - protected override void AddToCache(Tag model) - { - cache.Set(model.Id.ToString(), model); - cache.Set($"Tag_{model.BlogId}_{model.Slug}", model.Id); - } - - /// - /// Removes the given model from cache. - /// - /// The model - protected override void RemoveFromCache(Tag model) - { - cache.Remove(model.Id.ToString()); - cache.Remove($"Tag_{model.BlogId}_{model.Slug}"); - } - #endregion - } -} diff --git a/core/Piranha/Runtime/ContentTypeList.cs b/core/Piranha/Runtime/ContentTypeList.cs new file mode 100644 index 000000000..7b533ff1a --- /dev/null +++ b/core/Piranha/Runtime/ContentTypeList.cs @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using Piranha.Models; + +namespace Piranha.Runtime +{ + public sealed class ContentTypeList : List where T : ContentType + { + /// + /// Initializes the model from the given list of types. + /// + /// The content types + public void Init(IEnumerable types) + { + // Add the types + foreach (var type in types) + { + Add(type); + } + + // Register runtime hooks to update the collection + App.Hooks.RegisterOnAfterSave((model) => + { + var old = this.FirstOrDefault(t => t.Id == model.Id); + + if (old != null) + { + Remove(old); + } + Add(model); + }); + } + + /// + /// Gets the content type with the given id. + /// + /// The unique id + /// The content type + public T GetById(string id) + { + return this.FirstOrDefault(t => t.Id == id); + } + } +} diff --git a/core/Piranha/Runtime/HookManager.cs b/core/Piranha/Runtime/HookManager.cs index 719acb0c0..fa014d1dd 100644 --- a/core/Piranha/Runtime/HookManager.cs +++ b/core/Piranha/Runtime/HookManager.cs @@ -1,16 +1,17 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections; using System.Collections.Generic; +using Piranha.Models; namespace Piranha.Runtime { @@ -105,37 +106,42 @@ public void Clear() /// /// Gets the hooks available for aliases. /// - public RepositoryHooks Alias { get; private set; } = new RepositoryHooks(); + public RepositoryHooks Alias { get; private set; } = new RepositoryHooks(); /// - /// Gets the hooks available for categories. + /// Gets the hooks available for media. /// - public RepositoryHooks Category { get; private set; } = new RepositoryHooks(); + public RepositoryHooks Media { get; private set; } = new RepositoryHooks(); /// - /// Gets the hooks available for media. + /// Gets the hooks available for media folders. /// - public RepositoryHooks Media { get; private set; } = new RepositoryHooks(); + public RepositoryHooks MediaFolder { get; private set; } = new RepositoryHooks(); /// - /// Gets the hooks available for media folders. + /// Gets the hooks available for pages. /// - public RepositoryHooks MediaFolder { get; private set; } = new RepositoryHooks(); + public RepositoryHooks Pages { get; private set; } = new RepositoryHooks(); /// /// Gets the hooks available for params. /// - public RepositoryHooks Param { get; private set; } = new RepositoryHooks(); + public RepositoryHooks Param { get; private set; } = new RepositoryHooks(); + + /// + /// Gets the hooks available for posts. + /// + public RepositoryHooks Posts { get; private set; } = new RepositoryHooks(); /// /// Gets the hooks available for sites. /// - public RepositoryHooks Site { get; private set; } = new RepositoryHooks(); + public RepositoryHooks Site { get; private set; } = new RepositoryHooks(); /// - /// Gets the hooks available for tags. + /// Gets the hooks available for sites. /// - public RepositoryHooks Tag { get; private set; } = new RepositoryHooks(); + public RepositoryHooks SiteContent { get; private set; } = new RepositoryHooks(); /// /// Gets the hook for slug generation. @@ -244,7 +250,7 @@ internal void RegisterOnAfterDelete(ModelDelegate hook) /// Executes the registered hooks on the given model. /// /// The model - internal void OnLoad(T model) + public void OnLoad(T model) { if (_onLoad.ContainsKey(typeof(T))) { @@ -261,7 +267,7 @@ internal void OnLoad(T model) /// Executes the registered hooks on the given model. /// /// The model - internal void OnBeforeSave(T model) + public void OnBeforeSave(T model) { if (_onBeforeSave.ContainsKey(typeof(T))) { @@ -278,7 +284,7 @@ internal void OnBeforeSave(T model) /// Executes the registered hooks on the given model. /// /// The model - internal void OnAfterSave(T model) + public void OnAfterSave(T model) { if (_onAfterSave.ContainsKey(typeof(T))) { @@ -295,7 +301,7 @@ internal void OnAfterSave(T model) /// Executes the registered hooks on the given model. /// /// The model - internal void OnBeforeDelete(T model) + public void OnBeforeDelete(T model) { if (_onBeforeDelete.ContainsKey(typeof(T))) { @@ -312,7 +318,7 @@ internal void OnBeforeDelete(T model) /// Executes the registered hooks on the given model. /// /// The model - internal void OnAfterDelete(T model) + public void OnAfterDelete(T model) { if (_onAfterDelete.ContainsKey(typeof(T))) { diff --git a/core/Piranha/Services/AliasService.cs b/core/Piranha/Services/AliasService.cs new file mode 100644 index 000000000..d0340d6eb --- /dev/null +++ b/core/Piranha/Services/AliasService.cs @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class AliasService + { + private readonly IAliasRepository _repo; + private readonly SiteService _siteService; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The current db context + /// The optional model cache + public AliasService(IAliasRepository repo, SiteService siteService, ICache cache = null) + { + _repo = repo; + _siteService = siteService; + + if ((int)App.CacheLevel > 1) + { + _cache = cache; + } + } + + /// + /// Gets all available models for the specified site. + /// + /// The optional site id + /// The available models + public async Task> GetAllAsync(Guid? siteId = null) + { + if (!siteId.HasValue) + { + var site = await _siteService.GetDefaultAsync(); + if (site != null) + { + siteId = site.Id; + } + } + + if (siteId.HasValue) + { + return await _repo.GetAll(siteId.Value); + } + return null; + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public async Task GetByIdAsync(Guid id) + { + var model = _cache?.Get(id.ToString()); + + if (model == null) + { + model = await _repo.GetById(id); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the model with the given alias url. + /// + /// The unique url + /// The optional site id + /// The model + public async Task GetByAliasUrlAsync(string url, Guid? siteId = null) + { + if (!siteId.HasValue) + { + var site = await _siteService.GetDefaultAsync(); + if (site != null) + { + siteId = site.Id; + } + } + + var id = _cache?.Get($"AliasId_{siteId}_{url}"); + Alias model = null; + + if (id.HasValue) + { + model = await GetByIdAsync(id.Value); + } + else + { + model = await _repo.GetByAliasUrl(url, siteId.Value); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the model with the given redirect url. + /// + /// The unique url + /// The optional site id + /// The model + public async Task> GetByRedirectUrlAsync(string url, Guid? siteId = null) + { + if (!siteId.HasValue) + { + var site = await _siteService.GetDefaultAsync(); + if (site != null) + { + siteId = site.Id; + } + } + return await _repo.GetByRedirectUrl(url, siteId.Value); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(Alias model) + { + // Ensure id + if (model.Id == Guid.Empty) + { + model.Id = Guid.NewGuid(); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Fix urls + if (!model.AliasUrl.StartsWith("/")) + { + model.AliasUrl = "/" + model.AliasUrl; + } + if (!model.RedirectUrl.StartsWith("/") && !model.RedirectUrl.StartsWith("http://") && !model.RedirectUrl.StartsWith("https://")) + { + model.RedirectUrl = "/" + model.RedirectUrl; + } + + // Ensure url uniqueness + var alias = await _repo.GetByAliasUrl(model.AliasUrl, model.SiteId); + if (alias != null && alias.Id != model.Id) + { + throw new ValidationException($"The AliasUrl field must be unique"); + } + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(Guid id) + { + var model = await GetByIdAsync(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(Alias model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnLoad(Alias model) + { + if (model != null) + { + App.Hooks.OnLoad(model); + + if (_cache != null) + { + _cache.Set(model.Id.ToString(), model); + _cache.Set($"AliasId_{model.SiteId}_{model.AliasUrl}", model.Id); + } + } + } + + /// + /// Removes the given model from cache. + /// + /// The model + private void RemoveFromCache(Alias model) + { + if (_cache != null) + { + _cache.Remove(model.Id.ToString()); + _cache.Remove($"AliasId_{model.SiteId}_{model.AliasUrl}"); + } + } + } +} diff --git a/core/Piranha/Services/ArchiveService.cs b/core/Piranha/Services/ArchiveService.cs new file mode 100644 index 000000000..b52ee6ab9 --- /dev/null +++ b/core/Piranha/Services/ArchiveService.cs @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class ArchiveService + { + private readonly IArchiveRepository _repo; + private readonly PageService _pageService; + private readonly ParamService _paramService; + private readonly PostService _postService; + + /// + /// Default constructor. + /// + /// The main repository + /// The page service + /// The param service + /// The post service + public ArchiveService(IArchiveRepository repo, PageService pageService, ParamService paramService, PostService postService) + { + _repo = repo; + _pageService = pageService; + _paramService = paramService; + _postService = postService; + } + + [Obsolete("Please refer to GetById(archiveId, page, categoryId, tagId, year, month, pageSize)", true)] + public T GetById(Guid id, int? page = 1, Guid? categoryId = null, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage + { + return null; + } + + [Obsolete("Please refer to GetById(archiveId, page, categoryId, tagId, year, month, pageSize)", true)] + public T GetById(Guid id, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage + { + return null; + } + + [Obsolete("Please refer to GetById(archiveId, page, categoryId, tagId, year, month, pageSize)", true)] + public T GetByCategoryId(Guid id, Guid categoryId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage + { + return null; + } + + [Obsolete("Please refer to GetById(archiveId, page, categoryId, tagId, year, month, pageSize)", true)] + public T GetByTagId(Guid id, Guid tagId, int? page = 1, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage + { + return null; + } + + /// + /// Gets the specified post archive for the specified filter. + /// + /// The archive page id + /// The current page of the archive + /// The optional category id + /// The optional tag id + /// The optional year + /// The optional month + /// The optional page size + /// The archive model type + /// The archive model + public async Task GetByIdAsync(Guid archiveId, int? currentPage = 1, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null, int? pageSize = null) where T : Models.ArchivePage + { + // Get the requested blog page + var model = await _pageService.GetByIdAsync(archiveId); + + if (model != null) + { + // Ensure page size + if (!pageSize.HasValue) + { + using (var config = new Config(_paramService)) + { + pageSize = config.ArchivePageSize; + + if (!pageSize.HasValue || pageSize == 0) + { + pageSize = 5; + } + } + } + + // Set basic fields + model.Archive = new Models.PostArchive(); + model.Route = model.Route ?? "/archive"; + model.Archive.Year = year; + model.Archive.Month = month; + + // Get paging info + model.Archive.TotalPosts = await _repo.GetPostCount(archiveId, categoryId, tagId, year, month); + model.Archive.TotalPages = Math.Max(Convert.ToInt32(Math.Ceiling((double)model.Archive.TotalPosts / pageSize.Value)), 1); + model.Archive.CurrentPage = Math.Min(Math.Max(1, currentPage.HasValue ? currentPage.Value : 1), model.Archive.TotalPages); + + // Get the id of the current posts + var posts = await _repo.GetPosts(archiveId, pageSize.Value, model.Archive.CurrentPage, categoryId, tagId, year, month); + + // Get the posts + foreach (var postId in posts) + { + var post = await _postService.GetByIdAsync(postId); + + if (post != null) + { + model.Archive.Posts.Add(post); + } + } + return model; + } + return null; + } + } +} diff --git a/core/Piranha/Services/ContentFactory.cs b/core/Piranha/Services/ContentFactory.cs new file mode 100644 index 000000000..e405a595a --- /dev/null +++ b/core/Piranha/Services/ContentFactory.cs @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Piranha.Models; + +namespace Piranha.Services +{ + /// + /// The content factory is responsible for creating models and + /// initializing them after they have been loaded. + /// + public class ContentFactory : IContentFactory + { + private readonly IServiceProvider _services; + + /// + /// Default constructor. + /// + /// The current service provider + public ContentFactory(IServiceProvider services) + { + _services = services; + } + + /// + /// Creates and initializes a new content model. + /// + /// The content type + /// The model type + /// The new model + public T Create(ContentType type) where T : Content + { + if (typeof(IDynamicModel).IsAssignableFrom(typeof(T))) + { + return CreateDynamicModel(type); + } + else + { + return CreateModel(type); + } + } + + /// + /// Creates a new dynamic region. + /// + /// The content type + /// The region id + /// The new region value + public object CreateDynamicRegion(ContentType type, string regionId) + { + using (var scope = _services.CreateScope()) + { + var region = type.Regions.FirstOrDefault(r => r.Id == regionId); + + if (region != null) + { + return CreateDynamicRegion(scope, region); + } + return null; + } + } + + /// + /// Creates and initializes a new block of the specified type. + /// + /// The type name + /// The new block + public object CreateBlock(string typeName) + { + var blockType = App.Blocks.GetByType(typeName); + + if (blockType != null) + { + using (var scope = _services.CreateScope()) + { + var block = Activator.CreateInstance(blockType.Type); + + foreach (var prop in blockType.Type.GetProperties(App.PropertyBindings)) + { + if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) + { + var field = Activator.CreateInstance(prop.PropertyType); + InitField(scope, field); + prop.SetValue(block, field); + } + } + return block; + } + } + return null; + } + + /// + /// Creates and initializes a new dynamic model. + /// + /// The content type + /// The model type + /// The new model + private T CreateDynamicModel(ContentType type) where T : Content + { + using (var scope = _services.CreateScope()) + { + // Create a new instance of the specified type + var model = Activator.CreateInstance(); + + model.TypeId = type.Id; + + foreach (var regionType in type.Regions) + { + object region = null; + + if (!regionType.Collection) + { + // Create and initialize the region + region = CreateDynamicRegion(scope, regionType); + } + else + { + // Create a region item without initialization for type reference + var listObject = CreateDynamicRegion(scope, regionType, false); + + if (listObject != null) + { + // Create the region list + region = Activator.CreateInstance(typeof(RegionList<>).MakeGenericType(listObject.GetType())); + ((IRegionList)region).Model = (IDynamicModel)model; + ((IRegionList)region).TypeId = type.Id; + ((IRegionList)region).RegionId = regionType.Id; + } + } + + if (region != null) + { + ((IDictionary)((IDynamicModel)model).Regions).Add(regionType.Id, region); + } + } + return model; + } + } + + /// + /// Creates and initializes a new dynamic model. + /// + /// The content type + /// The model type + /// The new model + private T CreateModel(ContentType type) where T : Content + { + using (var scope = _services.CreateScope()) + { + var modelType = typeof(T); + + if (!typeof(IContentInfo).IsAssignableFrom(modelType)) + { + modelType = Type.GetType(type.CLRType); + + if (modelType != typeof(T) && !typeof(T).IsAssignableFrom(modelType)) + { + return null; + } + } + + // Create a new instance of the specified type + var model = (T)Activator.CreateInstance(modelType); + + model.TypeId = type.Id; + + foreach (var regionType in type.Regions) + { + object region = null; + + if (!regionType.Collection) + { + // Create and initialize the region + region = CreateRegion(scope, model, modelType, regionType); + } + else + { + var property = modelType.GetProperty(regionType.Id, App.PropertyBindings); + + if (property != null) + { + region = Activator.CreateInstance(typeof(List<>).MakeGenericType(property.PropertyType.GetGenericArguments()[0])); + } + } + + if (region != null) + { + modelType.SetPropertyValue(regionType.Id, model, region); + } + } + return model; + } + } + + /// + /// Initializes the given dynamic model. + /// + /// The model + /// The content type + /// The model type + /// The initialized model + public T InitDynamic(T model, ContentType type) where T : IDynamicModel + { + using (var scope = _services.CreateScope()) + { + foreach (var regionType in type.Regions) + { + // Try to get the region from the model + if (((IDictionary)model.Regions).TryGetValue(regionType.Id, out var region)) + { + if (!regionType.Collection) + { + // Initialize it + InitDynamicRegion(scope, region, regionType); + } + else + { + // This region was a collection. Initialize all items + foreach (var item in (IList)region) + { + InitDynamicRegion(scope, item, regionType); + } + } + } + } + + if (model is IBlockModel) + { + foreach (var block in ((IBlockModel)model).Blocks) + { + InitBlock(scope, block); + } + } + } + return model; + } + + /// + /// Initializes the given model. + /// + /// The model + /// The content type + /// The model type + /// The initialized model + public T Init(T model, ContentType type) where T : Content + { + if (model is IDynamicModel) + { + throw new ArgumentException("For dynamic models InitDynamic should be used."); + } + + using (var scope = _services.CreateScope()) + { + foreach (var regionType in type.Regions) + { + // Try to get the region from the model + var region = model.GetType().GetPropertyValue(regionType.Id, model); + + if (region != null) + { + if (!regionType.Collection) + { + // Initialize it + InitRegion(scope, region, regionType); + } + else + { + // This region was a collection. Initialize all items + foreach (var item in (IList)region) + { + InitRegion(scope, item, regionType); + } + } + } + } + + if (!(model is IContentInfo) && model is IBlockModel) + { + foreach (var block in ((IBlockModel)model).Blocks) + { + InitBlock(scope, block); + } + } + } + return model; + } + + /// + /// Initializes all fields in the given dynamic region. + /// + /// The current service scope + /// The region + /// The region type + private void InitDynamicRegion(IServiceScope scope, object region, RegionType regionType) + { + if (region != null) + { + if (regionType.Fields.Count == 1) + { + // This region only has one field, that means + // the region is in fact a field. + InitField(scope, region); + } + else + { + // Initialize all fields + foreach (var fieldType in regionType.Fields) + { + if (((IDictionary)region).TryGetValue(fieldType.Id, out var field)) + { + InitField(scope, field); + } + } + } + } + } + + /// + /// Initializes all fields in the given region. + /// + /// The current service scope + /// The region + /// The region type + private void InitRegion(IServiceScope scope, object region, RegionType regionType) + { + if (region != null) + { + if (regionType.Fields.Count == 1) + { + // This region only has one field, that means + // the region is in fact a field. + InitField(scope, region); + } + else + { + var type = region.GetType(); + + // Initialize all fields + foreach (var fieldType in regionType.Fields) + { + var field = type.GetPropertyValue(fieldType.Id, region); + + if (field != null) + { + InitField(scope, field); + } + } + } + } + } + + /// + /// Initializes all fields in the given block. + /// + /// The current service scope + /// The block + /// The region type + private void InitBlock(IServiceScope scope, Extend.Block block) + { + if (block != null) + { + var type = block.GetType(); + var properties = block.GetType().GetProperties(App.PropertyBindings); + + foreach (var property in properties) + { + if (typeof(Extend.IField).IsAssignableFrom(property.PropertyType)) + { + var field = property.GetValue(block); + + if (field != null) + { + InitField(scope, field); + } + } + } + } + } + + /// + /// Creates a new dynamic region. + /// + /// The current service scope + /// The region type + /// If fields should be initialized + /// The created region + private object CreateDynamicRegion(IServiceScope scope, RegionType regionType, bool initFields = true) + { + if (regionType.Fields.Count == 1) + { + var field = CreateField(regionType.Fields[0]); + if (field != null) + { + if (initFields) + { + InitField(scope, field); + } + return field; + } + } + else + { + var reg = new ExpandoObject(); + + foreach (var fieldType in regionType.Fields) + { + var field = CreateField(fieldType); + if (field != null) + { + if (initFields) + { + InitField(scope, field); + } + ((IDictionary)reg).Add(fieldType.Id, field); + } + } + return reg; + } + return null; + } + + /// + /// Creates a new region. + /// + /// The current service scope + /// The region type + /// If fields should be initialized + /// The created region + private object CreateRegion(IServiceScope scope, object model, Type modelType, RegionType regionType, bool initFields = true) + { + if (regionType.Fields.Count == 1) + { + var field = CreateField(regionType.Fields[0]); + if (field != null) + { + if (initFields) + { + InitField(scope, field); + } + return field; + } + } + else + { + var property = modelType.GetProperty(regionType.Id, App.PropertyBindings); + + if (property != null) + { + var reg = Activator.CreateInstance(property.PropertyType); + + foreach (var fieldType in regionType.Fields) + { + var field = CreateField(fieldType); + if (field != null) + { + if (initFields) + { + InitField(scope, field); + } + reg.GetType().SetPropertyValue(fieldType.Id, reg, field); + } + } + return reg; + } + } + return null; + } + + /// + /// Creates a new instance of the given field type. + /// + /// The field type + /// The new instance + protected object CreateField(FieldType fieldType) + { + var type = App.Fields.GetByType(fieldType.Type); + + if (type != null) + { + return Activator.CreateInstance(type.Type); + } + return null; + } + + /// + /// Initializes the given field. + /// + /// The current service scope + /// The field + /// The initialized field + protected object InitField(IServiceScope scope, object field) + { + var init = field.GetType().GetMethod("Init"); + + if (init != null) + { + var param = new List(); + + foreach (var p in init.GetParameters()) + { + param.Add(scope.ServiceProvider.GetService(p.ParameterType)); + } + + // Check for async + if (typeof(Task).IsAssignableFrom(init.ReturnType)) + { + Task.Run(async () => await (Task)init.Invoke(field, param.ToArray())).Wait(); + } + else + { + init.Invoke(field, param.ToArray()); + } + } + return field; + } + } +} \ No newline at end of file diff --git a/core/Piranha/Services/ContentService.cs b/core/Piranha/Services/ContentService.cs deleted file mode 100644 index 96cf6394a..000000000 --- a/core/Piranha/Services/ContentService.cs +++ /dev/null @@ -1,1079 +0,0 @@ -/* - * Copyright (c) 2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * https://github.com/piranhacms/piranha.core - * - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using AutoMapper; -using Piranha.Data; -using Piranha.Models; - -namespace Piranha.Services -{ - public class ContentService : IContentService - where TContent : Content - where TField : ContentField - where TModelBase : Content - { - // - // Members - protected readonly IServiceProvider _services; - protected readonly IMapper _mapper; - - /// - /// Default constructor. - /// - /// The current service provider - /// The AutoMapper instance to use - public ContentService(IServiceProvider services, IMapper mapper) - { - _services = services; - _mapper = mapper; - } - - /// - /// Creates and initializes a new content model. - /// - /// The model type - /// The content type id - /// The model - public T Create(Models.ContentType contentType) where T : Models.Content - { - using (var scope = _services.CreateScope()) - { - if (contentType != null) - { - var modelType = typeof(T); - - if (!typeof(IDynamicModel).IsAssignableFrom(modelType) && !typeof(IContentInfo).IsAssignableFrom(modelType)) - { - modelType = Type.GetType(contentType.CLRType); - - if (modelType != typeof(T) && !typeof(T).IsAssignableFrom(modelType)) - return null; - } - - var model = (T)Activator.CreateInstance(modelType); - model.TypeId = contentType.Id; - - if (!(model is IContentInfo)) - { - if (model is IDynamicModel) - { - var dynModel = (IDynamicModel)(object)model; - - foreach (var region in contentType.Regions) - { - object value = null; - - if (region.Collection) - { - var reg = CreateDynamicRegion(scope, region); - - if (reg != null) - { - value = Activator.CreateInstance(typeof(RegionList<>).MakeGenericType(reg.GetType())); - ((IRegionList)value).Model = (IDynamicModel)model; - ((IRegionList)value).TypeId = contentType.Id; - ((IRegionList)value).RegionId = region.Id; - } - } - else - { - value = CreateDynamicRegion(scope, region); - } - - if (value != null) - ((IDictionary)dynModel.Regions).Add(region.Id, value); - } - } - else - { - var type = model.GetType(); - - foreach (var region in contentType.Regions) - { - if (!region.Collection) - { - var prop = type.GetProperty(region.Id, App.PropertyBindings); - if (prop != null) - { - prop.SetValue(model, CreateRegion(scope, prop.PropertyType, region)); - } - } - } - } - } - return model; - } - } - return null; - } - - /// - /// Creates a new region. - /// - /// The content type id - /// The region id - /// The new region value - public object CreateDynamicRegion(Models.ContentType contentType, string regionId) - { - using (var scope = _services.CreateScope()) - { - var region = contentType.Regions.SingleOrDefault(r => r.Id == regionId); - - if (region != null) - return CreateDynamicRegion(scope, region); - return null; - } - } - - public object CreateBlock(string typeName) - { - var blockType = App.Blocks.GetByType(typeName); - - if (blockType != null) - { - using (var scope = _services.CreateScope()) - { - var block = Activator.CreateInstance(blockType.Type); - - foreach (var prop in blockType.Type.GetProperties(App.PropertyBindings)) - { - if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) - { - var field = Activator.CreateInstance(prop.PropertyType); - InitField(scope, field); - prop.SetValue(block, field); - } - } - return block; - } - } - return null; - } - - /// - /// Creates a dynamic region. - /// - /// The value type - /// The content type - /// The region id - /// The region value - public TValue CreateRegion(Models.ContentType contentType, string regionId) - { - using (var scope = _services.CreateScope()) - { - var region = contentType.Regions.SingleOrDefault(r => r.Id == regionId); - - if (region != null) - return (TValue)CreateRegion(scope, typeof(TValue), region); - return default(TValue); - } - } - - /// - /// Gets the enumerator for the given region collection. - /// - /// The model type - /// The model - /// The region id - /// The enumerator - public IEnumerable GetEnumerable(T model, string regionId) where T : Models.Content - { - object value = null; - - if (model is Models.IDynamicModel) - { - value = ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; - } - else - { - value = model.GetType().GetProperty(regionId, App.PropertyBindings).GetValue(model); - } - if (value is IEnumerable) - return (IEnumerable)value; - return null; - } - - /// - /// Gets the region with the given key. - /// - /// The model type - /// The model - /// The region id - /// The region - public object GetRegion(T model, string regionId) where T : Models.Content - { - if (model is Models.IDynamicModel) - { - return ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; - } - else - { - return model.GetType().GetProperty(regionId, App.PropertyBindings).GetValue(model); - } - } - - /// - /// Checks if the given model has a region with the specified id. - /// - /// The model type - /// The model - /// The region id - /// If the region exists - public bool HasRegion(T model, string regionId) where T : Models.Content - { - if (model is Models.IDynamicModel) - { - return ((IDictionary)((Models.IDynamicModel)(object)model).Regions).ContainsKey(regionId); - } - else - { - return model.GetType().GetProperty(regionId, App.PropertyBindings) != null; - } - } - - /// - /// Loads the given data into a new model. - /// - /// The model type - /// The content entity - /// The content type - /// The page model - public T Transform(TContent content, Models.ContentType type, Action process = null) - where T : Models.Content, TModelBase - { - using (var scope = _services.CreateScope()) - { - if (type != null) - { - var modelType = typeof(T); - - if (!typeof(Models.IDynamicModel).IsAssignableFrom(modelType) && !typeof(Models.IContentInfo).IsAssignableFrom(modelType)) - { - modelType = Type.GetType(type.CLRType); - - if (modelType != typeof(T) && !typeof(T).IsAssignableFrom(modelType)) - return null; - } - - // Create an initialized model - var model = Create(type); - - // Map basic fields - _mapper.Map(content, model); - - if (model is Models.RoutedContent) - { - var routeModel = (Models.RoutedContent)(object)model; - - // Map route (if available) - if (string.IsNullOrWhiteSpace(routeModel.Route) && type.Routes.Count > 0) - routeModel.Route = type.Routes.First(); - } - - // Map regions - if (!(model is IContentInfo)) - { - var currentRegions = type.Regions.Select(r => r.Id).ToArray(); - - foreach (var regionKey in currentRegions) - { - var region = type.Regions.Single(r => r.Id == regionKey); - var fields = content.Fields.Where(f => f.RegionId == regionKey).OrderBy(f => f.SortOrder).ToList(); - - if (!region.Collection) - { - foreach (var fieldDef in region.Fields) - { - var field = fields.SingleOrDefault(f => f.FieldId == fieldDef.Id && f.SortOrder == 0); - - if (field != null) - { - if (region.Fields.Count == 1) - { - SetSimpleValue(scope, model, regionKey, field); - break; - } - else - { - SetComplexValue(scope, model, regionKey, fieldDef.Id, field); - } - } - } - } - else - { - var fieldCount = content.Fields.Where(f => f.RegionId == regionKey).Select(f => f.SortOrder).DefaultIfEmpty(-1).Max() + 1; - var sortOrder = 0; - - while (fieldCount > sortOrder) - { - if (region.Fields.Count == 1) - { - var field = fields.SingleOrDefault(f => f.FieldId == region.Fields[0].Id && f.SortOrder == sortOrder); - if (field != null) - AddSimpleValue(scope, model, regionKey, field); - } - else - { - AddComplexValue(scope, model, type, regionKey, fields.Where(f => f.SortOrder == sortOrder).ToList()); - } - sortOrder++; - } - } - } - } - process?.Invoke(content, model); - - return model; - } - } - return null; - } - - /// - /// Transforms the given model into content data. - /// - /// The model - /// The conten type - /// The optional dest object - /// The content data - public TContent Transform(T model, Models.ContentType type, TContent dest = null) - where T : Models.Content, TModelBase - { - var content = dest == null ? Activator.CreateInstance() : dest; - - // Map id - if (model.Id != Guid.Empty) - { - content.Id = model.Id; - } - else - { - content.Id = model.Id = Guid.NewGuid(); - } - content.Created = DateTime.Now; - - // Map basic fields - _mapper.Map(model, content); - - // Map regions - var currentRegions = type.Regions.Select(r => r.Id).ToArray(); - - foreach (var regionKey in currentRegions) - { - // Check that the region exists in the current model - if (HasRegion(model, regionKey)) - { - var regionType = type.Regions.Single(r => r.Id == regionKey); - - if (!regionType.Collection) - { - MapRegion(model, content, GetRegion(model, regionKey), regionType, regionKey); - } - else - { - var items = new List(); - var sortOrder = 0; - foreach (var region in GetEnumerable(model, regionKey)) - { - var fields = MapRegion(model, content, region, regionType, regionKey, sortOrder++); - - if (fields.Count > 0) - items.AddRange(fields); - } - // Now delete removed collection items - var removedFields = content.Fields - .Where(f => f.RegionId == regionKey && !items.Contains(f.Id)) - .ToList(); - foreach (var removed in removedFields) - content.Fields.Remove(removed); - } - } - } - return content; - } - - /// - /// Transforms the given block data into block models. - /// - /// The data - /// The transformed blocks - public IList TransformBlocks(IEnumerable blocks) where T : IContentBlock - { - using (var scope = _services.CreateScope()) - { - var models = new List(); - - foreach (var contentBlock in blocks) - { - var block = contentBlock.Block; - var blockType = App.Blocks.GetByType(block.CLRType); - - if (blockType != null) - { - var model = (Extend.Block)Activator.CreateInstance(blockType.Type); - model.Id = block.Id; - - foreach (var field in block.Fields) - { - var prop = model.GetType().GetProperty(field.FieldId, App.PropertyBindings); - - if (prop != null) - { - var type = App.Fields.GetByType(field.CLRType); - var val = (Extend.IField)App.DeserializeObject(field.Value, type.Type); - - if (val != null) - { - var init = val.GetType().GetMethod("Init"); - - if (init != null) - { - var param = new List(); - - foreach (var p in init.GetParameters()) - { - param.Add(scope.ServiceProvider.GetService(p.ParameterType)); - } - - // Check for async - if (typeof(Task).IsAssignableFrom(init.ReturnType)) - { - Task.Run(async () => await (Task)init.Invoke(val, param.ToArray())).Wait(); - } - else - { - init.Invoke(val, param.ToArray()); - } - } - } - prop.SetValue(model, val); - } - } - - if (contentBlock.ParentId.HasValue) - { - var parent = blocks.FirstOrDefault(m => m.Id == contentBlock.ParentId.Value); - - if (parent != null) - { - ((Extend.BlockGroup)models.First(m => m.Id == parent.BlockId)) - .Items.Add(model); - } - else - { - throw new InvalidOperationException("Block parent is missing"); - } - } - else - { - models.Add(model); - } - } - } - return models; - } - } - - /// - /// Transforms the given block data into block models. - /// - /// The data - /// The transformed blocks - public IList TransformBlocks(IEnumerable blocks) - { - using (var scope = _services.CreateScope()) - { - var models = new List(); - - foreach (var block in blocks) - { - var blockType = App.Blocks.GetByType(block.CLRType); - - if (blockType != null) - { - var model = (Extend.Block)Activator.CreateInstance(blockType.Type); - model.Id = block.Id; - - foreach (var field in block.Fields) - { - var prop = model.GetType().GetProperty(field.FieldId, App.PropertyBindings); - - if (prop != null) - { - var type = App.Fields.GetByType(field.CLRType); - var val = (Extend.IField)App.DeserializeObject(field.Value, type.Type); - - if (val != null) - { - var init = val.GetType().GetMethod("Init"); - - if (init != null) - { - var param = new List(); - - foreach (var p in init.GetParameters()) - { - param.Add(scope.ServiceProvider.GetService(p.ParameterType)); - } - - // Check for async - if (typeof(Task).IsAssignableFrom(init.ReturnType)) - { - Task.Run(async () => await (Task)init.Invoke(val, param.ToArray())).Wait(); - } - else - { - init.Invoke(val, param.ToArray()); - } - } - } - prop.SetValue(model, val); - } - } - models.Add(model); - } - } - return models; - } - } - - /// - /// Transforms the given blocks to the internal data model. - /// - /// The blocks - /// The data model - public IList TransformBlocks(IList models) where T : IContentBlock - { - var blocks = new List(); - - if (models != null) - { - for (var n = 0; n < models.Count; n++) - { - var type = App.Blocks.GetByType(models[n].GetType().FullName); - - if (type != null) - { - var contentBlock = Activator.CreateInstance(); - contentBlock.Id = Guid.NewGuid(); - contentBlock.SortOrder = n; - - var block = new Block() - { - Id = models[n].Id != Guid.Empty ? models[n].Id : Guid.NewGuid(), - CLRType = models[n].GetType().FullName, - Created = DateTime.Now, - LastModified = DateTime.Now - }; - - foreach (var prop in models[n].GetType().GetProperties(App.PropertyBindings)) - { - if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) - { - // Only save fields to the database - var field = new BlockField() - { - Id = Guid.NewGuid(), - BlockId = block.Id, - FieldId = prop.Name, - SortOrder = 0, - CLRType = prop.PropertyType.FullName, - Value = App.SerializeObject(prop.GetValue(models[n]), prop.PropertyType) - }; - block.Fields.Add(field); - } - } - contentBlock.Block = block; - blocks.Add(contentBlock); - - if (typeof(Extend.BlockGroup).IsAssignableFrom(models[n].GetType())) - { - var blockItems = TransformBlocks(((Extend.BlockGroup)models[n]).Items); - - if (blockItems.Count() > 0) - { - foreach (var item in blockItems) - { - item.ParentId = contentBlock.Id; - } - blocks.AddRange(blockItems); - } - } - } - } - } - return blocks; - } - - /// - /// Transforms the given blocks to the internal data model. - /// - /// The blocks - /// The data model - public IList TransformBlocks(IList models) - { - var blocks = new List(); - - if (models != null) - { - for (var n = 0; n < models.Count; n++) - { - var type = App.Blocks.GetByType(models[n].GetType().FullName); - - if (type != null) - { - var block = new Block() - { - Id = models[n].Id != Guid.Empty ? models[n].Id : Guid.NewGuid(), - CLRType = models[n].GetType().FullName, - Created = DateTime.Now, - LastModified = DateTime.Now - }; - - foreach (var prop in models[n].GetType().GetProperties(App.PropertyBindings)) - { - if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) - { - // Only save fields to the database - var field = new BlockField() - { - Id = Guid.NewGuid(), - BlockId = block.Id, - FieldId = prop.Name, - SortOrder = 0, - CLRType = prop.PropertyType.FullName, - Value = App.SerializeObject(prop.GetValue(models[n]), prop.PropertyType) - }; - block.Fields.Add(field); - } - } - blocks.Add(block); - - if (typeof(Extend.BlockGroup).IsAssignableFrom(models[n].GetType())) - { - var blockItems = TransformBlocks(((Extend.BlockGroup)models[n]).Items); - - if (blockItems.Count() > 0) - { - foreach (var item in blockItems) - { - item.ParentId = block.Id; - } - blocks.AddRange(blockItems); - } - } - } - } - } - return blocks; - } - - /// - /// Maps a region to the given data entity. - /// - /// The model type - /// The model - /// The content entity - /// The region to map - /// The region type - /// The region id - /// The optional sort order - public IList MapRegion(T model, TContent content, object region, Models.RegionType regionType, string regionId, int sortOrder = 0) where T : Models.Content - { - var items = new List(); - - // Now map all of the fields - for (var n = 0; n < regionType.Fields.Count; n++) - { - var fieldDef = regionType.Fields[n]; - var fieldType = App.Fields.GetByShorthand(fieldDef.Type); - if (fieldType == null) - fieldType = App.Fields.GetByType(fieldDef.Type); - - if (fieldType != null) - { - object fieldValue = null; - if (regionType.Fields.Count == 1) - { - // Get the field value for simple region - fieldValue = region; - } - else - { - // Get the field value for complex region - fieldValue = GetComplexValue(region, fieldDef.Id); - } - - if (fieldValue != null) - { - // Check that the returned value matches the type specified - // for the page type, otherwise deserialization won't work - // when the model is retrieved from the database. - if (fieldValue.GetType() != fieldType.Type) - throw new ArgumentException("Given field value does not match the configured type"); - - // Check if we have the current field in the database already - var field = content.Fields - .SingleOrDefault(f => f.RegionId == regionId && f.FieldId == fieldDef.Id && f.SortOrder == sortOrder); - - // If not, create a new field - if (field == null) - { - field = Activator.CreateInstance(); - field.Id = Guid.NewGuid(); - field.RegionId = regionId; - field.FieldId = fieldDef.Id; - - content.Fields.Add(field); - } - - // Update field info & value - field.CLRType = fieldType.TypeName; - field.SortOrder = sortOrder; - field.Value = App.SerializeObject(fieldValue, fieldType.Type); - - items.Add(field.Id); - } - } - } - return items; - } - - /// - /// Sets the value of a simple single field region. - /// - /// The model type - /// The model - /// The region id - /// The field - private void SetSimpleValue(IServiceScope scope, T model, string regionId, TField field) where T : Models.Content - { - if (model is Models.IDynamicModel) - { - ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId] = - DeserializeValue(scope, field); - } - else - { - var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); - - if (regionProp != null) - { - regionProp.SetValue(model, DeserializeValue(scope, field)); - } - } - } - - /// - /// Adds a simple single field value to a collection region. - /// - /// The model type - /// The model - /// The region id - /// The field - private void AddSimpleValue(IServiceScope scope, T model, string regionId, TField field) where T : Models.Content - { - if (model is Models.IDynamicModel) - { - ((IList)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]).Add( - DeserializeValue(scope, field)); - } - else - { - var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); - - if (regionProp != null) - { - ((IList)regionProp.GetValue(model)).Add(DeserializeValue(scope, field)); - } - } - } - - /// - /// Sets the value of a complex region. - /// - /// The model - /// The model - /// The region id - /// The field id - /// The field - private void SetComplexValue(IServiceScope scope, T model, string regionId, string fieldId, TField field) where T : Models.Content - { - if (model is Models.IDynamicModel) - { - ((IDictionary)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId])[fieldId] = - DeserializeValue(scope, field); - } - else - { - var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); - - if (regionProp != null) - { - var obj = regionProp.GetValue(model); - if (obj != null) - { - var fieldProp = obj.GetType().GetProperty(fieldId, App.PropertyBindings); - - if (fieldProp != null) - { - fieldProp.SetValue(obj, DeserializeValue(scope, field)); - } - } - } - } - } - - /// - /// Adds a complex region to a collection region. - /// - /// The model type - /// The model - /// The region id - /// The field - private void AddComplexValue(IServiceScope scope, T model, Models.ContentType contentType, string regionId, IList fields) where T : Models.Content - { - if (fields.Count > 0) - { - if (model is Models.IDynamicModel) - { - var list = (IList)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; - var obj = CreateDynamicRegion(contentType, regionId); - - foreach (var field in fields) - { - if (((IDictionary)obj).ContainsKey(field.FieldId)) - { - ((IDictionary)obj)[field.FieldId] = - DeserializeValue(scope, field); - } - } - list.Add(obj); - } - else - { - var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); - - if (regionProp != null) - { - var list = (IList)regionProp.GetValue(model); - var obj = Activator.CreateInstance(list.GetType().GenericTypeArguments.First()); - - foreach (var field in fields) - { - var fieldProp = obj.GetType().GetProperty(field.FieldId, App.PropertyBindings); - if (fieldProp != null) - { - fieldProp.SetValue(obj, DeserializeValue(scope, field)); - } - } - list.Add(obj); - } - } - } - } - - /// - /// Deserializes the given field value. - /// - /// The page field - /// The value - private object DeserializeValue(IServiceScope scope, TField field) - { - var type = App.Fields.GetByType(field.CLRType); - - if (type != null) - { - var val = (Extend.IField)App.DeserializeObject(field.Value, type.Type); - if (val != null) - { - var init = val.GetType().GetMethod("Init"); - - if (init != null) - { - var param = new List(); - - foreach (var p in init.GetParameters()) - { - param.Add(scope.ServiceProvider.GetService(p.ParameterType)); - } - - // Check for async - if (typeof(Task).IsAssignableFrom(init.ReturnType)) - { - Task.Run(async () => await (Task)init.Invoke(val, param.ToArray())).Wait(); - } - else - { - init.Invoke(val, param.ToArray()); - } - } - } - return val; - } - return null; - } - - /// - /// Gets a field value from a complex region. - /// - /// The region - /// The field id - /// The value - private object GetComplexValue(object region, string fieldId) - { - if (region is ExpandoObject) - { - return ((IDictionary)region)[fieldId]; - } - else - { - var fieldProp = region.GetType().GetProperty(fieldId, App.PropertyBindings); - - if (fieldProp != null) - { - return fieldProp.GetValue(region); - } - return null; - } - } - - /// - /// Creates a dynamic region value. - /// - /// The region type - /// The created value - private object CreateDynamicRegion(IServiceScope scope, RegionType region) - { - if (region.Fields.Count == 1) - { - var type = App.Fields.GetByShorthand(region.Fields[0].Type); - if (type == null) - type = App.Fields.GetByType(region.Fields[0].Type); - - if (type != null) - return InitField(scope, Activator.CreateInstance(type.Type)); - } - else - { - var reg = new ExpandoObject(); - - foreach (var field in region.Fields) - { - var type = GetFieldType(field); - - if (type != null) - ((IDictionary)reg).Add(field.Id, InitField(scope, Activator.CreateInstance(type.Type))); - } - return reg; - } - return null; - } - - /// - /// Creates a region value. - /// - /// The region type - /// The region - /// The created value - private object CreateRegion(IServiceScope scope, Type regionType, RegionType region) - { - if (region.Fields.Count == 1) - { - return CreateField(scope, region.Fields[0], regionType); - } - else - { - var reg = Activator.CreateInstance(regionType); - var type = reg.GetType(); - - foreach (var field in region.Fields) - { - var fieldType = GetFieldType(field); - - if (type != null) - { - var prop = type.GetProperty(field.Id, App.PropertyBindings); - - if (prop != null && fieldType.Type == prop.PropertyType) - { - prop.SetValue(reg, InitField(scope, Activator.CreateInstance(fieldType.Type))); - } - } - } - return reg; - } - } - - /// - /// Gets the registered field type. - /// - /// The field - /// The type, null if not found - private Runtime.AppField GetFieldType(FieldType field) - { - var type = App.Fields.GetByShorthand(field.Type); - if (type == null) - type = App.Fields.GetByType(field.Type); - return type; - } - - /// - /// Creates a field of the given type. - /// - /// The field type - /// The expected type - /// - private object CreateField(IServiceScope scope, FieldType field, Type expectedType = null) - { - var type = GetFieldType(field); - - if (type != null && (expectedType == null || type.Type == expectedType)) - { - return InitField(scope, Activator.CreateInstance(type.Type)); - } - return null; - } - - private object InitField(IServiceScope scope, object field) - { - var init = field.GetType().GetMethod("Init"); - - if (init != null) - { - var param = new List(); - - foreach (var p in init.GetParameters()) - { - param.Add(scope.ServiceProvider.GetService(p.ParameterType)); - } - - // Check for async - if (typeof(Task).IsAssignableFrom(init.ReturnType)) - { - Task.Run(async () => await (Task)init.Invoke(field, param.ToArray())).Wait(); - } - else - { - init.Invoke(field, param.ToArray()); - } - } - return field; - } - } -} diff --git a/core/Piranha/Services/IContentFactory.cs b/core/Piranha/Services/IContentFactory.cs new file mode 100644 index 000000000..3ba08bdd5 --- /dev/null +++ b/core/Piranha/Services/IContentFactory.cs @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using Piranha.Models; + +namespace Piranha.Services +{ + public interface IContentFactory + { + /// + /// Creates and initializes a new content model. + /// + /// The content type + /// The model type + /// The new model + T Create(ContentType type) where T : Content; + + /// + /// Creates a new dynamic region. + /// + /// The content type + /// The region id + /// The new region value + object CreateDynamicRegion(ContentType type, string regionId); + + /// + /// Creates and initializes a new block of the specified type. + /// + /// The type name + /// The new block + object CreateBlock(string typeName); + + /// + /// Initializes the given model. + /// + /// The model + /// The content type + /// The model type + /// The initialized model + T Init(T model, ContentType type) where T : Content; + + /// + /// Initializes the given dynamic model. + /// + /// The model + /// The content type + /// The model type + /// The initialized model + T InitDynamic(T model, ContentType type) where T : IDynamicModel; + } +} \ No newline at end of file diff --git a/core/Piranha/Repositories/MediaRepository.cs b/core/Piranha/Services/MediaService.cs similarity index 50% rename from core/Piranha/Repositories/MediaRepository.cs rename to core/Piranha/Services/MediaService.cs index d70fdb7af..982c8a627 100644 --- a/core/Piranha/Repositories/MediaRepository.cs +++ b/core/Piranha/Services/MediaService.cs @@ -1,47 +1,50 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Piranha.Data; +using Piranha.Models; +using Piranha.Repositories; -namespace Piranha.Repositories +namespace Piranha.Services { - public class MediaRepository : IMediaRepository + public class MediaService { - private readonly IDb _db; - private readonly Api _api; + private readonly IMediaRepository _repo; + private readonly ParamService _paramService; private readonly IStorage _storage; - private readonly ICache _cache; private readonly IImageProcessor _processor; + private readonly ICache _cache; private static object ScaleMutex = new object(); private const string MEDIA_STRUCTURE = "MediaStructure"; /// /// Default constructor. /// - /// The current db context + /// The current repository + /// The current param service /// The current storage manager /// The optional model cache - public MediaRepository(Api api, IDb db, IStorage storage, ICache cache = null, IImageProcessor processor = null) + /// The optional image processor + public MediaService(IMediaRepository repo, ParamService paramService, IStorage storage, IImageProcessor processor = null, ICache cache = null) { - _api = api; - _db = db; + _repo = repo; + _paramService = paramService; _storage = storage; - _cache = cache; _processor = processor; + _cache = cache; } /// @@ -49,21 +52,19 @@ public MediaRepository(Api api, IDb db, IStorage storage, ICache cache = null, I /// /// The optional folder id /// The available media - public IEnumerable GetAll(Guid? folderId = null) + public async Task> GetAllAsync(Guid? folderId = null) { - List models = new List(); + var models = new List(); + var items = await _repo.GetAll(folderId); - var media = _db.Media - .AsNoTracking() - .Where(m => m.FolderId == folderId) - .OrderBy(m => m.Filename) - .Select(m => m.Id); - - foreach (var id in media) + foreach (var item in items) { - var model = GetById(id); - if (model != null) - models.Add(model); + var media = await GetByIdAsync(item); + + if (media != null) + { + models.Add(media); + } } return models; } @@ -74,21 +75,19 @@ public IEnumerable GetAll(Guid? folderId = null) /// /// The optional folder id /// The available media folders - public IEnumerable GetAllFolders(Guid? folderId = null) + public async Task> GetAllFoldersAsync(Guid? folderId = null) { - List models = new List(); + var models = new List(); + var items = await _repo.GetAllFolders(folderId); - var folders = _db.MediaFolders - .AsNoTracking() - .Where(f => f.ParentId == folderId) - .OrderBy(f => f.Name) - .Select(f => f.Id); - - foreach (var id in folders) + foreach (var item in items) { - var model = GetFolderById(id); - if (model != null) - models.Add(model); + var folder = await GetFolderByIdAsync(item); + + if (folder != null) + { + models.Add(folder); + } } return models; } @@ -98,24 +97,15 @@ public IEnumerable GetAllFolders(Guid? folderId = null) /// /// The unique id /// The media - public Media GetById(Guid id) + public async Task GetByIdAsync(Guid id) { - Media model = _cache?.Get(id.ToString()); + var model = _cache?.Get(id.ToString()); if (model == null) { - model = _db.Media - .AsNoTracking() - .FirstOrDefault(m => m.Id == id); - - if (model != null) - { - model.PublicUrl = GetPublicUrl(model); - App.Hooks.OnLoad(model); - } + model = await _repo.GetById(id); - if (_cache != null && model != null) - _cache.Set(model.Id.ToString(), model); + OnLoad(model); } return model; } @@ -125,21 +115,15 @@ public Media GetById(Guid id) /// /// The unique id /// The media folder - public MediaFolder GetFolderById(Guid id) + public async Task GetFolderByIdAsync(Guid id) { - MediaFolder model = _cache?.Get(id.ToString()); + var model = _cache?.Get(id.ToString()); if (model == null) { - model = _db.MediaFolders - .AsNoTracking() - .FirstOrDefault(f => f.Id == id); + model = await _repo.GetFolderById(id); - if (model != null) - App.Hooks.OnLoad(model); - - if (_cache != null && model != null) - _cache.Set(model.Id.ToString(), model); + OnFolderLoad(model); } return model; } @@ -148,46 +132,20 @@ public MediaFolder GetFolderById(Guid id) /// Gets the hierachical media structure. /// /// The media structure - public Models.MediaStructure GetStructure() + public async Task GetStructureAsync() { - Models.MediaStructure model = _cache?.Get(MEDIA_STRUCTURE); + var structure = _cache?.Get(MEDIA_STRUCTURE); - if (model == null) + if (structure == null) { - var folders = new List(); - var ids = _db.MediaFolders - .AsNoTracking() - .OrderBy(f => f.ParentId) - .ThenBy(f => f.Name) - .Select(f => f.Id) - .ToList(); - - foreach (var id in ids) + structure = await _repo.GetStructure(); + + if (structure != null) { - var folder = GetFolderById(id); - if (folder != null) - { - folders.Add(folder); - } + _cache?.Set(MEDIA_STRUCTURE, structure); } - - model = Sort(folders); - - if (_cache != null && model != null) - _cache.Set(MEDIA_STRUCTURE, model); } - return model; - } - - /// - /// Adds or updates the given model in the database depending on its state. - /// Please note that this method is not really synchronous, it's just a - /// wrapper for the async version. - /// - /// The content to save - public void Save(Models.MediaContent content) - { - Task.Run(() => SaveAsync(content)).Wait(); + return structure; } /// @@ -195,14 +153,19 @@ public void Save(Models.MediaContent content) /// depending on its state. /// /// The content to save - public async Task SaveAsync(Models.MediaContent content) + public async Task SaveAsync(MediaContent content) { if (!App.MediaTypes.IsSupported(content.Filename)) - throw new NotSupportedException("Filetype not supported."); + { + throw new ValidationException("Filetype not supported."); + } + + Media model = null; - var model = _db.Media - .Include(m => m.Versions) - .FirstOrDefault(m => m.Id == content.Id); + if (content.Id.HasValue) + { + model = await GetByIdAsync(content.Id.Value); + } if (model == null) { @@ -212,7 +175,6 @@ public async Task SaveAsync(Models.MediaContent content) Created = DateTime.Now }; content.Id = model.Id; - _db.Media.Add(model); } else { @@ -226,7 +188,7 @@ public async Task SaveAsync(Models.MediaContent content) // Delete version from storage await session.DeleteAsync(GetResourceName(model, version.Width, version.Height, version.FileExtension)); } - _db.MediaVersions.RemoveRange(model.Versions); + model.Versions.Clear(); } // Delete the old file because we might have a different filename @@ -241,19 +203,19 @@ public async Task SaveAsync(Models.MediaContent content) model.LastModified = DateTime.Now; // Pre-process if this is an image - if (_processor != null && model.Type == Models.MediaType.Image) + if (_processor != null && model.Type == MediaType.Image) { byte[] bytes; - if (content is Models.BinaryMediaContent) + if (content is BinaryMediaContent) { - bytes = ((Models.BinaryMediaContent)content).Data; + bytes = ((BinaryMediaContent)content).Data; } else { - var reader = new BinaryReader(((Models.StreamMediaContent)content).Data); + var reader = new BinaryReader(((StreamMediaContent)content).Data); bytes = reader.ReadBytes((int)reader.BaseStream.Length); - ((Models.StreamMediaContent)content).Data.Position = 0; + ((StreamMediaContent)content).Data.Position = 0; } int width, height; @@ -266,17 +228,17 @@ public async Task SaveAsync(Models.MediaContent content) // Upload to storage using (var session = await _storage.OpenAsync()) { - if (content is Models.BinaryMediaContent) + if (content is BinaryMediaContent) { - var bc = (Models.BinaryMediaContent)content; + var bc = (BinaryMediaContent)content; model.Size = bc.Data.Length; await session.PutAsync(model.Id + "-" + model.Filename, model.ContentType, bc.Data); } - else if (content is Models.StreamMediaContent) + else if (content is StreamMediaContent) { - var sc = (Models.StreamMediaContent)content; + var sc = (StreamMediaContent)content; var stream = sc.Data; model.Size = sc.Data.Length; @@ -286,7 +248,7 @@ await session.PutAsync(model.Id + "-" + model.Filename, } App.Hooks.OnBeforeSave(model); - _db.SaveChanges(); + await _repo.Save(model); App.Hooks.OnAfterSave(model); RemoveFromCache(model); @@ -297,31 +259,24 @@ await session.PutAsync(model.Id + "-" + model.Filename, /// depending on its state. /// /// The model - public void SaveFolder(Data.MediaFolder model) + public async Task SaveFolderAsync(MediaFolder model) { - App.Hooks.OnBeforeSave(model); - - var folder = _db.MediaFolders - .FirstOrDefault(f => f.Id == model.Id); - - if (folder == null) + // Ensure id + if (model.Id == Guid.Empty) { - folder = new Data.MediaFolder() - { - Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), - Created = DateTime.Now - }; - model.Id = folder.Id; - _db.MediaFolders.Add(folder); + model.Id = Guid.NewGuid(); } - App.Mapper.Map(model, folder); - - _db.SaveChanges(); + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.SaveFolder(model); App.Hooks.OnAfterSave(model); - RemoveFromCache(folder); + RemoveFromCache(model); RemoveStructureFromCache(); } @@ -330,22 +285,15 @@ public void SaveFolder(Data.MediaFolder model) /// /// The media /// The folder id - public void Move(Media model, Guid? folderId) + public async Task MoveAsync(Media model, Guid? folderId) { - var media = _db.Media.FirstOrDefault(m => m.Id == model.Id); - if (media != null) - { - media.FolderId = folderId; - _db.SaveChanges(); - - RemoveFromCache(media); - } + await _repo.Move(model, folderId); + RemoveFromCache(model); } /// /// Ensures that the image version with the given size exsists - /// and returns its public URL. Please note that this method is - /// not really synchronous, it's just a wrapper for the async version. + /// and returns its public URL. /// /// The unique id /// The requested width @@ -353,9 +301,7 @@ public void Move(Media model, Guid? folderId) /// The public URL public string EnsureVersion(Guid id, int width, int? height = null) { - var task = Task.Run(() => EnsureVersionAsync(id, width, height)); - task.Wait(); - return task.Result; + return EnsureVersionAsync(id, width, height).GetAwaiter().GetResult(); } /// @@ -370,133 +316,114 @@ public async Task EnsureVersionAsync(Guid id, int width, int? height = n { if (_processor != null) { - var query = _db.MediaVersions - .Where(v => v.MediaId == id && v.Width == width); - - if (height.HasValue) - { - query = query.Where(v => v.Height == height); - } - else - { - query = query.Where(v => !v.Height.HasValue); - } - - var version = query.FirstOrDefault(); + var media = await GetByIdAsync(id); - if (version == null) + if (media != null) { - var media = _db.Media - .FirstOrDefault(m => m.Id == id); + var query = media.Versions + .Where(v => v.Width == width); - // If the media is missing return false - if (media == null) - return null; + if (height.HasValue) + { + query = query.Where(v => v.Height == height); + } + else + { + query = query.Where(v => !v.Height.HasValue); + } - // If the requested size is equal to the original size, return true - if (media.Width == width && (!height.HasValue || media.Height == height.Value)) - return GetPublicUrl(media); + var version = query.FirstOrDefault(); - // Get the image file - using (var stream = new MemoryStream()) + if (version == null) { - using (var session = await _storage.OpenAsync()) - { - if (!await session.GetAsync(media.Id + "-" + media.Filename, stream)) - return null; - - // Reset strem position - stream.Position = 0; + // If the requested size is equal to the original size, return true + if (media.Width == width && (!height.HasValue || media.Height == height.Value)) + return GetPublicUrl(media); - using (var output = new MemoryStream()) + // Get the image file + using (var stream = new MemoryStream()) + { + using (var session = await _storage.OpenAsync()) { - if (height.HasValue) - { - _processor.CropScale(stream, output, width, height.Value); - } - else - { - _processor.Scale(stream, output, width); - } - output.Position = 0; - bool upload = false; + if (!await session.GetAsync(media.Id + "-" + media.Filename, stream)) + return null; + + // Reset strem position + stream.Position = 0; - lock (ScaleMutex) + using (var output = new MemoryStream()) { - // We have to make sure we don't scale multiple files - // at the same time as it can create index violations. - version = query.FirstOrDefault(); + if (height.HasValue) + { + _processor.CropScale(stream, output, width, height.Value); + } + else + { + _processor.Scale(stream, output, width); + } + output.Position = 0; + bool upload = false; - if (version == null) + lock (ScaleMutex) { - var info = new FileInfo(media.Filename); + // We have to make sure we don't scale multiple files + // at the same time as it can create index violations. + version = query.FirstOrDefault(); - version = new MediaVersion + if (version == null) { - Id = Guid.NewGuid(), - MediaId = media.Id, - Size = output.Length, - Width = width, - Height = height, - FileExtension = info.Extension - }; - _db.MediaVersions.Add(version); - _db.SaveChanges(); - - upload = true; + var info = new FileInfo(media.Filename); + + version = new MediaVersion + { + Id = Guid.NewGuid(), + Size = output.Length, + Width = width, + Height = height, + FileExtension = info.Extension + }; + media.Versions.Add(version); + + _repo.Save(media); + RemoveFromCache(media); + + upload = true; + } } - } - if (upload) - { - return await session.PutAsync(GetResourceName(media, width, height), media.ContentType, output); + if (upload) + { + return await session.PutAsync(GetResourceName(media, width, height), media.ContentType, output); + } } } } } - } - else - { - var media = _db.Media - .FirstOrDefault(m => m.Id == id); - - // If the media is missing return false - if (media == null) - return null; - - // If the requested size is equal to the original size, return true - if (media.Width == width && (!height.HasValue || media.Height == height.Value)) - return GetPublicUrl(media); - return GetPublicUrl(media, width, height, version.FileExtension); + else + { + // If the requested size is equal to the original size, return true + if (media.Width == width && (!height.HasValue || media.Height == height.Value)) + return GetPublicUrl(media); + return GetPublicUrl(media, width, height, version.FileExtension); + } } } return null; } - /// - /// Deletes the media with the given id. Please note that this method - /// is not really synchronous, it's just a wrapper for the async version. - /// - /// The unique id - public void Delete(Guid id) - { - Task.Run(() => DeleteAsync(id)).Wait(); - } - /// /// Deletes the media with the given id. /// /// The unique id public async Task DeleteAsync(Guid id) { - var media = _db.Media - .Include(m => m.Versions) - .FirstOrDefault(m => m.Id == id); + var media = await GetByIdAsync(id); if (media != null) { using (var session = await _storage.OpenAsync()) { + // Delete all versions if (media.Versions.Count > 0) { foreach (var version in media.Versions) @@ -504,32 +431,18 @@ public async Task DeleteAsync(Guid id) // Delete version from storage await session.DeleteAsync(GetResourceName(media, version.Width, version.Height, version.FileExtension)); } - _db.MediaVersions.RemoveRange(media.Versions); } - App.Hooks.OnBeforeDelete(media); - _db.Media.Remove(media); - _db.SaveChanges(); - - // Delete from storage + // Call hooks & save + App.Hooks.OnBeforeDelete(media); + await _repo.Delete(id); await session.DeleteAsync(media.Id + "-" + media.Filename); + App.Hooks.OnAfterDelete(media); } - App.Hooks.OnAfterDelete(media); - RemoveFromCache(media); } } - /// - /// Deletes the given model. Please note that this method - /// is not really synchronous, it's just a wrapper for the async version. - /// - /// The media - public void Delete(Media model) - { - Delete(model.Id); - } - /// /// Deletes the given model. /// @@ -543,27 +456,28 @@ public async Task DeleteAsync(Media model) /// Deletes the media folder with the given id. /// /// The unique id - public void DeleteFolder(Guid id) + public async Task DeleteFolderAsync(Guid id) { - var folder = _db.MediaFolders - .FirstOrDefault(f => f.Id == id); + var folder = await GetFolderByIdAsync(id); if (folder != null) { - var folderCount = _db.MediaFolders.Count(f => f.ParentId == id); - var mediaCount = _db.Media.Count(m => m.FolderId == id); - - if (folderCount == 0 && mediaCount == 0) - { - App.Hooks.OnBeforeDelete(folder); - - _db.MediaFolders.Remove(folder); - _db.SaveChanges(); - - App.Hooks.OnAfterDelete(folder); - - RemoveFromCache(folder); - } + // + // TODO: Check empty + // + // var folderCount = _db.MediaFolders.Count(f => f.ParentId == id); + // var mediaCount = _db.Media.Count(m => m.FolderId == id); + + // if (folderCount == 0 && mediaCount == 0) + // { + + // Call hooks & delete + App.Hooks.OnBeforeDelete(folder); + await _repo.DeleteFolder(id); + App.Hooks.OnAfterDelete(folder); + + RemoveFromCache(folder); + //} } } @@ -571,31 +485,40 @@ public void DeleteFolder(Guid id) /// Deletes the given model. /// /// The media - public void DeleteFolder(Data.MediaFolder model) + public async Task DeleteFolderAsync(MediaFolder model) { - DeleteFolder(model.Id); + await DeleteFolderAsync(model.Id); } /// - /// Sorts the items. + /// Processes the model on load. /// - /// The full folder list - /// The current parent id - /// The structure - private Models.MediaStructure Sort(IEnumerable folders, Guid? parentId = null, int level = 0) + /// The model + private void OnLoad(Media model) { - var result = new Models.MediaStructure(); - - foreach (var folder in folders.Where(f => f.ParentId == parentId).OrderBy(f => f.Name)) + if (model != null) { - var item = App.Mapper.Map(folder); + // Get public url + model.PublicUrl = GetPublicUrl(model); - item.Level = level; - item.Items = Sort(folders, folder.Id, level + 1); + App.Hooks.OnLoad(model); + + _cache?.Set(model.Id.ToString(), model); + } + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnFolderLoad(MediaFolder model) + { + if (model != null) + { + App.Hooks.OnLoad(model); - result.Add(item); + _cache?.Set(model.Id.ToString(), model); } - return result; } /// @@ -684,7 +607,7 @@ private string GetPublicUrl(Media media, int? width = null, int? height = null, { var name = GetResourceName(media, width, height, extension); - using (var config = new Config(_api)) + using (var config = new Config(_paramService)) { var cdn = config.MediaCDN; diff --git a/core/Piranha/Services/PageService.cs b/core/Piranha/Services/PageService.cs new file mode 100644 index 000000000..96596f6a6 --- /dev/null +++ b/core/Piranha/Services/PageService.cs @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class PageService + { + private readonly IPageRepository _repo; + private readonly IContentFactory _factory; + private readonly SiteService _siteService; + private readonly ParamService _paramService; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public PageService(IPageRepository repo, IContentFactory factory, SiteService siteService, ParamService paramService, ICache cache = null) + { + _repo = repo; + _factory = factory; + _siteService = siteService; + _paramService = paramService; + + if ((int)App.CacheLevel > 2) + { + _cache = cache; + } + } + + /// + /// Creates and initializes a new page of the specified type. + /// + /// The created page + public T Create(string typeId = null) where T : Models.PageBase + { + if (string.IsNullOrEmpty(typeId)) + { + typeId = typeof(T).Name; + } + + var type = App.PageTypes.GetById(typeId); + + if (type != null) + { + return _factory.Create(type); + } + return null; + } + + /// + /// Creates and initializes a copy of the given page. + /// + /// The orginal page + /// The created copy + public T Copy(T originalPage) where T : Models.PageBase + { + var model = Create(originalPage.TypeId); + model.OriginalPageId = originalPage.Id; + model.Slug = null; + + return model; + } + + /// + /// Detaches a copy and initializes it as a standalone page + /// + /// The standalone page + public async Task DetachAsync(T model) where T : Models.PageBase + { + if (!model.OriginalPageId.HasValue) + { + throw new ValidationException("Page is not an copy"); + } + + // Get the page and remove the original id + var page = await GetByIdAsync(model.Id); + page.OriginalPageId = null; + + // Reset blocks so they are recreated + foreach (var pageBlock in page.Blocks) + { + pageBlock.Id = Guid.Empty; + } + + await SaveAsync(page); + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync(Guid? siteId = null) + { + return GetAllAsync(siteId); + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAllAsync(Guid? siteId = null) where T : PageBase + { + var models = new List(); + var pages = await _repo.GetAll((await EnsureSiteIdAsync(siteId)).Value); + + foreach (var pageId in pages) + { + var page = await GetByIdAsync(pageId); + + if (page != null) + { + models.Add(page); + } + } + return models; + } + + /// + /// Gets the available blog pages for the current site. + /// + /// The optional site id + /// The pages + public Task> GetAllBlogsAsync(Guid? siteId = null) + { + return GetAllBlogsAsync(siteId); + } + + /// + /// Gets the available blog pages for the current site. + /// + /// The optional site id + /// The pages + public async Task> GetAllBlogsAsync(Guid? siteId = null) where T : Models.PageBase + { + var models = new List(); + var pages = await _repo.GetAllBlogs((await EnsureSiteIdAsync(siteId)).Value); + + foreach (var pageId in pages) + { + var page = await GetByIdAsync(pageId); + + if (page != null) + { + models.Add(page); + } + } + return models; + } + + /// + /// Gets the site startpage. + /// + /// The optional site id + /// The page model + public Task GetStartpageAsync(Guid? siteId = null) + { + return GetStartpageAsync(siteId); + } + + /// + /// Gets the site startpage. + /// + /// The model type + /// The optional site id + /// The page model + public async Task GetStartpageAsync(Guid? siteId = null) where T : Models.PageBase + { + siteId = await EnsureSiteIdAsync(siteId); + PageBase model = null; + + if (typeof(T) == typeof(Models.PageInfo)) + { + model = _cache?.Get($"PageInfo_{siteId.Value}"); + } + else if (!typeof(DynamicPage).IsAssignableFrom(typeof(T))) + { + model = _cache?.Get($"Page_{siteId.Value}"); + + if (model != null) + { + _factory.Init(model, App.PageTypes.GetById(model.TypeId)); + } + } + + if (model == null) + { + model = await _repo.GetStartpage(siteId.Value); + + OnLoad(model); + } + + if (model != null && model is T) + { + return await MapOriginalAsync((T)model); + } + return null; + } + + /// + /// Gets the page model with the specified id. + /// + /// The unique id + /// The page model + public Task GetByIdAsync(Guid id) + { + return GetByIdAsync(id); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public async Task GetByIdAsync(Guid id) where T : PageBase + { + PageBase model = null; + + if (typeof(T) == typeof(Models.PageInfo)) + { + model = _cache?.Get($"PageInfo_{id.ToString()}"); + } + else if (!typeof(DynamicPage).IsAssignableFrom(typeof(T))) + { + model = _cache?.Get(id.ToString()); + + if (model != null) + { + _factory.Init(model, App.PageTypes.GetById(model.TypeId)); + } + } + + if (model == null) + { + model = await _repo.GetById(id); + + OnLoad(model); + } + + if (model != null && model is T) + { + return await MapOriginalAsync((T)model); + } + return null; + } + + /// + /// Gets the page model with the specified slug. + /// + /// The unique slug + /// The optional site id + /// The page model + public Task GetBySlugAsync(string slug, Guid? siteId = null) + { + return GetBySlugAsync(slug, siteId); + } + + /// + /// Gets the page model with the specified slug. + /// + /// The model type + /// The unique slug + /// The optional site id + /// The page model + public async Task GetBySlugAsync(string slug, Guid? siteId = null) where T : Models.PageBase + { + siteId = await EnsureSiteIdAsync(siteId); + PageBase model = null; + + // Lets see if we can resolve the slug from cache + var pageId = _cache?.Get($"PageId_{siteId}_{slug}"); + + if (pageId.HasValue) + { + if (typeof(T) == typeof(Models.PageInfo)) + { + model = _cache?.Get($"PageInfo_{pageId.ToString()}"); + } + else if (!typeof(DynamicPage).IsAssignableFrom(typeof(T))) + { + model = _cache?.Get(pageId.ToString()); + + if (model != null) + { + _factory.Init(model, App.PageTypes.GetById(model.TypeId)); + } + } + } + + if (model == null) + { + model = await _repo.GetBySlug(slug, siteId.Value); + + OnLoad(model); + } + + if (model != null && model is T) + { + return await MapOriginalAsync((T)model); + } + return null; + } + + /// + /// Gets the id for the page with the given slug. + /// + /// The unique slug + /// The optional page id + /// The id + public async Task GetIdBySlugAsync(string slug, Guid? siteId = null) + { + siteId = await EnsureSiteIdAsync(siteId); + + // Lets see if we can resolve the slug from cache + var pageId = _cache?.Get($"PageId_{siteId}_{slug}"); + + if (!pageId.HasValue) + { + var info = await _repo.GetBySlug(slug, siteId.Value); + + if (info != null) + { + pageId = info.Id; + } + } + return pageId; + } + + /// + /// Moves the current page in the structure. + /// + /// The model type + /// The page to move + /// The new parent id + /// The new sort order + public async Task MoveAsync(T model, Guid? parentId, int sortOrder) where T : Models.PageBase + { + // Call hooks & save + App.Hooks.OnBeforeSave(model); + var affected = await _repo.Move(model, parentId, sortOrder); + App.Hooks.OnAfterSave(model); + + // Remove the moved page from cache + RemoveFromCache(model); + + // Remove all affected pages from cache + if (_cache != null) + { + foreach (var id in affected) + { + var page = await GetByIdAsync(id); + if (page != null) + { + RemoveFromCache(model); + } + } + } + await _siteService.InvalidateSitemapAsync(model.SiteId); + } + + /// + /// Saves the given page model + /// + /// The page model + public async Task SaveAsync(T model) where T : PageBase + { + // Ensure id + if (model.Id == Guid.Empty) + { + model.Id = Guid.NewGuid(); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Ensure title since this field isn't required in + // the Content base class + if (string.IsNullOrWhiteSpace(model.Title)) + { + throw new ValidationException("The Title field is required"); + } + + // Ensure type id since this field isn't required in + // the Content base class + if (string.IsNullOrWhiteSpace(model.TypeId)) + { + throw new ValidationException("The TypeId field is required"); + } + + // Ensure slug + if (string.IsNullOrWhiteSpace(model.Slug)) + { + var prefix = ""; + + // Check if we should generate hierarchical slugs + using (var config = new Config(_paramService)) + { + if (config.HierarchicalPageSlugs && model.ParentId.HasValue) + { + var parentSlug = (await GetByIdAsync(model.ParentId.Value))?.Slug; + + if (!string.IsNullOrWhiteSpace(parentSlug)) + { + prefix = parentSlug + "/"; + } + } + model.Slug = prefix + Utils.GenerateSlug(model.NavigationTitle != null ? model.NavigationTitle : model.Title); + } + } + else + { + model.Slug = Utils.GenerateSlug(model.Slug); + } + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + var affected = await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Remove from cache + RemoveFromCache(model); + + // Remove all affected pages from cache + if (_cache != null) + { + foreach (var id in affected) + { + var page = await GetByIdAsync(id); + if (page != null) + { + RemoveFromCache(model); + } + } + } + + // Invalidate sitemap if any other pages were affected + if (affected.Count() > 0) + { + await _siteService.InvalidateSitemapAsync(model.SiteId); + } + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(Guid id) + { + var model = await GetByIdAsync(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(T model) where T : Models.PageBase + { + // Call hooks & save + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Remove from cache & invalidate sitemap + RemoveFromCache(model); + + await _siteService.InvalidateSitemapAsync(model.SiteId); + } + + /// + /// Merges the given model with the original model and + /// returns it as a new instance. + /// + /// The model + /// The model type + /// The new merged model + private async Task MapOriginalAsync(T model) where T : PageBase + { + if (model == null || !model.OriginalPageId.HasValue) + { + return model; + } + + var original = await GetByIdAsync(model.OriginalPageId.Value); + + if (original != null) + { + // Clone the original in case we are caching in system + // memory, otherwise we'll destroy the original. + var copy = Utils.DeepClone(original); + + // Now let's move over the fields we want from the + // soft copy. + copy.Id = model.Id; + copy.SiteId = model.SiteId; + copy.Title = model.Title; + copy.NavigationTitle = model.NavigationTitle; + copy.Slug = model.Slug; + copy.ParentId = model.ParentId; + copy.SortOrder = model.SortOrder; + copy.IsHidden = model.IsHidden; + copy.Route = model.Route; + copy.OriginalPageId = model.OriginalPageId; + copy.Published = model.Published; + copy.Created = model.Created; + copy.LastModified = model.LastModified; + + return copy; + } + return null; + } + + /// + /// Checks if the given site id is empty, and if so + /// gets the site id of the default site. + /// + /// The optional site id + /// The site id + private async Task EnsureSiteIdAsync(Guid? siteId) + { + if (!siteId.HasValue) + { + var site = await _siteService.GetDefaultAsync(); + + if (site != null) + { + return site.Id; + } + } + return siteId; + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnLoad(PageBase model) + { + if (model != null) + { + // Initialize model + if (typeof(IDynamicModel).IsAssignableFrom(model.GetType())) + { + _factory.InitDynamic((DynamicPage)model, App.PageTypes.GetById(model.TypeId)); + } + else + { + _factory.Init(model, App.PageTypes.GetById(model.TypeId)); + } + + App.Hooks.OnLoad(model); + + // Never cache dynamic or simple instances + if (_cache != null && !(model is DynamicPage)) + { + if (model is PageInfo) + { + _cache.Set($"PageInfo_{model.Id.ToString()}", model); + } + else + { + _cache.Set(model.Id.ToString(), model); + } + _cache.Set($"PageId_{model.SiteId}_{model.Slug}", model.Id); + if (!model.ParentId.HasValue && model.SortOrder == 0) + { + if (model is PageInfo) + { + _cache.Set($"PageInfo_{model.SiteId}", model); + } + else + { + _cache.Set($"Page_{model.SiteId}", model); + } + } + } + } + } + + /// + /// Removes the given model from cache. + /// + /// The model + private void RemoveFromCache(PageBase model) + { + if (_cache != null) + { + _cache.Remove(model.Id.ToString()); + _cache.Remove($"PageInfo_{model.Id.ToString()}"); + _cache.Remove($"PageId_{model.SiteId}_{model.Slug}"); + if (!model.ParentId.HasValue && model.SortOrder == 0) + { + _cache.Remove($"Page_{model.SiteId}"); + _cache.Remove($"PageInfo_{model.SiteId}"); + } + } + } + } +} diff --git a/core/Piranha/Services/PageTypeService.cs b/core/Piranha/Services/PageTypeService.cs new file mode 100644 index 000000000..4d51f4738 --- /dev/null +++ b/core/Piranha/Services/PageTypeService.cs @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class PageTypeService + { + private readonly IPageTypeRepository _repo; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public PageTypeService(IPageTypeRepository repo, ICache cache) + { + _repo = repo; + + if ((int)App.CacheLevel > 1) + { + _cache = cache; + } + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync() + { + return GetTypes(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model + public async Task GetByIdAsync(string id) + { + var types = await GetTypes(); + + return types.FirstOrDefault(t => t.Id == id); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(PageType model) + { + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Clear cache + _cache?.Remove("Piranha_PageTypes"); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(string id) + { + var model = await _repo.GetById(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(PageType model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Clear cache + _cache?.Remove("Piranha_PageTypes"); + } + + /// + /// Reloads the page types from the database. + /// + private async Task> GetTypes() + { + var types = _cache?.Get>("Piranha_PageTypes"); + + if (types == null) + { + types = await _repo.GetAll(); + + _cache?.Set("Piranha_PageTypes", types); + } + return types; + } + } +} diff --git a/core/Piranha/Services/ParamService.cs b/core/Piranha/Services/ParamService.cs new file mode 100644 index 000000000..b28f4d561 --- /dev/null +++ b/core/Piranha/Services/ParamService.cs @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class ParamService + { + private readonly IParamRepository _repo; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public ParamService(IParamRepository repo, ICache cache = null) + { + _repo = repo; + + if ((int)App.CacheLevel > 0) + { + _cache = cache; + } + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync() + { + return _repo.GetAll(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public async Task GetByIdAsync(Guid id) + { + var model = _cache?.Get(id.ToString()); + + if (model == null) + { + model = await _repo.GetById(id); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the model with the given key. + /// + /// The unique key + /// The model + public async Task GetByKeyAsync(string key) { + var id = _cache?.Get($"ParamKey_{key}"); + Param model = null; + + if (id.HasValue) + { + model = await GetByIdAsync(id.Value); + } + else + { + model = await _repo.GetByKey(key); + + OnLoad(model); + } + return model; + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(Param model) + { + // Ensure id + if (model.Id == Guid.Empty) + { + model.Id = Guid.NewGuid(); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Ensure key uniqueness + var param = await _repo.GetByKey(model.Key); + if (param != null && param.Id != model.Id) + { + throw new ValidationException($"The Key field must be unique"); + } + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(Guid id) + { + var model = await GetByIdAsync(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(Param model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnLoad(Param model) + { + if (model != null) + { + App.Hooks.OnLoad(model); + + if (_cache != null) + { + _cache.Set(model.Id.ToString(), model); + _cache.Set($"ParamKey_{model.Key}", model.Id); + } + } + } + + /// + /// Removes the given model from cache. + /// + /// The model + private void RemoveFromCache(Param model) { + if (_cache != null) + { + _cache.Remove(model.Id.ToString()); + _cache.Remove($"ParamKey_{model.Key}"); + } + } + } +} diff --git a/core/Piranha/Services/PostService.cs b/core/Piranha/Services/PostService.cs new file mode 100644 index 000000000..b8d627ca8 --- /dev/null +++ b/core/Piranha/Services/PostService.cs @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class PostService + { + private readonly IPostRepository _repo; + private readonly IContentFactory _factory; + private readonly SiteService _siteService; + private readonly PageService _pageService; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The site service + /// The site service + /// The optional model cache + public PostService(IPostRepository repo, IContentFactory factory, SiteService siteService, PageService pageService, ICache cache = null) + { + _repo = repo; + _factory = factory; + _siteService = siteService; + _pageService = pageService; + + if ((int)App.CacheLevel > 2) + { + _cache = cache; + } + } + + /// + /// Creates and initializes a new post of the specified type. + /// + /// The created post + public T Create(string typeId = null) where T : PostBase + { + if (string.IsNullOrEmpty(typeId)) + { + typeId = typeof(T).Name; + } + + var type = App.PostTypes.GetById(typeId); + + if (type != null) + { + return _factory.Create(type); + } + return null; + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The posts + [Obsolete("Please refer to GetAllBySiteIdAsync(siteId)", true)] + public IEnumerable GetAll() + { + return GetAll(); + } + + /// + /// Gets the available post items. + /// + /// The posts + [Obsolete("Please refer to GetAllBySiteIdAsync(siteId)", true)] + public IEnumerable GetAll() where T : PostBase + { + return null; + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The unique blog id + /// The posts + public Task> GetAllAsync(Guid blogId) + { + return GetAllAsync(blogId); + } + + /// + /// Gets the available post items. + /// + /// The unique id + /// The posts + public async Task> GetAllAsync(Guid blogId) where T : PostBase + { + var models = new List(); + var posts = await _repo.GetAll(blogId); + var pages = new List(); + + foreach (var postId in posts) + { + var post = await GetByIdAsync(postId, pages); + + if (post != null) + { + models.Add(post); + } + } + return models; + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The optional site id + /// The posts + public Task> GetAllBySiteIdAsync(Guid? siteId = null) + { + return GetAllBySiteIdAsync(siteId); + } + + /// + /// Gets the available post items. + /// + /// The optional site id + /// The posts + public async Task> GetAllBySiteIdAsync(Guid? siteId = null) where T : PostBase + { + var models = new List(); + var posts = await _repo.GetAllBySiteId((await EnsureSiteIdAsync(siteId)).Value); + var pages = new List(); + + foreach (var postId in posts) + { + var post = await GetByIdAsync(postId, pages); + + if (post != null) + { + models.Add(post); + } + } + return models; + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The blog slug + /// The optional site id + /// The posts + public Task> GetAllAsync(string slug, Guid? siteId = null) + { + return GetAllAsync(slug, siteId); + } + + /// + /// Gets the available posts for the specified blog. + /// + /// The blog slug + /// The optional site id + /// The posts + public async Task> GetAllAsync(string slug, Guid? siteId = null) where T : PostBase + { + siteId = await EnsureSiteIdAsync(siteId); + var blogId = await _pageService.GetIdBySlugAsync(slug, siteId); + + if (blogId.HasValue) + { + return await GetAllAsync(blogId.Value); + } + return new List(); + } + + /// + /// Gets all available categories for the specified blog. + /// + /// The blog id + /// The available categories + public Task> GetAllCategoriesAsync(Guid blogId) + { + return _repo.GetAllCategories(blogId); + } + + /// + /// Gets all available tags for the specified blog. + /// + /// The blog id + /// The available tags + public Task> GetAllTagsAsync(Guid blogId) + { + return _repo.GetAllTags(blogId); + } + + /// + /// Gets the post model with the specified id. + /// + /// The unique id + /// The post model + public Task GetByIdAsync(Guid id) + { + return GetByIdAsync(id); + } + + /// + /// Gets the post model with the specified id. + /// + /// The model type + /// The unique id + /// The post model + public Task GetByIdAsync(Guid id) where T : PostBase + { + return GetByIdAsync(id, new List()); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The unique blog slug + /// The unique slug + /// The optional site id + /// The post model + public Task GetBySlugAsync(string blog, string slug, Guid? siteId = null) + { + return GetBySlugAsync(blog, slug, siteId); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The model type + /// The unique blog slug + /// The unique slug + /// The optional site id + /// The post model + public async Task GetBySlugAsync(string blog, string slug, Guid? siteId = null) where T : PostBase + { + siteId = await EnsureSiteIdAsync(siteId); + + var blogId = await _pageService.GetIdBySlugAsync(blog, siteId); + + if (blogId.HasValue) + { + return await GetBySlugAsync(blogId.Value, slug); + } + return null; + } + + /// + /// Gets the post model with the specified slug. + /// + /// The unique blog slug + /// The unique slug + /// The post model + public Task GetBySlugAsync(Guid blogId, string slug) + { + return GetBySlugAsync(blogId, slug); + } + + /// + /// Gets the post model with the specified slug. + /// + /// The model type + /// The unique blog slug + /// The unique slug + /// The post model + public async Task GetBySlugAsync(Guid blogId, string slug) where T : PostBase + { + PostBase model = null; + + // Lets see if we can resolve the slug from cache + var postId = _cache?.Get($"PostId_{blogId}_{slug}"); + + if (postId.HasValue) + { + if (typeof(T) == typeof(PostInfo)) + { + model = _cache?.Get($"PostInfo_{postId.ToString()}"); + } + else if (!typeof(DynamicPost).IsAssignableFrom(typeof(T))) + { + model = _cache?.Get(postId.ToString()); + } + } + + if (model == null) + { + model = await _repo.GetBySlug(blogId, slug); + + if (model != null) + { + var blog = await _pageService.GetByIdAsync(model.BlogId); + + OnLoad(model, blog); + } + } + + if (model != null && model is T) + { + return (T)model; + } + return null; + } + + /// + /// Gets the category with the given slug. + /// + /// The blog id + /// The unique slug + /// The model + public async Task GetCategoryBySlugAsync(Guid blogId, string slug) + { + var id = _cache?.Get($"Category_{blogId}_{slug}"); + Taxonomy model = null; + + if (id.HasValue) + { + model = _cache.Get(id.Value.ToString()); + } + + if (model == null) + { + model = await _repo.GetCategoryBySlug(blogId, slug); + + if (model != null && _cache != null) + { + _cache.Set(model.Id.ToString(), model); + _cache.Set($"Category_{blogId}_{slug}", model); + } + } + return model; + } + + /// + /// Gets the tag with the given slug. + /// + /// The blog id + /// The unique slug + /// The model + public async Task GetTagBySlugAsync(Guid blogId, string slug) + { + var id = _cache?.Get($"Tag_{blogId}_{slug}"); + Taxonomy model = null; + + if (id.HasValue) + { + model = _cache.Get(id.Value.ToString()); + } + + if (model == null) + { + model = await _repo.GetTagBySlug(blogId, slug); + + if (model != null && _cache != null) + { + _cache.Set(model.Id.ToString(), model); + _cache.Set($"Tag_{blogId}_{slug}", model); + } + } + return model; + } + + /// + /// Saves the given post model + /// + /// The post model + public async Task SaveAsync(T model) where T : PostBase + { + // Ensure id + if (model.Id == Guid.Empty) + { + model.Id = Guid.NewGuid(); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Ensure title since this field isn't required in + // the Content base class + if (string.IsNullOrWhiteSpace(model.Title)) + { + throw new ValidationException("The Title field is required"); + } + + // Ensure type id since this field isn't required in + // the Content base class + if (string.IsNullOrWhiteSpace(model.TypeId)) + { + throw new ValidationException("The TypeId field is required"); + } + + // Ensure slug + if (string.IsNullOrWhiteSpace(model.Slug)) + { + model.Slug = Utils.GenerateSlug(model.Title, false); + } + else + { + model.Slug = Utils.GenerateSlug(model.Slug, false); + } + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + if (_cache != null) + { + // Clear all categories from cache in case some + // unused where deleted. + var categories = await _repo.GetAllCategories(model.BlogId); + foreach (var category in categories) + { + _cache.Remove(category.Id.ToString()); + _cache.Remove($"Category_{model.BlogId}_{category.Slug}"); + } + + // Clear all tags from cache in case some + // unused where deleted. + var tags = await _repo.GetAllTags(model.BlogId); + foreach (var tag in tags) + { + _cache.Remove(tag.Id.ToString()); + _cache.Remove($"Tag_{model.BlogId}_{tag.Slug}"); + } + } + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(Guid id) + { + var model = await GetByIdAsync(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(T model) where T : PostBase + { + // Call hooks & save + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Remove from cache & invalidate sitemap + RemoveFromCache(model); + } + + /// + /// Gets the post model with the specified id. + /// + /// The model type + /// The unique id + /// The blog pages + /// The post model + private async Task GetByIdAsync(Guid id, IList blogPages) where T : PostBase + { + PostBase model = null; + + if (typeof(T) == typeof(PostInfo)) + { + model = _cache?.Get($"PostInfo_{id.ToString()}"); + } + else if (!typeof(DynamicPost).IsAssignableFrom(typeof(T))) + { + model = _cache?.Get(id.ToString()); + } + + if (model == null) + { + model = await _repo.GetById(id); + + if (model != null) + { + var blog = blogPages.FirstOrDefault(p => p.Id == model.BlogId); + + if (blog == null) + { + blog = await _pageService.GetByIdAsync(model.BlogId); + blogPages.Add(blog); + } + + OnLoad(model, blog); + } + } + + if (model != null && model is T) + { + return (T)model; + } + return null; + } + + /// + /// Checks if the given site id is empty, and if so + /// gets the site id of the default site. + /// + /// The optional site id + /// The site id + private async Task EnsureSiteIdAsync(Guid? siteId) + { + if (!siteId.HasValue) + { + var site = await _siteService.GetDefaultAsync(); + + if (site != null) + { + return site.Id; + } + } + return siteId; + } + + /// + /// Processes the model on load. + /// + /// The model + /// The blog page the post belongs to + private void OnLoad(PostBase model, PageInfo blog) + { + if (model != null) + { + // Format permalink + model.Permalink = $"/{blog.Slug}/{model.Slug}"; + + // Initialize model + if (typeof(IDynamicModel).IsAssignableFrom(model.GetType())) + { + _factory.InitDynamic((DynamicPost)model, App.PostTypes.GetById(model.TypeId)); + } + else + { + _factory.Init(model, App.PostTypes.GetById(model.TypeId)); + } + + App.Hooks.OnLoad(model); + + // Never cache dynamic or simple instances + if (_cache != null && !(model is DynamicPost)) + { + if (model is PostInfo) + { + _cache.Set($"PostInfo_{model.Id.ToString()}", model); + } + else + { + _cache.Set(model.Id.ToString(), model); + } + _cache.Set($"PostId_{model.BlogId}_{model.Slug}", model.Id); + } + } + } + + /// + /// Removes the given model from cache. + /// + /// The post + private void RemoveFromCache(PostBase post) + { + if (_cache != null) + { + _cache.Remove(post.Id.ToString()); + _cache.Remove($"PostId_{post.BlogId}_{post.Slug}"); + } + } + } +} diff --git a/core/Piranha/Services/PostTypeService.cs b/core/Piranha/Services/PostTypeService.cs new file mode 100644 index 000000000..41a66f6f2 --- /dev/null +++ b/core/Piranha/Services/PostTypeService.cs @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class PostTypeService + { + private readonly IPostTypeRepository _repo; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public PostTypeService(IPostTypeRepository repo, ICache cache) + { + _repo = repo; + + if ((int)App.CacheLevel > 1) + { + _cache = cache; + } + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync() + { + return GetTypes(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public async Task GetByIdAsync(string id) + { + var types = await GetTypes(); + + return types.FirstOrDefault(t => t.Id == id); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(PostType model) + { + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Clear cache + _cache?.Remove("Piranha_PostTypes"); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(string id) + { + var model = await _repo.GetById(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(PostType model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Clear cache + _cache?.Remove("Piranha_PostTypes"); + } + + /// + /// Reloads the page types from the database. + /// + private async Task> GetTypes() + { + var types = _cache?.Get>("Piranha_PostTypes"); + + if (types == null) + { + types = await _repo.GetAll(); + + _cache?.Set("Piranha_PostTypes", types); + } + return types; + } + } +} diff --git a/core/Piranha/Services/ServiceExtensions.cs b/core/Piranha/Services/ServiceExtensions.cs deleted file mode 100644 index e31fb7f94..000000000 --- a/core/Piranha/Services/ServiceExtensions.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * https://github.com/piranhacms/piranha.core - * - */ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Piranha; -using Piranha.Services; -using System; - -public static class ServiceExtensions -{ - /// - /// Adds the default services needed to run Piranha over - /// Entity Framework Core. - /// - /// The current service collection - /// The optional lifetime - /// The updated service collection - public static IServiceCollection AddPiranhaEF(this IServiceCollection services, - ServiceLifetime scope = ServiceLifetime.Scoped) - { - services.Add(new ServiceDescriptor(typeof(IContentServiceFactory), typeof(ContentServiceFactory), ServiceLifetime.Singleton)); - services.Add(new ServiceDescriptor(typeof(IDb), typeof(Db), scope)); - services.Add(new ServiceDescriptor(typeof(IApi), typeof(Api), scope)); - - return services; - } - - /// - /// Adds the DbContext and the default services needed to run - /// Piranha over Entity Framework Core. - /// - /// The current service collection - /// The DbContext options builder - /// The optional lifetime - /// The updated service collection - public static IServiceCollection AddPiranhaEF(this IServiceCollection services, - Action dboptions, - ServiceLifetime scope = ServiceLifetime.Scoped) - { - services.AddDbContext(dboptions); - - return AddPiranhaEF(services, scope); - } - - /// - /// Adds the memory cache service for repository caching. - /// - /// The current service collection - /// The updated service collection - [Obsolete("Please use AddPiranhaSimpleCache instead", true)] - public static IServiceCollection AddPiranhaMemCache(this IServiceCollection services) - { - return services; - } - - /// - /// Adds the distributed cache service for repository caching. - /// - /// The current service collection - /// The updated service collection - public static IServiceCollection AddPiranhaDistributedCache(this IServiceCollection services) - { - return services.AddSingleton(); - } - - /// - /// Adds the memory cache service for repository caching. - /// - /// The current service collection - /// The updated service collection - public static IServiceCollection AddPiranhaMemoryCache(this IServiceCollection services) - { - return services.AddSingleton(); - } - - /// - /// Adds the simple cache service for repository caching. - /// - /// The current service collection - /// The updated service collection - public static IServiceCollection AddPiranhaSimpleCache(this IServiceCollection services) - { - return services.AddSingleton(); - } -} diff --git a/core/Piranha/Services/SiteService.cs b/core/Piranha/Services/SiteService.cs new file mode 100644 index 000000000..400a7c9a3 --- /dev/null +++ b/core/Piranha/Services/SiteService.cs @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class SiteService + { + [Serializable] + public class SiteMapping + { + public Guid Id { get; set; } + public string Hostnames { get; set; } + } + + private readonly ISiteRepository _repo; + private readonly IContentFactory _factory; + private readonly ICache _cache; + private const string SITE_MAPPINGS = "Site_Mappings"; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public SiteService(ISiteRepository repo, IContentFactory factory, ICache cache = null) + { + _repo = repo; + _factory = factory; + + if ((int)App.CacheLevel > 0) + { + _cache = cache; + } + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync() + { + return _repo.GetAll(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or null if it doesn't exist + public async Task GetByIdAsync(Guid id) + { + var model = _cache?.Get(id.ToString()); + + if (model == null) + { + model = await _repo.GetById(id); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the model with the given internal id. + /// + /// The unique internal i + /// The model + public async Task GetByInternalIdAsync(string internalId) + { + var id = _cache?.Get($"SiteId_{internalId}"); + Site model = null; + + if (id != null) + { + model = await GetByIdAsync(id.Value); + } + else + { + model = await _repo.GetByInternalId(internalId); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the model with the given hostname. + /// + /// The hostname + /// The model + public async Task GetByHostnameAsync(string hostname) + { + IList mappings; + + if (_cache != null) + { + mappings = _cache.Get>(SITE_MAPPINGS); + + if (mappings == null) + { + var sites = await GetAllAsync(); + mappings = sites + .Where(s => s.Hostnames != null) + .Select(s => new SiteMapping + { + Id = s.Id, + Hostnames = s.Hostnames + }) + .ToList(); + _cache.Set(SITE_MAPPINGS, mappings); + } + } + else + { + var sites = await GetAllAsync(); + mappings = sites + .Where(s => s.Hostnames != null) + .Select(s => new SiteMapping + { + Id = s.Id, + Hostnames = s.Hostnames + }) + .ToList(); + } + + foreach (var mapping in mappings) + { + foreach (var host in mapping.Hostnames.Split(new [] { ',' })) + { + if (host.Trim().ToLower() == hostname) + { + return await GetByIdAsync(mapping.Id); + } + } + } + return null; + } + + /// + /// Gets the default side. + /// + /// The modell, or NULL if it doesnt exist + public async Task GetDefaultAsync() + { + var model = _cache?.Get($"Site_{Guid.Empty}"); + + if (model == null) + { + model = await _repo.GetDefault(); + + OnLoad(model); + } + return model; + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site content model + public Task GetContentByIdAsync(Guid id) + { + return _repo.GetContentById(id); + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site model type + /// The site content model + public async Task GetContentByIdAsync(Guid id) where T : SiteContent + { + var model = _cache?.Get($"SiteContent_{id}"); + + if (model == null) + { + model = await _repo.GetContentById(id); + + OnLoadContent(model); + } + return model; + } + + /// + /// Gets the hierachical sitemap structure. + /// + /// The optional site id + /// If only published items should be included + /// The sitemap + public async Task GetSitemapAsync(Guid? id = null, bool onlyPublished = true) + { + if (!id.HasValue) + { + var site = await GetDefaultAsync(); + + if (site != null) + { + id = site.Id; + } + } + + if (id != null) + { + var sitemap = onlyPublished ? _cache?.Get($"Sitemap_{id}") : null; + + if (sitemap == null) + { + sitemap = await _repo.GetSitemap(id.Value, onlyPublished); + + if (onlyPublished && _cache != null) + { + _cache.Set($"Sitemap_{id}", sitemap); + } + } + return sitemap; + } + return null; + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(Site model) + { + // Ensure id + if (model.Id == Guid.Empty) + { + model.Id = Guid.NewGuid(); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Ensure InternalId + if (string.IsNullOrWhiteSpace(model.InternalId)) + { + model.InternalId = Utils.GenerateInteralId(model.Title); + } + + // Ensure InternalId uniqueness + var site = await _repo.GetByInternalId(model.InternalId); + if (site != null && site.Id != model.Id) + { + throw new ValidationException($"The InternalId field must be unique"); + } + + // Ensure we have a default site + if (model.IsDefault) + { + // Make sure no other site is default first + var def = await GetDefaultAsync(); + + if (def != null && def.Id != model.Id) + { + def.IsDefault = false; + await _repo.Save(def); + } + } + else + { + // Make sure we have a default site + var def = await _repo.GetDefault(); + if (def == null || def.Id == model.Id) + model.IsDefault = true; + } + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Saves the given site content to the site with the + /// given id. + /// + /// The site id + /// The site content model + /// The site content type + public async Task SaveContentAsync(Guid siteId, T model) where T : SiteContent + { + // Ensure id + if (model.Id != siteId) + { + model.Id = siteId; + } + if (model.Id == Guid.Empty) + { + throw new ValidationException($"The Id field is required for this operation"); + } + + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.SaveContent(siteId, model); + App.Hooks.OnAfterSave(model); + + // Remove from cache + RemoveContentFromCache(model); + } + + /// + /// Creates and initializes a new site content model of the specified type. + /// + /// The created site content + public T CreateContent(string typeId = null) where T : Models.SiteContentBase + { + if (string.IsNullOrEmpty(typeId)) + { + typeId = typeof(T).Name; + } + + var type = App.SiteTypes.GetById(typeId); + + if (type != null) + { + return _factory.Create(type); + } + return null; + } + + /// + /// Invalidates the cached version of the sitemap with the + /// given id, if caching is enabled. + /// + /// The site id + /// If the global last modified date should be updated + public async Task InvalidateSitemapAsync(Guid id, bool updateLastModified = true) + { + if (updateLastModified) + { + var site = await GetByIdAsync(id); + + if (site != null) + { + site.ContentLastModified = DateTime.Now; + await SaveAsync(site); + } + } + _cache?.Remove($"Sitemap_{id}"); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(Guid id) + { + var model = await GetByIdAsync(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(Site model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Remove from cache + RemoveFromCache(model); + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnLoad(Site model) + { + if (model != null) + { + App.Hooks.OnLoad(model); + + if (_cache != null) + { + _cache.Set(model.Id.ToString(), model); + _cache.Set($"SiteId_{model.InternalId}", model.Id); + if (model.IsDefault) + { + _cache.Set($"Site_{Guid.Empty}", model); + } + } + } + } + + /// + /// Processes the model on load. + /// + /// The model + private void OnLoadContent(Models.SiteContentBase model) + { + if (model != null) + { + // Initialize model + if (typeof(Models.IDynamicModel).IsAssignableFrom(model.GetType())) + { + _factory.InitDynamic((Models.DynamicSiteContent)model, App.SiteTypes.GetById(model.TypeId)); + } + else + { + _factory.Init(model, App.SiteTypes.GetById(model.TypeId)); + } + + App.Hooks.OnLoad(model); + + if (_cache != null) + { + _cache.Set($"SiteContent_{model.Id}", model); + } + } + } + + /// + /// Removes the given model from cache. + /// + /// The model + private void RemoveFromCache(Site model) + { + if (_cache != null) + { + _cache.Remove(model.Id.ToString()); + _cache.Remove($"SiteId_{model.InternalId}"); + + if (model.IsDefault) + { + _cache.Remove($"Site_{Guid.Empty}"); + } + _cache.Remove(SITE_MAPPINGS); + } + } + + /// + /// Removes the given model from cache. + /// + /// The model + private void RemoveContentFromCache(T model) where T : Models.SiteContentBase + { + _cache?.Remove($"SiteContent_{model.Id}"); + } + } +} diff --git a/core/Piranha/Services/SiteTypeService.cs b/core/Piranha/Services/SiteTypeService.cs new file mode 100644 index 000000000..afc68174c --- /dev/null +++ b/core/Piranha/Services/SiteTypeService.cs @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Piranha.Models; +using Piranha.Repositories; + +namespace Piranha.Services +{ + public class SiteTypeService + { + private readonly ISiteTypeRepository _repo; + private readonly ICache _cache; + + /// + /// Default constructor. + /// + /// The main repository + /// The optional model cache + public SiteTypeService(ISiteTypeRepository repo, ICache cache) + { + _repo = repo; + + if ((int)App.CacheLevel > 1) + { + _cache = cache; + } + } + + /// + /// Gets all available models. + /// + /// The available models + public Task> GetAllAsync() + { + return GetTypes(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public async Task GetByIdAsync(string id) + { + var types = await GetTypes(); + + return types.FirstOrDefault(t => t.Id == id); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveAsync(SiteType model) + { + // Validate model + var context = new ValidationContext(model); + Validator.ValidateObject(model, context, true); + + // Call hooks & save + App.Hooks.OnBeforeSave(model); + await _repo.Save(model); + App.Hooks.OnAfterSave(model); + + // Clear cache + _cache?.Remove("Piranha_SiteTypes"); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task DeleteAsync(string id) + { + var model = await _repo.GetById(id); + + if (model != null) + { + await DeleteAsync(model); + } + } + + /// + /// Deletes the given model. + /// + /// The model + public async Task DeleteAsync(SiteType model) + { + // Call hooks & delete + App.Hooks.OnBeforeDelete(model); + await _repo.Delete(model.Id); + App.Hooks.OnAfterDelete(model); + + // Clear cache + _cache?.Remove("Piranha_SiteTypes"); + } + + /// + /// Reloads the page types from the database. + /// + private async Task> GetTypes() + { + var types = _cache?.Get>("Piranha_SiteTypes"); + + if (types == null) + { + types = await _repo.GetAll(); + + _cache?.Set("Piranha_SiteTypes", types); + } + return types; + } + } +} diff --git a/core/Piranha/Utils.cs b/core/Piranha/Utils.cs index e7017db96..c77c60879 100644 --- a/core/Piranha/Utils.cs +++ b/core/Piranha/Utils.cs @@ -15,6 +15,7 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; +using Newtonsoft.Json; namespace Piranha { @@ -199,5 +200,60 @@ public static string GetAssemblyVersion(Assembly assembly) return $"{version.Major}.{version.Minor}.{version.Build}"; } + + /// + /// Clones the entire given object into a new instance. + /// + /// The object to clone + /// The object type + /// The cloned instance + public static T DeepClone(T obj) + { + if (obj == null) + return obj; + + var settings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }; + var json = JsonConvert.SerializeObject(obj, settings); + + return JsonConvert.DeserializeObject(json, settings); + } + + /// + /// Gets the value of the property with the given name for the + /// given instance. + /// + /// The property name + /// The object instance + /// The property value + public static object GetPropertyValue(this Type type, string propertyName, object instance) + { + var property = type.GetProperty(propertyName, App.PropertyBindings); + + if (property != null) + { + return property.GetValue(instance); + } + return null; + } + + /// + /// Sets the value of the property with the given name for the + /// given instance. + /// + /// The property name + /// The object instance + /// The value to set + public static void SetPropertyValue(this Type type, string propertyName, object instance, object value) + { + var property = type.GetProperty(propertyName, App.PropertyBindings); + + if (property != null) + { + property.SetValue(instance, value); + } + } } } diff --git a/core/Piranha/Web/AliasRouter.cs b/core/Piranha/Web/AliasRouter.cs index 6fcfe8d77..3b38462e8 100644 --- a/core/Piranha/Web/AliasRouter.cs +++ b/core/Piranha/Web/AliasRouter.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Web { @@ -21,16 +23,16 @@ public class AliasRouter /// The requested url /// The requested site id /// The piranha response, null if no matching page was found - public static IRouteResponse Invoke(IApi api, string url, Guid siteId) + public static async Task InvokeAsync(IApi api, string url, Guid siteId) { - if (!String.IsNullOrWhiteSpace(url) && url.Length > 1) + if (!String.IsNullOrWhiteSpace(url) && url.Length > 1) { // Check if we can find an alias with the requested url - var alias = api.Aliases.GetByAliasUrl(url, siteId); + var alias = await api.Aliases.GetByAliasUrlAsync(url, siteId); - if (alias != null) + if (alias != null) { - return new RouteResponse + return new RouteResponse { IsPublished = true, RedirectUrl = alias.RedirectUrl, diff --git a/core/Piranha/Web/ArchiveRouter.cs b/core/Piranha/Web/ArchiveRouter.cs index a77b35c1c..c9bb7a407 100644 --- a/core/Piranha/Web/ArchiveRouter.cs +++ b/core/Piranha/Web/ArchiveRouter.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Web { @@ -21,7 +23,7 @@ public class ArchiveRouter /// The requested url /// The requested site id /// The piranha response, null if no matching page was found - public static IRouteResponse Invoke(IApi api, string url, Guid siteId) + public static async Task InvokeAsync(IApi api, string url, Guid siteId) { if (!String.IsNullOrWhiteSpace(url) && url.Length > 1) { @@ -29,7 +31,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) if (segments.Length >= 1) { - var blog = api.Pages.GetBySlug(segments[0], siteId); + var blog = await api.Pages.GetBySlugAsync(segments[0], siteId); if (blog != null && blog.ContentType == "Blog") { @@ -83,7 +85,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) { try { - categoryId = api.Categories.GetBySlug(blog.Id, segments[n])?.Id; + categoryId = (await api.Posts.GetCategoryBySlugAsync(blog.Id, segments[n]))?.Id; if (!categoryId.HasValue) categoryId = Guid.Empty; @@ -98,7 +100,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) { try { - tagId = api.Tags.GetBySlug(blog.Id, segments[n])?.Id; + tagId = (await api.Posts.GetTagBySlugAsync(blog.Id, segments[n]))?.Id; if (!tagId.HasValue) tagId = Guid.Empty; @@ -115,7 +117,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) { page = Convert.ToInt32(segments[n]); } - catch + catch { // We don't care about the exception, we just // discard malformed input @@ -132,7 +134,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) if (year.Value > DateTime.Now.Year) year = DateTime.Now.Year; } - catch + catch { // We don't care about the exception, we just // discard malformed input @@ -144,8 +146,8 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) { month = Math.Max(Math.Min(Convert.ToInt32(segments[n]), 12), 1); } - catch - { + catch + { // We don't care about the exception, we just // discard malformed input } diff --git a/core/Piranha/Web/PageRouter.cs b/core/Piranha/Web/PageRouter.cs index e8349df84..fdfffbd93 100644 --- a/core/Piranha/Web/PageRouter.cs +++ b/core/Piranha/Web/PageRouter.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Web { @@ -21,7 +23,7 @@ public class PageRouter /// The requested url /// The requested site id /// The piranha response, null if no matching page was found - public static IRouteResponse Invoke(IApi api, string url, Guid siteId) + public static async Task InvokeAsync(IApi api, string url, Guid siteId) { if (!String.IsNullOrWhiteSpace(url) && url.Length > 1) { @@ -33,15 +35,15 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) for (var n = include; n > 0; n--) { var slug = string.Join("/", segments.Subset(0, n)); - var page = api.Pages.GetBySlug(slug, siteId); + var page = await api.Pages.GetBySlugAsync(slug, siteId); if (page != null && page.ContentType == "Page") { if (string.IsNullOrWhiteSpace(page.RedirectUrl)) { - var site = api.Sites.GetById(siteId); + var site = await api.Sites.GetByIdAsync(siteId); var route = page.Route ?? "/page"; - var lastModified = !site.ContentLastModified.HasValue || page.LastModified > site.ContentLastModified + var lastModified = !site.ContentLastModified.HasValue || page.LastModified > site.ContentLastModified ? page.LastModified : site.ContentLastModified.Value; if (n < include) diff --git a/core/Piranha/Web/PostRouter.cs b/core/Piranha/Web/PostRouter.cs index f1fa426d8..53fea5af8 100644 --- a/core/Piranha/Web/PostRouter.cs +++ b/core/Piranha/Web/PostRouter.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Web { @@ -21,7 +23,7 @@ public class PostRouter /// The requested url /// The requested site id /// The piranha response, null if no matching post was found - public static IRouteResponse Invoke(IApi api, string url, Guid siteId) + public static async Task InvokeAsync(IApi api, string url, Guid siteId) { if (!String.IsNullOrWhiteSpace(url) && url.Length > 1) { @@ -29,14 +31,14 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) if (segments.Length >= 2) { - var post = api.Posts.GetBySlug(segments[0], segments[1], siteId); + var post = await api.Posts.GetBySlugAsync(segments[0], segments[1], siteId); if (post != null) { - var page = api.Pages.GetById(post.BlogId); - var site = api.Sites.GetById(page.SiteId); + var page = await api.Pages.GetByIdAsync(post.BlogId); + var site = await api.Sites.GetByIdAsync(page.SiteId); var route = post.Route ?? "/post"; - var lastModified = !site.ContentLastModified.HasValue || post.LastModified > site.ContentLastModified + var lastModified = !site.ContentLastModified.HasValue || post.LastModified > site.ContentLastModified ? post.LastModified : site.ContentLastModified.Value; if (segments.Length > 2) diff --git a/core/Piranha/Web/StartPageRouter.cs b/core/Piranha/Web/StartPageRouter.cs index 16369c8e0..3c5d94cf6 100644 --- a/core/Piranha/Web/StartPageRouter.cs +++ b/core/Piranha/Web/StartPageRouter.cs @@ -1,14 +1,16 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; +using System.Threading.Tasks; +using Piranha.Services; namespace Piranha.Web { @@ -21,18 +23,18 @@ public class StartPageRouter /// The requested url /// The optional hostname /// The piranha response, null if no matching page was found - public static IRouteResponse Invoke(IApi api, string url, Guid siteId) + public static async Task InvokeAsync(IApi api, string url, Guid siteId) { if (string.IsNullOrWhiteSpace(url) || url == "/") { - var page = api.Pages.GetStartpage(siteId); + var page = await api.Pages.GetStartpageAsync(siteId); if (page != null) { if (page.ContentType == "Page") { - var site = api.Sites.GetById(siteId); - var lastModified = !site.ContentLastModified.HasValue || page.LastModified > site.ContentLastModified + var site = await api.Sites.GetByIdAsync(siteId); + var lastModified = !site.ContentLastModified.HasValue || page.LastModified > site.ContentLastModified ? page.LastModified : site.ContentLastModified.Value; return new RouteResponse @@ -50,7 +52,7 @@ public static IRouteResponse Invoke(IApi api, string url, Guid siteId) } else if (page.ContentType == "Blog") { - return ArchiveRouter.Invoke(api, $"/{page.Slug}", siteId); + return await ArchiveRouter.InvokeAsync(api, $"/{page.Slug}", siteId); } } } diff --git a/core/Piranha/Data/IModel.cs b/data/Piranha.Data.EF/Data/Alias.cs similarity index 51% rename from core/Piranha/Data/IModel.cs rename to data/Piranha.Data.EF/Data/Alias.cs index b006424d8..13fa7c397 100644 --- a/core/Piranha/Data/IModel.cs +++ b/data/Piranha.Data.EF/Data/Alias.cs @@ -1,25 +1,24 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using System; - -namespace Piranha.Data -{ - /// - /// Interface for models with a Guid id. - /// - public interface IModel - { - /// - /// Gets/sets the unique id. - /// - Guid Id { get; set; } - } -} +/* + * Copyright (c) 2018-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; + +namespace Piranha.Data +{ + [Serializable] + public sealed class Alias : Models.Alias + { + /// + /// Gets/sets the site this alias is for. + /// + /// + public Site Site { get; set; } + } +} \ No newline at end of file diff --git a/core/Piranha/Data/Block.cs b/data/Piranha.Data.EF/Data/Block.cs similarity index 87% rename from core/Piranha/Data/Block.cs rename to data/Piranha.Data.EF/Data/Block.cs index 3b853fbed..1b917aa49 100644 --- a/core/Piranha/Data/Block.cs +++ b/data/Piranha.Data.EF/Data/Block.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -17,7 +17,7 @@ namespace Piranha.Data /// /// Reusable content block. /// - public sealed class Block : IModel, ICreated, IModified + public sealed class Block { /// /// Gets/sets the unique id. @@ -28,7 +28,7 @@ public sealed class Block : IModel, ICreated, IModified /// This is not part of the data model. It's only used /// for internal mapping. /// - internal Guid? ParentId { get; set; } + public Guid? ParentId { get; set; } /// /// Gets/sets the CLR type of the block. @@ -65,6 +65,6 @@ public sealed class Block : IModel, ICreated, IModified /// /// Gets/sets the available page connections. /// - public IList PageBlocks { get; set; } = new List(); + //public IList PageBlocks { get; set; } = new List(); } } diff --git a/core/Piranha/Data/BlockField.cs b/data/Piranha.Data.EF/Data/BlockField.cs similarity index 93% rename from core/Piranha/Data/BlockField.cs rename to data/Piranha.Data.EF/Data/BlockField.cs index 5f296134e..0cf0f395e 100644 --- a/core/Piranha/Data/BlockField.cs +++ b/data/Piranha.Data.EF/Data/BlockField.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,7 +16,7 @@ namespace Piranha.Data /// /// Content field for a block. /// - public sealed class BlockField : IModel + public sealed class BlockField { /// /// Gets/sets the unique id. diff --git a/core/Piranha/Data/Category.cs b/data/Piranha.Data.EF/Data/Category.cs similarity index 82% rename from core/Piranha/Data/Category.cs rename to data/Piranha.Data.EF/Data/Category.cs index 97c44590c..77e2338a4 100644 --- a/core/Piranha/Data/Category.cs +++ b/data/Piranha.Data.EF/Data/Category.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2017 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Data { [Serializable] - public sealed class Category : Taxonomy, IModel, ICreated, IModified + public sealed class Category : Taxonomy { /// /// Gets/sets the id of the blog page this diff --git a/core/Piranha/Data/Content.cs b/data/Piranha.Data.EF/Data/Content.cs similarity index 91% rename from core/Piranha/Data/Content.cs rename to data/Piranha.Data.EF/Data/Content.cs index f1c727542..460c13498 100644 --- a/core/Piranha/Data/Content.cs +++ b/data/Piranha.Data.EF/Data/Content.cs @@ -3,13 +3,14 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace Piranha.Data { @@ -24,6 +25,8 @@ public abstract class Content where T : ContentField /// /// Gets/sets the main title. /// + [Required] + [StringLength(128)] public string Title { get; set; } /// diff --git a/core/Piranha/Data/ContentField.cs b/data/Piranha.Data.EF/Data/ContentField.cs similarity index 100% rename from core/Piranha/Data/ContentField.cs rename to data/Piranha.Data.EF/Data/ContentField.cs diff --git a/core/Piranha/Data/ContentType.cs b/data/Piranha.Data.EF/Data/ContentType.cs similarity index 100% rename from core/Piranha/Data/ContentType.cs rename to data/Piranha.Data.EF/Data/ContentType.cs diff --git a/core/Piranha/Data/IContentBlock.cs b/data/Piranha.Data.EF/Data/IContentBlock.cs similarity index 100% rename from core/Piranha/Data/IContentBlock.cs rename to data/Piranha.Data.EF/Data/IContentBlock.cs diff --git a/data/Piranha.Data.EF/Data/Media.cs b/data/Piranha.Data.EF/Data/Media.cs new file mode 100644 index 000000000..03894ec7e --- /dev/null +++ b/data/Piranha.Data.EF/Data/Media.cs @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; + +namespace Piranha.Data +{ + [Serializable] + public sealed class Media : Models.Media + { + /// + /// Gets/sets the optional folder id. + /// + public Guid? FolderId { get; set; } + + /// + /// Gets/sets the optional folder. + /// + public MediaFolder Folder { get; set; } + + /// + /// Gets/sets the available versions. + /// + public IList Versions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/Data/MediaFolder.cs b/data/Piranha.Data.EF/Data/MediaFolder.cs new file mode 100644 index 000000000..53a819065 --- /dev/null +++ b/data/Piranha.Data.EF/Data/MediaFolder.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; + +namespace Piranha.Data +{ + [Serializable] + public sealed class MediaFolder : Models.MediaFolder + { + /// + /// Gets/sets the optional parent id. + /// + public Guid? ParentId { get; set; } + + /// + /// Gets/sets the available media. + /// + public IList Media { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/Data/MediaVersion.cs b/data/Piranha.Data.EF/Data/MediaVersion.cs new file mode 100644 index 000000000..100f25644 --- /dev/null +++ b/data/Piranha.Data.EF/Data/MediaVersion.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using Newtonsoft.Json; + +namespace Piranha.Data +{ + [Serializable] + public sealed class MediaVersion : Models.MediaVersion + { + /// + /// Gets/sets the id of the media this is + /// a version of. + /// + public Guid MediaId { get; set; } + + /// + /// Gets/sets the media this is a version of. + /// + [JsonIgnore] + public Media Media { get; set; } + } +} \ No newline at end of file diff --git a/core/Piranha/Data/Page.cs b/data/Piranha.Data.EF/Data/Page.cs similarity index 91% rename from core/Piranha/Data/Page.cs rename to data/Piranha.Data.EF/Data/Page.cs index 42c071d7a..5875fd7a8 100644 --- a/core/Piranha/Data/Page.cs +++ b/data/Piranha.Data.EF/Data/Page.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011-2018 Håkan Edling + * Copyright (c) 2011-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; @@ -14,7 +14,7 @@ namespace Piranha.Data { [Serializable] - public sealed class Page : RoutedContent, IModel, ICreated, IModified + public sealed class Page : RoutedContent { /// /// Gets/sets the page type id. diff --git a/core/Piranha/Data/PageBlock.cs b/data/Piranha.Data.EF/Data/PageBlock.cs similarity index 92% rename from core/Piranha/Data/PageBlock.cs rename to data/Piranha.Data.EF/Data/PageBlock.cs index 78ccd3cda..cbe19e5da 100644 --- a/core/Piranha/Data/PageBlock.cs +++ b/data/Piranha.Data.EF/Data/PageBlock.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,7 +16,7 @@ namespace Piranha.Data /// /// Connection between a page and a block. /// - public sealed class PageBlock : IModel, IContentBlock + public sealed class PageBlock : IContentBlock { /// /// Gets/sets the unique id. diff --git a/core/Piranha/Data/PageField.cs b/data/Piranha.Data.EF/Data/PageField.cs similarity index 78% rename from core/Piranha/Data/PageField.cs rename to data/Piranha.Data.EF/Data/PageField.cs index d20e8e792..998fcce73 100644 --- a/core/Piranha/Data/PageField.cs +++ b/data/Piranha.Data.EF/Data/PageField.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2017 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Data { [Serializable] - public sealed class PageField : ContentField, IModel + public sealed class PageField : ContentField { /// /// Gets/sets the page id. diff --git a/core/Piranha/Data/PageType.cs b/data/Piranha.Data.EF/Data/PageType.cs similarity index 63% rename from core/Piranha/Data/PageType.cs rename to data/Piranha.Data.EF/Data/PageType.cs index a27f09fc6..d52d6dca6 100644 --- a/core/Piranha/Data/PageType.cs +++ b/data/Piranha.Data.EF/Data/PageType.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,5 +13,5 @@ namespace Piranha.Data { [Serializable] - public sealed class PageType : ContentType, ICreated, IModified { } + public sealed class PageType : ContentType { } } diff --git a/data/Piranha.Data.EF/Data/Param.cs b/data/Piranha.Data.EF/Data/Param.cs new file mode 100644 index 000000000..bde696101 --- /dev/null +++ b/data/Piranha.Data.EF/Data/Param.cs @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; + +namespace Piranha.Data +{ + /// + /// String parameter. + /// + [Serializable] + public sealed class Param : Models.Param { } +} diff --git a/core/Piranha/Data/Post.cs b/data/Piranha.Data.EF/Data/Post.cs similarity index 89% rename from core/Piranha/Data/Post.cs rename to data/Piranha.Data.EF/Data/Post.cs index db2dc2f8a..a79d9f281 100644 --- a/core/Piranha/Data/Post.cs +++ b/data/Piranha.Data.EF/Data/Post.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011-2018 Håkan Edling + * Copyright (c) 2011-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; @@ -14,7 +14,7 @@ namespace Piranha.Data { [Serializable] - public sealed class Post : RoutedContent, IModel, ICreated, IModified + public sealed class Post : RoutedContent { /// /// Gets/sets the post type id. diff --git a/core/Piranha/Data/PostBlock.cs b/data/Piranha.Data.EF/Data/PostBlock.cs similarity index 92% rename from core/Piranha/Data/PostBlock.cs rename to data/Piranha.Data.EF/Data/PostBlock.cs index f215f1515..368e46c0f 100644 --- a/core/Piranha/Data/PostBlock.cs +++ b/data/Piranha.Data.EF/Data/PostBlock.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -16,7 +16,7 @@ namespace Piranha.Data /// /// Connection between a post and a block. /// - public sealed class PostBlock : IModel, IContentBlock + public sealed class PostBlock : IContentBlock { /// /// Gets/sets the unique id. diff --git a/core/Piranha/Data/PostField.cs b/data/Piranha.Data.EF/Data/PostField.cs similarity index 82% rename from core/Piranha/Data/PostField.cs rename to data/Piranha.Data.EF/Data/PostField.cs index a57c62bac..6bb1985af 100644 --- a/core/Piranha/Data/PostField.cs +++ b/data/Piranha.Data.EF/Data/PostField.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Data { [Serializable] - public sealed class PostField : ContentField, IModel + public sealed class PostField : ContentField { /// /// Gets/sets the post id. diff --git a/core/Piranha/Data/PostTag.cs b/data/Piranha.Data.EF/Data/PostTag.cs similarity index 99% rename from core/Piranha/Data/PostTag.cs rename to data/Piranha.Data.EF/Data/PostTag.cs index 4b9dc1405..41339feed 100644 --- a/core/Piranha/Data/PostTag.cs +++ b/data/Piranha.Data.EF/Data/PostTag.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; diff --git a/core/Piranha/Data/PostType.cs b/data/Piranha.Data.EF/Data/PostType.cs similarity index 64% rename from core/Piranha/Data/PostType.cs rename to data/Piranha.Data.EF/Data/PostType.cs index 204c83153..f0ecc592e 100644 --- a/core/Piranha/Data/PostType.cs +++ b/data/Piranha.Data.EF/Data/PostType.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,5 +13,5 @@ namespace Piranha.Data { [Serializable] - public sealed class PostType : ContentType, ICreated, IModified { } + public sealed class PostType : ContentType { } } diff --git a/core/Piranha/Data/RoutedContent.cs b/data/Piranha.Data.EF/Data/RoutedContent.cs similarity index 100% rename from core/Piranha/Data/RoutedContent.cs rename to data/Piranha.Data.EF/Data/RoutedContent.cs diff --git a/core/Piranha/Data/Site.cs b/data/Piranha.Data.EF/Data/Site.cs similarity index 87% rename from core/Piranha/Data/Site.cs rename to data/Piranha.Data.EF/Data/Site.cs index 83dece9df..9a47ef43b 100644 --- a/core/Piranha/Data/Site.cs +++ b/data/Piranha.Data.EF/Data/Site.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Data { [Serializable] - public sealed class Site : Content, IModel, ICreated, IModified + public sealed class Site : Content { /// /// Gets/sets the optional site type id. diff --git a/core/Piranha/Data/SiteField.cs b/data/Piranha.Data.EF/Data/SiteField.cs similarity index 82% rename from core/Piranha/Data/SiteField.cs rename to data/Piranha.Data.EF/Data/SiteField.cs index 0b15cb696..a968cafc8 100644 --- a/core/Piranha/Data/SiteField.cs +++ b/data/Piranha.Data.EF/Data/SiteField.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,7 +13,7 @@ namespace Piranha.Data { [Serializable] - public sealed class SiteField : ContentField, IModel + public sealed class SiteField : ContentField { /// /// Gets/sets the site id. diff --git a/core/Piranha/Data/SiteType.cs b/data/Piranha.Data.EF/Data/SiteType.cs similarity index 64% rename from core/Piranha/Data/SiteType.cs rename to data/Piranha.Data.EF/Data/SiteType.cs index 6d7827470..d5a9aae51 100644 --- a/core/Piranha/Data/SiteType.cs +++ b/data/Piranha.Data.EF/Data/SiteType.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,5 +13,5 @@ namespace Piranha.Data { [Serializable] - public sealed class SiteType : ContentType, ICreated, IModified { } + public sealed class SiteType : ContentType { } } diff --git a/core/Piranha/Data/Tag.cs b/data/Piranha.Data.EF/Data/Tag.cs similarity index 75% rename from core/Piranha/Data/Tag.cs rename to data/Piranha.Data.EF/Data/Tag.cs index 19343374e..210b660a5 100644 --- a/core/Piranha/Data/Tag.cs +++ b/data/Piranha.Data.EF/Data/Tag.cs @@ -1,11 +1,11 @@ /* - * Copyright (c) 2016-2017 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -13,8 +13,8 @@ namespace Piranha.Data { [Serializable] - public sealed class Tag : Taxonomy, IModel, ICreated, IModified - { + public sealed class Tag : Taxonomy + { /// /// Gets/sets the id of the blog page this /// category belongs to. @@ -24,6 +24,6 @@ public sealed class Tag : Taxonomy, IModel, ICreated, IModified /// /// Gets/sets the blog page this category belongs to. /// - public Page Blog { get; set; } + public Page Blog { get; set; } } } diff --git a/core/Piranha/Data/Taxonomy.cs b/data/Piranha.Data.EF/Data/Taxonomy.cs similarity index 99% rename from core/Piranha/Data/Taxonomy.cs rename to data/Piranha.Data.EF/Data/Taxonomy.cs index 79424555e..d96feb49c 100644 --- a/core/Piranha/Data/Taxonomy.cs +++ b/data/Piranha.Data.EF/Data/Taxonomy.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; diff --git a/core/Piranha/Db.cs b/data/Piranha.Data.EF/Db.cs similarity index 99% rename from core/Piranha/Db.cs rename to data/Piranha.Data.EF/Db.cs index 66b01521c..923ae1653 100644 --- a/core/Piranha/Db.cs +++ b/data/Piranha.Data.EF/Db.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using Microsoft.EntityFrameworkCore; @@ -100,7 +100,7 @@ public sealed class Db : DbContext, IDb /// /// Gets/sets the post field set. - /// + /// public DbSet PostFields { get; set; } /// @@ -120,7 +120,7 @@ public sealed class Db : DbContext, IDb /// /// Gets/sets the site field set. - /// + /// public DbSet SiteFields { get; set; } /// @@ -170,6 +170,7 @@ protected override void OnModelCreating(ModelBuilder mb) mb.Entity().ToTable("Piranha_Blocks"); mb.Entity().Property(b => b.CLRType).IsRequired().HasMaxLength(256); mb.Entity().Property(b => b.Title).HasMaxLength(128); + mb.Entity().Ignore(b => b.ParentId); mb.Entity().ToTable("Piranha_BlockFields"); mb.Entity().Property(f => f.FieldId).IsRequired().HasMaxLength(64); @@ -267,7 +268,7 @@ protected override void OnModelCreating(ModelBuilder mb) mb.Entity().ToTable("Piranha_SiteTypes"); mb.Entity().Property(s => s.Id).HasMaxLength(64).IsRequired(); - mb.Entity().Property(s => s.CLRType).HasMaxLength(256); + mb.Entity().Property(s => s.CLRType).HasMaxLength(256); mb.Entity().ToTable("Piranha_Tags"); mb.Entity().Property(t => t.Title).IsRequired().HasMaxLength(64); @@ -363,7 +364,7 @@ private void Seed() Created = DateTime.Now, LastModified = DateTime.Now }); - + // // Make sure we don't have NULL values in Piranha_MediaVersions.FileExtension // diff --git a/core/Piranha/DbFactory.cs b/data/Piranha.Data.EF/DbFactory.cs similarity index 100% rename from core/Piranha/DbFactory.cs rename to data/Piranha.Data.EF/DbFactory.cs diff --git a/core/Piranha/IDb.cs b/data/Piranha.Data.EF/IDb.cs similarity index 93% rename from core/Piranha/IDb.cs rename to data/Piranha.Data.EF/IDb.cs index d0341ab00..0be0430f7 100644 --- a/core/Piranha/IDb.cs +++ b/data/Piranha.Data.EF/IDb.cs @@ -10,6 +10,8 @@ using Microsoft.EntityFrameworkCore; using System; +using System.Threading; +using System.Threading.Tasks; namespace Piranha { @@ -132,5 +134,10 @@ public interface IDb : IDisposable /// Saves the changes made to the context. /// int SaveChanges(); + + /// + /// Saves the changes made to the context. + /// + Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/core/Piranha/Migrations/20171129204436_InitialCreate.Designer.cs b/data/Piranha.Data.EF/Migrations/20171129204436_InitialCreate.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20171129204436_InitialCreate.Designer.cs rename to data/Piranha.Data.EF/Migrations/20171129204436_InitialCreate.Designer.cs diff --git a/core/Piranha/Migrations/20171129204436_InitialCreate.cs b/data/Piranha.Data.EF/Migrations/20171129204436_InitialCreate.cs similarity index 100% rename from core/Piranha/Migrations/20171129204436_InitialCreate.cs rename to data/Piranha.Data.EF/Migrations/20171129204436_InitialCreate.cs diff --git a/core/Piranha/Migrations/20171215130248_AddBlogging.Designer.cs b/data/Piranha.Data.EF/Migrations/20171215130248_AddBlogging.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20171215130248_AddBlogging.Designer.cs rename to data/Piranha.Data.EF/Migrations/20171215130248_AddBlogging.Designer.cs diff --git a/core/Piranha/Migrations/20171215130248_AddBlogging.cs b/data/Piranha.Data.EF/Migrations/20171215130248_AddBlogging.cs similarity index 100% rename from core/Piranha/Migrations/20171215130248_AddBlogging.cs rename to data/Piranha.Data.EF/Migrations/20171215130248_AddBlogging.cs diff --git a/core/Piranha/Migrations/20180124170844_AddMediaVersions.Designer.cs b/data/Piranha.Data.EF/Migrations/20180124170844_AddMediaVersions.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180124170844_AddMediaVersions.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180124170844_AddMediaVersions.Designer.cs diff --git a/core/Piranha/Migrations/20180124170844_AddMediaVersions.cs b/data/Piranha.Data.EF/Migrations/20180124170844_AddMediaVersions.cs similarity index 100% rename from core/Piranha/Migrations/20180124170844_AddMediaVersions.cs rename to data/Piranha.Data.EF/Migrations/20180124170844_AddMediaVersions.cs diff --git a/core/Piranha/Migrations/20180203063923_AddAliases.Designer.cs b/data/Piranha.Data.EF/Migrations/20180203063923_AddAliases.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180203063923_AddAliases.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180203063923_AddAliases.Designer.cs diff --git a/core/Piranha/Migrations/20180203063923_AddAliases.cs b/data/Piranha.Data.EF/Migrations/20180203063923_AddAliases.cs similarity index 100% rename from core/Piranha/Migrations/20180203063923_AddAliases.cs rename to data/Piranha.Data.EF/Migrations/20180203063923_AddAliases.cs diff --git a/core/Piranha/Migrations/20180331212736_AddCLRContentType.Designer.cs b/data/Piranha.Data.EF/Migrations/20180331212736_AddCLRContentType.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180331212736_AddCLRContentType.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180331212736_AddCLRContentType.Designer.cs diff --git a/core/Piranha/Migrations/20180331212736_AddCLRContentType.cs b/data/Piranha.Data.EF/Migrations/20180331212736_AddCLRContentType.cs similarity index 100% rename from core/Piranha/Migrations/20180331212736_AddCLRContentType.cs rename to data/Piranha.Data.EF/Migrations/20180331212736_AddCLRContentType.cs diff --git a/core/Piranha/Migrations/20180424093712_AddBlocks.Designer.cs b/data/Piranha.Data.EF/Migrations/20180424093712_AddBlocks.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180424093712_AddBlocks.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180424093712_AddBlocks.Designer.cs diff --git a/core/Piranha/Migrations/20180424093712_AddBlocks.cs b/data/Piranha.Data.EF/Migrations/20180424093712_AddBlocks.cs similarity index 100% rename from core/Piranha/Migrations/20180424093712_AddBlocks.cs rename to data/Piranha.Data.EF/Migrations/20180424093712_AddBlocks.cs diff --git a/core/Piranha/Migrations/20180430082044_AddOriginalPageIdToPage.Designer.cs b/data/Piranha.Data.EF/Migrations/20180430082044_AddOriginalPageIdToPage.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180430082044_AddOriginalPageIdToPage.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180430082044_AddOriginalPageIdToPage.Designer.cs diff --git a/core/Piranha/Migrations/20180430082044_AddOriginalPageIdToPage.cs b/data/Piranha.Data.EF/Migrations/20180430082044_AddOriginalPageIdToPage.cs similarity index 100% rename from core/Piranha/Migrations/20180430082044_AddOriginalPageIdToPage.cs rename to data/Piranha.Data.EF/Migrations/20180430082044_AddOriginalPageIdToPage.cs diff --git a/core/Piranha/Migrations/20180514215842_AddPostBlocks.Designer.cs b/data/Piranha.Data.EF/Migrations/20180514215842_AddPostBlocks.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180514215842_AddPostBlocks.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180514215842_AddPostBlocks.Designer.cs diff --git a/core/Piranha/Migrations/20180514215842_AddPostBlocks.cs b/data/Piranha.Data.EF/Migrations/20180514215842_AddPostBlocks.cs similarity index 100% rename from core/Piranha/Migrations/20180514215842_AddPostBlocks.cs rename to data/Piranha.Data.EF/Migrations/20180514215842_AddPostBlocks.cs diff --git a/core/Piranha/Migrations/20180529100047_AddSiteContent.Designer.cs b/data/Piranha.Data.EF/Migrations/20180529100047_AddSiteContent.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180529100047_AddSiteContent.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180529100047_AddSiteContent.Designer.cs diff --git a/core/Piranha/Migrations/20180529100047_AddSiteContent.cs b/data/Piranha.Data.EF/Migrations/20180529100047_AddSiteContent.cs similarity index 100% rename from core/Piranha/Migrations/20180529100047_AddSiteContent.cs rename to data/Piranha.Data.EF/Migrations/20180529100047_AddSiteContent.cs diff --git a/core/Piranha/Migrations/20180531092411_AddMediaVersionExtension.Designer.cs b/data/Piranha.Data.EF/Migrations/20180531092411_AddMediaVersionExtension.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180531092411_AddMediaVersionExtension.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180531092411_AddMediaVersionExtension.Designer.cs diff --git a/core/Piranha/Migrations/20180531092411_AddMediaVersionExtension.cs b/data/Piranha.Data.EF/Migrations/20180531092411_AddMediaVersionExtension.cs similarity index 100% rename from core/Piranha/Migrations/20180531092411_AddMediaVersionExtension.cs rename to data/Piranha.Data.EF/Migrations/20180531092411_AddMediaVersionExtension.cs diff --git a/core/Piranha/Migrations/20180611095956_AddBlockParentId.Designer.cs b/data/Piranha.Data.EF/Migrations/20180611095956_AddBlockParentId.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20180611095956_AddBlockParentId.Designer.cs rename to data/Piranha.Data.EF/Migrations/20180611095956_AddBlockParentId.Designer.cs diff --git a/core/Piranha/Migrations/20180611095956_AddBlockParentId.cs b/data/Piranha.Data.EF/Migrations/20180611095956_AddBlockParentId.cs similarity index 100% rename from core/Piranha/Migrations/20180611095956_AddBlockParentId.cs rename to data/Piranha.Data.EF/Migrations/20180611095956_AddBlockParentId.cs diff --git a/core/Piranha/Migrations/20181016205919_AddSiteContentLastModified.Designer.cs b/data/Piranha.Data.EF/Migrations/20181016205919_AddSiteContentLastModified.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20181016205919_AddSiteContentLastModified.Designer.cs rename to data/Piranha.Data.EF/Migrations/20181016205919_AddSiteContentLastModified.Designer.cs diff --git a/core/Piranha/Migrations/20181016205919_AddSiteContentLastModified.cs b/data/Piranha.Data.EF/Migrations/20181016205919_AddSiteContentLastModified.cs similarity index 100% rename from core/Piranha/Migrations/20181016205919_AddSiteContentLastModified.cs rename to data/Piranha.Data.EF/Migrations/20181016205919_AddSiteContentLastModified.cs diff --git a/core/Piranha/Migrations/20181019125207_AddSiteCulture.Designer.cs b/data/Piranha.Data.EF/Migrations/20181019125207_AddSiteCulture.Designer.cs similarity index 100% rename from core/Piranha/Migrations/20181019125207_AddSiteCulture.Designer.cs rename to data/Piranha.Data.EF/Migrations/20181019125207_AddSiteCulture.Designer.cs diff --git a/core/Piranha/Migrations/20181019125207_AddSiteCulture.cs b/data/Piranha.Data.EF/Migrations/20181019125207_AddSiteCulture.cs similarity index 100% rename from core/Piranha/Migrations/20181019125207_AddSiteCulture.cs rename to data/Piranha.Data.EF/Migrations/20181019125207_AddSiteCulture.cs diff --git a/core/Piranha/Migrations/DbModelSnapshot.cs b/data/Piranha.Data.EF/Migrations/DbModelSnapshot.cs similarity index 100% rename from core/Piranha/Migrations/DbModelSnapshot.cs rename to data/Piranha.Data.EF/Migrations/DbModelSnapshot.cs diff --git a/data/Piranha.Data.EF/Module.cs b/data/Piranha.Data.EF/Module.cs new file mode 100644 index 000000000..922c6f2cd --- /dev/null +++ b/data/Piranha.Data.EF/Module.cs @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System.Collections.Generic; +using AutoMapper; +using Piranha.Extend; +using Piranha.Security; + +namespace Piranha.Data.EF +{ + /// + /// The identity module. + /// + public class Module : IModule + { + public static IMapper Mapper { get; private set; } + + /// + /// Gets the Author + /// + public string Author => "Piranha"; + + /// + /// Gets the Name + /// + public string Name => "Piranha.Data.EF"; + + /// + /// Gets the Version + /// + public string Version => Piranha.Utils.GetAssemblyVersion(GetType().Assembly); + + /// + /// Gets the release date + /// + public string ReleaseDate => "2019-03-03"; + + /// + /// Gets the description + /// + public string Description => "Data implementation for Entity Framework Core."; + + /// + /// Gets the package url. + /// + public string PackageURL => "https://www.nuget.org/packages/Piranha.Data.EF"; + + /// + /// Create automapping. + /// + static Module() + { + var mapperConfig = new MapperConfiguration(cfg => + { + cfg.CreateMap() + .ForMember(a => a.Id, o => o.Ignore()) + .ForMember(a => a.Created, o => o.Ignore()); + cfg.CreateMap() + .ForMember(c => c.Id, o => o.Ignore()) + .ForMember(c => c.Created, o => o.Ignore()); + cfg.CreateMap() + .ForMember(f => f.Id, o => o.Ignore()) + .ForMember(f => f.Created, o => o.Ignore()) + .ForMember(f => f.Media, o => o.Ignore()); + cfg.CreateMap() + .ForMember(f => f.Level, o => o.Ignore()) + .ForMember(f => f.Items, o => o.Ignore()); + cfg.CreateMap() + .ForMember(p => p.TypeId, o => o.MapFrom(m => m.PageTypeId)) + .ForMember(p => p.Permalink, o => o.MapFrom(m => "/" + m.Slug)) + .ForMember(p => p.Blocks, o => o.Ignore()); + cfg.CreateMap() + .ForMember(p => p.PageTypeId, o => o.MapFrom(m => m.TypeId)) + .ForMember(p => p.Blocks, o => o.Ignore()) + .ForMember(p => p.Fields, o => o.Ignore()) + .ForMember(p => p.Created, o => o.Ignore()) + .ForMember(p => p.LastModified, o => o.Ignore()) + .ForMember(p => p.PageType, o => o.Ignore()) + .ForMember(p => p.Site, o => o.Ignore()) + .ForMember(p => p.Parent, o => o.Ignore()); + cfg.CreateMap() + .ForMember(p => p.MenuTitle, o => o.Ignore()) + .ForMember(p => p.Level, o => o.Ignore()) + .ForMember(p => p.Items, o => o.Ignore()) + .ForMember(p => p.PageTypeName, o => o.Ignore()) + .ForMember(p => p.Permalink, o => o.MapFrom(d => !d.ParentId.HasValue && d.SortOrder == 0 ? "/" : "/" + d.Slug)); + cfg.CreateMap() + .ForMember(p => p.Id, o => o.Ignore()) + .ForMember(p => p.Created, o => o.Ignore()); + cfg.CreateMap() + .ForMember(p => p.TypeId, o => o.MapFrom(m => m.PostTypeId)) + .ForMember(p => p.Permalink, o => o.Ignore()) + .ForMember(p => p.Blocks, o => o.Ignore()); + cfg.CreateMap() + .ForMember(p => p.Id, o => o.MapFrom(m => m.TagId)) + .ForMember(p => p.Title, o => o.MapFrom(m => m.Tag.Title)) + .ForMember(p => p.Slug, o => o.MapFrom(m => m.Tag.Slug)); + cfg.CreateMap() + .ForMember(p => p.PostTypeId, o => o.MapFrom(m => m.TypeId)) + .ForMember(p => p.CategoryId, o => o.MapFrom(m => m.Category.Id)) + .ForMember(p => p.Blocks, o => o.Ignore()) + .ForMember(p => p.Fields, o => o.Ignore()) + .ForMember(p => p.Created, o => o.Ignore()) + .ForMember(p => p.LastModified, o => o.Ignore()) + .ForMember(p => p.PostType, o => o.Ignore()) + .ForMember(p => p.Blog, o => o.Ignore()) + .ForMember(p => p.Category, o => o.Ignore()) + .ForMember(p => p.Tags, o => o.Ignore()); + cfg.CreateMap() + .ForMember(s => s.Id, o => o.Ignore()) + .ForMember(s => s.Created, o => o.Ignore()); + cfg.CreateMap() + .ForMember(s => s.TypeId, o => o.MapFrom(m => m.SiteTypeId)); + cfg.CreateMap() + .ForMember(s => s.SiteTypeId, o => o.Ignore()) + .ForMember(s => s.InternalId, o => o.Ignore()) + .ForMember(s => s.Description, o => o.Ignore()) + .ForMember(s => s.Hostnames, o => o.Ignore()) + .ForMember(s => s.IsDefault, o => o.Ignore()) + .ForMember(s => s.Culture, o => o.Ignore()) + .ForMember(s => s.Fields, o => o.Ignore()) + .ForMember(s => s.Created, o => o.Ignore()) + .ForMember(s => s.LastModified, o => o.Ignore()) + .ForMember(s => s.ContentLastModified, o => o.Ignore()); + cfg.CreateMap() + .ForMember(t => t.Id, o => o.Ignore()) + .ForMember(t => t.Created, o => o.Ignore()); + }); + mapperConfig.AssertConfigurationIsValid(); + Mapper = mapperConfig.CreateMapper(); + } + + /// + /// Initializes the module. + /// + public void Init() { } + } +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/Piranha.Data.EF.csproj b/data/Piranha.Data.EF/Piranha.Data.EF.csproj new file mode 100644 index 000000000..59f9acb95 --- /dev/null +++ b/data/Piranha.Data.EF/Piranha.Data.EF.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 6.0.0-alpha1 + Piranha CMS + + + + + + + + + + + + + diff --git a/data/Piranha.Data.EF/PiranhaEFExtensions.cs b/data/Piranha.Data.EF/PiranhaEFExtensions.cs new file mode 100644 index 000000000..521d01532 --- /dev/null +++ b/data/Piranha.Data.EF/PiranhaEFExtensions.cs @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Piranha; +using Piranha.Repositories; +using Piranha.Services; +using System; + +public static class PiranhaEFExtensions +{ + /// + /// Adds the default services needed to run Piranha over + /// Entity Framework Core. + /// + /// The current service collection + /// The optional lifetime + /// The updated service collection + public static IServiceCollection AddPiranhaEF(this IServiceCollection services, + ServiceLifetime scope = ServiceLifetime.Scoped) + { + return AddPiranhaEF(services, scope); + } + + /// + /// Adds the default services needed to run Piranha over + /// Entity Framework Core. + /// + /// The current service collection + /// The optional lifetime + /// The DbContext type + /// The updated service collection + public static IServiceCollection AddPiranhaEF(this IServiceCollection services, + ServiceLifetime scope = ServiceLifetime.Scoped) where T : DbContext, IDb + { + // Add the identity module + App.Modules.Register(); + + // Register repositories + services.Add(new ServiceDescriptor(typeof(IAliasRepository), typeof(AliasRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IArchiveRepository), typeof(ArchiveRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IMediaRepository), typeof(MediaRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IPageRepository), typeof(PageRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IPageTypeRepository), typeof(PageTypeRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IParamRepository), typeof(ParamRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IPostRepository), typeof(PostRepository), scope)); + services.Add(new ServiceDescriptor(typeof(IPostTypeRepository), typeof(PostTypeRepository), scope)); + services.Add(new ServiceDescriptor(typeof(ISiteRepository), typeof(SiteRepository), scope)); + services.Add(new ServiceDescriptor(typeof(ISiteTypeRepository), typeof(SiteTypeRepository), scope)); + + // Register services + services.Add(new ServiceDescriptor(typeof(IContentFactory), typeof(ContentFactory), ServiceLifetime.Singleton)); + services.Add(new ServiceDescriptor(typeof(IContentServiceFactory), typeof(ContentServiceFactory), ServiceLifetime.Singleton)); + services.Add(new ServiceDescriptor(typeof(IDb), typeof(T), scope)); + services.Add(new ServiceDescriptor(typeof(IApi), typeof(Api), scope)); + + return services; + } + + /// + /// Adds the DbContext and the default services needed to run + /// Piranha over Entity Framework Core. + /// + /// The current service collection + /// The DbContext options builder + /// The optional lifetime + /// The updated service collection + public static IServiceCollection AddPiranhaEF(this IServiceCollection services, + Action dboptions, + ServiceLifetime scope = ServiceLifetime.Scoped) + { + return AddPiranhaEF(services, dboptions, scope); + } + + /// + /// Adds the DbContext and the default services needed to run + /// Piranha over Entity Framework Core. + /// + /// The current service collection + /// The DbContext options builder + /// The optional lifetime + /// The DbContext type + /// The updated service collection + public static IServiceCollection AddPiranhaEF(this IServiceCollection services, + Action dboptions, + ServiceLifetime scope = ServiceLifetime.Scoped) where T : DbContext, IDb + { + services.AddDbContext(dboptions); + + return AddPiranhaEF(services, scope); + } +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/Repositories/AliasRepository.cs b/data/Piranha.Data.EF/Repositories/AliasRepository.cs new file mode 100644 index 000000000..17a4a7299 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/AliasRepository.cs @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Piranha.Models; +using Piranha.Services; + +namespace Piranha.Repositories +{ + public class AliasRepository : IAliasRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db context + public AliasRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all available models for the specified site. + /// + /// The site id + /// The available models + public async Task> GetAll(Guid siteId) + { + return await _db.Aliases + .AsNoTracking() + .Where(a => a.SiteId == siteId) + .OrderBy(a => a.AliasUrl) + .ThenBy(a => a.RedirectUrl) + .Select(a => new Alias + { + Id = a.Id, + SiteId = a.SiteId, + AliasUrl = a.AliasUrl, + RedirectUrl = a.RedirectUrl, + Type = a.Type, + Created = a.Created, + LastModified = a.LastModified + }) + .ToListAsync(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or NULL if it doesn't exist + public Task GetById(Guid id) + { + return _db.Aliases + .AsNoTracking() + .Where(a => a.Id == id) + .Select(a => new Alias + { + Id = a.Id, + SiteId = a.SiteId, + AliasUrl = a.AliasUrl, + RedirectUrl = a.RedirectUrl, + Type = a.Type, + Created = a.Created, + LastModified = a.LastModified + }) + .FirstOrDefaultAsync(); + } + + /// + /// Gets the model with the given alias url. + /// + /// The unique url + /// The site id + /// The model + public Task GetByAliasUrl(string url, Guid siteId) + { + return _db.Aliases + .AsNoTracking() + .Where(a => a.SiteId == siteId && a.AliasUrl == url) + .Select(a => new Alias + { + Id = a.Id, + SiteId = a.SiteId, + AliasUrl = a.AliasUrl, + RedirectUrl = a.RedirectUrl, + Type = a.Type, + Created = a.Created, + LastModified = a.LastModified + }) + .FirstOrDefaultAsync(); + } + + /// + /// Gets the model with the given redirect url. + /// + /// The unique url + /// The site id + /// The model + public async Task> GetByRedirectUrl(string url, Guid siteId) + { + return await _db.Aliases + .AsNoTracking() + .Where(a => a.SiteId == siteId && a.RedirectUrl == url) + .Select(a => new Alias + { + Id = a.Id, + SiteId = a.SiteId, + AliasUrl = a.AliasUrl, + RedirectUrl = a.RedirectUrl, + Type = a.Type, + Created = a.Created, + LastModified = a.LastModified + }) + .ToListAsync(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(Alias model) + { + var alias = await _db.Aliases + .FirstOrDefaultAsync(p => p.Id == model.Id); + + if (alias == null) + { + alias = new Data.Alias + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + Created = DateTime.Now + }; + await _db.Aliases.AddAsync(alias); + } + alias.SiteId = model.SiteId; + alias.AliasUrl = model.AliasUrl; + alias.RedirectUrl = model.RedirectUrl; + alias.Type = model.Type; + alias.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(Guid id) + { + var alias = await _db.Aliases + .FirstOrDefaultAsync(a => a.Id == id); + + if (alias != null) + { + _db.Aliases.Remove(alias); + await _db.SaveChangesAsync(); + } + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/ArchiveRepository.cs b/data/Piranha.Data.EF/Repositories/ArchiveRepository.cs new file mode 100644 index 000000000..e3ec7a929 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/ArchiveRepository.cs @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Piranha.Repositories +{ + public class ArchiveRepository : IArchiveRepository + { + /// + /// The current db context. + /// + private readonly IDb _db; + + /// + /// Default internal constructor. + /// + /// The current db context + public ArchiveRepository(IDb db) + { + _db = db; + } + + public Task GetPostCount(Guid archiveId, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null) + { + return GetQuery(archiveId, categoryId, tagId, year, month) + .CountAsync(); + } + + public async Task> GetPosts(Guid archiveId, int pageSize, int currentPage, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null) + { + return await GetQuery(archiveId, categoryId, tagId, year, month) + .OrderByDescending(p => p.Published) + .Skip((currentPage - 1) * pageSize) + .Take(pageSize) + .Select(p => p.Id) + .ToListAsync(); + } + + private IQueryable GetQuery(Guid archiveId, Guid? categoryId = null, Guid? tagId = null, int? year = null, int? month = null) + { + // Build the query. + var now = DateTime.Now; + var query = _db.Posts + .Where(p => p.BlogId == archiveId && p.Published <= now); + + if (categoryId.HasValue) + { + query = query.Where(p => p.CategoryId == categoryId.Value); + } + if (tagId.HasValue) + { + query = query.Where(p => p.Tags.Any(t => t.TagId == tagId.Value)); + } + + if (year.HasValue) + { + DateTime from; + DateTime to; + + if (month.HasValue) + { + from = new DateTime(year.Value, month.Value, 1); + to = from.AddMonths(1); + } + else + { + from = new DateTime(year.Value, 1, 1); + to = from.AddYears(1); + } + query = query.Where(p => p.Published >= from && p.Published < to); + } + return query; + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/MediaRepository.cs b/data/Piranha.Data.EF/Repositories/MediaRepository.cs new file mode 100644 index 000000000..fa306d44f --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/MediaRepository.cs @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Piranha.Data; +using Piranha.Data.EF; + +namespace Piranha.Repositories +{ + public class MediaRepository : IMediaRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db context + public MediaRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all media available in the specified folder. + /// + /// The optional folder id + /// The available media + public async Task> GetAll(Guid? folderId = null) + { + return await _db.Media + .AsNoTracking() + .Where(m => m.FolderId == folderId) + .OrderBy(m => m.Filename) + .Select(m => m.Id) + .ToListAsync(); + } + + /// + /// Gets all media folders available in the specified + /// folder. + /// + /// The optional folder id + /// The available media folders + public async Task> GetAllFolders(Guid? folderId = null) + { + return await _db.MediaFolders + .AsNoTracking() + .Where(f => f.ParentId == folderId) + .OrderBy(f => f.Name) + .Select(f => f.Id) + .ToListAsync(); + } + + /// + /// Gets the media with the given id. + /// + /// The unique id + /// The media + public Task GetById(Guid id) + { + return _db.Media + .AsNoTracking() + .Include(m => m.Versions) + .Select(m => new Models.Media + { + Id = m.Id, + FolderId = m.FolderId, + Type = m.Type, + Filename = m.Filename, + ContentType = m.ContentType, + Size = m.Size, + Width = m.Width, + Height = m.Height, + Created = m.Created, + LastModified = m.LastModified, + Versions = m.Versions.Select(v => new Models.MediaVersion + { + Id = v.Id, + Size = v.Size, + Width = v.Width, + Height = v.Height, + FileExtension = v.FileExtension + }).ToList() + }) + .FirstOrDefaultAsync(m => m.Id == id); + } + + /// + /// Gets the media folder with the given id. + /// + /// The unique id + /// The media folder + public Task GetFolderById(Guid id) + { + return _db.MediaFolders + .AsNoTracking() + .Select(f => new Models.MediaFolder + { + Id = f.Id, + ParentId = f.ParentId, + Name = f.Name, + Created = f.Created + }) + .FirstOrDefaultAsync(f => f.Id == id); + } + + /// + /// Gets the hierachical media structure. + /// + /// The media structure + public async Task GetStructure() + { + var folders = await _db.MediaFolders + .AsNoTracking() + .OrderBy(f => f.ParentId) + .ThenBy(f => f.Name) + .ToListAsync(); + + return Sort(folders); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The content to save + public async Task Save(Models.Media model) + { + var media = await _db.Media + .Include(m => m.Versions) + .FirstOrDefaultAsync(m => m.Id == model.Id); + + if (media == null) + { + media = new Media() + { + Id = model.Id, + Created = DateTime.Now + }; + await _db.Media.AddAsync(media); + } + + media.Filename = model.Filename; + media.FolderId = model.FolderId; + media.Type = model.Type; + media.ContentType = model.ContentType; + media.LastModified = DateTime.Now; + media.Width = model.Width; + media.Height = model.Height; + + // Delete removed versions + var current = model.Versions.Select(v => v.Id).ToArray(); + var removed = media.Versions.Where(v => !current.Contains(v.Id)).ToArray(); + + if (removed.Length > 0) + { + _db.MediaVersions.RemoveRange(removed); + } + + // Add new versions + foreach (var version in model.Versions) + { + if (!media.Versions.Any(v => v.Id == version.Id)) + { + media.Versions.Add(new MediaVersion + { + Id = version.Id, + MediaId = media.Id, + Size = version.Size, + Width = version.Width, + Height = version.Height, + FileExtension = version.FileExtension + }); + } + } + + // Save all changes + await _db.SaveChangesAsync(); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task SaveFolder(Models.MediaFolder model) + { + var folder = await _db.MediaFolders + .FirstOrDefaultAsync(f => f.Id == model.Id); + + if (folder == null) + { + folder = new Data.MediaFolder() + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + Created = DateTime.Now + }; + model.Id = folder.Id; + await _db.MediaFolders.AddAsync(folder); + } + folder.ParentId = model.ParentId; + folder.Name = model.Name; + + await _db.SaveChangesAsync(); + } + + /// + /// Moves the media to the folder with the specified id. + /// + /// The media + /// The folder id + public async Task Move(Models.Media model, Guid? folderId) + { + var media = await _db.Media.FirstOrDefaultAsync(m => m.Id == model.Id); + if (media != null) + { + media.FolderId = folderId; + await _db.SaveChangesAsync(); + } + } + + /// + /// Deletes the media with the given id. + /// + /// The unique id + public async Task Delete(Guid id) + { + var media = await _db.Media + .Include(m => m.Versions) + .FirstOrDefaultAsync(m => m.Id == id); + + if (media != null) + { + _db.Media.Remove(media); + await _db.SaveChangesAsync(); + } + } + + /// + /// Deletes the media folder with the given id. + /// + /// The unique id + public async Task DeleteFolder(Guid id) + { + var folder = await _db.MediaFolders + .FirstOrDefaultAsync(f => f.Id == id); + + if (folder != null) + { + _db.MediaFolders.Remove(folder); + await _db.SaveChangesAsync(); + } + } + + /// + /// Sorts the items. + /// + /// The full folder list + /// The current parent id + /// The structure + private Models.MediaStructure Sort(IEnumerable folders, Guid? parentId = null, int level = 0) + { + var result = new Models.MediaStructure(); + + foreach (var folder in folders.Where(f => f.ParentId == parentId).OrderBy(f => f.Name)) + { + var item = Module.Mapper.Map(folder); + + item.Level = level; + item.Items = Sort(folders, folder.Id, level + 1); + + result.Add(item); + } + return result; + } + } +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/Repositories/PageRepository.cs b/data/Piranha.Data.EF/Repositories/PageRepository.cs new file mode 100644 index 000000000..67c3c3552 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/PageRepository.cs @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2016-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Piranha.Data; +using Piranha.Services; + +namespace Piranha.Repositories +{ + public class PageRepository : IPageRepository + { + private readonly IDb _db; + private readonly IContentService _contentService; + + /// + /// Default constructor. + /// + /// The current db context + /// The content service factory + public PageRepository(IDb db, IContentServiceFactory factory) + { + _db = db; + _contentService = factory.CreatePageService(); + } + + /// + /// Gets all of the available pages for the current site. + /// + /// The site id + /// The pages + public async Task> GetAll(Guid siteId) + { + return await _db.Pages + .AsNoTracking() + .Where(p => p.SiteId == siteId) + .OrderBy(p => p.ParentId) + .ThenBy(p => p.SortOrder) + .Select(p => p.Id) + .ToListAsync(); + } + + /// + /// Gets the available blog pages for the current site. + /// + /// The site id + /// The pages + public async Task> GetAllBlogs(Guid siteId) + { + return await _db.Pages + .AsNoTracking() + .Where(p => p.SiteId == siteId && p.ContentType == "Blog") + .OrderBy(p => p.ParentId) + .ThenBy(p => p.SortOrder) + .Select(p => p.Id) + .ToListAsync(); + } + + /// + /// Gets the site startpage. + /// + /// The model type + /// The site id + /// The page model + public async Task GetStartpage(Guid siteId) where T : Models.PageBase + { + var page = await GetQuery(out var fullQuery) + .FirstOrDefaultAsync(p => p.SiteId == siteId && p.ParentId == null && p.SortOrder == 0); + + if (page != null) + { + return _contentService.Transform(page, App.PageTypes.GetById(page.PageTypeId), Process); + } + return null; + } + + /// + /// Gets the page model with the specified id. + /// + /// The model type + /// The unique id + /// The page model + public async Task GetById(Guid id) where T : Models.PageBase + { + var page = await GetQuery(out var fullQuery) + .FirstOrDefaultAsync(p => p.Id == id); + + if (page != null) + { + return _contentService.Transform(page, App.PageTypes.GetById(page.PageTypeId), Process); + } + return null; + } + + /// + /// Gets the page model with the specified slug. + /// + /// The model type + /// The unique slug + /// The site id + /// The page model + public async Task GetBySlug(string slug, Guid siteId) where T : Models.PageBase + { + var page = await GetQuery(out var fullQuery) + .FirstOrDefaultAsync(p => p.SiteId == siteId && p.Slug == slug); + + if (page != null) + { + return _contentService.Transform(page, App.PageTypes.GetById(page.PageTypeId), Process); + } + return null; + } + + /// + /// Moves the current page in the structure. + /// + /// The model type + /// The page to move + /// The new parent id + /// The new sort order + /// The other pages that were affected by the move + public async Task> Move(T model, Guid? parentId, int sortOrder) where T : Models.PageBase + { + var affected = new List(); + + // Remove the old position for the page + affected.AddRange(await MovePages(model.Id, model.SiteId, model.ParentId, model.SortOrder + 1, false)); + // Add room for the new position of the page + affected.AddRange(await MovePages(model.Id, model.SiteId, parentId, sortOrder, true)); + + // Update the position of the current page + var page = await _db.Pages + .FirstOrDefaultAsync(p => p.Id == model.Id); + page.ParentId = parentId; + page.SortOrder = sortOrder; + + await _db.SaveChangesAsync(); + + return affected; + } + + /// + /// Saves the given page model + /// + /// The page model + public async Task> Save(T model) where T : Models.PageBase + { + var type = App.PageTypes.GetById(model.TypeId); + var affected = new List(); + var isNew = false; + + if (type != null) + { + // Set content type + model.ContentType = type.ContentTypeId; + + var page = await _db.Pages + .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) + .Include(p => p.Fields) + .FirstOrDefaultAsync(p => p.Id == model.Id); + + if (page == null) + { + isNew = true; + } + + if (model.OriginalPageId.HasValue) + { + var originalPageIsCopy = (await _db.Pages.FirstOrDefaultAsync(p => p.Id == model.OriginalPageId))?.OriginalPageId.HasValue ?? false; + if (originalPageIsCopy) + { + throw new InvalidOperationException("Can not set copy of a copy"); + } + + var originalPageType = (await _db.Pages.FirstOrDefaultAsync(p => p.Id == model.OriginalPageId))?.PageTypeId; + if (originalPageType != model.TypeId) + { + throw new InvalidOperationException("Copy can not have a different content type"); + } + + // Transform the model + if (page == null) + { + page = new Page() + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + }; + + _db.Pages.Add(page); + + // Make room for the new page + affected.AddRange(await MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true)); + } + else + { + // Check if the page has been moved + if (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder) + { + // Remove the old position for the page + affected.AddRange(await MovePages(page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false)); + // Add room for the new position of the page + affected.AddRange(await MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true)); + } + } + + if (isNew || page.Title != model.Title || page.NavigationTitle != model.NavigationTitle) + { + // If this is new page or title has been updated it means + // the global sitemap changes. Notify the service. + affected.Add(page.Id); + } + + page.PageTypeId = model.TypeId; + page.OriginalPageId = model.OriginalPageId; + page.SiteId = model.SiteId; + page.Title = model.Title; + page.NavigationTitle = model.NavigationTitle; + page.Slug = model.Slug; + page.ParentId = model.ParentId; + page.SortOrder = model.SortOrder; + page.IsHidden = model.IsHidden; + page.Route = model.Route; + page.Published = model.Published; + + await _db.SaveChangesAsync(); + + return affected; + } + + // Transform the model + if (page == null) + { + page = new Page + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + ParentId = model.ParentId, + SortOrder = model.SortOrder, + PageTypeId = model.TypeId, + Created = DateTime.Now, + LastModified = DateTime.Now + }; + _db.Pages.Add(page); + model.Id = page.Id; + + // Make room for the new page + affected.AddRange(await MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true)); + } + else + { + // Check if the page has been moved + if (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder) + { + // Remove the old position for the page + affected.AddRange(await MovePages(page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false)); + // Add room for the new position of the page + affected.AddRange(await MovePages(page.Id, model.SiteId, model.ParentId, model.SortOrder, true)); + } + page.LastModified = DateTime.Now; + } + + if (isNew || page.Title != model.Title || page.NavigationTitle != model.NavigationTitle) + { + // If this is new page or title has been updated it means + // the global sitemap changes. Notify the service. + affected.Add(page.Id); + } + + page = _contentService.Transform(model, type, page); + + // Transform blocks + var blockModels = model.Blocks; + + if (blockModels != null) + { + var blocks = _contentService.TransformBlocks(blockModels); + var current = blocks.Select(b => b.Id).ToArray(); + + // Delete removed blocks + var removed = page.Blocks + .Where(b => !current.Contains(b.BlockId) && !b.Block.IsReusable) + .Select(b => b.Block); + _db.Blocks.RemoveRange(removed); + + // Delete the old page blocks + page.Blocks.Clear(); + + // Now map the new block + for (var n = 0; n < blocks.Count; n++) + { + var block = await _db.Blocks + .Include(b => b.Fields) + .FirstOrDefaultAsync(b => b.Id == blocks[n].Id); + if (block == null) + { + block = new Block + { + Id = blocks[n].Id != Guid.Empty ? blocks[n].Id : Guid.NewGuid(), + Created = DateTime.Now + }; + await _db.Blocks.AddAsync(block); + } + block.CLRType = blocks[n].CLRType; + block.IsReusable = blocks[n].IsReusable; + block.Title = blocks[n].Title; + block.LastModified = DateTime.Now; + + var currentFields = blocks[n].Fields.Select(f => f.FieldId).Distinct(); + var removedFields = block.Fields.Where(f => !currentFields.Contains(f.FieldId)); + _db.BlockFields.RemoveRange(removedFields); + + foreach (var newField in blocks[n].Fields) + { + var field = block.Fields.FirstOrDefault(f => f.FieldId == newField.FieldId); + if (field == null) + { + field = new BlockField + { + Id = newField.Id != Guid.Empty ? newField.Id : Guid.NewGuid(), + BlockId = block.Id, + FieldId = newField.FieldId + }; + await _db.BlockFields.AddAsync(field); + block.Fields.Add(field); + } + field.SortOrder = newField.SortOrder; + field.CLRType = newField.CLRType; + field.Value = newField.Value; + } + + // Create the page block + page.Blocks.Add(new PageBlock + { + Id = Guid.NewGuid(), + ParentId = blocks[n].ParentId, + BlockId = block.Id, + Block = block, + PageId = page.Id, + SortOrder = n + }); + } + } + await _db.SaveChangesAsync(); + } + return affected; + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task> Delete(Guid id) + { + var model = await _db.Pages + .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) + .Include(p => p.Fields) + .FirstOrDefaultAsync(p => p.Id == id); + var affected = new List(); + + if (model != null) + { + // Make sure this page isn't copied + var copyCount = await _db.Pages.CountAsync(p => p.OriginalPageId == model.Id); + if (copyCount > 0) + { + throw new InvalidOperationException("Can not delete page because it has copies"); + } + + // Make sure this page doesn't have child pages + var childCount = await _db.Pages.CountAsync(p => p.ParentId == model.Id); + if (childCount > 0) + { + throw new InvalidOperationException("Can not delete page because it has children"); + } + + // Remove all blocks that are not reusable + foreach (var pageBlock in model.Blocks) + { + if (!pageBlock.Block.IsReusable) + { + _db.Blocks.Remove(pageBlock.Block); + } + } + + // Remove the main page. + _db.Pages.Remove(model); + + // Move all remaining pages after this page in the site structure. + affected.AddRange(await MovePages(id, model.SiteId, model.ParentId, model.SortOrder + 1, false)); + + await _db.SaveChangesAsync(); + } + return affected; + } + + /// + /// Gets the base query for loading pages. + /// + /// If this is a full load or not + /// The requested model type + /// The queryable + private IQueryable GetQuery(out bool fullModel) + { + var loadRelated = !typeof(Models.IContentInfo).IsAssignableFrom(typeof(T)); + + var query = _db.Pages + .AsNoTracking(); + + if (loadRelated) + { + query = query + .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) + .Include(p => p.Fields); + fullModel = true; + } + else + { + fullModel = false; + } + return query; + } + + /// + /// Performs additional processing and loads related models. + /// + /// The source page + /// The targe model + private void Process(Data.Page page, T model) where T : Models.PageBase + { + if (!(model is Models.IContentInfo)) + { + if (page.Blocks.Count > 0) + { + //model.Blocks = _contentService.TransformBlocks(page.Blocks.OrderBy(b => b.SortOrder)); + model.Blocks = _contentService.TransformBlocks(page.Blocks.OrderBy(b => b.SortOrder).Select(b => b.Block)); + } + } + } + + /// + /// Moves the pages around. This is done when a page is deleted or moved in the structure. + /// + /// The id of the page that is moved + /// The site id + /// The parent id + /// The sort order + /// If sort order should be increase or decreased + private async Task> MovePages(Guid pageId, Guid siteId, Guid? parentId, int sortOrder, bool increase) + { + var pages = await _db.Pages + .Where(p => p.SiteId == siteId && p.ParentId == parentId && p.SortOrder >= sortOrder && p.Id != pageId) + .ToListAsync(); + + foreach (var page in pages) + { + page.SortOrder = increase ? page.SortOrder + 1 : page.SortOrder - 1; + } + return pages.Select(p => p.Id).ToList(); + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/PageTypeRepository.cs b/data/Piranha.Data.EF/Repositories/PageTypeRepository.cs new file mode 100644 index 000000000..b3124d63a --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/PageTypeRepository.cs @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Piranha.Models; + +namespace Piranha.Repositories +{ + public class PageTypeRepository : IPageTypeRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db connection + public PageTypeRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAll() + { + var models = new List(); + var types = await _db.PageTypes + .AsNoTracking() + .OrderBy(t => t.Id) + .ToListAsync(); + + foreach (var type in types) + { + models.Add(JsonConvert.DeserializeObject(type.Body)); + } + return models; + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public async Task GetById(string id) + { + var type = await _db.PageTypes + .AsNoTracking() + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + return JsonConvert.DeserializeObject(type.Body); + } + return null; + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(PageType model) + { + var type = await _db.PageTypes + .FirstOrDefaultAsync(t => t.Id == model.Id); + + if (type == null) { + type = new Data.PageType + { + Id = model.Id, + Created = DateTime.Now + }; + await _db.PageTypes.AddAsync(type); + } + type.CLRType = model.CLRType; + type.Body = JsonConvert.SerializeObject(model); + type.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(string id) + { + var type = await _db.PageTypes + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + _db.PageTypes.Remove(type); + await _db.SaveChangesAsync(); + } + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/ParamRepository.cs b/data/Piranha.Data.EF/Repositories/ParamRepository.cs new file mode 100644 index 000000000..f665803ee --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/ParamRepository.cs @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Piranha.Models; + +namespace Piranha.Repositories +{ + public class ParamRepository : IParamRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db context + public ParamRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAll() + { + return await _db.Params + .AsNoTracking() + .OrderBy(p => p.Key) + .Select(p => new Param + { + Id = p.Id, + Key = p.Key, + Description = p.Description, + Value = p.Value, + Created = p.Created, + LastModified = p.LastModified + }) + .ToListAsync(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or NULL if it doesn't exist + public Task GetById(Guid id) + { + return _db.Params + .AsNoTracking() + .Select(p => new Param + { + Id = p.Id, + Key = p.Key, + Description = p.Description, + Value = p.Value, + Created = p.Created, + LastModified = p.LastModified + }) + .FirstOrDefaultAsync(p => p.Id == id); + } + + /// + /// Gets the model with the given key. + /// + /// The unique key + /// The model + public Task GetByKey(string key) { + return _db.Params + .AsNoTracking() + .Select(p => new Param + { + Id = p.Id, + Key = p.Key, + Description = p.Description, + Value = p.Value, + Created = p.Created, + LastModified = p.LastModified + }) + .FirstOrDefaultAsync(p => p.Key == key); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(Param model) + { + var param = await _db.Params + .FirstOrDefaultAsync(p => p.Id == model.Id); + + if (param == null) + { + param = new Data.Param + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + Created = DateTime.Now + }; + await _db.Params.AddAsync(param); + } + param.Key = model.Key; + param.Description = model.Description; + param.Value = model.Value; + param.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(Guid id) + { + var param = await _db.Params + .FirstOrDefaultAsync(p => p.Id == id); + + if (param != null) + { + _db.Params.Remove(param); + await _db.SaveChangesAsync(); + } + } + } +} diff --git a/core/Piranha/Repositories/PostRepository.cs b/data/Piranha.Data.EF/Repositories/PostRepository.cs similarity index 52% rename from core/Piranha/Repositories/PostRepository.cs rename to data/Piranha.Data.EF/Repositories/PostRepository.cs index 9ac119c59..4bb77a134 100644 --- a/core/Piranha/Repositories/PostRepository.cs +++ b/data/Piranha.Data.EF/Repositories/PostRepository.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 Håkan Edling + * Copyright (c) 2016-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Piranha.Data; using Piranha.Services; @@ -20,158 +21,91 @@ namespace Piranha.Repositories public class PostRepository : IPostRepository { private readonly IDb _db; - private readonly IApi _api; private readonly IContentService _contentService; - private readonly ICache _cache; /// /// Default constructor. /// - /// The current api /// The current db connection /// The current content service factory - /// The optional model cache - public PostRepository(IApi api, IDb db, IContentServiceFactory factory, ICache cache = null) + public PostRepository(IDb db, IContentServiceFactory factory) { _db = db; - _api = api; _contentService = factory.CreatePostService(); - _cache = cache; } /// - /// Creates and initializes a new post of the specified type. - /// - /// The created post - public T Create(string typeId = null) where T : Models.PostBase - { - if (string.IsNullOrWhiteSpace(typeId)) - { - typeId = typeof(T).Name; - } - return _contentService.Create(_api.PostTypes.GetById(typeId)); - } - - /// - /// Gets the available posts for the specified blog. + /// Gets the available posts for the specified archive. /// + /// The blog id /// The posts - public IEnumerable GetAll() + public async Task> GetAll(Guid blogId) { - return GetAll(); - } - - /// - /// Gets the available post items. - /// - /// The posts - public IEnumerable GetAll() where T : Models.PostBase - { - var posts = _db.Posts + return await _db.Posts .AsNoTracking() + .Where(p => p.BlogId == blogId) .OrderByDescending(p => p.Published) .ThenByDescending(p => p.LastModified) .ThenBy(p => p.Title) - .Select(p => p.Id); - - var models = new List(); - - foreach (var post in posts) - { - var model = GetById(post); - - if (model != null) - { - models.Add(model); - } - } - return models; + .Select(p => p.Id) + .ToListAsync(); } /// - /// Gets the available posts for the specified blog. + /// Gets the available post items for the given site. /// - /// The unique blog id + /// The site id /// The posts - public IEnumerable GetAll(Guid blogId) + public async Task> GetAllBySiteId(Guid siteId) { - return GetAll(blogId); - } - - /// - /// Gets the available post items. - /// - /// The unique id - /// The posts - public IEnumerable GetAll(Guid blogId) where T : Models.PostBase - { - var posts = _db.Posts + return await _db.Posts .AsNoTracking() - .Where(p => p.BlogId == blogId) + .Where(p => p.Blog.SiteId == siteId) .OrderByDescending(p => p.Published) .ThenByDescending(p => p.LastModified) .ThenBy(p => p.Title) - .Select(p => p.Id); - - var models = new List(); - - foreach (var post in posts) - { - var model = GetById(post); - - if (model != null) - { - models.Add(model); - } - } - return models; + .Select(p => p.Id) + .ToListAsync(); } /// - /// Gets the available posts for the specified blog. + /// Gets all available categories for the specified blog. /// - /// The blog slug - /// The optional site id - /// The posts - public IEnumerable GetAll(string slug, Guid? siteId = null) + /// The blog id + /// The available categories + public async Task> GetAllCategories(Guid blogId) { - return GetAll(slug, siteId); - } - - /// - /// Gets the available posts for the specified blog. - /// - /// The blog slug - /// The optional site id - /// The posts - public IEnumerable GetAll(string slug, Guid? siteId = null) where T : Models.PostBase - { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) + return await _db.Categories + .AsNoTracking() + .Where(c => c.BlogId == blogId) + .OrderBy(c => c.Title) + .Select(c => new Models.Taxonomy { - siteId = site.Id; - } - } - - var blogId = _api.Pages.GetIdBySlug(slug, siteId); - - if (blogId.HasValue) - { - return GetAll(blogId.Value); - } - return new List(); + Id = c.Id, + Title = c.Title, + Slug = c.Slug + }) + .ToListAsync(); } /// - /// Gets the post model with the specified id. + /// Gets all available tags for the specified blog. /// - /// The unique id - /// The post model - public Models.DynamicPost GetById(Guid id) + /// The blog id + /// The available tags + public async Task> GetAllTags(Guid blogId) { - return GetById(id); + return await _db.Tags + .AsNoTracking() + .Where(c => c.BlogId == blogId) + .OrderBy(c => c.Title) + .Select(c => new Models.Taxonomy + { + Id = c.Id, + Title = c.Title, + Slug = c.Slug + }) + .ToListAsync(); } /// @@ -180,157 +114,112 @@ public Models.DynamicPost GetById(Guid id) /// The model type /// The unique id /// The post model - public T GetById(Guid id) where T : Models.PostBase + public async Task GetById(Guid id) where T : Models.PostBase { - var post = _cache?.Get(id.ToString()); - - if (post == null) - { - post = GetQuery(out var fullQuery) - .FirstOrDefault(p => p.Id == id); - - if (post != null) - { - if (_cache != null && fullQuery) - { - AddToCache(post); - } - post.Category = _api.Categories.GetById(post.CategoryId); - // - // TODO: Ugly hardcoded reference!!!! - // - post.Blog = ((PageRepository)_api.Pages).GetPageById(post.BlogId); - } - } + var post = await GetQuery() + .FirstOrDefaultAsync(p => p.Id == id); if (post != null) { - return _contentService.Transform(post, _api.PostTypes.GetById(post.PostTypeId), Process); + return _contentService.Transform(post, App.PostTypes.GetById(post.PostTypeId), Process); } return null; } - /// - /// Gets the post model with the specified slug. - /// - /// The unique blog slug - /// The unique slug - /// The optional site id - /// The post model - public Models.DynamicPost GetBySlug(string blog, string slug, Guid? siteId = null) - { - return GetBySlug(blog, slug, siteId); - } - /// /// Gets the post model with the specified slug. /// /// The model type - /// The unique blog slug + /// The blog id /// The unique slug - /// The optional site id /// The post model - public T GetBySlug(string blog, string slug, Guid? siteId = null) where T : Models.PostBase + public async Task GetBySlug(Guid blogId, string slug) where T : Models.PostBase { - if (!siteId.HasValue) - { - var site = _api.Sites.GetDefault(); - if (site != null) - { - siteId = site.Id; - } - } - - var blogId = _api.Pages.GetIdBySlug(blog, siteId); + // No cache found, load from database + var post = await GetQuery() + .FirstOrDefaultAsync(p => p.BlogId == blogId && p.Slug == slug); - if (blogId.HasValue) + if (post != null) { - return GetBySlug(blogId.Value, slug); + return _contentService.Transform(post, App.PostTypes.GetById(post.PostTypeId), Process); } return null; } /// - /// Gets the post model with the specified slug. + /// Gets the category with the given slug. /// - /// The unique blog slug + /// The blog id /// The unique slug - /// The post model - public Models.DynamicPost GetBySlug(Guid blogId, string slug) + /// The category + public Task GetCategoryBySlug(Guid blogId, string slug) { - return GetBySlug(blogId, slug); + return _db.Categories + .Where(c => c.BlogId == blogId && c.Slug == slug) + .Select(c => new Models.Taxonomy + { + Id = c.Id, + Title = c.Title, + Slug = c.Slug + }).FirstOrDefaultAsync(); } /// - /// Gets the post model with the specified slug. + /// Gets the tag with the given slug. /// - /// The model type - /// The unique blog slug + /// The blog id /// The unique slug - /// The post model - public T GetBySlug(Guid blogId, string slug) where T : Models.PostBase + /// The tag + public Task GetTagBySlug(Guid blogId, string slug) { - var postId = _cache?.Get($"PostId_{blogId}_{slug}"); - - if (postId.HasValue) - { - // Load the post by id instead - return GetById(postId.Value); - } - else - { - // No cache found, load from database - var post = GetQuery(out var fullQuery) - .FirstOrDefault(p => p.BlogId == blogId && p.Slug == slug); - - if (post != null) + return _db.Tags + .Where(c => c.BlogId == blogId && c.Slug == slug) + .Select(c => new Models.Taxonomy { - if (_cache != null && fullQuery) - { - AddToCache(post); - } - post.Category = _api.Categories.GetById(post.CategoryId); - post.Blog = ((PageRepository)_api.Pages).GetPageById(post.BlogId); - - return _contentService.Transform(post, _api.PostTypes.GetById(post.PostTypeId), Process); - } - return null; - } + Id = c.Id, + Title = c.Title, + Slug = c.Slug + }).FirstOrDefaultAsync(); } /// /// Saves the given post model /// /// The post model - public void Save(T model) where T : Models.PostBase + public async Task Save(T model) where T : Models.PostBase { - var type = _api.PostTypes.GetById(model.TypeId); + var type = App.PostTypes.GetById(model.TypeId); if (type != null) { // Ensure category - if (model.Category.Id == Guid.Empty) - { - Category category = null; + var category = await _db.Categories.FirstOrDefaultAsync(c => c.Id == model.Category.Id); + if (category == null) + { if (!string.IsNullOrWhiteSpace(model.Category.Slug)) { - category = _api.Categories.GetBySlug(model.BlogId, model.Category.Slug); + category = await _db.Categories + .FirstOrDefaultAsync(c => c.BlogId == model.BlogId && c.Slug == model.Category.Slug); } if (category == null && !string.IsNullOrWhiteSpace(model.Category.Title)) { - category = _api.Categories.GetByTitle(model.BlogId, model.Category.Title); + category = await _db.Categories + .FirstOrDefaultAsync(c => c.BlogId == model.BlogId && c.Title == model.Category.Title); } if (category == null) { category = new Category { - Id = Guid.NewGuid(), + Id = model.Category.Id != Guid.Empty ? model.Category.Id : Guid.NewGuid(), BlogId = model.BlogId, - Title = model.Category.Title + Title = model.Category.Title, + Slug = Utils.GenerateSlug(model.Category.Title), + Created = DateTime.Now, + LastModified = DateTime.Now }; - _api.Categories.Save(category); + await _db.Categories.AddAsync(category); } model.Category.Id = category.Id; } @@ -338,28 +227,33 @@ public void Save(T model) where T : Models.PostBase // Ensure tags foreach (var t in model.Tags) { - if (t.Id == Guid.Empty) - { - Tag tag = null; + var tag = await _db.Tags.FirstOrDefaultAsync(tg => tg.Id == t.Id); + if (tag == null) + { if (!string.IsNullOrWhiteSpace(t.Slug)) { - tag = _api.Tags.GetBySlug(model.BlogId, t.Slug); + tag = await _db.Tags + .FirstOrDefaultAsync(tg => tg.BlogId == model.BlogId && tg.Slug == t.Slug); } if (tag == null && !string.IsNullOrWhiteSpace(t.Title)) { - tag = _api.Tags.GetByTitle(model.BlogId, t.Title); + tag = await _db.Tags + .FirstOrDefaultAsync(tg => tg.BlogId == model.BlogId && tg.Title == t.Title); } if (tag == null) { tag = new Tag { - Id = Guid.NewGuid(), + Id = t.Id != Guid.Empty ? t.Id : Guid.NewGuid(), BlogId = model.BlogId, - Title = t.Title + Title = t.Title, + Slug = Utils.GenerateSlug(t.Title), + Created = DateTime.Now, + LastModified = DateTime.Now }; - _api.Tags.Save(tag); + await _db.Tags.AddAsync(tag); } t.Id = tag.Id; } @@ -375,11 +269,11 @@ public void Save(T model) where T : Models.PostBase model.Slug = Utils.GenerateSlug(model.Slug, false); } - var post = _db.Posts + var post = await _db.Posts .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) .Include(p => p.Fields) .Include(p => p.Tags) - .FirstOrDefault(p => p.Id == model.Id); + .FirstOrDefaultAsync(p => p.Id == model.Id); // If not, create a new post if (post == null) @@ -390,7 +284,7 @@ public void Save(T model) where T : Models.PostBase Created = DateTime.Now, LastModified = DateTime.Now }; - _db.Posts.Add(post); + await _db.Posts.AddAsync(post); model.Id = post.Id; } else @@ -429,7 +323,7 @@ public void Save(T model) where T : Models.PostBase Id = blocks[n].Id != Guid.Empty ? blocks[n].Id : Guid.NewGuid(), Created = DateTime.Now }; - _db.Blocks.Add(block); + await _db.Blocks.AddAsync(block); } block.CLRType = blocks[n].CLRType; block.IsReusable = blocks[n].IsReusable; @@ -451,7 +345,7 @@ public void Save(T model) where T : Models.PostBase BlockId = block.Id, FieldId = newField.FieldId }; - _db.BlockFields.Add(field); + await _db.BlockFields.AddAsync(field); block.Fields.Add(field); } field.SortOrder = newField.SortOrder; @@ -464,6 +358,7 @@ public void Save(T model) where T : Models.PostBase { Id = Guid.NewGuid(), BlockId = block.Id, + ParentId = blocks[n].ParentId, Block = block, PostId = post.Id, SortOrder = n @@ -496,12 +391,10 @@ public void Save(T model) where T : Models.PostBase }); } - _db.SaveChanges(); + await _db.SaveChangesAsync(); - if (_cache != null) - { - RemoveFromCache(post); - } + await DeleteUnusedCategories(model.BlogId); + await DeleteUnusedTags(model.BlogId); } } @@ -509,12 +402,12 @@ public void Save(T model) where T : Models.PostBase /// Deletes the model with the specified id. /// /// The unique id - public void Delete(Guid id) + public async Task Delete(Guid id) { - var model = _db.Posts + var model = await _db.Posts .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) .Include(p => p.Fields) - .FirstOrDefault(p => p.Id == id); + .FirstOrDefaultAsync(p => p.Id == id); if (model != null) { @@ -529,61 +422,90 @@ public void Delete(Guid id) _db.Posts.Remove(model); + // + // TODO + // // If this is a published post, update last modified for the // blog page for caching purposes. if (model.Published.HasValue) { - var page = _db.Pages - .FirstOrDefault(p => p.Id == model.BlogId); + var page = await _db.Pages + .FirstOrDefaultAsync(p => p.Id == model.BlogId); page.LastModified = DateTime.Now; } - _db.SaveChanges(); + await _db.SaveChangesAsync(); - // Check if we have the post in cache, and if so remove it - if (_cache != null) - { - var post = _cache.Get(model.Id.ToString()); - if (post != null) - { - RemoveFromCache(post); - } - } + await DeleteUnusedCategories(model.BlogId); + await DeleteUnusedTags(model.BlogId); } } /// - /// Deletes the given model. + /// Deletes all unused categories for the specified blog. /// - /// The model - public void Delete(T model) where T : Models.PostBase + /// The blog id + private async Task DeleteUnusedCategories(Guid blogId) { - Delete(model.Id); + var used = await _db.Posts + .Where(p => p.BlogId == blogId) + .Select(p => p.CategoryId) + .Distinct() + .ToArrayAsync(); + + var unused = await _db.Categories + .Where(c => c.BlogId == blogId && !used.Contains(c.Id)) + .ToListAsync(); + + if (unused.Count > 0) + { + _db.Categories.RemoveRange(unused); + await _db.SaveChangesAsync(); + } + } + + /// + /// Deletes all unused tags for the specified blog. + /// + /// The blog id + private async Task DeleteUnusedTags(Guid blogId) + { + var used = await _db.PostTags + .Where(t => t.Post.BlogId == blogId) + .Select(t => t.TagId) + .Distinct() + .ToArrayAsync(); + + var unused = await _db.Tags + .Where(t => t.BlogId == blogId && !used.Contains(t.Id)) + .ToListAsync(); + + if (unused.Count > 0) + { + _db.Tags.RemoveRange(unused); + await _db.SaveChangesAsync(); + } } /// /// Gets the base query for loading posts. /// - /// If this is a full load or not /// The requested model type /// The queryable - private IQueryable GetQuery(out bool fullModel) + private IQueryable GetQuery() { var loadRelated = !typeof(Models.IContentInfo).IsAssignableFrom(typeof(T)); - var query = _db.Posts - .AsNoTracking(); + IQueryable query = _db.Posts + .AsNoTracking() + .Include(p => p.Category) + .Include(p => p.Tags).ThenInclude(t => t.Tag); if (loadRelated) { query = query .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) .Include(p => p.Fields); - fullModel = true; - } - else - { - fullModel = false; } return query; } @@ -606,32 +528,6 @@ private void Process(Data.Post post, T model) where T : Models.PostBase model.Blocks = _contentService.TransformBlocks(blocks); } } - model.Category = _api.Categories.GetById(post.CategoryId); - - foreach (var tag in _api.Tags.GetByPostId(post.Id).OrderBy(t => t.Title)) - { - model.Tags.Add(tag); - } - } - - /// - /// Adds the given model to cache. - /// - /// The post - private void AddToCache(Post post) - { - _cache.Set(post.Id.ToString(), post); - _cache.Set($"PostId_{post.CategoryId}_{post.Slug}", post.Id); - } - - /// - /// Removes the given model from cache. - /// - /// The post - private void RemoveFromCache(Post post) - { - _cache.Remove(post.Id.ToString()); - _cache.Remove($"PostId_{post.CategoryId}_{post.Slug}"); } } } diff --git a/data/Piranha.Data.EF/Repositories/PostTypeRepository.cs b/data/Piranha.Data.EF/Repositories/PostTypeRepository.cs new file mode 100644 index 000000000..d898c1269 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/PostTypeRepository.cs @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Piranha.Models; + +namespace Piranha.Repositories +{ + public class PostTypeRepository : IPostTypeRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db connection + public PostTypeRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAll() + { + var models = new List(); + var types = await _db.PostTypes + .AsNoTracking() + .OrderBy(t => t.Id) + .ToListAsync(); + + foreach (var type in types) + { + models.Add(JsonConvert.DeserializeObject(type.Body)); + } + return models; + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public async Task GetById(string id) + { + var type = await _db.PostTypes + .AsNoTracking() + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + return JsonConvert.DeserializeObject(type.Body); + } + return null; + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(PostType model) + { + var type = await _db.PostTypes + .FirstOrDefaultAsync(t => t.Id == model.Id); + + if (type == null) { + type = new Data.PostType + { + Id = model.Id, + Created = DateTime.Now + }; + await _db.PostTypes.AddAsync(type); + } + type.CLRType = model.CLRType; + type.Body = JsonConvert.SerializeObject(model); + type.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(string id) + { + var type = await _db.PostTypes + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + _db.PostTypes.Remove(type); + await _db.SaveChangesAsync(); + } + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/SiteRepository.cs b/data/Piranha.Data.EF/Repositories/SiteRepository.cs new file mode 100644 index 000000000..164186d25 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/SiteRepository.cs @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Piranha.Data; +using Piranha.Data.EF; +using Piranha.Services; + +namespace Piranha.Repositories +{ + public class SiteRepository : ISiteRepository + { + private readonly IDb _db; + private readonly IContentService _contentService; + + /// + /// Default constructor. + /// + /// The current db context + /// The content service factory + public SiteRepository(IDb db, IContentServiceFactory factory) + { + _db = db; + _contentService = factory.CreateSiteService(); + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAll() + { + return await _db.Sites + .AsNoTracking() + .OrderBy(s => s.Title) + .Select(s => new Models.Site + { + Id = s.Id, + SiteTypeId = s.SiteTypeId, + Title = s.Title, + InternalId = s.InternalId, + Description = s.Description, + Hostnames = s.Hostnames, + IsDefault = s.IsDefault, + Culture = s.Culture, + ContentLastModified = s.ContentLastModified, + Created = s.Created, + LastModified = s.LastModified + }) + .ToListAsync(); + } + + /// + /// Gets the model with the specified id. + /// + /// The unique id + /// The model, or NULL if it doesn't exist + public Task GetById(Guid id) + { + return _db.Sites + .AsNoTracking() + .Select(s => new Models.Site + { + Id = s.Id, + SiteTypeId = s.SiteTypeId, + Title = s.Title, + InternalId = s.InternalId, + Description = s.Description, + Hostnames = s.Hostnames, + IsDefault = s.IsDefault, + Culture = s.Culture, + ContentLastModified = s.ContentLastModified, + Created = s.Created, + LastModified = s.LastModified + }) + .FirstOrDefaultAsync(s => s.Id == id); + } + + /// + /// Gets the model with the given internal id. + /// + /// The unique internal i + /// The model + public Task GetByInternalId(string internalId) + { + return _db.Sites + .AsNoTracking() + .Select(s => new Models.Site + { + Id = s.Id, + SiteTypeId = s.SiteTypeId, + Title = s.Title, + InternalId = s.InternalId, + Description = s.Description, + Hostnames = s.Hostnames, + IsDefault = s.IsDefault, + Culture = s.Culture, + ContentLastModified = s.ContentLastModified, + Created = s.Created, + LastModified = s.LastModified + }) + .FirstOrDefaultAsync(s => s.InternalId == internalId); + } + + /// + /// Gets the default side. + /// + /// The modell, or NULL if it doesnt exist + public Task GetDefault() + { + return _db.Sites + .AsNoTracking() + .Select(s => new Models.Site + { + Id = s.Id, + SiteTypeId = s.SiteTypeId, + Title = s.Title, + InternalId = s.InternalId, + Description = s.Description, + Hostnames = s.Hostnames, + IsDefault = s.IsDefault, + Culture = s.Culture, + ContentLastModified = s.ContentLastModified, + Created = s.Created, + LastModified = s.LastModified + }) + .FirstOrDefaultAsync(s => s.IsDefault); + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site content model + public Task GetContentById(Guid id) + { + return GetContentById(id); + } + + /// + /// Gets the site content for given site id. + /// + /// Site id + /// The site model type + /// The site content model + public async Task GetContentById(Guid id) where T : Models.SiteContent + { + var site = await _db.Sites + .Include(s => s.Fields) + .Where(s => s.Id == id) + .FirstOrDefaultAsync(); + + if (site == null) + { + return null; + } + + if (string.IsNullOrEmpty(site.SiteTypeId)) + return null; + + var type = App.SiteTypes.GetById(site.SiteTypeId); + if (type == null) + return null; + + return _contentService.Transform(site, type); + } + + /// + /// Gets the hierachical sitemap structure. + /// + /// The optional site id + /// If only published items should be included + /// The sitemap + public async Task GetSitemap(Guid id, bool onlyPublished = true) + { + var pages = await _db.Pages + .AsNoTracking() + .Where(p => p.SiteId == id) + .OrderBy(p => p.ParentId) + .ThenBy(p => p.SortOrder) + .ToListAsync(); + + if (onlyPublished) + { + pages = pages.Where(p => p.Published.HasValue).ToList(); + } + return Sort(pages); + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(Models.Site model) + { + var site = await _db.Sites + .FirstOrDefaultAsync(s => s.Id == model.Id); + + if (site == null) + { + site = new Data.Site + { + Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), + Created = DateTime.Now + }; + await _db.Sites.AddAsync(site); + } + site.SiteTypeId = model.SiteTypeId; + site.Title = model.Title; + site.InternalId = model.InternalId; + site.Description = model.Description; + site.Hostnames = model.Hostnames; + site.Culture = model.Culture; + site.IsDefault = model.IsDefault; + site.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Saves the given site content to the site with the + /// given id. + /// + /// The site id + /// The site content + /// The site content type + public async Task SaveContent(Guid siteId, T content) where T : Models.SiteContent + { + var site = await _db.Sites + .Include(s => s.Fields) + .FirstOrDefaultAsync(s => s.Id == siteId); + + if (site != null) + { + if (string.IsNullOrEmpty(site.SiteTypeId)) + { + throw new MissingFieldException("Can't save content for a site that doesn't have a Site Type Id."); + } + + var type = App.SiteTypes.GetById(site.SiteTypeId); + if (type == null) + { + throw new MissingFieldException("The specified Site Type is missing. Can't save content."); + } + + content.Id = siteId; + content.TypeId = site.SiteTypeId; + content.Title = site.Title; + + _contentService.Transform(content, type, site); + + // Since we've updated global site content, update the + // global last modified date for the site. + site.ContentLastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(Guid id) + { + var site = await _db.Sites + .FirstOrDefaultAsync(s => s.Id == id); + + if (site != null) + { + _db.Sites.Remove(site); + await _db.SaveChangesAsync(); + } + } + + /// + /// Sorts the items. + /// + /// The full page list + /// The current parent id + /// The sitemap + private Models.Sitemap Sort(IEnumerable pages, Guid? parentId = null, int level = 0) + { + var result = new Models.Sitemap(); + + foreach (var page in pages.Where(p => p.ParentId == parentId).OrderBy(p => p.SortOrder)) + { + var item = Module.Mapper.Map(page); + + if (!string.IsNullOrEmpty(page.RedirectUrl)) + { + item.Permalink = page.RedirectUrl; + } + + item.Level = level; + item.PageTypeName = App.PageTypes.First(t => t.Id == page.PageTypeId).Title; + item.Items = Sort(pages, page.Id, level + 1); + + result.Add(item); + } + return result; + } + } +} diff --git a/data/Piranha.Data.EF/Repositories/SiteTypeRepository.cs b/data/Piranha.Data.EF/Repositories/SiteTypeRepository.cs new file mode 100644 index 000000000..bfd74f941 --- /dev/null +++ b/data/Piranha.Data.EF/Repositories/SiteTypeRepository.cs @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017-2019 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Piranha.Models; + +namespace Piranha.Repositories +{ + public class SiteTypeRepository : ISiteTypeRepository + { + private readonly IDb _db; + + /// + /// Default constructor. + /// + /// The current db connection + public SiteTypeRepository(IDb db) + { + _db = db; + } + + /// + /// Gets all available models. + /// + /// The available models + public async Task> GetAll() + { + var models = new List(); + var types = await _db.SiteTypes + .AsNoTracking() + .OrderBy(t => t.Id) + .ToListAsync(); + + foreach (var type in types) + { + models.Add(JsonConvert.DeserializeObject(type.Body)); + } + return models; + } + + /// + /// Gets the model with the specified id. + /// + /// The unique i + /// + public async Task GetById(string id) + { + var type = await _db.SiteTypes + .AsNoTracking() + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + return JsonConvert.DeserializeObject(type.Body); + } + return null; + } + + /// + /// Adds or updates the given model in the database + /// depending on its state. + /// + /// The model + public async Task Save(SiteType model) + { + var type = await _db.SiteTypes + .FirstOrDefaultAsync(t => t.Id == model.Id); + + if (type == null) { + type = new Data.SiteType + { + Id = model.Id, + Created = DateTime.Now + }; + await _db.SiteTypes.AddAsync(type); + } + type.CLRType = model.CLRType; + type.Body = JsonConvert.SerializeObject(model); + type.LastModified = DateTime.Now; + + await _db.SaveChangesAsync(); + } + + /// + /// Deletes the model with the specified id. + /// + /// The unique id + public async Task Delete(string id) + { + var type = await _db.SiteTypes + .FirstOrDefaultAsync(t => t.Id == id); + + if (type != null) + { + _db.SiteTypes.Remove(type); + await _db.SaveChangesAsync(); + } + } + } +} diff --git a/data/Piranha.Data.EF/Services/ContentService.cs b/data/Piranha.Data.EF/Services/ContentService.cs new file mode 100644 index 000000000..a35b38942 --- /dev/null +++ b/data/Piranha.Data.EF/Services/ContentService.cs @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2018 Håkan Edling + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using AutoMapper; +using Piranha.Data; +using Piranha.Models; + +namespace Piranha.Services +{ + public class ContentService : IContentService + where TContent : Content + where TField : ContentField + where TModelBase : Content + { + // + // Members + protected readonly IContentFactory _factory; + protected readonly IMapper _mapper; + + /// + /// Default constructor. + /// + /// The AutoMapper instance to use + public ContentService(IContentFactory factory, IMapper mapper) + { + _factory = factory; + _mapper = mapper; + } + + /// + /// Loads the given data into a new model. + /// + /// The model type + /// The content entity + /// The content type + /// The page model + public T Transform(TContent content, Models.ContentType type, Action process = null) + where T : Models.Content, TModelBase + { + if (type != null) + { + var modelType = typeof(T); + + if (!typeof(Models.IDynamicModel).IsAssignableFrom(modelType) && !typeof(Models.IContentInfo).IsAssignableFrom(modelType)) + { + modelType = Type.GetType(type.CLRType); + + if (modelType != typeof(T) && !typeof(T).IsAssignableFrom(modelType)) + return null; + } + + // Create an initialized model + var model = _factory.Create(type); + + // Map basic fields + _mapper.Map(content, model); + + if (model is Models.RoutedContent) + { + var routeModel = (Models.RoutedContent)(object)model; + + // Map route (if available) + if (string.IsNullOrWhiteSpace(routeModel.Route) && type.Routes.Count > 0) + routeModel.Route = type.Routes.First(); + } + + // Map regions + if (!(model is IContentInfo)) + { + var currentRegions = type.Regions.Select(r => r.Id).ToArray(); + + foreach (var regionKey in currentRegions) + { + var region = type.Regions.Single(r => r.Id == regionKey); + var fields = content.Fields.Where(f => f.RegionId == regionKey).OrderBy(f => f.SortOrder).ToList(); + + if (!region.Collection) + { + foreach (var fieldDef in region.Fields) + { + var field = fields.SingleOrDefault(f => f.FieldId == fieldDef.Id && f.SortOrder == 0); + + if (field != null) + { + if (region.Fields.Count == 1) + { + SetSimpleValue(model, regionKey, field); + break; + } + else + { + SetComplexValue(model, regionKey, fieldDef.Id, field); + } + } + } + } + else + { + var fieldCount = content.Fields.Where(f => f.RegionId == regionKey).Select(f => f.SortOrder).DefaultIfEmpty(-1).Max() + 1; + var sortOrder = 0; + + while (fieldCount > sortOrder) + { + if (region.Fields.Count == 1) + { + var field = fields.SingleOrDefault(f => f.FieldId == region.Fields[0].Id && f.SortOrder == sortOrder); + if (field != null) + AddSimpleValue(model, regionKey, field); + } + else + { + AddComplexValue(model, type, regionKey, fields.Where(f => f.SortOrder == sortOrder).ToList()); + } + sortOrder++; + } + } + } + } + process?.Invoke(content, model); + + return model; + } + return null; + } + + /// + /// Transforms the given model into content data. + /// + /// The model + /// The conten type + /// The optional dest object + /// The content data + public TContent Transform(T model, Models.ContentType type, TContent dest = null) + where T : Models.Content, TModelBase + { + var content = dest == null ? Activator.CreateInstance() : dest; + + // Map id + if (model.Id != Guid.Empty) + { + content.Id = model.Id; + } + else + { + content.Id = model.Id = Guid.NewGuid(); + } + content.Created = DateTime.Now; + + // Map basic fields + _mapper.Map(model, content); + + // Map regions + var currentRegions = type.Regions.Select(r => r.Id).ToArray(); + + foreach (var regionKey in currentRegions) + { + // Check that the region exists in the current model + if (HasRegion(model, regionKey)) + { + var regionType = type.Regions.Single(r => r.Id == regionKey); + + if (!regionType.Collection) + { + MapRegion(model, content, GetRegion(model, regionKey), regionType, regionKey); + } + else + { + var items = new List(); + var sortOrder = 0; + foreach (var region in GetEnumerable(model, regionKey)) + { + var fields = MapRegion(model, content, region, regionType, regionKey, sortOrder++); + + if (fields.Count > 0) + items.AddRange(fields); + } + // Now delete removed collection items + var removedFields = content.Fields + .Where(f => f.RegionId == regionKey && !items.Contains(f.Id)) + .ToList(); + foreach (var removed in removedFields) + content.Fields.Remove(removed); + } + } + } + return content; + } + + /// + /// Transforms the given block data into block models. + /// + /// The data + /// The transformed blocks + public IList TransformBlocks(IEnumerable blocks) + { + var models = new List(); + + foreach (var block in blocks) + { + var blockType = App.Blocks.GetByType(block.CLRType); + + if (blockType != null) + { + var model = (Extend.Block)Activator.CreateInstance(blockType.Type); + model.Id = block.Id; + + foreach (var field in block.Fields) + { + var prop = model.GetType().GetProperty(field.FieldId, App.PropertyBindings); + + if (prop != null) + { + var type = App.Fields.GetByType(field.CLRType); + var val = (Extend.IField)App.DeserializeObject(field.Value, type.Type); + + prop.SetValue(model, val); + } + } + models.Add(model); + } + } + return models; + } + + /// + /// Transforms the given blocks to the internal data model. + /// + /// The blocks + /// The data model + public IList TransformBlocks(IList models) + { + var blocks = new List(); + + if (models != null) + { + for (var n = 0; n < models.Count; n++) + { + var type = App.Blocks.GetByType(models[n].GetType().FullName); + + if (type != null) + { + var block = new Block() + { + Id = models[n].Id != Guid.Empty ? models[n].Id : Guid.NewGuid(), + CLRType = models[n].GetType().FullName, + Created = DateTime.Now, + LastModified = DateTime.Now + }; + + foreach (var prop in models[n].GetType().GetProperties(App.PropertyBindings)) + { + if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) + { + // Only save fields to the database + var field = new BlockField() + { + Id = Guid.NewGuid(), + BlockId = block.Id, + FieldId = prop.Name, + SortOrder = 0, + CLRType = prop.PropertyType.FullName, + Value = App.SerializeObject(prop.GetValue(models[n]), prop.PropertyType) + }; + block.Fields.Add(field); + } + } + blocks.Add(block); + + if (typeof(Extend.BlockGroup).IsAssignableFrom(models[n].GetType())) + { + var blockItems = TransformBlocks(((Extend.BlockGroup)models[n]).Items); + + if (blockItems.Count() > 0) + { + foreach (var item in blockItems) + { + item.ParentId = block.Id; + } + blocks.AddRange(blockItems); + } + } + } + } + } + return blocks; + } + + /// + /// Gets the enumerator for the given region collection. + /// + /// The model type + /// The model + /// The region id + /// The enumerator + private IEnumerable GetEnumerable(T model, string regionId) where T : Models.Content + { + object value = null; + + if (model is Models.IDynamicModel) + { + value = ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; + } + else + { + value = model.GetType().GetProperty(regionId, App.PropertyBindings).GetValue(model); + } + if (value is IEnumerable) + return (IEnumerable)value; + return null; + } + + /// + /// Gets the region with the given key. + /// + /// The model type + /// The model + /// The region id + /// The region + private object GetRegion(T model, string regionId) where T : Models.Content + { + if (model is Models.IDynamicModel) + { + return ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; + } + else + { + return model.GetType().GetProperty(regionId, App.PropertyBindings).GetValue(model); + } + } + + /// + /// Checks if the given model has a region with the specified id. + /// + /// The model type + /// The model + /// The region id + /// If the region exists + private bool HasRegion(T model, string regionId) where T : Models.Content + { + if (model is Models.IDynamicModel) + { + return ((IDictionary)((Models.IDynamicModel)(object)model).Regions).ContainsKey(regionId); + } + else + { + return model.GetType().GetProperty(regionId, App.PropertyBindings) != null; + } + } + + /// + /// Maps a region to the given data entity. + /// + /// The model type + /// The model + /// The content entity + /// The region to map + /// The region type + /// The region id + /// The optional sort order + private IList MapRegion(T model, TContent content, object region, Models.RegionType regionType, string regionId, int sortOrder = 0) where T : Models.Content + { + var items = new List(); + + // Now map all of the fields + for (var n = 0; n < regionType.Fields.Count; n++) + { + var fieldDef = regionType.Fields[n]; + var fieldType = App.Fields.GetByShorthand(fieldDef.Type); + if (fieldType == null) + fieldType = App.Fields.GetByType(fieldDef.Type); + + if (fieldType != null) + { + object fieldValue = null; + if (regionType.Fields.Count == 1) + { + // Get the field value for simple region + fieldValue = region; + } + else + { + // Get the field value for complex region + fieldValue = GetComplexValue(region, fieldDef.Id); + } + + if (fieldValue != null) + { + // Check that the returned value matches the type specified + // for the page type, otherwise deserialization won't work + // when the model is retrieved from the database. + if (fieldValue.GetType() != fieldType.Type) + throw new ArgumentException("Given field value does not match the configured type"); + + // Check if we have the current field in the database already + var field = content.Fields + .SingleOrDefault(f => f.RegionId == regionId && f.FieldId == fieldDef.Id && f.SortOrder == sortOrder); + + // If not, create a new field + if (field == null) + { + field = Activator.CreateInstance(); + field.Id = Guid.NewGuid(); + field.RegionId = regionId; + field.FieldId = fieldDef.Id; + + content.Fields.Add(field); + } + + // Update field info & value + field.CLRType = fieldType.TypeName; + field.SortOrder = sortOrder; + field.Value = App.SerializeObject(fieldValue, fieldType.Type); + + items.Add(field.Id); + } + } + } + return items; + } + + /// + /// Sets the value of a simple single field region. + /// + /// The model type + /// The model + /// The region id + /// The field + private void SetSimpleValue(T model, string regionId, TField field) where T : Models.Content + { + if (model is Models.IDynamicModel) + { + ((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId] = + DeserializeValue(field); + } + else + { + var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); + + if (regionProp != null) + { + regionProp.SetValue(model, DeserializeValue(field)); + } + } + } + + /// + /// Adds a simple single field value to a collection region. + /// + /// The model type + /// The model + /// The region id + /// The field + private void AddSimpleValue(T model, string regionId, TField field) where T : Models.Content + { + if (model is Models.IDynamicModel) + { + ((IList)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]).Add( + DeserializeValue(field)); + } + else + { + var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); + + if (regionProp != null) + { + ((IList)regionProp.GetValue(model)).Add(DeserializeValue(field)); + } + } + } + + /// + /// Sets the value of a complex region. + /// + /// The model + /// The model + /// The region id + /// The field id + /// The field + private void SetComplexValue(T model, string regionId, string fieldId, TField field) where T : Models.Content + { + if (model is Models.IDynamicModel) + { + ((IDictionary)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId])[fieldId] = + DeserializeValue(field); + } + else + { + var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); + + if (regionProp != null) + { + var obj = regionProp.GetValue(model); + if (obj != null) + { + var fieldProp = obj.GetType().GetProperty(fieldId, App.PropertyBindings); + + if (fieldProp != null) + { + fieldProp.SetValue(obj, DeserializeValue(field)); + } + } + } + } + } + + /// + /// Adds a complex region to a collection region. + /// + /// The model type + /// The model + /// The region id + /// The field + private void AddComplexValue(T model, Models.ContentType contentType, string regionId, IList fields) where T : Models.Content + { + if (fields.Count > 0) + { + if (model is Models.IDynamicModel) + { + var list = (IList)((IDictionary)((Models.IDynamicModel)(object)model).Regions)[regionId]; + var obj = _factory.CreateDynamicRegion(contentType, regionId); + + foreach (var field in fields) + { + if (((IDictionary)obj).ContainsKey(field.FieldId)) + { + ((IDictionary)obj)[field.FieldId] = + DeserializeValue(field); + } + } + list.Add(obj); + } + else + { + var regionProp = model.GetType().GetProperty(regionId, App.PropertyBindings); + + if (regionProp != null) + { + var list = (IList)regionProp.GetValue(model); + var obj = Activator.CreateInstance(list.GetType().GenericTypeArguments.First()); + + foreach (var field in fields) + { + var fieldProp = obj.GetType().GetProperty(field.FieldId, App.PropertyBindings); + if (fieldProp != null) + { + fieldProp.SetValue(obj, DeserializeValue(field)); + } + } + list.Add(obj); + } + } + } + } + + /// + /// Deserializes the given field value. + /// + /// The page field + /// The value + private object DeserializeValue(TField field) + { + var type = App.Fields.GetByType(field.CLRType); + + if (type != null) + { + return App.DeserializeObject(field.Value, type.Type); + } + return null; + } + + /// + /// Gets a field value from a complex region. + /// + /// The region + /// The field id + /// The value + private object GetComplexValue(object region, string fieldId) + { + if (region is ExpandoObject) + { + return ((IDictionary)region)[fieldId]; + } + else + { + var fieldProp = region.GetType().GetProperty(fieldId, App.PropertyBindings); + + if (fieldProp != null) + { + return fieldProp.GetValue(region); + } + return null; + } + } + } +} diff --git a/core/Piranha/Services/ContentServiceFactory.cs b/data/Piranha.Data.EF/Services/ContentServiceFactory.cs similarity index 84% rename from core/Piranha/Services/ContentServiceFactory.cs rename to data/Piranha.Data.EF/Services/ContentServiceFactory.cs index 658879b57..8ec001e62 100644 --- a/core/Piranha/Services/ContentServiceFactory.cs +++ b/data/Piranha.Data.EF/Services/ContentServiceFactory.cs @@ -1,29 +1,30 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; using AutoMapper; +using Piranha.Data.EF; namespace Piranha.Services { public class ContentServiceFactory : IContentServiceFactory { - private readonly IServiceProvider _services; + private readonly IContentFactory _factory; /// /// Default constructor. /// /// The service provider - public ContentServiceFactory(IServiceProvider services) + public ContentServiceFactory(IContentFactory factory) { - _services = services; + _factory = factory; } /// @@ -36,7 +37,7 @@ public IContentService Create(_services, mapper); + return new ContentService(_factory, mapper); } /// @@ -45,7 +46,7 @@ public IContentService CreateThe content service public IContentService CreatePageService() { - return new ContentService(_services, App.Mapper); + return new ContentService(_factory, Module.Mapper); } /// @@ -54,7 +55,7 @@ public IContentService CreateThe content service public IContentService CreatePostService() { - return new ContentService(_services, App.Mapper); + return new ContentService(_factory, Module.Mapper); } /// @@ -63,7 +64,7 @@ public IContentService CreateThe content service public IContentService CreateSiteService() { - return new ContentService(_services, App.Mapper); + return new ContentService(_factory, Module.Mapper); } } } diff --git a/core/Piranha/Services/IContentService.cs b/data/Piranha.Data.EF/Services/IContentService.cs similarity index 58% rename from core/Piranha/Services/IContentService.cs rename to data/Piranha.Data.EF/Services/IContentService.cs index 8acfb2b33..d279e5ff8 100644 --- a/core/Piranha/Services/IContentService.cs +++ b/data/Piranha.Data.EF/Services/IContentService.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * https://github.com/piranhacms/piranha.core - * + * */ using System; @@ -14,36 +14,10 @@ namespace Piranha.Services { public interface IContentService - where TContent : Data.Content + where TContent : Data.Content where TField : Data.ContentField where TModelBase : Models.Content { - /// - /// Creates a new content model of the given type. - /// - /// The content type - /// The model - T Create(Models.ContentType contentType) where T : Models.Content; - - /// - /// Creates a new region. - /// - /// The content type id - /// The region id - /// The new region value - object CreateDynamicRegion(Models.ContentType contentType, string regionId); - - /// - /// Creates a dynamic region. - /// - /// The value type - /// The content type - /// The region id - /// The region value - T CreateRegion(Models.ContentType contentType, string regionId); - - object CreateBlock(string typeName); - /// /// Transforms the given data into a new model. /// @@ -51,7 +25,7 @@ public interface IContentService /// The content entity /// The content type /// The page model - T Transform(TContent content, Models.ContentType type, Action process = null) + T Transform(TContent content, Models.ContentType type, Action process = null) where T : Models.Content, TModelBase; /// @@ -70,7 +44,6 @@ TContent Transform(T model, Models.ContentType type, TContent dest = null) /// The data /// The transformed blocks IList TransformBlocks(IEnumerable blocks); - IList TransformBlocks(IEnumerable blocks) where T : Data.IContentBlock; /// /// Transforms the given blocks to the internal data model. @@ -78,6 +51,5 @@ TContent Transform(T model, Models.ContentType type, TContent dest = null) /// The blocks /// The data model IList TransformBlocks(IList models); - IList TransformBlocks(IList models) where T : Data.IContentBlock; } } diff --git a/core/Piranha/Services/IContentServiceFactory.cs b/data/Piranha.Data.EF/Services/IContentServiceFactory.cs similarity index 93% rename from core/Piranha/Services/IContentServiceFactory.cs rename to data/Piranha.Data.EF/Services/IContentServiceFactory.cs index 61eaf25e7..3f8880aeb 100644 --- a/core/Piranha/Services/IContentServiceFactory.cs +++ b/data/Piranha.Data.EF/Services/IContentServiceFactory.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using AutoMapper; @@ -19,8 +19,8 @@ public interface IContentServiceFactory /// /// The AutoMapper instance to use for transformation /// The content service - IContentService Create(IMapper mapper) - where TContent : Data.Content + IContentService Create(IMapper mapper) + where TContent : Data.Content where TField : Data.ContentField where TModelBase : Models.Content; diff --git a/examples/MvcWeb/Controllers/CmsController.cs b/examples/MvcWeb/Controllers/CmsController.cs index 811f25591..e496878ee 100644 --- a/examples/MvcWeb/Controllers/CmsController.cs +++ b/examples/MvcWeb/Controllers/CmsController.cs @@ -1,8 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; using Piranha; using Piranha.Models; -using System; -using System.Linq; +using Piranha.Services; namespace MvcWeb.Controllers { @@ -15,8 +17,8 @@ public class CmsController : Controller /// Default constructor. /// /// The current app - public CmsController(IApi api, IDb db) - { + public CmsController(IApi api, IDb db) + { _api = api; _db = db; } @@ -31,18 +33,10 @@ public CmsController(IApi api, IDb db) /// The optional category /// The optional tag [Route("archive")] - public IActionResult Archive(Guid id, int? year = null, int? month = null, int? page = null, - Guid? category = null, Guid? tag = null) + public async Task Archive(Guid id, int? year = null, int? month = null, int? page = null, + Guid? category = null, Guid? tag = null) { - Models.BlogArchive model; - - if (category.HasValue) - model = _api.Archives.GetByCategoryId(id, category.Value, page, year, month); - else if (tag.HasValue) - model = _api.Archives.GetByTagId(id, tag.Value, page, year, month); - else model = _api.Archives.GetById(id, page, year, month); - - return View(model); + return View(await _api.Archives.GetByIdAsync(id, page, category, tag, year, month)); } /// @@ -50,9 +44,9 @@ public IActionResult Archive(Guid id, int? year = null, int? month = null, int? /// /// The unique page id [Route("page")] - public IActionResult Page(Guid id) + public async Task Page(Guid id) { - var model = _api.Pages.GetById(id); + var model = await _api.Pages.GetByIdAsync(id); return View(model); } @@ -62,9 +56,9 @@ public IActionResult Page(Guid id) /// /// The unique page id [Route("pagewide")] - public IActionResult PageWide(Guid id) + public async Task PageWide(Guid id) { - var model = _api.Pages.GetById(id); + var model = await _api.Pages.GetByIdAsync(id); return View(model); } @@ -73,11 +67,11 @@ public IActionResult PageWide(Guid id) /// Gets the post with the given id. /// /// The unique post id - /// + /// [Route("post")] - public IActionResult Post(Guid id) + public async Task Post(Guid id) { - var model = _api.Posts.GetById(id); + var model = await _api.Posts.GetByIdAsync(id); return View(model); } @@ -88,9 +82,9 @@ public IActionResult Post(Guid id) /// The page id /// If this is the startpage of the site [Route("teaserpage")] - public IActionResult TeaserPage(Guid id, bool startpage = false) + public async Task TeaserPage(Guid id, bool startpage = false) { - var model = _api.Pages.GetById(id); + var model = await _api.Pages.GetByIdAsync(id); if (startpage) { @@ -101,8 +95,8 @@ public IActionResult TeaserPage(Guid id, bool startpage = false) .Select(p => p.Id); if (latest.Count() > 0) { - model.LatestPost = _api.Posts - .GetById(latest.First()); + model.LatestPost = await _api.Posts + .GetByIdAsync(latest.First()); } return View("startpage", model); } diff --git a/examples/MvcWeb/MvcWeb.csproj b/examples/MvcWeb/MvcWeb.csproj index 9481dcf4a..3b19e8cfc 100644 --- a/examples/MvcWeb/MvcWeb.csproj +++ b/examples/MvcWeb/MvcWeb.csproj @@ -21,5 +21,6 @@ + diff --git a/examples/MvcWeb/Seed.cs b/examples/MvcWeb/Seed.cs index 5fa5d6a7d..78d136e05 100644 --- a/examples/MvcWeb/Seed.cs +++ b/examples/MvcWeb/Seed.cs @@ -1,15 +1,17 @@ using System; using System.IO; +using System.Threading.Tasks; using Piranha; using Piranha.Extend.Blocks; +using Piranha.Services; namespace MvcWeb { public static class Seed { - public static void Run(IApi api) + public static async Task RunAsync(IApi api) { - if (api.Pages.GetStartpage() == null) + if ((await api.Pages.GetStartpageAsync()) == null) { var images = new dynamic [] { @@ -21,14 +23,14 @@ public static void Run(IApi api) }; // Get the default site id - var siteId = api.Sites.GetDefault().Id; + var siteId = (await api.Sites.GetDefaultAsync()).Id; // Upload images foreach (var image in images) { using (var stream = File.OpenRead("seed/" + image.filename)) { - api.Media.Save(new Piranha.Models.StreamMediaContent() + api.Media.Save(new Piranha.Models.StreamMediaContent() { Id = image.id, Filename = image.filename, @@ -48,7 +50,7 @@ public static void Run(IApi api) // Start page hero startpage.Hero.Subtitle = "By developers - for developers"; startpage.Hero.PrimaryImage = images[1].id; - startpage.Hero.Ingress = + startpage.Hero.Ingress = "

A lightweight & unobtrusive CMS for ASP.NET Core.

" + "

Stable version 5.2.1 - 2018-10-17 - Changelog

"; @@ -98,7 +100,7 @@ public static void Run(IApi api) } } startpage.Published = DateTime.Now; - api.Pages.Save(startpage); + await api.Pages.SaveAsync(startpage); // Features page var featurespage = Models.StandardPage.Create(api); @@ -106,7 +108,7 @@ public static void Run(IApi api) featurespage.Title = "Features"; featurespage.Route = "/pagewide"; featurespage.SortOrder = 1; - + // Features hero featurespage.Hero.Subtitle = "Features"; featurespage.Hero.Ingress = "

It's all about who has the sharpest teeth in the pond.

"; @@ -117,7 +119,7 @@ public static void Run(IApi api) using (var reader = new StreamReader(stream)) { var body = reader.ReadToEnd(); - + foreach (var section in body.Split("%")) { var blocks = section.Split("@"); @@ -140,7 +142,7 @@ public static void Run(IApi api) Column1 = App.Markdown.Transform(cols[0].Trim()), Column2 = App.Markdown.Transform(cols[1].Trim()) }); - + if (n < blocks.Length - 1) { featurespage.Blocks.Add(new Models.Blocks.SeparatorBlock()); @@ -151,7 +153,7 @@ public static void Run(IApi api) } } featurespage.Published = DateTime.Now; - api.Pages.Save(featurespage); + await api.Pages.SaveAsync(featurespage); // Blog Archive var blogpage = Models.BlogArchive.Create(api); @@ -168,7 +170,7 @@ public static void Run(IApi api) blogpage.Hero.Ingress = "

Welcome to the blog, the best place to stay up to date with what's happening in the Piranha infested waters.

"; blogpage.Published = DateTime.Now; - api.Pages.Save(blogpage); + await api.Pages.SaveAsync(blogpage); // Blog Post var blogpost = Models.BlogPost.Create(api); @@ -193,7 +195,7 @@ public static void Run(IApi api) } } blogpost.Published = DateTime.Now; - api.Posts.Save(blogpost); + await api.Posts.SaveAsync(blogpost); } } } diff --git a/examples/MvcWeb/Startup.cs b/examples/MvcWeb/Startup.cs index 144fe3795..e574092f7 100644 --- a/examples/MvcWeb/Startup.cs +++ b/examples/MvcWeb/Startup.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -16,23 +17,22 @@ public class Startup // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - services.AddMvc(config => + services.AddMvc(config => { config.ModelBinderProviders.Insert(0, new Piranha.Manager.Binders.AbstractModelBinderProvider()); }); + services.AddPiranha(); services.AddPiranhaApplication(); services.AddPiranhaFileStorage(); services.AddPiranhaImageSharp(); - services.AddPiranhaEF(options => + services.AddPiranhaEF(options => options.UseSqlite("Filename=./piranha.mvcweb.db")); - services.AddPiranhaIdentityWithSeed(options => + services.AddPiranhaIdentityWithSeed(options => options.UseSqlite("Filename=./piranha.mvcweb.db")); services.AddPiranhaManager(); services.AddMemoryCache(); services.AddPiranhaMemoryCache(); - - App.Init(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -43,8 +43,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApi api app.UseDeveloperExceptionPage(); } + App.Init(api); + // Configure cache level - // App.CacheLevel = Piranha.Cache.CacheLevel.Basic; App.CacheLevel = Piranha.Cache.CacheLevel.Full; // Custom components @@ -54,12 +55,12 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApi api var pageTypeBuilder = new Piranha.AttributeBuilder.PageTypeBuilder(api) .AddType(typeof(Models.BlogArchive)) .AddType(typeof(Models.StandardPage)) - .AddType(typeof(Models.TeaserPage)); - pageTypeBuilder.Build() + .AddType(typeof(Models.TeaserPage)) + .Build() .DeleteOrphans(); var postTypeBuilder = new Piranha.AttributeBuilder.PostTypeBuilder(api) - .AddType(typeof(Models.BlogPost)); - postTypeBuilder.Build() + .AddType(typeof(Models.BlogPost)) + .Build() .DeleteOrphans(); // Register middleware @@ -67,7 +68,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApi api app.UseAuthentication(); app.UsePiranha(); app.UsePiranhaManager(); - app.UseMvc(routes => + app.UseMvc(routes => { routes.MapRoute(name: "areaRoute", template: "{area:exists}/{controller}/{action}/{id?}", @@ -78,7 +79,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApi api template: "{controller=home}/{action=index}/{id?}"); }); - Seed.Run(api); + Seed.RunAsync(api).GetAwaiter().GetResult(); } } } diff --git a/examples/MvcWeb/Views/Cms/DisplayTemplates/Hero.cshtml b/examples/MvcWeb/Views/Cms/DisplayTemplates/Hero.cshtml index 3b0d29b32..e34d71d15 100644 --- a/examples/MvcWeb/Views/Cms/DisplayTemplates/Hero.cshtml +++ b/examples/MvcWeb/Views/Cms/DisplayTemplates/Hero.cshtml @@ -8,7 +8,7 @@
- @if (Model.PrimaryImage.HasValue) + @if (Model.PrimaryImage.HasValue) { } diff --git a/test/Piranha.AttributeBuilder.Tests/AttributeBuilder.cs b/test/Piranha.AttributeBuilder.Tests/AttributeBuilder.cs index 9cc4e0b46..fb844b098 100644 --- a/test/Piranha.AttributeBuilder.Tests/AttributeBuilder.cs +++ b/test/Piranha.AttributeBuilder.Tests/AttributeBuilder.cs @@ -1,20 +1,21 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using Xunit; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.AttributeBuilder.Tests { @@ -72,14 +73,17 @@ public class BodyRegion [Region(Title = "Main content")] public BodyRegion Content { get; set; } } - + public AttributeBuilder() { - App.Init(); + using (var api = CreateApi()) + { + App.Init(api); + } } [Fact] public void AddSimple() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var builder = new PageTypeBuilder(api) .AddType(typeof(SimplePageType)); builder.Build(); @@ -95,7 +99,7 @@ public void AddSimple() { [Fact] public void AddComplex() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var builder = new PageTypeBuilder(api) .AddType(typeof(ComplexPageType)); builder.Build(); @@ -122,7 +126,7 @@ public void AddComplex() { [Fact] public void DeleteOrphans() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var builder = new PageTypeBuilder(api) .AddType(typeof(SimplePageType)) .AddType(typeof(ComplexPageType)); @@ -140,7 +144,7 @@ public void DeleteOrphans() { [Fact] public void AddSimpleSiteType() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var builder = new SiteTypeBuilder(api) .AddType(typeof(SimpleSiteType)); builder.Build(); @@ -156,7 +160,7 @@ public void AddSimpleSiteType() { [Fact] public void AddComplexSiteType() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var builder = new SiteTypeBuilder(api) .AddType(typeof(ComplexSiteType)); builder.Build(); @@ -179,7 +183,7 @@ public void AddComplexSiteType() { } public void Dispose() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), null)) { + using (var api = CreateApi()) { var types = api.PageTypes.GetAll(); foreach (var t in types) @@ -202,5 +206,27 @@ private IDb GetDb() { return new Db(builder.Options); } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db) + ); + } } } diff --git a/test/Piranha.AttributeBuilder.Tests/Piranha.AttributeBuilder.Tests.csproj b/test/Piranha.AttributeBuilder.Tests/Piranha.AttributeBuilder.Tests.csproj index 9e766b113..b88d9ab58 100644 --- a/test/Piranha.AttributeBuilder.Tests/Piranha.AttributeBuilder.Tests.csproj +++ b/test/Piranha.AttributeBuilder.Tests/Piranha.AttributeBuilder.Tests.csproj @@ -3,13 +3,20 @@ Exe netcoreapp2.1 + + + + + + + diff --git a/test/Piranha.ImageSharp.Tests/MediaRepository.cs b/test/Piranha.ImageSharp.Tests/MediaRepository.cs index e44fd6255..26732787b 100644 --- a/test/Piranha.ImageSharp.Tests/MediaRepository.cs +++ b/test/Piranha.ImageSharp.Tests/MediaRepository.cs @@ -1,17 +1,18 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using System.IO; using Xunit; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.ImageSharp.Tests { @@ -21,8 +22,8 @@ public class MediaRepository : BaseTests private Guid imageId; protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { - App.Init(); + using (var api = CreateApi()) { + App.Init(api); // Add media using (var stream = File.OpenRead("../../../Assets/HLD_Screenshot_01_mech_1080.png")) { @@ -37,14 +38,14 @@ protected override void Init() { } } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { api.Media.Delete(imageId); } } [Fact] public void GetOriginal() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { var media = api.Media.GetById(imageId); Assert.NotNull(media); @@ -54,7 +55,7 @@ public void GetOriginal() { [Fact] public void GetScaled() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { var url = api.Media.EnsureVersion(imageId, 640); Assert.NotNull(url); @@ -64,7 +65,7 @@ public void GetScaled() { [Fact] public void GetCropped() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { var url = api.Media.EnsureVersion(imageId, 640, 300); Assert.NotNull(url); @@ -74,7 +75,7 @@ public void GetCropped() { [Fact] public void GetScaledOrgSize() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { var url = api.Media.EnsureVersion(imageId, 1920); Assert.NotNull(url); @@ -84,12 +85,36 @@ public void GetScaledOrgSize() { [Fact] public void GetCroppedOrgSize() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, null, processor)) { + using (var api = CreateApi()) { var url = api.Media.EnsureVersion(imageId, 1920, 1080); Assert.NotNull(url); Assert.Equal($"~/uploads/{imageId}-HLD_Screenshot_01_mech_1080.png", url); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage, + processor: processor + ); + } } } diff --git a/test/Piranha.ImageSharp.Tests/Piranha.ImageSharp.Tests.csproj b/test/Piranha.ImageSharp.Tests/Piranha.ImageSharp.Tests.csproj index 6d030680c..5f1bb49e1 100644 --- a/test/Piranha.ImageSharp.Tests/Piranha.ImageSharp.Tests.csproj +++ b/test/Piranha.ImageSharp.Tests/Piranha.ImageSharp.Tests.csproj @@ -12,9 +12,11 @@ + + diff --git a/test/Piranha.Tests/App.cs b/test/Piranha.Tests/App.cs index 21bc3440a..2b418f193 100644 --- a/test/Piranha.Tests/App.cs +++ b/test/Piranha.Tests/App.cs @@ -1,17 +1,18 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Extend; -using Piranha.Services; using System.Linq; using Xunit; +using Piranha.Extend; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests { @@ -21,7 +22,10 @@ public class App : BaseTests /// Sets up & initializes the tests. ///
protected override void Init() { - Piranha.App.Init(); + using (var api = CreateApi()) + { + Piranha.App.Init(api); + } } /// @@ -32,7 +36,10 @@ protected override void Cleanup() { } [Fact] public void AppInit() { - Piranha.App.Init(); + using (var api = CreateApi()) + { + Piranha.App.Init(api); + } } [Fact] @@ -63,11 +70,6 @@ public void Fields() { Assert.NotEmpty(Piranha.App.Fields); } - [Fact] - public void Mapper() { - Assert.NotNull(Piranha.App.Mapper); - } - [Fact] public void Modules() { Assert.NotNull(Piranha.App.Modules); @@ -79,5 +81,27 @@ public void PropertyBindings() { Assert.True(Piranha.App.PropertyBindings.HasFlag(System.Reflection.BindingFlags.Public)); Assert.True(Piranha.App.PropertyBindings.HasFlag(System.Reflection.BindingFlags.Instance)); } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db) + ); + } } } diff --git a/test/Piranha.Tests/Blocks.cs b/test/Piranha.Tests/Blocks.cs index a87bedf47..6d720e33a 100644 --- a/test/Piranha.Tests/Blocks.cs +++ b/test/Piranha.Tests/Blocks.cs @@ -1,22 +1,23 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Piranha.Data; -using Piranha.Services; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using Xunit; +using Piranha.Data; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests { @@ -36,10 +37,10 @@ protected override void Init() { .AddSingleton() .BuildServiceProvider(); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); - contentService = new ContentService(services, Piranha.App.Mapper); + contentService = new ContentService(new ContentFactory(services), Piranha.Data.EF.Module.Mapper); // Add media using (var stream = File.OpenRead("../../../Assets/HLD_Screenshot_01_mech_1080.png")) { @@ -50,7 +51,7 @@ protected override void Init() { api.Media.Save(image1); image1Id = image1.Id.Value; - } + } } } @@ -59,7 +60,7 @@ protected override void Init() { /// created by the test. /// protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { var media = api.Media.GetAll(); foreach (var item in media) { @@ -83,7 +84,7 @@ public void DeserializeHtmlBlock() { Created = DateTime.Now, LastModified = DateTime.Now }); - + var models = contentService.TransformBlocks(blocks); Assert.NotNull(models); @@ -101,7 +102,7 @@ public void SerializeHtmlBlock() { Value = "

Lorem ipsum

" } }); - + var blocks = contentService.TransformBlocks(models); Assert.NotNull(blocks); @@ -154,7 +155,7 @@ public void SerializeHtmlColumnBlock() { Value = "

Column 2

" }, }); - + var blocks = contentService.TransformBlocks(models); Assert.NotNull(blocks); @@ -190,8 +191,9 @@ public void DeserializeImageBlock() { Assert.Single(models); Assert.Equal(typeof(Extend.Blocks.ImageBlock), models.First().GetType()); - Assert.NotNull(((Extend.Blocks.ImageBlock)models[0]).Body.Media); - Assert.Equal("HLD_Screenshot_01_mech_1080.png", ((Extend.Blocks.ImageBlock)models[0]).Body.Media.Filename); + Assert.Null(((Extend.Blocks.ImageBlock)models[0]).Body.Media); + //Assert.NotNull(((Extend.Blocks.ImageBlock)models[0]).Body.Media); + //Assert.Equal("HLD_Screenshot_01_mech_1080.png", ((Extend.Blocks.ImageBlock)models[0]).Body.Media.Filename); } [Fact] @@ -202,7 +204,7 @@ public void SerializeImageBlock() { Id = image1Id } }); - + var blocks = contentService.TransformBlocks(models); Assert.NotNull(blocks); @@ -246,7 +248,7 @@ public void SerializeTextBlock() { Value = "Lorem ipsum" } }); - + var blocks = contentService.TransformBlocks(models); Assert.NotNull(blocks); @@ -290,7 +292,7 @@ public void SerializeQuoteBlock() { Value = "Lorem ipsum" } }); - + var blocks = contentService.TransformBlocks(models); Assert.NotNull(blocks); @@ -300,5 +302,28 @@ public void SerializeQuoteBlock() { Assert.Equal(typeof(Extend.Fields.TextField).FullName, blocks[0].Fields[0].CLRType); Assert.Equal("Lorem ipsum", blocks[0].Fields[0].Value); } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Config.cs b/test/Piranha.Tests/Config.cs index 84147cf46..9c24cf370 100644 --- a/test/Piranha.Tests/Config.cs +++ b/test/Piranha.Tests/Config.cs @@ -1,17 +1,18 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Extend; -using Piranha.Services; using System.Linq; using Xunit; +using Piranha.Extend; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests { @@ -22,8 +23,8 @@ public class Config : BaseTests /// Sets up & initializes the tests. ///
protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); using (var config = new Piranha.Config(api)) { config.CacheExpiresPages = 0; @@ -42,7 +43,7 @@ protected override void Cleanup() { } [Fact] public void CacheExpiresPages() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { Assert.Equal(0, config.CacheExpiresPages); @@ -55,7 +56,7 @@ public void CacheExpiresPages() { [Fact] public void CacheExpiresPosts() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { Assert.Equal(0, config.CacheExpiresPosts); @@ -68,7 +69,7 @@ public void CacheExpiresPosts() { [Fact] public void HierarchicalPageSlugs() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { Assert.True(config.HierarchicalPageSlugs); @@ -81,7 +82,7 @@ public void HierarchicalPageSlugs() { [Fact] public void ManagerExpandedSitemapLevels() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { Assert.Equal(0, config.ManagerExpandedSitemapLevels); @@ -94,24 +95,47 @@ public void ManagerExpandedSitemapLevels() { [Fact] public void MediaCDN() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.MediaCDN = "https://mycdn.org/uploads/"; Assert.Equal("https://mycdn.org/uploads/", config.MediaCDN); } - } + } } [Fact] public void MediaCDNTrailingSlash() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.MediaCDN = "https://mycdn.org/uploads"; Assert.Equal("https://mycdn.org/uploads/", config.MediaCDN); } - } + } + } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); } } } diff --git a/test/Piranha.Tests/Fields.cs b/test/Piranha.Tests/Fields.cs index 25e01b48f..1fe439167 100644 --- a/test/Piranha.Tests/Fields.cs +++ b/test/Piranha.Tests/Fields.cs @@ -1,20 +1,23 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Extend; -using Piranha.Runtime; -using Piranha.Services; using System; using System.Linq; using System.Text; +using System.Threading.Tasks; using Xunit; +using Piranha.Extend; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Runtime; +using Piranha.Services; namespace Piranha.Tests { @@ -40,11 +43,14 @@ public enum MyEnum { /// Sets up & initializes the tests. ///
protected override void Init() { - Piranha.App.Init(); - Piranha.App.Fields.Register(); + using (var api = CreateApi()) + { + Piranha.App.Init(api); + Piranha.App.Fields.Register(); - fields.Register(); - fields.Register(); + fields.Register(); + fields.Register(); + } } /// @@ -169,10 +175,10 @@ public void CheckBoxFieldConversions() [Fact] public void CheckBoxFieldEquals() { - var field1 = new Piranha.Extend.Fields.CheckBoxField { + var field1 = new Piranha.Extend.Fields.CheckBoxField { Value = true }; - var field2 = new Piranha.Extend.Fields.CheckBoxField { + var field2 = new Piranha.Extend.Fields.CheckBoxField { Value = true }; @@ -183,17 +189,17 @@ public void CheckBoxFieldEquals() { [Fact] public void CheckBoxFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.CheckBoxField { + var field1 = new Piranha.Extend.Fields.CheckBoxField { Value = true }; - var field2 = new Piranha.Extend.Fields.CheckBoxField { + var field2 = new Piranha.Extend.Fields.CheckBoxField { Value = false }; Assert.True(field1 != field2); Assert.True(!field1.Equals(field2)); Assert.True(!field1.Equals((object)field2)); - } + } [Fact] public void DateFieldConversions() { @@ -205,10 +211,10 @@ public void DateFieldConversions() { [Fact] public void DateFieldEquals() { - var field1 = new Piranha.Extend.Fields.DateField { + var field1 = new Piranha.Extend.Fields.DateField { Value = new DateTime(2018, 1, 1) }; - var field2 = new Piranha.Extend.Fields.DateField { + var field2 = new Piranha.Extend.Fields.DateField { Value = new DateTime(2018, 1, 1) }; @@ -219,17 +225,17 @@ public void DateFieldEquals() { [Fact] public void DateFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.DateField { + var field1 = new Piranha.Extend.Fields.DateField { Value = new DateTime(2018, 1, 1) }; - var field2 = new Piranha.Extend.Fields.DateField { + var field2 = new Piranha.Extend.Fields.DateField { Value = new DateTime(2017, 1, 1) }; Assert.True(field1 != field2); Assert.True(!field1.Equals(field2)); Assert.True(!field1.Equals((object)field2)); - } + } [Fact] public void HtmlFieldConversions() { @@ -244,10 +250,10 @@ public void HtmlFieldConversions() { [Fact] public void HtmlFieldEquals() { - var field1 = new Piranha.Extend.Fields.HtmlField { + var field1 = new Piranha.Extend.Fields.HtmlField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.HtmlField { + var field2 = new Piranha.Extend.Fields.HtmlField { Value = "Sollicitudin Justo Tristique" }; @@ -258,10 +264,10 @@ public void HtmlFieldEquals() { [Fact] public void HtmlFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.HtmlField { + var field1 = new Piranha.Extend.Fields.HtmlField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.HtmlField { + var field2 = new Piranha.Extend.Fields.HtmlField { Value = "Sollicitudin Tristique" }; @@ -272,7 +278,7 @@ public void HtmlFieldNotEquals() { [Fact] public void ImageFieldConversions() { - var media = new Data.Media() { + var media = new Media() { Id = Guid.NewGuid() }; @@ -281,13 +287,13 @@ public void ImageFieldConversions() { } [Fact] - public void ImageFieldInitMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + public async Task ImageFieldInitMissing() { + using (var api = CreateApi()) { var field = new Piranha.Extend.Fields.ImageField { Id = Guid.NewGuid() }; - field.Init(api); + await field.Init(api); Assert.Null(field.Id); } @@ -295,10 +301,10 @@ public void ImageFieldInitMissing() { [Fact] public void ImageFieldEquals() { - var field1 = new Piranha.Extend.Fields.ImageField { + var field1 = new Piranha.Extend.Fields.ImageField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.ImageField { + var field2 = new Piranha.Extend.Fields.ImageField { Id = field1.Id }; @@ -309,10 +315,10 @@ public void ImageFieldEquals() { [Fact] public void ImageFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.ImageField { + var field1 = new Piranha.Extend.Fields.ImageField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.ImageField { + var field2 = new Piranha.Extend.Fields.ImageField { Id = null }; @@ -323,7 +329,7 @@ public void ImageFieldNotEquals() { [Fact] public void DocumentFieldConversions() { - var media = new Data.Media() { + var media = new Media() { Id = Guid.NewGuid() }; @@ -332,13 +338,13 @@ public void DocumentFieldConversions() { } [Fact] - public void DocumentFieldInitMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + public async Task DocumentFieldInitMissing() { + using (var api = CreateApi()) { var field = new Piranha.Extend.Fields.DocumentField { Id = Guid.NewGuid() }; - field.Init(api); + await field.Init(api); Assert.Null(field.Id); } @@ -346,10 +352,10 @@ public void DocumentFieldInitMissing() { [Fact] public void DocumentFieldEquals() { - var field1 = new Piranha.Extend.Fields.DocumentField { + var field1 = new Piranha.Extend.Fields.DocumentField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.DocumentField { + var field2 = new Piranha.Extend.Fields.DocumentField { Id = field1.Id }; @@ -360,10 +366,10 @@ public void DocumentFieldEquals() { [Fact] public void DocumentFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.DocumentField { + var field1 = new Piranha.Extend.Fields.DocumentField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.DocumentField { + var field2 = new Piranha.Extend.Fields.DocumentField { Id = null }; @@ -374,7 +380,7 @@ public void DocumentFieldNotEquals() { [Fact] public void VideoFieldConversions() { - var media = new Data.Media() { + var media = new Media() { Id = Guid.NewGuid() }; @@ -383,13 +389,13 @@ public void VideoFieldConversions() { } [Fact] - public void VideoFieldInitMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + public async Task VideoFieldInitMissing() { + using (var api = CreateApi()) { var field = new Piranha.Extend.Fields.VideoField { Id = Guid.NewGuid() }; - field.Init(api); + await field.Init(api); Assert.Null(field.Id); } @@ -397,10 +403,10 @@ public void VideoFieldInitMissing() { [Fact] public void VideoFieldEquals() { - var field1 = new Piranha.Extend.Fields.VideoField { + var field1 = new Piranha.Extend.Fields.VideoField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.VideoField { + var field2 = new Piranha.Extend.Fields.VideoField { Id = field1.Id }; @@ -411,10 +417,10 @@ public void VideoFieldEquals() { [Fact] public void VideoFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.VideoField { + var field1 = new Piranha.Extend.Fields.VideoField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.VideoField { + var field2 = new Piranha.Extend.Fields.VideoField { Id = null }; @@ -425,7 +431,7 @@ public void VideoFieldNotEquals() { [Fact] public void MediaFieldConversions() { - var media = new Data.Media() { + var media = new Media() { Id = Guid.NewGuid() }; @@ -434,13 +440,13 @@ public void MediaFieldConversions() { } [Fact] - public void MediaFieldInitMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + public async Task MediaFieldInitMissing() { + using (var api = CreateApi()) { var field = new Piranha.Extend.Fields.MediaField { Id = Guid.NewGuid() }; - field.Init(api); + await field.Init(api); Assert.Null(field.Id); } @@ -448,10 +454,10 @@ public void MediaFieldInitMissing() { [Fact] public void MediaFieldEquals() { - var field1 = new Piranha.Extend.Fields.MediaField { + var field1 = new Piranha.Extend.Fields.MediaField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.MediaField { + var field2 = new Piranha.Extend.Fields.MediaField { Id = field1.Id }; @@ -462,10 +468,10 @@ public void MediaFieldEquals() { [Fact] public void MediaFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.MediaField { + var field1 = new Piranha.Extend.Fields.MediaField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.MediaField { + var field2 = new Piranha.Extend.Fields.MediaField { Id = null }; @@ -532,10 +538,10 @@ public void MarkdownFieldConversions() { [Fact] public void MarkdownFieldEquals() { - var field1 = new Piranha.Extend.Fields.MarkdownField { + var field1 = new Piranha.Extend.Fields.MarkdownField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.MarkdownField { + var field2 = new Piranha.Extend.Fields.MarkdownField { Value = "Sollicitudin Justo Tristique" }; @@ -546,10 +552,10 @@ public void MarkdownFieldEquals() { [Fact] public void MarkdownFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.MarkdownField { + var field1 = new Piranha.Extend.Fields.MarkdownField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.MarkdownField { + var field2 = new Piranha.Extend.Fields.MarkdownField { Value = "Sollicitudin Tristique" }; @@ -568,10 +574,10 @@ public void NumberFieldConversions() { [Fact] public void NumberFieldEquals() { - var field1 = new Piranha.Extend.Fields.NumberField { + var field1 = new Piranha.Extend.Fields.NumberField { Value = 23 }; - var field2 = new Piranha.Extend.Fields.NumberField { + var field2 = new Piranha.Extend.Fields.NumberField { Value = 23 }; @@ -582,10 +588,10 @@ public void NumberFieldEquals() { [Fact] public void NumberFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.NumberField { + var field1 = new Piranha.Extend.Fields.NumberField { Value = 23 }; - var field2 = new Piranha.Extend.Fields.NumberField { + var field2 = new Piranha.Extend.Fields.NumberField { Value = null }; @@ -596,10 +602,10 @@ public void NumberFieldNotEquals() { [Fact] public void PageFieldEquals() { - var field1 = new Piranha.Extend.Fields.PageField { + var field1 = new Piranha.Extend.Fields.PageField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.PageField { + var field2 = new Piranha.Extend.Fields.PageField { Id = field1.Id }; @@ -610,24 +616,24 @@ public void PageFieldEquals() { [Fact] public void PageFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.PageField { + var field1 = new Piranha.Extend.Fields.PageField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.PageField { + var field2 = new Piranha.Extend.Fields.PageField { Id = null }; Assert.True(field1 != field2); Assert.True(!field1.Equals(field2)); Assert.True(!field1.Equals((object)field2)); - } + } [Fact] public void PostFieldEquals() { - var field1 = new Piranha.Extend.Fields.PostField { + var field1 = new Piranha.Extend.Fields.PostField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.PostField { + var field2 = new Piranha.Extend.Fields.PostField { Id = field1.Id }; @@ -638,24 +644,24 @@ public void PostFieldEquals() { [Fact] public void PostFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.PostField { + var field1 = new Piranha.Extend.Fields.PostField { Id = Guid.NewGuid() }; - var field2 = new Piranha.Extend.Fields.PostField { + var field2 = new Piranha.Extend.Fields.PostField { Id = null }; Assert.True(field1 != field2); Assert.True(!field1.Equals(field2)); Assert.True(!field1.Equals((object)field2)); - } + } [Fact] public void SelectFieldEquals() { - var field1 = new Piranha.Extend.Fields.SelectField { + var field1 = new Piranha.Extend.Fields.SelectField { Value = MyEnum.Value1 }; - var field2 = new Piranha.Extend.Fields.SelectField { + var field2 = new Piranha.Extend.Fields.SelectField { Value = MyEnum.Value1 }; @@ -666,10 +672,10 @@ public void SelectFieldEquals() { [Fact] public void SelectFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.SelectField { + var field1 = new Piranha.Extend.Fields.SelectField { Value = MyEnum.Value1 }; - var field2 = new Piranha.Extend.Fields.SelectField { + var field2 = new Piranha.Extend.Fields.SelectField { Value = MyEnum.Value2 }; @@ -686,15 +692,15 @@ public void StringFieldConversions() { Assert.Equal(inStr, field.Value); string outStr = field; - Assert.Equal(inStr, outStr); + Assert.Equal(inStr, outStr); } [Fact] public void StringFieldEquals() { - var field1 = new Piranha.Extend.Fields.StringField { + var field1 = new Piranha.Extend.Fields.StringField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.StringField { + var field2 = new Piranha.Extend.Fields.StringField { Value = "Sollicitudin Justo Tristique" }; @@ -705,10 +711,10 @@ public void StringFieldEquals() { [Fact] public void StringFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.StringField { + var field1 = new Piranha.Extend.Fields.StringField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.StringField { + var field2 = new Piranha.Extend.Fields.StringField { Value = "Sollicitudin Tristique" }; @@ -725,15 +731,15 @@ public void TextFieldConversions() { Assert.Equal(inStr, field.Value); string outStr = field; - Assert.Equal(inStr, outStr); + Assert.Equal(inStr, outStr); } [Fact] public void TextFieldEquals() { - var field1 = new Piranha.Extend.Fields.TextField { + var field1 = new Piranha.Extend.Fields.TextField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.TextField { + var field2 = new Piranha.Extend.Fields.TextField { Value = "Sollicitudin Justo Tristique" }; @@ -744,10 +750,10 @@ public void TextFieldEquals() { [Fact] public void TextFieldNotEquals() { - var field1 = new Piranha.Extend.Fields.TextField { + var field1 = new Piranha.Extend.Fields.TextField { Value = "Sollicitudin Justo Tristique" }; - var field2 = new Piranha.Extend.Fields.TextField { + var field2 = new Piranha.Extend.Fields.TextField { Value = "Sollicitudin Tristique" }; @@ -778,8 +784,30 @@ public void GetFieldTitleMaxLength() { } Piranha.Extend.Fields.TextField field = sb.ToString(); - + Assert.Equal(43, field.GetTitle().Length); } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db) + ); + } } } diff --git a/test/Piranha.Tests/Hooks/Aliases.cs b/test/Piranha.Tests/Hooks/Aliases.cs index 293de0a16..5315ee7db 100644 --- a/test/Piranha.Tests/Hooks/Aliases.cs +++ b/test/Piranha.Tests/Hooks/Aliases.cs @@ -1,18 +1,20 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using System.Data.SqlClient; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Hooks { @@ -30,18 +32,18 @@ class AliasOnBeforeDeleteException : Exception {} class AliasOnAfterDeleteException : Exception {} protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Initialize - Piranha.App.Init(); + Piranha.App.Init(api); // Create site - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { Id = SITE_ID, Title = "Alias Hook Site" }); // Create test alias - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { Id = ID, SiteId = SITE_ID, AliasUrl = ALIAS, @@ -51,7 +53,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Remove test data var aliases = api.Aliases.GetAll(); @@ -65,8 +67,7 @@ protected override void Cleanup() { [Fact] public void OnLoad() { Piranha.App.Hooks.Alias.RegisterOnLoad(m => throw new AliasOnLoadException()); - - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Aliases.GetById(ID); }); @@ -77,9 +78,9 @@ public void OnLoad() { [Fact] public void OnBeforeSave() { Piranha.App.Hooks.Alias.RegisterOnBeforeSave(m => throw new AliasOnBeforeSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = "/my-first-alias", RedirectUrl = "/my-first-redirect" @@ -92,9 +93,9 @@ public void OnBeforeSave() { [Fact] public void OnAfterSave() { Piranha.App.Hooks.Alias.RegisterOnAfterSave(m => throw new AliasOnAfterSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = "/my-second-alias", RedirectUrl = "/my-seconf-redirect" @@ -107,23 +108,46 @@ public void OnAfterSave() { [Fact] public void OnBeforeDelete() { Piranha.App.Hooks.Alias.RegisterOnBeforeDelete(m => throw new AliasOnBeforeDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Aliases.Delete(ID); }); } Piranha.App.Hooks.Alias.Clear(); - } + } [Fact] public void OnAfterDelete() { Piranha.App.Hooks.Alias.RegisterOnAfterDelete(m => throw new AliasOnAfterDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Aliases.Delete(ID); }); } - Piranha.App.Hooks.Alias.Clear(); - } + Piranha.App.Hooks.Alias.Clear(); + } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Hooks/Categories.cs b/test/Piranha.Tests/Hooks/Categories.cs deleted file mode 100644 index 547096a2a..000000000 --- a/test/Piranha.Tests/Hooks/Categories.cs +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using Piranha.AttributeBuilder; -using Piranha.Services; -using System; -using System.Data.SqlClient; -using System.Linq; -using Xunit; - -namespace Piranha.Tests.Hooks -{ - [Collection("Integration tests")] - public class Categories : BaseTests - { - private Guid SITE_ID = Guid.NewGuid(); - private Guid BLOG_ID = Guid.NewGuid(); - private Guid ID = Guid.NewGuid(); - - [PageType(Title = "Blog page")] - public class BlogPage : Models.Page { } - class CategoryOnLoadException : Exception {} - class CategoryOnBeforeSaveException : Exception {} - class CategoryOnAfterSaveException : Exception {} - class CategoryOnBeforeDeleteException : Exception {} - class CategoryOnAfterDeleteException : Exception {} - - protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - // Initialize - Piranha.App.Init(); - - var pageTypeBuilder = new PageTypeBuilder(api) - .AddType(typeof(BlogPage)); - pageTypeBuilder.Build(); - - // Add site - var site = new Data.Site() { - Id = SITE_ID, - Title = "Hook Site", - IsDefault = true - }; - api.Sites.Save(site); - - // Add blog page - var page = BlogPage.Create(api); - page.Id = BLOG_ID; - page.SiteId = SITE_ID; - page.Title = "Hook Blog"; - api.Pages.Save(page); - - // Add category - api.Categories.Save(new Data.Category() { - Id = ID, - BlogId = BLOG_ID, - Title = "Hook Category" - }); - } - } - - protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - // Remove test data - var categories = api.Categories.GetAll(BLOG_ID); - foreach (var c in categories) - api.Categories.Delete(c); - - var pages = api.Pages.GetAll(SITE_ID); - foreach (var p in pages) - api.Pages.Delete(p); - - var sites = api.Sites.GetAll(); - foreach (var s in sites) - api.Sites.Delete(s); - } - } - - [Fact] - public void OnLoad() { - Piranha.App.Hooks.Category.RegisterOnLoad(m => throw new CategoryOnLoadException()); - - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Categories.GetById(ID); - }); - } - Piranha.App.Hooks.Category.Clear(); - } - - [Fact] - public void OnBeforeSave() { - Piranha.App.Hooks.Category.RegisterOnBeforeSave(m => throw new CategoryOnBeforeSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID, - Title = "My First Hook Category" - }); - }); - } - Piranha.App.Hooks.Category.Clear(); - } - - [Fact] - public void OnAfterSave() { - Piranha.App.Hooks.Category.RegisterOnAfterSave(m => throw new CategoryOnAfterSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID, - Title = "My Second Hook Category" - }); - }); - } - Piranha.App.Hooks.Category.Clear(); - } - - [Fact] - public void OnBeforeDelete() { - Piranha.App.Hooks.Category.RegisterOnBeforeDelete(m => throw new CategoryOnBeforeDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Categories.Delete(ID); - }); - } - Piranha.App.Hooks.Category.Clear(); - } - - [Fact] - public void OnAfterDelete() { - Piranha.App.Hooks.Category.RegisterOnAfterDelete(m => throw new CategoryOnAfterDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Categories.Delete(ID); - }); - } - Piranha.App.Hooks.Category.Clear(); - } - } -} diff --git a/test/Piranha.Tests/Hooks/Params.cs b/test/Piranha.Tests/Hooks/Params.cs index fe6f1bf0c..326d23cf8 100644 --- a/test/Piranha.Tests/Hooks/Params.cs +++ b/test/Piranha.Tests/Hooks/Params.cs @@ -1,18 +1,20 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using System.Data.SqlClient; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Hooks { @@ -29,12 +31,12 @@ class ParamOnBeforeDeleteException : Exception {} class ParamOnAfterDeleteException : Exception {} protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Initialize - Piranha.App.Init(); + Piranha.App.Init(api); // Create test param - api.Params.Save(new Data.Param() { + api.Params.Save(new Param() { Id = ID, Key = KEY }); @@ -42,7 +44,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Remove test data var param = api.Params.GetAll(); @@ -54,8 +56,7 @@ protected override void Cleanup() { [Fact] public void OnLoad() { Piranha.App.Hooks.Param.RegisterOnLoad(m => throw new ParamOnLoadException()); - - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Params.GetById(ID); }); @@ -66,9 +67,9 @@ public void OnLoad() { [Fact] public void OnBeforeSave() { Piranha.App.Hooks.Param.RegisterOnBeforeSave(m => throw new ParamOnBeforeSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Params.Save(new Data.Param() { + api.Params.Save(new Param() { Key = "MyFirstHookKey" }); }); @@ -79,9 +80,9 @@ public void OnBeforeSave() { [Fact] public void OnAfterSave() { Piranha.App.Hooks.Param.RegisterOnAfterSave(m => throw new ParamOnAfterSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Params.Save(new Data.Param() { + api.Params.Save(new Param() { Key = "MySecondHookKey" }); }); @@ -92,23 +93,46 @@ public void OnAfterSave() { [Fact] public void OnBeforeDelete() { Piranha.App.Hooks.Param.RegisterOnBeforeDelete(m => throw new ParamOnBeforeDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Params.Delete(ID); }); } Piranha.App.Hooks.Param.Clear(); - } + } [Fact] public void OnAfterDelete() { Piranha.App.Hooks.Param.RegisterOnAfterDelete(m => throw new ParamOnAfterDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Params.Delete(ID); }); } - Piranha.App.Hooks.Param.Clear(); - } + Piranha.App.Hooks.Param.Clear(); + } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Hooks/Sites.cs b/test/Piranha.Tests/Hooks/Sites.cs index 1b252ba63..6064b2a00 100644 --- a/test/Piranha.Tests/Hooks/Sites.cs +++ b/test/Piranha.Tests/Hooks/Sites.cs @@ -1,18 +1,20 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using System.Data.SqlClient; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Hooks { @@ -29,12 +31,12 @@ class SiteOnBeforeDeleteException : Exception {} class SiteOnAfterDeleteException : Exception {} protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Initialize - Piranha.App.Init(); + Piranha.App.Init(api); // Create test param - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { Id = ID, Title = TITLE }); @@ -42,7 +44,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Remove test data var sites = api.Sites.GetAll(); @@ -54,8 +56,7 @@ protected override void Cleanup() { [Fact] public void OnLoad() { Piranha.App.Hooks.Site.RegisterOnLoad(m => throw new SiteOnLoadException()); - - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Sites.GetById(ID); }); @@ -66,9 +67,9 @@ public void OnLoad() { [Fact] public void OnBeforeSave() { Piranha.App.Hooks.Site.RegisterOnBeforeSave(m => throw new SiteOnBeforeSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { Title = "My First Hook Site" }); }); @@ -79,9 +80,9 @@ public void OnBeforeSave() { [Fact] public void OnAfterSave() { Piranha.App.Hooks.Site.RegisterOnAfterSave(m => throw new SiteOnAfterSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { Title = "My Second Hook Site" }); }); @@ -92,23 +93,46 @@ public void OnAfterSave() { [Fact] public void OnBeforeDelete() { Piranha.App.Hooks.Site.RegisterOnBeforeDelete(m => throw new SiteOnBeforeDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Sites.Delete(ID); }); } Piranha.App.Hooks.Site.Clear(); - } + } [Fact] public void OnAfterDelete() { Piranha.App.Hooks.Site.RegisterOnAfterDelete(m => throw new SiteOnAfterDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { Assert.Throws(() => { api.Sites.Delete(ID); }); } - Piranha.App.Hooks.Site.Clear(); - } + Piranha.App.Hooks.Site.Clear(); + } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Hooks/Tags.cs b/test/Piranha.Tests/Hooks/Tags.cs deleted file mode 100644 index 9301e0345..000000000 --- a/test/Piranha.Tests/Hooks/Tags.cs +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2018 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using Piranha.AttributeBuilder; -using Piranha.Services; -using System; -using System.Data.SqlClient; -using System.Linq; -using Xunit; - -namespace Piranha.Tests.Hooks -{ - [Collection("Integration tests")] - public class Tags : BaseTests - { - private Guid SITE_ID = Guid.NewGuid(); - private Guid BLOG_ID = Guid.NewGuid(); - private Guid ID = Guid.NewGuid(); - - [PageType(Title = "Blog page")] - public class BlogPage : Models.Page { } - class TagOnLoadException : Exception {} - class TagOnBeforeSaveException : Exception {} - class TagOnAfterSaveException : Exception {} - class TagOnBeforeDeleteException : Exception {} - class TagOnAfterDeleteException : Exception {} - - protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - // Initialize - Piranha.App.Init(); - - var pageTypeBuilder = new PageTypeBuilder(api) - .AddType(typeof(BlogPage)); - pageTypeBuilder.Build(); - - // Add site - var site = new Data.Site() { - Id = SITE_ID, - Title = "Hook Site", - IsDefault = true - }; - api.Sites.Save(site); - - // Add blog page - var page = BlogPage.Create(api); - page.Id = BLOG_ID; - page.SiteId = SITE_ID; - page.Title = "Hook Blog"; - api.Pages.Save(page); - - // Add tag - api.Tags.Save(new Data.Tag() { - Id = ID, - BlogId = BLOG_ID, - Title = "Hook Tag" - }); - } - } - - protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - // Remove test data - var tags = api.Tags.GetAll(BLOG_ID); - foreach (var t in tags) - api.Tags.Delete(t); - - var pages = api.Pages.GetAll(SITE_ID); - foreach (var p in pages) - api.Pages.Delete(p); - - var types = api.PageTypes.GetAll(); - foreach (var t in types) - api.PageTypes.Delete(t); - - var sites = api.Sites.GetAll(); - foreach (var s in sites) - api.Sites.Delete(s); - } - } - - [Fact] - public void OnLoad() { - Piranha.App.Hooks.Tag.RegisterOnLoad(m => throw new TagOnLoadException()); - - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Tags.GetById(ID); - }); - } - Piranha.App.Hooks.Tag.Clear(); - } - - [Fact] - public void OnBeforeSave() { - Piranha.App.Hooks.Tag.RegisterOnBeforeSave(m => throw new TagOnBeforeSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID, - Title = "My First Hook Tag" - }); - }); - } - Piranha.App.Hooks.Tag.Clear(); - } - - [Fact] - public void OnAfterSave() { - Piranha.App.Hooks.Tag.RegisterOnAfterSave(m => throw new TagOnAfterSaveException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID, - Title = "My Second Hook Tag" - }); - }); - } - Piranha.App.Hooks.Tag.Clear(); - } - - [Fact] - public void OnBeforeDelete() { - Piranha.App.Hooks.Tag.RegisterOnBeforeDelete(m => throw new TagOnBeforeDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Tags.Delete(ID); - }); - } - Piranha.App.Hooks.Tag.Clear(); - } - - [Fact] - public void OnAfterDelete() { - Piranha.App.Hooks.Tag.RegisterOnAfterDelete(m => throw new TagOnAfterDeleteException()); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Assert.Throws(() => { - api.Tags.Delete(ID); - }); - } - Piranha.App.Hooks.Tag.Clear(); - } - } -} diff --git a/test/Piranha.Tests/Piranha.Tests.csproj b/test/Piranha.Tests/Piranha.Tests.csproj index db8a196f1..ec9c9d8bf 100644 --- a/test/Piranha.Tests/Piranha.Tests.csproj +++ b/test/Piranha.Tests/Piranha.Tests.csproj @@ -3,6 +3,7 @@ Exe netcoreapp2.1 + @@ -10,8 +11,11 @@ + + + diff --git a/test/Piranha.Tests/Repositories/Aliases.cs b/test/Piranha.Tests/Repositories/Aliases.cs index 0a3ee76d4..56855e074 100644 --- a/test/Piranha.Tests/Repositories/Aliases.cs +++ b/test/Piranha.Tests/Repositories/Aliases.cs @@ -1,17 +1,19 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -41,9 +43,9 @@ public class Aliases : BaseTests #endregion protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { // Add site - var site = new Data.Site() { + var site = new Site() { Id = SITE_ID, Title = "Alias Site", InternalId = "AliasSite", @@ -52,19 +54,19 @@ protected override void Init() { api.Sites.Save(site); // Add aliases - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { Id = ALIAS_1_ID, SiteId = SITE_ID, AliasUrl = ALIAS_1, RedirectUrl = "/redirect-1" }); - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = ALIAS_4, RedirectUrl = "/redirect-4" }); - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = ALIAS_5, RedirectUrl = "/redirect-5" @@ -73,7 +75,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var aliases = api.Aliases.GetAll(); foreach (var a in aliases) api.Aliases.Delete(a); @@ -86,15 +88,15 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(AliasesCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(AliasesCached), ((Api)api).IsCached); } - } + } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Aliases.Save(new Data.Alias() { + using (var api = CreateApi()) { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = ALIAS_2, RedirectUrl = "/redirect-2" @@ -104,9 +106,9 @@ public void Add() { [Fact] public void AddDuplicateKey() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { Assert.ThrowsAny(() => - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { SiteId = SITE_ID, AliasUrl = ALIAS_1, RedirectUrl = "/duplicate-alias" @@ -116,7 +118,7 @@ public void AddDuplicateKey() { [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Aliases.GetById(Guid.NewGuid()); Assert.Null(none); @@ -125,7 +127,7 @@ public void GetNoneById() { [Fact] public void GetNoneByAliasUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Aliases.GetByAliasUrl("/none-existing-alias"); Assert.Null(none); @@ -134,16 +136,16 @@ public void GetNoneByAliasUrl() { [Fact] public void GetNoneByRedirectUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Aliases.GetByRedirectUrl("/none-existing-alias"); Assert.Empty(none); - } + } } [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.Aliases.GetAll(); Assert.NotNull(models); @@ -153,7 +155,7 @@ public void GetAll() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Aliases.GetById(ALIAS_1_ID); Assert.NotNull(model); @@ -163,7 +165,7 @@ public void GetById() { [Fact] public void GetByAliasUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Aliases.GetByAliasUrl(ALIAS_1); Assert.NotNull(model); @@ -173,7 +175,7 @@ public void GetByAliasUrl() { [Fact] public void GetByRedirectUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.Aliases.GetByRedirectUrl("/redirect-1"); Assert.Single(models); @@ -183,7 +185,7 @@ public void GetByRedirectUrl() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Aliases.GetById(ALIAS_1_ID); Assert.Equal("/redirect-1", model.RedirectUrl); @@ -196,8 +198,8 @@ public void Update() { [Fact] public void FixAliasUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = new Data.Alias() { + using (var api = CreateApi()) { + var model = new Alias() { SiteId = SITE_ID, AliasUrl = "the-alias-url-1", RedirectUrl = "/the-redirect-1" @@ -206,13 +208,13 @@ public void FixAliasUrl() { api.Aliases.Save(model); Assert.Equal("/the-alias-url-1", model.AliasUrl); - } + } } [Fact] public void FixRedirectUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = new Data.Alias() { + using (var api = CreateApi()) { + var model = new Alias() { SiteId = SITE_ID, AliasUrl = "/the-alias-url-2", RedirectUrl = "the-redirect-2" @@ -221,13 +223,13 @@ public void FixRedirectUrl() { api.Aliases.Save(model); Assert.Equal("/the-redirect-2", model.RedirectUrl); - } + } } [Fact] public void AllowHttpUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = new Data.Alias() { + using (var api = CreateApi()) { + var model = new Alias() { SiteId = SITE_ID, AliasUrl = "/the-alias-url-3", RedirectUrl = "http://redirect.com" @@ -236,13 +238,13 @@ public void AllowHttpUrl() { api.Aliases.Save(model); Assert.Equal("http://redirect.com", model.RedirectUrl); - } + } } [Fact] public void AllowHttpsUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = new Data.Alias() { + using (var api = CreateApi()) { + var model = new Alias() { SiteId = SITE_ID, AliasUrl = "/the-alias-url-4", RedirectUrl = "https://redirect.com" @@ -251,12 +253,12 @@ public void AllowHttpsUrl() { api.Aliases.Save(model); Assert.Equal("https://redirect.com", model.RedirectUrl); - } + } } [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Aliases.GetByAliasUrl(ALIAS_4); Assert.NotNull(model); @@ -267,7 +269,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Aliases.GetByAliasUrl(ALIAS_5); Assert.NotNull(model); @@ -275,5 +277,29 @@ public void DeleteById() { api.Aliases.Delete(model.Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Categories.cs b/test/Piranha.Tests/Repositories/Categories.cs deleted file mode 100644 index 208e1e6a2..000000000 --- a/test/Piranha.Tests/Repositories/Categories.cs +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; -using System; -using System.Data.SqlClient; -using System.Linq; -using Xunit; - -namespace Piranha.Tests.Repositories -{ - [Collection("Integration tests")] - public class CategoriesCached : Categories - { - protected override void Init() { - cache = new Cache.SimpleCache(); - - base.Init(); - } - } - - [Collection("Integration tests")] - public class Categories : BaseTests - { - #region Members - private const string CAT_1 = "My First Category"; - private const string CAT_2 = "My Second Category"; - private const string CAT_3 = "My Third Category"; - private const string CAT_4 = "My Fourth Category"; - private const string CAT_5 = "My Fifth Category"; - - private Guid SITE_ID = Guid.NewGuid(); - private Guid BLOG_ID = Guid.NewGuid(); - private Guid CAT_1_ID = Guid.NewGuid(); - private Guid CAT_5_ID = Guid.NewGuid(); - - protected ICache cache; - #endregion - - [PageType(Title = "Blog page")] - public class BlogPage : Models.Page { } - - protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); - - var pageTypeBuilder = new PageTypeBuilder(api) - .AddType(typeof(BlogPage)); - pageTypeBuilder.Build(); - - // Add site - var site = new Data.Site() { - Id = SITE_ID, - Title = "Category Site", - InternalId = "CategorySite", - IsDefault = true - }; - api.Sites.Save(site); - - // Add blog page - var page = BlogPage.Create(api); - page.Id = BLOG_ID; - page.SiteId = SITE_ID; - page.Title = "Blog"; - api.Pages.Save(page); - - // Add categories - api.Categories.Save(new Data.Category() { - Id = CAT_1_ID, - BlogId = BLOG_ID, - Title = CAT_1 - }); - - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID, - Title = CAT_4 - }); - api.Categories.Save(new Data.Category() { - Id = CAT_5_ID, - BlogId = BLOG_ID, - Title = CAT_5 - }); - } - } - - protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var categories = api.Categories.GetAll(BLOG_ID); - - foreach (var c in categories) - api.Categories.Delete(c); - - api.Pages.Delete(BLOG_ID); - - var types = api.PageTypes.GetAll(); - foreach (var t in types) - api.PageTypes.Delete(t); - - api.Sites.Delete(SITE_ID); - } - } - - [Fact] - public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(CategoriesCached), api.IsCached); - } - } - - [Fact] - public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID, - Title = CAT_2 - }); - } - } - - [Fact] - public void AddDuplicateSlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID, - Title = CAT_1 - })); - } - } - - [Fact] - public void AddNoTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Categories.Save(new Data.Category() { - BlogId = BLOG_ID - })); - } - } - - [Fact] - public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var none = api.Categories.GetById(Guid.NewGuid()); - - Assert.Null(none); - } - } - - [Fact] - public void GetNoneBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var none = api.Categories.GetBySlug(BLOG_ID, "none-existing-slug"); - - Assert.Null(none); - } - } - - [Fact] - public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var models = api.Categories.GetAll(BLOG_ID); - - Assert.NotNull(models); - Assert.NotEmpty(models); - } - } - - [Fact] - public void GetNone() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var models = api.Categories.GetAll(Guid.NewGuid()); - - Assert.NotNull(models); - Assert.Empty(models); - } - } - - [Fact] - public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetById(CAT_1_ID); - - Assert.NotNull(model); - Assert.Equal(CAT_1, model.Title); - } - } - - [Fact] - public void GetBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug(CAT_1)); - - Assert.NotNull(model); - Assert.Equal(CAT_1, model.Title); - } - } - - [Fact] - public void GetByTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetByTitle(BLOG_ID, CAT_1); - - Assert.NotNull(model); - Assert.Equal(CAT_1, model.Title); - } - } - - [Fact] - public void GetNoneByTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetByTitle(BLOG_ID, "Missing Title"); - - Assert.Null(model); - } - } - - [Fact] - public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetById(CAT_1_ID); - - Assert.Equal(CAT_1, model.Title); - - model.Title = "Updated"; - - api.Categories.Save(model); - } - } - - [Fact] - public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug(CAT_4)); - - Assert.NotNull(model); - - api.Categories.Delete(model); - } - } - - [Fact] - public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Categories.GetById(CAT_5_ID); - - Assert.NotNull(model); - - api.Categories.Delete(model.Id); - } - } - } -} diff --git a/test/Piranha.Tests/Repositories/Medias.cs b/test/Piranha.Tests/Repositories/Medias.cs index dd5691b3c..f714132f0 100644 --- a/test/Piranha.Tests/Repositories/Medias.cs +++ b/test/Piranha.Tests/Repositories/Medias.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,11 +8,13 @@ * */ -using Piranha.Services; using System; using System.IO; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -37,11 +39,11 @@ public class Medias : BaseTests protected ICache cache; protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); // Add media folders - var folder1 = new Data.MediaFolder() { + var folder1 = new MediaFolder() { Name = "Images" }; api.Media.SaveFolder(folder1); @@ -82,7 +84,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetAll(); foreach (var item in media) { @@ -104,14 +106,14 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(MediasCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(MediasCached), ((Api)api).IsCached); } } [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetAll(); Assert.NotEmpty(media); @@ -120,7 +122,7 @@ public void GetAll() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetById(image1Id); Assert.NotNull(media); @@ -132,7 +134,7 @@ public void GetById() { [Fact] public void GetByFolderId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetAll(folder1Id).ToList(); Assert.NotEmpty(media); @@ -142,7 +144,7 @@ public void GetByFolderId() { [Fact] public void Move() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetById(image1Id); Assert.NotNull(media); Assert.Null(media.FolderId); @@ -158,7 +160,7 @@ public void Move() { [Fact] public void Insert() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { using (var stream = File.OpenRead("../../../Assets/HLD_Screenshot_BETA_entrance.png")) { var image = new Models.StreamMediaContent() { Filename = "HLD_Screenshot_BETA_entrance.png", @@ -175,7 +177,7 @@ public void Insert() { [Fact] public void PublicUrl() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.MediaCDN = null; } @@ -189,7 +191,7 @@ public void PublicUrl() { [Fact] public void PublicUrlCDN() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.MediaCDN = "https://mycdn.org/uploads"; } @@ -203,7 +205,7 @@ public void PublicUrlCDN() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var media = api.Media.GetById(image3Id); api.Media.Delete(media); @@ -212,9 +214,33 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { api.Media.Delete(image4Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } \ No newline at end of file diff --git a/test/Piranha.Tests/Repositories/PageTypes.cs b/test/Piranha.Tests/Repositories/PageTypes.cs index 17a2bdbb5..43059dc83 100644 --- a/test/Piranha.Tests/Repositories/PageTypes.cs +++ b/test/Piranha.Tests/Repositories/PageTypes.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,11 +8,12 @@ * */ -using Piranha.Models; -using Piranha.Services; using System.Collections.Generic; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -106,7 +107,7 @@ public class PageTypes : BaseTests #endregion protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { api.PageTypes.Save(pageTypes[0]); api.PageTypes.Save(pageTypes[3]); api.PageTypes.Save(pageTypes[4]); @@ -114,7 +115,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pageTypes = api.PageTypes.GetAll(); foreach (var p in pageTypes) @@ -124,21 +125,21 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(PageTypesCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(PageTypesCached), ((Api)api).IsCached); } } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { api.PageTypes.Save(pageTypes[1]); } } [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.PageTypes.GetAll(); Assert.NotNull(models); @@ -148,7 +149,7 @@ public void GetAll() { [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.PageTypes.GetById("none-existing-type"); Assert.Null(none); @@ -157,7 +158,7 @@ public void GetNoneById() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PageTypes.GetById(pageTypes[0].Id); Assert.NotNull(model); @@ -167,7 +168,7 @@ public void GetById() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PageTypes.GetById(pageTypes[0].Id); Assert.Null(model.Title); @@ -180,7 +181,7 @@ public void Update() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PageTypes.GetById(pageTypes[3].Id); Assert.NotNull(model); @@ -191,7 +192,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PageTypes.GetById(pageTypes[4].Id); Assert.NotNull(model); @@ -199,5 +200,29 @@ public void DeleteById() { api.PageTypes.Delete(model.Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Pages.cs b/test/Piranha.Tests/Repositories/Pages.cs index 066bb4dc7..ce54072bd 100644 --- a/test/Piranha.Tests/Repositories/Pages.cs +++ b/test/Piranha.Tests/Repositories/Pages.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,14 +8,16 @@ * */ -using Microsoft.Extensions.DependencyInjection; -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Xunit; +using Piranha.AttributeBuilder; +using Piranha.Extend.Fields; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -122,8 +124,8 @@ protected override void Init() { .AddSingleton() .BuildServiceProvider(); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); Piranha.App.Fields.Register(); @@ -135,20 +137,13 @@ protected override void Init() { .AddType(typeof(MyDIPage)); builder.Build(); - var site = new Data.Site { + var site = new Site { Id = SITE_ID, Title = "Default Site", InternalId = "DefaultSite", IsDefault = true }; - var emptysite = new Data.Site { - Id = SITE_ID, - Title = "Empty Site", - InternalId = "EmptySite", - IsDefault = false - }; api.Sites.Save(site); - api.Sites.Save(emptysite); var page1 = MyPage.Create(api); page1.Id = PAGE_1_ID; @@ -231,7 +226,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(SITE_ID); foreach (var page in pages.Where(p => p.OriginalPageId.HasValue)) @@ -253,14 +248,14 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(PagesCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(PagesCached), ((Api)api).IsCached); } } [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Pages.GetById(Guid.NewGuid()); Assert.Null(none); @@ -269,7 +264,7 @@ public void GetNoneById() { [Fact] public void GetNoneBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Pages.GetBySlug("none-existing-slug"); Assert.Null(none); @@ -278,7 +273,7 @@ public void GetNoneBySlug() { [Fact] public void GetStartpage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetStartpage(); Assert.NotNull(model); @@ -289,7 +284,7 @@ public void GetStartpage() { [Fact] public void GetStartpageBySite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetStartpage(SITE_ID); Assert.NotNull(model); @@ -300,7 +295,7 @@ public void GetStartpageBySite() { [Fact] public void GetStartpageNone() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetStartpage(SITE_EMPTY_ID); Assert.Null(model); @@ -309,7 +304,7 @@ public void GetStartpageNone() { [Fact] public void GetIdBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetIdBySlug("my-first-page"); Assert.NotNull(model); @@ -319,7 +314,7 @@ public void GetIdBySlug() { [Fact] public void GetIdBySlugSiteId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetIdBySlug("my-first-page", SITE_ID); Assert.NotNull(model); @@ -329,7 +324,7 @@ public void GetIdBySlugSiteId() { [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(SITE_ID); Assert.NotNull(pages); @@ -339,7 +334,7 @@ public void GetAll() { [Fact] public void GetAllByBaseClass() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(SITE_ID); Assert.NotNull(pages); @@ -349,7 +344,7 @@ public void GetAllByBaseClass() { [Fact] public void GetAllBlogs() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAllBlogs(SITE_ID); Assert.NotNull(pages); @@ -359,7 +354,7 @@ public void GetAllBlogs() { [Fact] public void GetAllBlogsByBaseClass() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAllBlogs(SITE_ID); Assert.NotNull(pages); @@ -369,7 +364,7 @@ public void GetAllBlogsByBaseClass() { [Fact] public void GetAllByMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(SITE_ID); Assert.NotNull(pages); @@ -379,7 +374,7 @@ public void GetAllByMissing() { [Fact] public void GetGenericById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -390,7 +385,7 @@ public void GetGenericById() { [Fact] public void GetBaseClassById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -402,7 +397,7 @@ public void GetBaseClassById() { [Fact] public void GetBlocksById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -414,7 +409,7 @@ public void GetBlocksById() { [Fact] public void GetMissingById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.Null(model); @@ -423,7 +418,7 @@ public void GetMissingById() { [Fact] public void GetInfoById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -434,7 +429,7 @@ public void GetInfoById() { [Fact] public void GetGenericBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page"); Assert.NotNull(model); @@ -445,7 +440,7 @@ public void GetGenericBySlug() { [Fact] public void GetBaseClassBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page"); Assert.NotNull(model); @@ -457,7 +452,7 @@ public void GetBaseClassBySlug() { [Fact] public void GetMissingBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page"); Assert.Null(model); @@ -466,7 +461,7 @@ public void GetMissingBySlug() { [Fact] public void GetInfoBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page"); Assert.NotNull(model); @@ -477,7 +472,7 @@ public void GetInfoBySlug() { [Fact] public void GetDynamicById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -488,7 +483,7 @@ public void GetDynamicById() { [Fact] public void GetDynamicBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page"); Assert.NotNull(model); @@ -499,7 +494,7 @@ public void GetDynamicBySlug() { [Fact] public void CheckPermlinkSyntax() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(model); @@ -510,7 +505,7 @@ public void CheckPermlinkSyntax() { [Fact] public void GetCollectionPage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetBySlug("my-collection-page"); Assert.NotNull(page); @@ -521,7 +516,7 @@ public void GetCollectionPage() { [Fact] public void GetCollectionPageBaseClass() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetBySlug("my-collection-page"); Assert.NotNull(page); @@ -533,7 +528,7 @@ public void GetCollectionPageBaseClass() { [Fact] public void GetDynamicCollectionPage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetBySlug("my-collection-page"); Assert.NotNull(page); @@ -544,7 +539,7 @@ public void GetDynamicCollectionPage() { [Fact] public void EmptyCollectionPage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = MyCollectionPage.Create(api); Assert.Equal(0, page.Texts.Count); @@ -562,7 +557,7 @@ public void EmptyCollectionPage() { [Fact] public void EmptyDynamicCollectionPage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = Piranha.Models.DynamicPage.Create(api, "MyCollectionPage"); Assert.Equal(0, page.Regions.Texts.Count); @@ -580,7 +575,7 @@ public void EmptyDynamicCollectionPage() { [Fact] public void EmptyCollectionPageComplex() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = MyCollectionPage.Create(api); Assert.Equal(0, page.Teasers.Count); @@ -598,7 +593,7 @@ public void EmptyCollectionPageComplex() { [Fact] public void EmptyDynamicCollectionPageComplex() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = Piranha.Models.DynamicPage.Create(api, "MyCollectionPage"); Assert.Equal(0, page.Regions.Teasers.Count); @@ -616,7 +611,7 @@ public void EmptyDynamicCollectionPageComplex() { [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var count = api.Pages.GetAll(SITE_ID).Count(); var page = MyPage.Create(api, "MyPage"); page.SiteId = SITE_ID; @@ -632,7 +627,7 @@ public void Add() { [Fact] public void AddHierarchical() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.HierarchicalPageSlugs = true; } @@ -659,7 +654,7 @@ public void AddHierarchical() { [Fact] public void AddNonHierarchical() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { using (var config = new Piranha.Config(api)) { config.HierarchicalPageSlugs = false; } @@ -686,7 +681,7 @@ public void AddNonHierarchical() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(page); @@ -706,7 +701,7 @@ public void Update() { [Fact] public void UpdateCollectionPage() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetBySlug("my-collection-page", SITE_ID); Assert.NotNull(page); @@ -727,7 +722,7 @@ public void UpdateCollectionPage() { [Fact] public void Move() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_1_ID); Assert.NotNull(page); @@ -745,7 +740,7 @@ public void Move() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_3_ID); var count = api.Pages.GetAll(SITE_ID).Count(); @@ -759,7 +754,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var count = api.Pages.GetAll(SITE_ID).Count(); api.Pages.Delete(PAGE_2_ID); @@ -770,7 +765,7 @@ public void DeleteById() { [Fact] public void GetDIGeneric() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_DI_ID); Assert.NotNull(page); @@ -780,7 +775,7 @@ public void GetDIGeneric() { [Fact] public void GetDIDynamic() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_DI_ID); Assert.NotNull(page); @@ -790,7 +785,7 @@ public void GetDIDynamic() { [Fact] public void CreateDIGeneric() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = MyDIPage.Create(api); Assert.NotNull(page); @@ -800,7 +795,7 @@ public void CreateDIGeneric() { [Fact] public void CreateDIDynamic() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = Models.DynamicPage.Create(api, nameof(MyDIPage)); Assert.NotNull(page); @@ -810,7 +805,7 @@ public void CreateDIDynamic() { [Fact] public void GetCopyGenericById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetById(PAGE_8_ID); Assert.NotNull(model); @@ -828,7 +823,7 @@ public void GetCopyGenericById() { [Fact] public void GetCopyGenericBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Pages.GetBySlug("my-first-page/my-copied-page"); Assert.NotNull(model); @@ -846,7 +841,7 @@ public void GetCopyGenericBySlug() { [Fact] public void UpdatingCopyShouldIgnoreBodyAndDate() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = api.Pages.GetById(PAGE_8_ID); page.Created = DateTime.Parse("2001-01-01"); page.LastModified = DateTime.Parse("2001-01-01"); @@ -863,7 +858,7 @@ public void UpdatingCopyShouldIgnoreBodyAndDate() { [Fact] public void CanNotUpdateCopyOriginalPageWithAnotherCopy() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = MyPage.Create(api); page.Title = "New title"; page.OriginalPageId = PAGE_8_ID; // PAGE_8 is an copy of PAGE_7 @@ -878,7 +873,7 @@ public void CanNotUpdateCopyOriginalPageWithAnotherCopy() { [Fact] public void CanNotUpdateCopyWithAnotherTypeIdOtherThanOriginalPageTypeId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var page = MissingPage.Create(api); page.Title = "New title"; page.OriginalPageId = PAGE_7_ID; @@ -893,7 +888,7 @@ public void CanNotUpdateCopyWithAnotherTypeIdOtherThanOriginalPageTypeId() { [Fact] public void DetachShouldCopyBlocks() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var originalPage = api.Pages.GetById(PAGE_7_ID); var copy = api.Pages.GetById(PAGE_8_ID); var originalBlock = new Extend.Blocks.TextBlock { @@ -917,7 +912,7 @@ public void DetachShouldCopyBlocks() { [Fact] public void DetachShouldCopyRegions() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var originalPage = api.Pages.GetById(PAGE_7_ID); originalPage.Body = "body to be copied"; originalPage.Ingress = "ingress to be copied"; @@ -939,12 +934,36 @@ public void DetachShouldCopyRegions() { [Fact] public void DeleteShouldThrowWhenPageHasCopies() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var exn = Assert.Throws(() => { api.Pages.Delete(PAGE_7_ID); }); Assert.Equal("Can not delete page because it has copies", exn.Message); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Params.cs b/test/Piranha.Tests/Repositories/Params.cs index c09d6c01c..87530c1b2 100644 --- a/test/Piranha.Tests/Repositories/Params.cs +++ b/test/Piranha.Tests/Repositories/Params.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,11 +8,14 @@ * */ -using Piranha.Services; using System; +using System.ComponentModel.DataAnnotations; using System.Data.SqlClient; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -41,24 +44,24 @@ public class Params : BaseTests #endregion protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Params.Save(new Data.Param() { + using (var api = CreateApi()) { + api.Params.Save(new Param() { Id = PARAM_1_ID, Key = PARAM_1, Value = PARAM_1_VALUE }); - api.Params.Save(new Data.Param() { + api.Params.Save(new Param() { Key = PARAM_4, }); - api.Params.Save(new Data.Param() { + api.Params.Save(new Param() { Key = PARAM_5, }); } } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var param = api.Params.GetAll(); foreach (var p in param) @@ -68,15 +71,15 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(ParamsCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(ParamsCached), ((Api)api).IsCached); } } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Params.Save(new Data.Param() { + using (var api = CreateApi()) { + api.Params.Save(new Param() { Key = PARAM_2, Value = "My second value" }); @@ -85,18 +88,37 @@ public void Add() { [Fact] public void AddDuplicateKey() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Params.Save(new Data.Param() { + using (var api = CreateApi()) { + Assert.Throws(() => + api.Params.Save(new Param() { Key = PARAM_1, Value = "My duplicate value" })); } } + [Fact] + public void AddEmptyKey() + { + using (var api = CreateApi()) { + Assert.Throws(() => api.Params.Save(new Param())); + } + } + + [Fact] + public void AddTooLongKey() { + using (var api = CreateApi()) { + Assert.Throws(() => + api.Params.Save(new Param + { + Key = "IntegerPosuereEratAnteVenenatisDapibusPosuereVelitAliquetNullamQuisRisusEgetUrnaMollisOrnareVelEuLeo", + })); + } + } + [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Params.GetById(Guid.NewGuid()); Assert.Null(none); @@ -105,7 +127,7 @@ public void GetNoneById() { [Fact] public void GetNoneByKey() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Params.GetByKey("none-existing-key"); Assert.Null(none); @@ -114,7 +136,7 @@ public void GetNoneByKey() { [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.Params.GetAll(); Assert.NotNull(models); @@ -124,7 +146,7 @@ public void GetAll() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Params.GetById(PARAM_1_ID); Assert.NotNull(model); @@ -134,7 +156,7 @@ public void GetById() { [Fact] public void GetByKey() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Params.GetByKey(PARAM_1); Assert.NotNull(model); @@ -144,7 +166,7 @@ public void GetByKey() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Params.GetById(PARAM_1_ID); Assert.Equal(PARAM_1_VALUE, model.Value); @@ -157,7 +179,7 @@ public void Update() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Params.GetByKey(PARAM_4); Assert.NotNull(model); @@ -168,7 +190,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Params.GetByKey(PARAM_4); Assert.NotNull(model); @@ -176,5 +198,29 @@ public void DeleteById() { api.Params.Delete(model.Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/PostTypes.cs b/test/Piranha.Tests/Repositories/PostTypes.cs index 4354e4b9e..a2ab3a1fe 100644 --- a/test/Piranha.Tests/Repositories/PostTypes.cs +++ b/test/Piranha.Tests/Repositories/PostTypes.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,11 +8,12 @@ * */ -using Piranha.Models; -using Piranha.Services; using System.Collections.Generic; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -106,7 +107,7 @@ public class PostTypes : BaseTests #endregion protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { api.PostTypes.Save(postTypes[0]); api.PostTypes.Save(postTypes[3]); api.PostTypes.Save(postTypes[4]); @@ -114,7 +115,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var postTypes = api.PostTypes.GetAll(); foreach (var p in postTypes) @@ -124,21 +125,21 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(PostTypesCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(PostTypesCached), ((Api)api).IsCached); } } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { api.PostTypes.Save(postTypes[1]); } } [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.PostTypes.GetAll(); Assert.NotNull(models); @@ -148,7 +149,7 @@ public void GetAll() { [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.PostTypes.GetById("none-existing-type"); Assert.Null(none); @@ -157,7 +158,7 @@ public void GetNoneById() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PostTypes.GetById(postTypes[0].Id); Assert.NotNull(model); @@ -167,7 +168,7 @@ public void GetById() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PostTypes.GetById(postTypes[0].Id); Assert.Null(model.Title); @@ -180,7 +181,7 @@ public void Update() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PostTypes.GetById(postTypes[3].Id); Assert.NotNull(model); @@ -191,7 +192,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.PostTypes.GetById(postTypes[4].Id); Assert.NotNull(model); @@ -199,5 +200,29 @@ public void DeleteById() { api.PostTypes.Delete(model.Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Posts.cs b/test/Piranha.Tests/Repositories/Posts.cs index 0d170567a..88c4816ef 100644 --- a/test/Piranha.Tests/Repositories/Posts.cs +++ b/test/Piranha.Tests/Repositories/Posts.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,14 +8,16 @@ * */ -using Microsoft.Extensions.DependencyInjection; -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Xunit; +using Piranha.AttributeBuilder; +using Piranha.Extend.Fields; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -104,8 +106,8 @@ protected override void Init() { .AddSingleton() .BuildServiceProvider(); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); Piranha.App.Fields.Register(); @@ -120,7 +122,7 @@ protected override void Init() { postTypeBuilder.Build(); // Add site - var site = new Data.Site() { + var site = new Site() { Id = SITE_ID, Title = "Post Site", InternalId = "PostSite", @@ -135,12 +137,10 @@ protected override void Init() { page.Title = "Blog"; api.Pages.Save(page); - var category = new Data.Category() { + var category = new Models.Taxonomy() { Id = CAT_1_ID, - BlogId = BLOG_ID, Title = "My category" }; - api.Categories.Save(category); var post1 = MyPost.Create(api); post1.Id = POST_1_ID; @@ -200,7 +200,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll(BLOG_ID); foreach (var post in posts) api.Posts.Delete(post); @@ -209,13 +209,9 @@ protected override void Cleanup() { foreach (var t in types) api.PostTypes.Delete(t); - var category = api.Categories.GetById(CAT_1_ID); - if (category != null) - api.Categories.Delete(category); - - var tags = api.Tags.GetAll(BLOG_ID); - foreach (var tag in tags) - api.Tags.Delete(tag); + //var tags = api.Tags.GetAll(BLOG_ID); + //foreach (var tag in tags) + // api.Tags.Delete(tag); api.Pages.Delete(BLOG_ID); @@ -229,14 +225,14 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(PostsCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(PostsCached), ((Api)api).IsCached); } } [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Posts.GetById(Guid.NewGuid()); Assert.Null(none); @@ -245,7 +241,7 @@ public void GetNoneById() { [Fact] public void GetNoneBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Posts.GetBySlug("blog", "none-existing-slug"); Assert.Null(none); @@ -254,7 +250,7 @@ public void GetNoneBySlug() { [Fact] public void GetNoneBySlugId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Posts.GetBySlug(BLOG_ID, "none-existing-slug"); Assert.Null(none); @@ -263,7 +259,7 @@ public void GetNoneBySlugId() { [Fact] public void GetNoneBySlugBlog() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Posts.GetBySlug("no-blog", "none-existing-slug"); Assert.Null(none); @@ -272,7 +268,7 @@ public void GetNoneBySlugBlog() { [Fact] public void GetNoneBySlugBlogId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Posts.GetBySlug(Guid.NewGuid(), "none-existing-slug"); Assert.Null(none); @@ -281,8 +277,8 @@ public void GetNoneBySlugBlogId() { [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var posts = api.Posts.GetAll(); + using (var api = CreateApi()) { + var posts = api.Posts.GetAllBySiteId(); Assert.NotNull(posts); Assert.NotEmpty(posts); @@ -291,8 +287,8 @@ public void GetAll() { [Fact] public void GetAllBaseClass() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var posts = api.Posts.GetAll(); + using (var api = CreateApi()) { + var posts = api.Posts.GetAllBySiteId(); Assert.NotNull(posts); Assert.NotEmpty(posts); @@ -301,7 +297,7 @@ public void GetAllBaseClass() { [Fact] public void GetAllById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll(BLOG_ID); Assert.NotNull(posts); @@ -311,7 +307,7 @@ public void GetAllById() { [Fact] public void GetAllBaseClassById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll(BLOG_ID); Assert.NotNull(posts); @@ -321,7 +317,7 @@ public void GetAllBaseClassById() { [Fact] public void GetAllByIdMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll(BLOG_ID); Assert.NotNull(posts); @@ -331,7 +327,7 @@ public void GetAllByIdMissing() { [Fact] public void GetAllBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("blog"); Assert.NotNull(posts); @@ -341,7 +337,7 @@ public void GetAllBySlug() { [Fact] public void GetAllBaseClassBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("blog"); Assert.NotNull(posts); @@ -351,7 +347,7 @@ public void GetAllBaseClassBySlug() { [Fact] public void GetAllBySlugMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("blog"); Assert.NotNull(posts); @@ -361,7 +357,7 @@ public void GetAllBySlugMissing() { [Fact] public void GetAllBySlugAndSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("blog", SITE_ID); Assert.NotNull(posts); @@ -371,7 +367,7 @@ public void GetAllBySlugAndSite() { [Fact] public void GetAllNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll(Guid.NewGuid()); Assert.NotNull(posts); @@ -381,7 +377,7 @@ public void GetAllNoneById() { [Fact] public void GetAllNoneBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("no-blog"); Assert.NotNull(posts); @@ -391,7 +387,7 @@ public void GetAllNoneBySlug() { [Fact] public void GetAllNoneBySlugAndSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var posts = api.Posts.GetAll("blog", Guid.NewGuid()); Assert.NotNull(posts); @@ -401,7 +397,7 @@ public void GetAllNoneBySlugAndSite() { [Fact] public void GetGenericById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -413,7 +409,7 @@ public void GetGenericById() { [Fact] public void GetBaseClassById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -426,7 +422,7 @@ public void GetBaseClassById() { [Fact] public void GetBlocksById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -438,7 +434,7 @@ public void GetBlocksById() { [Fact] public void GetMissingById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.Null(model); @@ -447,7 +443,7 @@ public void GetMissingById() { [Fact] public void GetInfoById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -459,7 +455,7 @@ public void GetInfoById() { [Fact] public void GetGenericBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetBySlug("blog", "my-first-post"); Assert.NotNull(model); @@ -471,7 +467,7 @@ public void GetGenericBySlug() { [Fact] public void GetBaseClassBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetBySlug("blog", "my-first-post"); Assert.NotNull(model); @@ -484,7 +480,7 @@ public void GetBaseClassBySlug() { [Fact] public void GetMissingBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetBySlug("blog", "my-first-post"); Assert.Null(model); @@ -493,7 +489,7 @@ public void GetMissingBySlug() { [Fact] public void GetInfoBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetBySlug("blog", "my-first-post"); Assert.NotNull(model); @@ -505,7 +501,7 @@ public void GetInfoBySlug() { [Fact] public void GetDynamicById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -517,7 +513,7 @@ public void GetDynamicById() { [Fact] public void GetDynamicBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetBySlug("blog", "my-first-post"); Assert.NotNull(model); @@ -528,7 +524,7 @@ public void GetDynamicBySlug() { [Fact] public void CheckPermlinkSyntax() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Posts.GetById(POST_1_ID); Assert.NotNull(model); @@ -539,7 +535,7 @@ public void CheckPermlinkSyntax() { [Fact] public void GetCollectionPost() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetBySlug(BLOG_ID, "my-collection-post"); Assert.NotNull(post); @@ -550,7 +546,7 @@ public void GetCollectionPost() { [Fact] public void GetBaseClassCollectionPost() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetBySlug(BLOG_ID, "my-collection-post"); Assert.NotNull(post); @@ -561,7 +557,7 @@ public void GetBaseClassCollectionPost() { } [Fact] public void GetDynamicCollectionPost() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetBySlug(BLOG_ID, "my-collection-post"); Assert.NotNull(post); @@ -572,9 +568,9 @@ public void GetDynamicCollectionPost() { [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var count = api.Posts.GetAll(BLOG_ID).Count(); - var catCount = api.Categories.GetAll(BLOG_ID).Count(); + var catCount = api.Posts.GetAllCategories(BLOG_ID).Count(); var post = MyPost.Create(api, "MyPost"); post.BlogId = BLOG_ID; post.Category = "My category"; @@ -585,16 +581,16 @@ public void Add() { api.Posts.Save(post); Assert.Equal(count + 1, api.Posts.GetAll(BLOG_ID).Count()); - Assert.Equal(catCount, api.Categories.GetAll(BLOG_ID).Count()); + Assert.Equal(catCount, api.Posts.GetAllCategories(BLOG_ID).Count()); } } [Fact] public void AddWithTags() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var count = api.Posts.GetAll(BLOG_ID).Count(); - var catCount = api.Categories.GetAll(BLOG_ID).Count(); - var tagCount = api.Tags.GetAll(BLOG_ID).Count(); + var catCount = api.Posts.GetAllCategories(BLOG_ID).Count(); + var tagCount = api.Posts.GetAllTags(BLOG_ID).Count(); var post = MyPost.Create(api, "MyPost"); post.BlogId = BLOG_ID; @@ -607,8 +603,8 @@ public void AddWithTags() { api.Posts.Save(post); Assert.Equal(count + 1, api.Posts.GetAll(BLOG_ID).Count()); - Assert.Equal(catCount, api.Categories.GetAll(BLOG_ID).Count()); - Assert.Equal(tagCount + 3, api.Tags.GetAll(BLOG_ID).Count()); + Assert.Equal(catCount, api.Posts.GetAllCategories(BLOG_ID).Count()); + Assert.Equal(tagCount + 3, api.Posts.GetAllTags(BLOG_ID).Count()); post = api.Posts.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug("My fifth post")); @@ -618,7 +614,7 @@ public void AddWithTags() { api.Posts.Save(post); - Assert.Equal(tagCount + 4, api.Tags.GetAll(BLOG_ID).Count()); + Assert.Equal(tagCount + 4, api.Posts.GetAllTags(BLOG_ID).Count()); post = api.Posts.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug("My fifth post")); @@ -629,7 +625,7 @@ public void AddWithTags() { [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetById(POST_1_ID); Assert.NotNull(post); @@ -647,7 +643,7 @@ public void Update() { [Fact] public void UpdateCollectionPost() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetBySlug("blog", "my-collection-post"); Assert.NotNull(post); @@ -668,7 +664,7 @@ public void UpdateCollectionPost() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetById(POST_3_ID); var count = api.Posts.GetAll(BLOG_ID).Count(); @@ -682,7 +678,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var count = api.Posts.GetAll(BLOG_ID).Count(); api.Posts.Delete(POST_2_ID); @@ -693,7 +689,7 @@ public void DeleteById() { [Fact] public void GetDIGeneric() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetById(POST_DI_ID); Assert.NotNull(post); @@ -703,12 +699,36 @@ public void GetDIGeneric() { [Fact] public void GetDIDynamic() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var post = api.Posts.GetById(POST_DI_ID); Assert.NotNull(post); Assert.Equal("My service value", post.Regions.Body.Value); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/SiteTypes.cs b/test/Piranha.Tests/Repositories/SiteTypes.cs index 4a51212b5..31e8a0e04 100644 --- a/test/Piranha.Tests/Repositories/SiteTypes.cs +++ b/test/Piranha.Tests/Repositories/SiteTypes.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,11 +8,12 @@ * */ -using Piranha.Models; -using Piranha.Services; using System.Collections.Generic; using System.Linq; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -131,7 +132,7 @@ public class SiteTypes : BaseTests protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { api.SiteTypes.Save(siteTypes[0]); api.SiteTypes.Save(siteTypes[3]); @@ -141,7 +142,7 @@ protected override void Init() protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var siteTypes = api.SiteTypes.GetAll(); @@ -155,16 +156,16 @@ protected override void Cleanup() [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { - Assert.Equal(this.GetType() == typeof(SiteTypesCached), api.IsCached); + Assert.Equal(this.GetType() == typeof(SiteTypesCached), ((Api)api).IsCached); } } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { api.SiteTypes.Save(siteTypes[1]); } @@ -173,7 +174,7 @@ public void Add() [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var models = api.SiteTypes.GetAll(); @@ -185,7 +186,7 @@ public void GetAll() [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var none = api.SiteTypes.GetById("none-existing-type"); @@ -196,7 +197,7 @@ public void GetNoneById() [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var model = api.SiteTypes.GetById(siteTypes[0].Id); @@ -208,7 +209,7 @@ public void GetById() [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var model = api.SiteTypes.GetById(siteTypes[0].Id); @@ -223,7 +224,7 @@ public void Update() [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var model = api.SiteTypes.GetById(siteTypes[3].Id); @@ -236,7 +237,7 @@ public void Delete() [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) + using (var api = CreateApi()) { var model = api.SiteTypes.GetById(siteTypes[4].Id); @@ -245,5 +246,29 @@ public void DeleteById() api.SiteTypes.Delete(model.Id); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Sites.cs b/test/Piranha.Tests/Repositories/Sites.cs index bcc810bcc..c42ec6043 100644 --- a/test/Piranha.Tests/Repositories/Sites.cs +++ b/test/Piranha.Tests/Repositories/Sites.cs @@ -1,19 +1,22 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using Xunit; +using Piranha.AttributeBuilder; +using Piranha.Extend.Fields; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Repositories { @@ -55,11 +58,11 @@ public class MySiteContent : Models.SiteContent [Region] public HtmlField Footer { get; set; } - } + } protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); var builder = new PageTypeBuilder(api) .AddType(typeof(MyPage)); @@ -69,7 +72,7 @@ protected override void Init() { .AddType(typeof(MySiteContent)); siteBuilder.Build(); - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { Id = SITE_1_ID, SiteTypeId = "MySiteContent", InternalId = SITE_1, @@ -78,33 +81,33 @@ protected override void Init() { IsDefault = true }); - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { InternalId = SITE_4, Title = SITE_4 }); - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { InternalId = SITE_5, Title = SITE_5 }); - api.Sites.Save(new Data.Site() { + api.Sites.Save(new Site() { InternalId = SITE_6, Title = SITE_6 }); // Sites for testing hostname routing - api.Sites.Save(new Data.Site + api.Sites.Save(new Site { InternalId = "RoutingTest1", Title = "RoutingTest1", Hostnames = "mydomain.com,localhost" }); - api.Sites.Save(new Data.Site + api.Sites.Save(new Site { InternalId = "RoutingTest2", Title = "RoutingTest2", Hostnames = " mydomain.com/en" }); - api.Sites.Save(new Data.Site + api.Sites.Save(new Site { InternalId = "RoutingTest3", Title = "RoutingTest3", @@ -142,7 +145,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(SITE_1_ID); foreach (var page in pages.Where(p => p.ParentId.HasValue)) api.Pages.Delete(page); @@ -165,15 +168,15 @@ protected override void Cleanup() { [Fact] public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(SitesCached), api.IsCached); + using (var api = CreateApi()) { + Assert.Equal(this.GetType() == typeof(SitesCached), ((Api)api).IsCached); } - } + } [Fact] public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Sites.Save(new Data.Site() { + using (var api = CreateApi()) { + api.Sites.Save(new Site() { InternalId = SITE_2, Title = SITE_2 }); @@ -182,9 +185,9 @@ public void Add() { [Fact] public void AddDuplicateKey() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Sites.Save(new Data.Site() { + using (var api = CreateApi()) { + Assert.ThrowsAny(() => + api.Sites.Save(new Site() { InternalId = SITE_1, Title = SITE_1 })); @@ -193,18 +196,18 @@ public void AddDuplicateKey() { [Fact] public void AddEmptyFailure() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Sites.Save(new Data.Site())); - } + using (var api = CreateApi()) { + Assert.ThrowsAny(() => + api.Sites.Save(new Site())); + } } [Fact] public void AddAndGenerateInternalId() { var id = Guid.NewGuid(); - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Sites.Save(new Data.Site() { + using (var api = CreateApi()) { + api.Sites.Save(new Site() { Id = id, Title = "Generate internal id" }); @@ -218,7 +221,7 @@ public void AddAndGenerateInternalId() { [Fact] public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var models = api.Sites.GetAll(); Assert.NotNull(models); @@ -228,7 +231,7 @@ public void GetAll() { [Fact] public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Sites.GetById(Guid.NewGuid()); Assert.Null(none); @@ -237,7 +240,7 @@ public void GetNoneById() { [Fact] public void GetNoneByInternalId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var none = api.Sites.GetByInternalId("none-existing-id"); Assert.Null(none); @@ -246,7 +249,7 @@ public void GetNoneByInternalId() { [Fact] public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetById(SITE_1_ID); Assert.NotNull(model); @@ -256,7 +259,7 @@ public void GetById() { [Fact] public void GetByInternalId() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByInternalId(SITE_1); Assert.NotNull(model); @@ -266,7 +269,7 @@ public void GetByInternalId() { [Fact] public void GetDefault() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetDefault(); Assert.NotNull(model); @@ -276,7 +279,7 @@ public void GetDefault() { [Fact] public void GetSitemap() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(); Assert.NotNull(sitemap); @@ -287,7 +290,7 @@ public void GetSitemap() { [Fact] public void GetSitemapById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(SITE_1_ID); Assert.NotNull(sitemap); @@ -298,7 +301,7 @@ public void GetSitemapById() { [Fact] public void CheckPermlinkSyntax() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(); foreach (var item in sitemap) { @@ -306,33 +309,33 @@ public void CheckPermlinkSyntax() { Assert.StartsWith("/", item.Permalink); } } - } + } [Fact] public void GetUnpublishedSitemap() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(onlyPublished: false); Assert.NotNull(sitemap); Assert.Equal(2, sitemap.Count); Assert.Equal("Startpage", sitemap[0].Title); - Assert.Equal(1, sitemap[1].Items.Count); + Assert.Single(sitemap[1].Items); Assert.Equal("Subpage", sitemap[1].Items[0].Title); } } [Fact] public void CheckHiddenSitemapItems() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(); Assert.Equal(1, sitemap.Count(s => s.IsHidden)); - } + } } [Fact] public void ChangeDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var site6 = api.Sites.GetByInternalId(SITE_6); Assert.False(site6.IsDefault); @@ -344,12 +347,12 @@ public void ChangeDefaultSite() { Assert.False(site1.IsDefault); site1.IsDefault = true; api.Sites.Save(site1); - } + } } [Fact] public void CantRemoveDefault() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var site1 = api.Sites.GetById(SITE_1_ID); Assert.True(site1.IsDefault); @@ -359,25 +362,25 @@ public void CantRemoveDefault() { site1 = api.Sites.GetById(SITE_1_ID); Assert.True(site1.IsDefault); - } + } } [Fact] public void GetUnpublishedSitemapById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var sitemap = api.Sites.GetSitemap(SITE_1_ID, onlyPublished: false); Assert.NotNull(sitemap); Assert.Equal(2, sitemap.Count); Assert.Equal("Startpage", sitemap[0].Title); - Assert.Equal(1, sitemap[1].Items.Count); + Assert.Single(sitemap[1].Items); Assert.Equal("Subpage", sitemap[1].Items[0].Title); } } [Fact] public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetById(SITE_1_ID); Assert.Equal(SITE_1_HOSTS, model.Hostnames); @@ -390,7 +393,7 @@ public void Update() { [Fact] public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByInternalId(SITE_4); Assert.NotNull(model); @@ -401,7 +404,7 @@ public void Delete() { [Fact] public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByInternalId(SITE_5); Assert.NotNull(model); @@ -412,17 +415,17 @@ public void DeleteById() { [Fact] public void GetSiteContent() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); Assert.Equal("

Lorem ipsum

", model.Header.Value); - } + } } [Fact] public void UpdateSiteContent() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); @@ -431,23 +434,23 @@ public void UpdateSiteContent() { model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); - Assert.Equal("

Fusce Parturient

", model.Footer.Value); - } + Assert.Equal("

Fusce Parturient

", model.Footer.Value); + } } [Fact] public void GetDynamicSiteContent() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); Assert.Equal("

Lorem ipsum

", model.Regions.Header.Value); - } + } } [Fact] public void UpdateDynamicSiteContent() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); @@ -456,14 +459,14 @@ public void UpdateDynamicSiteContent() { model = api.Sites.GetContentById(SITE_1_ID); Assert.NotNull(model); - Assert.Equal("

Purus Sit

", model.Regions.Footer.Value); - } + Assert.Equal("

Purus Sit

", model.Regions.Footer.Value); + } } [Fact] public void GetByHostname() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("mydomain.com"); Assert.NotNull(model); @@ -474,7 +477,7 @@ public void GetByHostname() [Fact] public void GetByHostnameSecond() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("localhost"); Assert.NotNull(model); @@ -485,7 +488,7 @@ public void GetByHostnameSecond() [Fact] public void GetByHostnameSuffix() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("mydomain.com/en"); Assert.NotNull(model); @@ -496,7 +499,7 @@ public void GetByHostnameSuffix() [Fact] public void GetByHostnameSubdomain() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("sub.mydomain.com"); Assert.NotNull(model); @@ -507,7 +510,7 @@ public void GetByHostnameSubdomain() [Fact] public void GetByHostnameSubdomainSecond() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("sub2.localhost"); Assert.NotNull(model); @@ -518,11 +521,35 @@ public void GetByHostnameSubdomainSecond() [Fact] public void GetByHostnameMissing() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { + using (var api = CreateApi()) { var model = api.Sites.GetByHostname("nosite.com"); Assert.Null(model); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + cache: cache, + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Repositories/Tags.cs b/test/Piranha.Tests/Repositories/Tags.cs deleted file mode 100644 index 85af72a5c..000000000 --- a/test/Piranha.Tests/Repositories/Tags.cs +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2017 Håkan Edling - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - * http://github.com/piranhacms/piranha - * - */ - -using Piranha.AttributeBuilder; -using Piranha.Services; -using System; -using System.Data.SqlClient; -using System.Linq; -using Xunit; - -namespace Piranha.Tests.Repositories -{ - [Collection("Integration tests")] - public class TagsCached : Tags - { - protected override void Init() { - cache = new Cache.SimpleCache(); - - base.Init(); - } - } - - [Collection("Integration tests")] - public class Tags : BaseTests - { - #region Members - private const string TAG_1 = "My First Tag"; - private const string TAG_2 = "My Second Tag"; - private const string TAG_4 = "My Fourth Tag"; - private const string TAG_5 = "My Fifth Tag"; - private const string TAG_6 = "My Sixth Tag"; - private const string TAG_6_SLUG = "my-sixth-tag"; - - private Guid SITE_ID = Guid.NewGuid(); - private Guid BLOG_ID = Guid.NewGuid(); - private Guid TAG_1_ID = Guid.NewGuid(); - private Guid TAG_5_ID = Guid.NewGuid(); - - protected ICache cache; - #endregion - - [PageType(Title = "Blog page")] - public class BlogPage : Models.Page { } - - protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Piranha.App.Init(); - - var pageTypeBuilder = new PageTypeBuilder(api) - .AddType(typeof(BlogPage)); - pageTypeBuilder.Build(); - - // Add site - var site = new Data.Site() { - Id = SITE_ID, - Title = "Category Site", - InternalId = "CategorySite", - IsDefault = true - }; - api.Sites.Save(site); - - // Add blog page - var page = BlogPage.Create(api); - page.Id = BLOG_ID; - page.SiteId = SITE_ID; - page.Title = "Blog"; - api.Pages.Save(page); - - // Add tags - api.Tags.Save(new Data.Tag() { - Id = TAG_1_ID, - BlogId = BLOG_ID, - Title = TAG_1 - }); - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID, - Title = TAG_4 - }); - api.Tags.Save(new Data.Tag() { - Id = TAG_5_ID, - BlogId = BLOG_ID, - Title = TAG_5 - }); - } - } - - protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var tags = api.Tags.GetAll(BLOG_ID); - - foreach (var t in tags) - api.Tags.Delete(t); - - api.Pages.Delete(BLOG_ID); - - var types = api.PageTypes.GetAll(); - foreach (var t in types) - api.PageTypes.Delete(t); - - api.Sites.Delete(SITE_ID); - } - } - - [Fact] - public void IsCached() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.Equal(this.GetType() == typeof(TagsCached), api.IsCached); - } - } - - [Fact] - public void Add() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID, - Title = TAG_2 - }); - } - } - - [Fact] - public void AddWithSlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = new Data.Tag() { - BlogId = BLOG_ID, - Title = TAG_6, - Slug = TAG_6_SLUG - }; - api.Tags.Save(model); - - Assert.Equal(TAG_6_SLUG, model.Slug); - } - } - - [Fact] - public void AddDuplicateSlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID, - Title = TAG_1 - })); - } - } - - [Fact] - public void AddNoTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - Assert.ThrowsAny(() => - api.Tags.Save(new Data.Tag() { - BlogId = BLOG_ID - })); - } - } - - [Fact] - public void GetNoneById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var none = api.Tags.GetById(Guid.NewGuid()); - - Assert.Null(none); - } - } - - [Fact] - public void GetNoneBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var none = api.Tags.GetBySlug(BLOG_ID, "none-existing-slug"); - - Assert.Null(none); - } - } - - [Fact] - public void GetNoneBySlugBlog() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var none = api.Tags.GetBySlug(Guid.NewGuid(), "none-existing-slug"); - - Assert.Null(none); - } - } - - [Fact] - public void GetAll() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var models = api.Tags.GetAll(BLOG_ID); - - Assert.NotNull(models); - Assert.NotEmpty(models); - } - } - - [Fact] - public void GetById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetById(TAG_1_ID); - - Assert.NotNull(model); - Assert.Equal(TAG_1, model.Title); - } - } - - [Fact] - public void GetBySlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug(TAG_1)); - - Assert.NotNull(model); - Assert.Equal(TAG_1, model.Title); - } - } - - [Fact] - public void GetByTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetByTitle(BLOG_ID, TAG_1); - - Assert.NotNull(model); - Assert.Equal(TAG_1, model.Title); - } - } - - [Fact] - public void GetNoneByTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetByTitle(BLOG_ID, "Missing Title"); - - Assert.Null(model); - } - } - - [Fact] - public void Update() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetById(TAG_1_ID); - - Assert.Equal(TAG_1, model.Title); - - model.Title = "Updated"; - - api.Tags.Save(model); - } - } - - [Fact] - public void UpdateNoTitle() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetById(TAG_1_ID); - - model.Title = null; - - Assert.ThrowsAny(() => api.Tags.Save(model)); - } - } - - [Fact] - public void UpdateNoSlug() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetById(TAG_1_ID); - - model.Slug = null; - - api.Tags.Save(model); - - Assert.NotNull(model.Slug); - } - } - - [Fact] - public void Delete() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetBySlug(BLOG_ID, Piranha.Utils.GenerateSlug(TAG_4)); - - Assert.NotNull(model); - - api.Tags.Delete(model); - } - } - - [Fact] - public void DeleteById() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage, cache)) { - var model = api.Tags.GetById(TAG_5_ID); - - Assert.NotNull(model); - - api.Tags.Delete(model.Id); - } - } - } -} diff --git a/test/Piranha.Tests/Routers/Aliases.cs b/test/Piranha.Tests/Routers/Aliases.cs index 8e832bee1..4bb950dfa 100644 --- a/test/Piranha.Tests/Routers/Aliases.cs +++ b/test/Piranha.Tests/Routers/Aliases.cs @@ -1,18 +1,20 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; -using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using Xunit; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Routers { @@ -23,9 +25,9 @@ public class Aliases : BaseTests private Guid SITE2_ID = Guid.NewGuid(); protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { // Add site - var site1 = new Data.Site() { + var site1 = new Site() { Id = SITE1_ID, Title = "Alias Site", InternalId = "AliasSite", @@ -33,7 +35,7 @@ protected override void Init() { }; api.Sites.Save(site1); - var site2 = new Data.Site() { + var site2 = new Site() { Id = SITE2_ID, Title = "Alias Site 2", InternalId = "AliasSite2", @@ -43,13 +45,13 @@ protected override void Init() { api.Sites.Save(site2); // Add aliases - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { Id = Guid.NewGuid(), SiteId = SITE1_ID, AliasUrl = "/old-url", RedirectUrl = "/new-url" }); - api.Aliases.Save(new Data.Alias() { + api.Aliases.Save(new Alias() { Id = Guid.NewGuid(), SiteId = SITE2_ID, AliasUrl = "/old-url", @@ -59,7 +61,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { var aliases = api.Aliases.GetAll(); foreach (var a in aliases) api.Aliases.Delete(a); @@ -71,9 +73,9 @@ protected override void Cleanup() { } [Fact] - public void GetAliasByUrlDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.AliasRouter.Invoke(api, "/old-url", SITE1_ID); + public async Task GetAliasByUrlDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.AliasRouter.InvokeAsync(api, "/old-url", SITE1_ID); Assert.NotNull(response); Assert.Equal("/new-url", response.RedirectUrl); @@ -81,18 +83,18 @@ public void GetAliasByUrlDefaultSite() { } [Fact] - public void GetAliasByUrlNoneDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.AliasRouter.Invoke(api, "/missing-url", SITE1_ID); + public async Task GetAliasByUrlNoneDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.AliasRouter.InvokeAsync(api, "/missing-url", SITE1_ID); Assert.Null(response); } } [Fact] - public void GetAliasByUrlOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.AliasRouter.Invoke(api, "/old-url", SITE2_ID); + public async Task GetAliasByUrlOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.AliasRouter.InvokeAsync(api, "/old-url", SITE2_ID); Assert.NotNull(response); Assert.Equal("/another-new-url", response.RedirectUrl); @@ -100,12 +102,35 @@ public void GetAliasByUrlOtherSite() { } [Fact] - public void GetAliasByUrlNoneOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.AliasRouter.Invoke(api, "/missing-url", SITE2_ID); + public async Task GetAliasByUrlNoneOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.AliasRouter.InvokeAsync(api, "/missing-url", SITE2_ID); Assert.Null(response); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Routers/Pages.cs b/test/Piranha.Tests/Routers/Pages.cs index 03498f578..91317c46f 100644 --- a/test/Piranha.Tests/Routers/Pages.cs +++ b/test/Piranha.Tests/Routers/Pages.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,13 +8,15 @@ * */ -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; using System; -using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using Xunit; +using Piranha.AttributeBuilder; +using Piranha.Extend.Fields; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Routers { @@ -35,15 +37,15 @@ public class MyPage : Models.Page } protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); var builder = new PageTypeBuilder(api) .AddType(typeof(MyPage)); builder.Build(); // Add site - var site1 = new Data.Site() { + var site1 = new Site() { Id = SITE1_ID, Title = "Page Site", InternalId = "PageSite", @@ -51,7 +53,7 @@ protected override void Init() { }; api.Sites.Save(site1); - var site2 = new Data.Site() { + var site2 = new Site() { Id = SITE2_ID, Title = "Page Site 2", InternalId = "PageSite2", @@ -90,7 +92,7 @@ protected override void Init() { } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(); foreach (var p in pages) api.Pages.Delete(p); @@ -106,9 +108,9 @@ protected override void Cleanup() { } [Fact] - public void GetPageByUrlDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-first-page", SITE1_ID); + public async Task GetPageByUrlDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-first-page", SITE1_ID); Assert.NotNull(response); Assert.Equal("/page", response.Route); @@ -118,9 +120,9 @@ public void GetPageByUrlDefaultSite() { } [Fact] - public void GetPageByUrlDefaultSiteWithAction() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-first-page/action", SITE1_ID); + public async Task GetPageByUrlDefaultSiteWithAction() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-first-page/action", SITE1_ID); Assert.NotNull(response); Assert.Equal("/page/action", response.Route); @@ -130,9 +132,9 @@ public void GetPageByUrlDefaultSiteWithAction() { } [Fact] - public void GetPageByUrlDefaultSiteWithRedirect() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-third-page", SITE1_ID); + public async Task GetPageByUrlDefaultSiteWithRedirect() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-third-page", SITE1_ID); Assert.NotNull(response); Assert.Equal("http://www.redirect.com", response.RedirectUrl); @@ -141,9 +143,9 @@ public void GetPageByUrlDefaultSiteWithRedirect() { } [Fact] - public void GetStartpageDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.StartPageRouter.Invoke(api, "/", SITE1_ID); + public async Task GetStartpageDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.StartPageRouter.InvokeAsync(api, "/", SITE1_ID); Assert.NotNull(response); Assert.Equal("/page", response.Route); @@ -153,27 +155,27 @@ public void GetStartpageDefaultSite() { } [Fact] - public void GetPageByUrlNoneDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-second-page", SITE1_ID); + public async Task GetPageByUrlNoneDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-second-page", SITE1_ID); Assert.Null(response); } } [Fact] - public void GetStartpageDefaultSiteNone() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.StartPageRouter.Invoke(api, "/slug", SITE1_ID); + public async Task GetStartpageDefaultSiteNone() { + using (var api = CreateApi()) { + var response = await Piranha.Web.StartPageRouter.InvokeAsync(api, "/slug", SITE1_ID); Assert.Null(response); } } [Fact] - public void GetPageByUrlOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-second-page", SITE2_ID); + public async Task GetPageByUrlOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-second-page", SITE2_ID); Assert.NotNull(response); Assert.Equal("/page", response.Route); @@ -183,9 +185,9 @@ public void GetPageByUrlOtherSite() { } [Fact] - public void GetPageByUrlOtherSiteWithAction() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-second-page/action", SITE2_ID); + public async Task GetPageByUrlOtherSiteWithAction() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-second-page/action", SITE2_ID); Assert.NotNull(response); Assert.Equal("/page/action", response.Route); @@ -195,9 +197,9 @@ public void GetPageByUrlOtherSiteWithAction() { } [Fact] - public void GetStartpageOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.StartPageRouter.Invoke(api, "/", SITE2_ID); + public async Task GetStartpageOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.StartPageRouter.InvokeAsync(api, "/", SITE2_ID); Assert.NotNull(response); Assert.Equal("/page", response.Route); @@ -207,21 +209,44 @@ public void GetStartpageOtherSite() { } [Fact] - public void GetPageByUrlNoneOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PageRouter.Invoke(api, "/my-first-page", SITE2_ID); + public async Task GetPageByUrlNoneOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PageRouter.InvokeAsync(api, "/my-first-page", SITE2_ID); Assert.Null(response); } } [Fact] - public void GetStartpageOtherSiteNone() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.StartPageRouter.Invoke(api, "/slug", SITE2_ID); + public async Task GetStartpageOtherSiteNone() { + using (var api = CreateApi()) { + var response = await Piranha.Web.StartPageRouter.InvokeAsync(api, "/slug", SITE2_ID); Assert.Null(response); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Routers/Posts.cs b/test/Piranha.Tests/Routers/Posts.cs index 0f506e124..a4acdd313 100644 --- a/test/Piranha.Tests/Routers/Posts.cs +++ b/test/Piranha.Tests/Routers/Posts.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Håkan Edling + * Copyright (c) 2018-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,13 +8,16 @@ * */ -using Piranha.AttributeBuilder; -using Piranha.Extend.Fields; -using Piranha.Services; using System; using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using Xunit; +using Piranha.AttributeBuilder; +using Piranha.Extend.Fields; +using Piranha.Models; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Routers { @@ -45,8 +48,8 @@ public class MyPost : Models.Post } protected override void Init() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - Piranha.App.Init(); + using (var api = CreateApi()) { + Piranha.App.Init(api); var pageBuilder = new PageTypeBuilder(api) .AddType(typeof(MyPage)); @@ -57,7 +60,7 @@ protected override void Init() { postBuilder.Build(); // Add site - var site1 = new Data.Site() { + var site1 = new Site() { Id = SITE1_ID, Title = "Page Site", InternalId = "PostSite", @@ -65,7 +68,7 @@ protected override void Init() { }; api.Sites.Save(site1); - var site2 = new Data.Site() { + var site2 = new Site() { Id = SITE2_ID, Title = "Page Site 2", InternalId = "PostSite2", @@ -90,34 +93,15 @@ protected override void Init() { api.Pages.Save(page2); // Add categories - var category1 = new Data.Category() { + var category1 = new Models.Taxonomy() { Id = CATEGORY1_ID, - BlogId = PAGE1_ID, Title = "Default category" }; - api.Categories.Save(category1); - var category2 = new Data.Category() { + var category2 = new Models.Taxonomy() { Id = CATEGORY2_ID, - BlogId = PAGE2_ID, Title = "Default category" }; - api.Categories.Save(category2); - - // Add tags - var tag = new Data.Tag() { - Id = TAG1_ID, - BlogId = PAGE1_ID, - Title = "My tag" - }; - api.Tags.Save(tag); - - tag = new Data.Tag() { - Id = TAG2_ID, - BlogId = PAGE2_ID, - Title = "My other tag" - }; - api.Tags.Save(tag); // Add posts var post1 = MyPost.Create(api); @@ -126,7 +110,11 @@ protected override void Init() { post1.Category = category1; post1.Title = "My first post"; post1.Body = "My first body"; - post1.Tags.Add("My tag"); + post1.Tags.Add(new Models.Taxonomy + { + Id = TAG1_ID, + Title = "My tag" + }); post1.Published = DateTime.Now; api.Posts.Save(post1); @@ -136,14 +124,18 @@ protected override void Init() { post2.Category = category2; post2.Title = "My second post"; post2.Body = "My second body"; - post2.Tags.Add("My other tag"); + post2.Tags.Add(new Models.Taxonomy + { + Id = TAG2_ID, + Title = "My other tag" + }); post2.Published = DateTime.Now; api.Posts.Save(post2); } } protected override void Cleanup() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { + using (var api = CreateApi()) { var pages = api.Pages.GetAll(); foreach (var p in pages) api.Pages.Delete(p); @@ -163,9 +155,9 @@ protected override void Cleanup() { } [Fact] - public void GetPostByUrlDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/blog/my-first-post", SITE1_ID); + public async Task GetPostByUrlDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/blog/my-first-post", SITE1_ID); Assert.NotNull(response); Assert.Equal("/post", response.Route); @@ -175,9 +167,9 @@ public void GetPostByUrlDefaultSite() { } [Fact] - public void GetPostByUrlDefaultSiteWithAction() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/blog/my-first-post/action", SITE1_ID); + public async Task GetPostByUrlDefaultSiteWithAction() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/blog/my-first-post/action", SITE1_ID); Assert.NotNull(response); Assert.Equal("/post/action", response.Route); @@ -187,18 +179,18 @@ public void GetPostByUrlDefaultSiteWithAction() { } [Fact] - public void GetPostByUrlNoneDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/news/my-second-page", SITE1_ID); + public async Task GetPostByUrlNoneDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/news/my-second-page", SITE1_ID); Assert.Null(response); } } [Fact] - public void GetArchiveDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog", SITE1_ID); + public async Task GetArchiveDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -207,9 +199,9 @@ public void GetArchiveDefaultSite() { } [Fact] - public void GetArchiveYearDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/2018", SITE1_ID); + public async Task GetArchiveYearDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/2018", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -218,9 +210,9 @@ public void GetArchiveYearDefaultSite() { } [Fact] - public void GetArchiveYearMonthDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/2018/2", SITE1_ID); + public async Task GetArchiveYearMonthDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/2018/2", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -229,9 +221,9 @@ public void GetArchiveYearMonthDefaultSite() { } [Fact] - public void GetArchiveYearMonthPageDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/2018/2/page/1", SITE1_ID); + public async Task GetArchiveYearMonthPageDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/2018/2/page/1", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -240,9 +232,9 @@ public void GetArchiveYearMonthPageDefaultSite() { } [Fact] - public void GetArchiveYearMonthPageCategoryDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/category/default-category/2018/2/page/1", SITE1_ID); + public async Task GetArchiveYearMonthPageCategoryDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/category/default-category/2018/2/page/1", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -251,9 +243,9 @@ public void GetArchiveYearMonthPageCategoryDefaultSite() { } [Fact] - public void GetArchiveCategoryDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/category/default-category", SITE1_ID); + public async Task GetArchiveCategoryDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/category/default-category", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -262,9 +254,9 @@ public void GetArchiveCategoryDefaultSite() { } [Fact] - public void GetArchiveMissingCategoryDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/category/missing-category", SITE1_ID); + public async Task GetArchiveMissingCategoryDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/category/missing-category", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -273,9 +265,9 @@ public void GetArchiveMissingCategoryDefaultSite() { } [Fact] - public void GetArchiveTagDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/tag/my-tag", SITE1_ID); + public async Task GetArchiveTagDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/tag/my-tag", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -284,9 +276,9 @@ public void GetArchiveTagDefaultSite() { } [Fact] - public void GetArchiveMissingTagDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/tag/my-other-tag", SITE1_ID); + public async Task GetArchiveMissingTagDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/tag/my-other-tag", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -295,9 +287,9 @@ public void GetArchiveMissingTagDefaultSite() { } [Fact] - public void GetArchivePageDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog/page/1", SITE1_ID); + public async Task GetArchivePageDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog/page/1", SITE1_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -306,18 +298,18 @@ public void GetArchivePageDefaultSite() { } [Fact] - public void GetArchiveNoneDefaultSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/news", SITE1_ID); + public async Task GetArchiveNoneDefaultSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/news", SITE1_ID); Assert.Null(response); } } [Fact] - public void GetPostByUrlOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/news/my-second-post", SITE2_ID); + public async Task GetPostByUrlOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/news/my-second-post", SITE2_ID); Assert.NotNull(response); Assert.Equal("/post", response.Route); @@ -327,9 +319,9 @@ public void GetPostByUrlOtherSite() { } [Fact] - public void GetPostByUrlOtherSiteAction() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/news/my-second-post/action", SITE2_ID); + public async Task GetPostByUrlOtherSiteAction() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/news/my-second-post/action", SITE2_ID); Assert.NotNull(response); Assert.Equal("/post/action", response.Route); @@ -339,18 +331,18 @@ public void GetPostByUrlOtherSiteAction() { } [Fact] - public void GetPostByUrlNoneOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.PostRouter.Invoke(api, "/blog/my-first-post", SITE2_ID); + public async Task GetPostByUrlNoneOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.PostRouter.InvokeAsync(api, "/blog/my-first-post", SITE2_ID); Assert.Null(response); } } [Fact] - public void GetArchiveOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/news", SITE2_ID); + public async Task GetArchiveOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/news", SITE2_ID); Assert.NotNull(response); Assert.Equal("/archive", response.Route); @@ -359,12 +351,35 @@ public void GetArchiveOtherSite() { } [Fact] - public void GetArchiveNoneOtherSite() { - using (var api = new Api(GetDb(), new ContentServiceFactory(services), storage)) { - var response = Piranha.Web.ArchiveRouter.Invoke(api, "/blog", SITE2_ID); + public async Task GetArchiveNoneOtherSite() { + using (var api = CreateApi()) { + var response = await Piranha.Web.ArchiveRouter.InvokeAsync(api, "/blog", SITE2_ID); Assert.Null(response); } } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db), + storage: storage + ); + } } } diff --git a/test/Piranha.Tests/Taxonomies.cs b/test/Piranha.Tests/Taxonomies.cs index d4266b2d2..15edc0cf9 100644 --- a/test/Piranha.Tests/Taxonomies.cs +++ b/test/Piranha.Tests/Taxonomies.cs @@ -3,9 +3,9 @@ * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ using System; @@ -28,53 +28,5 @@ public void StringToTaxonomy() { Assert.Equal("Test", t.Title); Assert.Null(t.Slug); } - - [Fact] - public void CategoryToTaxonomy() { - var id = Guid.NewGuid(); - - Models.Taxonomy t = new Data.Category { - Id = id, - Title = "Test", - Slug = "test" - }; - - Assert.NotNull(t); - Assert.Equal(id, t.Id); - Assert.Equal("Test", t.Title); - Assert.Equal("test", t.Slug); - } - - [Fact] - public void NullCategoryToTaxonomy() { - Data.Category category = null; - Models.Taxonomy t = category; - - Assert.Null(t); - } - - [Fact] - public void TagToTaxonomy() { - var id = Guid.NewGuid(); - - Models.Taxonomy t = new Data.Tag { - Id = id, - Title = "Test", - Slug = "test" - }; - - Assert.NotNull(t); - Assert.Equal(id, t.Id); - Assert.Equal("Test", t.Title); - Assert.Equal("test", t.Slug); - } - - [Fact] - public void NullTagToTaxonomy() { - Data.Tag tag = null; - Models.Taxonomy t = tag; - - Assert.Null(t); - } } } \ No newline at end of file diff --git a/test/Piranha.Tests/Utils/UI.cs b/test/Piranha.Tests/Utils/UI.cs index be7706ed8..60960675d 100644 --- a/test/Piranha.Tests/Utils/UI.cs +++ b/test/Piranha.Tests/Utils/UI.cs @@ -1,16 +1,17 @@ /* - * Copyright (c) 2017 Håkan Edling + * Copyright (c) 2017-2019 Håkan Edling * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. - * + * * http://github.com/piranhacms/piranha - * + * */ -using Piranha.Services; using System; using Xunit; +using Piranha.Repositories; +using Piranha.Services; namespace Piranha.Tests.Utils { @@ -20,7 +21,10 @@ public class UI : BaseTests /// Sets up & initializes the tests. ///
protected override void Init() { - Piranha.App.Init(); + using (var api = CreateApi()) + { + Piranha.App.Init(api); + } } /// @@ -70,5 +74,27 @@ public void NoFirstParagraphHtml() { Assert.Equal("", Piranha.Utils.FirstParagraph(field)); } + + private IApi CreateApi() + { + var factory = new ContentFactory(services); + var serviceFactory = new ContentServiceFactory(factory); + + var db = GetDb(); + + return new Api( + factory, + new AliasRepository(db), + new ArchiveRepository(db), + new Piranha.Repositories.MediaRepository(db), + new PageRepository(db, serviceFactory), + new PageTypeRepository(db), + new ParamRepository(db), + new PostRepository(db, serviceFactory), + new PostTypeRepository(db), + new SiteRepository(db, serviceFactory), + new SiteTypeRepository(db) + ); + } } }