From 9c00c95f0194d763a9a50c757b4e784c921c24e7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Jan 2023 15:02:59 +0100 Subject: [PATCH] v13: New Backoffice in CMS (POC) (#13664) * Added a new executeable to new backoffice * add new backoffice client as submodule * add new backoffice client as project * add bootstrap of backoffice client * experimentally allow CORS from local vite app running the backoffice * fix base path * move new backoffice projects to NewBackoffice folder * add support for redirect urls to login page (temporary) * update references to v13 * override databaseinstall/index.cshtml * copy ignore lines from normal project * remove redirect to AuthorizeUpgrade * codeql: checkout submodules * Section catch-all route * fixed tests * remove starter-kit file * remove grid views Co-authored-by: Bjarke Berg --- .github/workflows/codeql-analysis.yml | 1 + .gitmodules | 3 + .../Umbraco.Cms.StaticAssets.csproj | 8 +- .../Controllers/BackOfficeController.cs | 15 +--- .../Routing/BackOfficeAreaRoutes.cs | 15 ++++ src/Umbraco.Web.UI.New.Client | 1 + src/Umbraco.Web.UI.New/.gitignore | 12 +++ .../BackOfficeLoginController.cs | 14 +++ .../ControllersAsServicesComposer.cs | 65 ++++++++++++++ src/Umbraco.Web.UI.New/Program.cs | 19 ++++ .../Properties/launchSettings.json | 28 ++++++ src/Umbraco.Web.UI.New/Startup.cs | 81 ++++++++++++++++++ .../Umbraco.Web.UI.New.csproj | 44 ++++++++++ .../Views/Partials/blocklist/default.cshtml | 13 +++ .../appsettings.Development.template.json | 47 ++++++++++ .../appsettings.template.json | 74 ++++++++++++++++ .../umbraco/UmbracoBackOffice/Default.cshtml | 16 ++++ .../umbraco/UmbracoInstall/Index.cshtml | 16 ++++ .../umbraco/UmbracoLogin/Index.cshtml | 54 ++++++++++++ src/Umbraco.Web.UI.New/wwwroot/favicon.ico | Bin 0 -> 15406 bytes .../Routing/BackOfficeAreaRoutesTests.cs | 4 +- umbraco.sln | 32 +++++++ 22 files changed, 542 insertions(+), 20 deletions(-) create mode 100644 .gitmodules create mode 160000 src/Umbraco.Web.UI.New.Client create mode 100644 src/Umbraco.Web.UI.New/.gitignore create mode 100644 src/Umbraco.Web.UI.New/BackOfficeLoginController.cs create mode 100644 src/Umbraco.Web.UI.New/Composers/ControllersAsServicesComposer.cs create mode 100644 src/Umbraco.Web.UI.New/Program.cs create mode 100644 src/Umbraco.Web.UI.New/Properties/launchSettings.json create mode 100644 src/Umbraco.Web.UI.New/Startup.cs create mode 100644 src/Umbraco.Web.UI.New/Umbraco.Web.UI.New.csproj create mode 100644 src/Umbraco.Web.UI.New/Views/Partials/blocklist/default.cshtml create mode 100644 src/Umbraco.Web.UI.New/appsettings.Development.template.json create mode 100644 src/Umbraco.Web.UI.New/appsettings.template.json create mode 100644 src/Umbraco.Web.UI.New/umbraco/UmbracoBackOffice/Default.cshtml create mode 100644 src/Umbraco.Web.UI.New/umbraco/UmbracoInstall/Index.cshtml create mode 100644 src/Umbraco.Web.UI.New/umbraco/UmbracoLogin/Index.cshtml create mode 100644 src/Umbraco.Web.UI.New/wwwroot/favicon.ico diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 86d160aef3..a831fba4d4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,6 +37,7 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 + submodules: true # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..2106b81d2f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/Umbraco.Web.UI.New.Client"] + path = src/Umbraco.Web.UI.New.Client + url = https://github.com/umbraco/Umbraco.CMS.Backoffice.git diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index 652ab3417e..d082dff945 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -25,10 +25,10 @@ - + - - + + @@ -37,7 +37,7 @@ - + diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index bd63b51711..bd40d300c2 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -118,19 +118,6 @@ public class BackOfficeController : UmbracoController [AllowAnonymous] public async Task Default() { - // Check if we not are in an run state, if so we need to redirect - if (_runtimeState.Level != RuntimeLevel.Run) - { - if (_runtimeState.Level == RuntimeLevel.Upgrade) - { - return RedirectToAction(nameof(AuthorizeUpgrade), routeValues: new RouteValueDictionary() - { - ["redir"] = _globalSettings.GetBackOfficePath(_hostingEnvironment), - }); - } - return Redirect("/"); - } - // force authentication to occur since this is not an authorized endpoint AuthenticateResult result = await this.AuthenticateBackOfficeAsync(); @@ -227,7 +214,7 @@ public class BackOfficeController : UmbracoController return new LocalRedirectResult(installerUrl); } - var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(AuthorizeUpgrade) + ".cshtml"); + var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(Default) + ".cshtml"); return await RenderDefaultOrProcessExternalLoginAsync( result, diff --git a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs index 1b95e836dc..24a90fa0f1 100644 --- a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs +++ b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs @@ -1,3 +1,6 @@ +using System.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; @@ -42,6 +45,8 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes /// public void CreateRoutes(IEndpointRouteBuilder endpoints) { + + switch (_runtimeState.Level) { case RuntimeLevel.Install: @@ -75,6 +80,16 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes new { action = @"[a-zA-Z]*", id = @"[a-zA-Z]*" }); endpoints.MapUmbracoApiRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeApiArea, true, string.Empty); + + endpoints.MapAreaControllerRoute( + "catch-all-sections-to-client", + Constants.Web.Mvc.BackOfficeArea, + new StringBuilder(_umbracoPathSegment).Append("/section/{**slug}").ToString(), + new + { + Controller = ControllerExtensions.GetControllerName(), + Action = nameof(BackOfficeController.Default) + }); } /// diff --git a/src/Umbraco.Web.UI.New.Client b/src/Umbraco.Web.UI.New.Client new file mode 160000 index 0000000000..99258d1e00 --- /dev/null +++ b/src/Umbraco.Web.UI.New.Client @@ -0,0 +1 @@ +Subproject commit 99258d1e00306838f13a7428bf6906409413cdc6 diff --git a/src/Umbraco.Web.UI.New/.gitignore b/src/Umbraco.Web.UI.New/.gitignore new file mode 100644 index 0000000000..4f24056a10 --- /dev/null +++ b/src/Umbraco.Web.UI.New/.gitignore @@ -0,0 +1,12 @@ +umbraco/[Dd]ata/ +umbraco/[Ll]ogs/ +umbraco/[Mm]odels/ +Views/ +!Views/Partials/blocklist/ +!Views/Partials/grid/ +!Views/_ViewImports.cshtml +appsettings.json +appsettings.Development.json +appsettings.Local.json +appsettings-schema.json +appsettings-schema.*.json diff --git a/src/Umbraco.Web.UI.New/BackOfficeLoginController.cs b/src/Umbraco.Web.UI.New/BackOfficeLoginController.cs new file mode 100644 index 0000000000..bb2f1d9cfd --- /dev/null +++ b/src/Umbraco.Web.UI.New/BackOfficeLoginController.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Umbraco.Cms.Web.UI; + +[ApiExplorerSettings(IgnoreApi=true)] +[Route("/umbraco/login")] +public class BackOfficeLoginController : Controller +{ + // GET + public IActionResult Index() + { + return View("/umbraco/UmbracoLogin/Index.cshtml"); + } +} diff --git a/src/Umbraco.Web.UI.New/Composers/ControllersAsServicesComposer.cs b/src/Umbraco.Web.UI.New/Composers/ControllersAsServicesComposer.cs new file mode 100644 index 0000000000..d70a2183f4 --- /dev/null +++ b/src/Umbraco.Web.UI.New/Composers/ControllersAsServicesComposer.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Website.Controllers; +using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; + +namespace Umbraco.Cms.Web.UI.Composers +{ + /// + /// Adds controllers to the service collection. + /// + /// + /// + /// Umbraco 9 out of the box, makes use of which doesn't resolve controller + /// instances from the IOC container, instead it resolves the required dependencies of the controller and constructs an instance + /// of the controller. + /// + /// + /// Some users may wish to switch to (perhaps to make use of interception/decoration). + /// + /// + /// This composer exists to help us detect ambiguous constructors in the CMS such that we do not cause unnecessary effort downstream. + /// + /// + /// This Composer is not shipped by the Umbraco.Templates package. + /// + /// + public class ControllersAsServicesComposer : IComposer + { + /// + public void Compose(IUmbracoBuilder builder) => builder.Services + .AddMvc() + .AddControllersAsServicesWithoutChangingActivator(); + } + + internal static class MvcBuilderExtensions + { + /// + /// but without the replacement of + /// . + /// + /// + /// We don't need to opt in to to ensure container validation + /// passes. + /// + public static IMvcBuilder AddControllersAsServicesWithoutChangingActivator(this IMvcBuilder builder) + { + var feature = new ControllerFeature(); + builder.PartManager.PopulateFeature(feature); + + foreach (Type controller in feature.Controllers.Select(c => c.AsType())) + { + builder.Services.TryAddTransient(controller, controller); + } + + builder.Services.AddUnique(x => new RenderNoContentController(x.GetService()!, x.GetService>()!, x.GetService()!)); + return builder; + } + } +} diff --git a/src/Umbraco.Web.UI.New/Program.cs b/src/Umbraco.Web.UI.New/Program.cs new file mode 100644 index 0000000000..cebb7b0370 --- /dev/null +++ b/src/Umbraco.Web.UI.New/Program.cs @@ -0,0 +1,19 @@ +namespace Umbraco.Cms.Web.UI +{ + public class Program + { + public static void Main(string[] args) + => CreateHostBuilder(args) + .Build() + .Run(); + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureUmbracoDefaults() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStaticWebAssets(); + webBuilder.UseStartup(); + }); + } +} diff --git a/src/Umbraco.Web.UI.New/Properties/launchSettings.json b/src/Umbraco.Web.UI.New/Properties/launchSettings.json new file mode 100644 index 0000000000..2631298a10 --- /dev/null +++ b/src/Umbraco.Web.UI.New/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:11000", + "sslPort": 44339 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Umbraco.Web.UI": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:44339;http://localhost:11000" + } + } +} diff --git a/src/Umbraco.Web.UI.New/Startup.cs b/src/Umbraco.Web.UI.New/Startup.cs new file mode 100644 index 0000000000..973e0279c5 --- /dev/null +++ b/src/Umbraco.Web.UI.New/Startup.cs @@ -0,0 +1,81 @@ +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.UI +{ + public class Startup + { + private readonly IWebHostEnvironment _env; + private readonly IConfiguration _config; + + /// + /// Initializes a new instance of the class. + /// + /// The web hosting environment. + /// The configuration. + /// + /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337. + /// + public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) + { + _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); + _config = config ?? throw new ArgumentNullException(nameof(config)); + } + + /// + /// Configures the services. + /// + /// The services. + /// + /// This method gets called by the runtime. Use this method to add services to the container. + /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940. + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddUmbraco(_env, _config) + .AddBackOffice() + .AddWebsite() + .AddComposers() + .Build(); + } + + /// + /// Configures the application. + /// + /// The application builder. + /// The web hosting environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + + Console.WriteLine(@"Enabling CORS in development mode"); + app.UseCors(u => + { + u.AllowCredentials(); + u.AllowAnyMethod(); + u.AllowAnyHeader(); + u.WithOrigins(new[] { "http://127.0.0.1:5173", "http://localhost:5173" }); + }); + } +#if (UseHttpsRedirect) + + app.UseHttpsRedirection(); +#endif + + app.UseUmbraco() + .WithMiddleware(u => + { + u.UseBackOffice(); + u.UseWebsite(); + }) + .WithEndpoints(u => + { + u.UseInstallerEndpoints(); + u.UseBackOfficeEndpoints(); + u.UseWebsiteEndpoints(); + }); + } + } +} diff --git a/src/Umbraco.Web.UI.New/Umbraco.Web.UI.New.csproj b/src/Umbraco.Web.UI.New/Umbraco.Web.UI.New.csproj new file mode 100644 index 0000000000..8f51af614a --- /dev/null +++ b/src/Umbraco.Web.UI.New/Umbraco.Web.UI.New.csproj @@ -0,0 +1,44 @@ + + + Umbraco.Cms.Web.UI.New + false + + + + + + + + + + + + + + + + + + true + + + + + false + false + + + + <_ContentIncludedByDefault Remove="umbraco\UmbracoInstall\Index.cshtml" /> + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.New/Views/Partials/blocklist/default.cshtml b/src/Umbraco.Web.UI.New/Views/Partials/blocklist/default.cshtml new file mode 100644 index 0000000000..accca2ef37 --- /dev/null +++ b/src/Umbraco.Web.UI.New/Views/Partials/blocklist/default.cshtml @@ -0,0 +1,13 @@ +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@{ + if (Model?.Any() != true) { return; } +} +
+ @foreach (var block in Model) + { + if (block?.ContentUdi == null) { continue; } + var data = block.Content; + + @await Html.PartialAsync("blocklist/Components/" + data.ContentType.Alias, block) + } +
diff --git a/src/Umbraco.Web.UI.New/appsettings.Development.template.json b/src/Umbraco.Web.UI.New/appsettings.Development.template.json new file mode 100644 index 0000000000..b2bde5bf2d --- /dev/null +++ b/src/Umbraco.Web.UI.New/appsettings.Development.template.json @@ -0,0 +1,47 @@ +{ + "$schema" : "./appsettings-schema.json", + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Examine.Lucene.Providers.LuceneIndex": "Debug", + "Examine.BaseIndexProvider": "Debug", + "Examine.Lucene.LoggingReplicationClient": "Debug", + "Examine.Lucene.ExamineReplicator": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "Console" + } + ] + } + } + ] + }, + "Umbraco": { + "CMS": { + "Examine": { + "LuceneDirectoryFactory": "TempFileSystemDirectoryFactory" + }, + "Global": { + "Smtp": { + //"From": "your@email.here", + //"Host": "localhost", + // "Port": "25" + } + }, + "Hosting": { + "Debug": true + }, + "RuntimeMinification": { + "useInMemoryCache": true, + "cacheBuster": "Timestamp" + } + } + } +} diff --git a/src/Umbraco.Web.UI.New/appsettings.template.json b/src/Umbraco.Web.UI.New/appsettings.template.json new file mode 100644 index 0000000000..b7aa07acf8 --- /dev/null +++ b/src/Umbraco.Web.UI.New/appsettings.template.json @@ -0,0 +1,74 @@ +{ + "$schema": "./appsettings-schema.json", + "ConnectionStrings": { + "umbracoDbDSN": "" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + } + }, + "Umbraco": { + "CMS": { + "Content": { + "Notifications": { + "Email": "your@email.here" + }, + "MacroErrors": "Throw" + }, + "Global": { + "DefaultUILanguage": "en-us", + "HideTopLevelNodeFromPath": true, + "TimeOutInMinutes": 20, + "UseHttps": false + }, + "Hosting": { + "Debug": false + }, + "KeepAlive": { + "DisableKeepAliveTask": false, + "KeepAlivePingUrl": "~/api/keepalive/ping" + }, + "RequestHandler": { + "ConvertUrlsToAscii": "try" + }, + "RuntimeMinification": { + "dataFolder": "umbraco/Data/TEMP/Smidge", + "version": "637642136775050602" + }, + "Security": { + "KeepUserLoggedIn": false, + "UsernameIsEmail": true, + "HideDisabledUsersInBackoffice": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\", + "UserPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 + }, + "MemberPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 + } + }, + "Tours": { + "EnableTours": true + }, + "ModelsBuilder": { + "ModelsMode": "InMemoryAuto" + } + } + } +} diff --git a/src/Umbraco.Web.UI.New/umbraco/UmbracoBackOffice/Default.cshtml b/src/Umbraco.Web.UI.New/umbraco/UmbracoBackOffice/Default.cshtml new file mode 100644 index 0000000000..101cbcbaa8 --- /dev/null +++ b/src/Umbraco.Web.UI.New/umbraco/UmbracoBackOffice/Default.cshtml @@ -0,0 +1,16 @@ + + + + + + + + Umbraco + + + + + + + + diff --git a/src/Umbraco.Web.UI.New/umbraco/UmbracoInstall/Index.cshtml b/src/Umbraco.Web.UI.New/umbraco/UmbracoInstall/Index.cshtml new file mode 100644 index 0000000000..101cbcbaa8 --- /dev/null +++ b/src/Umbraco.Web.UI.New/umbraco/UmbracoInstall/Index.cshtml @@ -0,0 +1,16 @@ + + + + + + + + Umbraco + + + + + + + + diff --git a/src/Umbraco.Web.UI.New/umbraco/UmbracoLogin/Index.cshtml b/src/Umbraco.Web.UI.New/umbraco/UmbracoLogin/Index.cshtml new file mode 100644 index 0000000000..da0b17499e --- /dev/null +++ b/src/Umbraco.Web.UI.New/umbraco/UmbracoLogin/Index.cshtml @@ -0,0 +1,54 @@ +@{ + string returnTo = "/umbraco"; + var redirectToValues = Context.Request.Query["redirectTo"]; + if (redirectToValues.Count > 0) + { + var redirectTo = redirectToValues[0]; + if (redirectTo is not null) + { + returnTo = redirectTo; + } + } +} + + + + Umbraco + + + + + + +
+

Umbraco login universe

+

We have not yet implemented login, so for your pleasure, we present you a login button that just works™

+ +
+ + + + diff --git a/src/Umbraco.Web.UI.New/wwwroot/favicon.ico b/src/Umbraco.Web.UI.New/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0749ddf7f1b68606b0672aa8d709779d6b0817a GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs index 6966c09566..946b6a14d1 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs @@ -47,11 +47,11 @@ public class BackOfficeAreaRoutesTests Assert.AreEqual(1, endpoints.DataSources.Count); var route = endpoints.DataSources.First(); - Assert.AreEqual(3, route.Endpoints.Count); + Assert.AreEqual(4, route.Endpoints.Count); AssertMinimalBackOfficeRoutes(route); - var endpoint4 = (RouteEndpoint)route.Endpoints[2]; + var endpoint4 = (RouteEndpoint)route.Endpoints[3]; var apiControllerName = ControllerExtensions.GetControllerName(); Assert.AreEqual( $"umbraco/backoffice/api/{apiControllerName.ToLowerInvariant()}/{{action}}/{{id?}}", diff --git a/umbraco.sln b/umbraco.sln index 7cbd12cee6..804983d0a7 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -34,6 +34,29 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "ht StartServerOnDebug = "false" EndProjectSection EndProject +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.New.Client", "http://localhost:3961", "{17E2067E-9D0F-4891-B6BA-CD828886B968}" + ProjectSection(WebsiteProperties) = preProject + UseIISExpress = "true" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.5.2" + Debug.AspNetCompiler.VirtualPath = "/localhost_3961" + Debug.AspNetCompiler.PhysicalPath = "src\Umbraco.Web.UI.New.Client\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3961\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/localhost_3961" + Release.AspNetCompiler.PhysicalPath = "src\Umbraco.Web.UI.New.Client\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3961\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + SlnRelativePath = "src\Umbraco.Web.UI.New.Client\" + DefaultWebSiteLanguage = "Visual C#" + StartServerOnDebug = "false" + EndProjectSection +EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Tests.AcceptanceTest", "http://localhost:58896", "{9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}" ProjectSection(WebsiteProperties) = preProject UseIISExpress = "true" @@ -158,6 +181,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Imaging.ImageSh EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{05878304-40EB-4F84-B40B-91BDB70DE094}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI.New", "src\Umbraco.Web.UI.New\Umbraco.Web.UI.New.csproj", "{C55CA725-9F4E-4618-9435-6B8AE05DA14D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Api.Common", "src\Umbraco.Cms.Api.Common\Umbraco.Cms.Api.Common.csproj", "{D48B5D6B-82FF-4235-986C-CDE646F41DEC}" EndProject Global @@ -318,6 +342,12 @@ Global {C280181E-597B-4AA5-82E7-D7017E928749}.Release|Any CPU.Build.0 = Release|Any CPU {C280181E-597B-4AA5-82E7-D7017E928749}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU {C280181E-597B-4AA5-82E7-D7017E928749}.SkipTests|Any CPU.Build.0 = Debug|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.Release|Any CPU.Build.0 = Release|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU + {C55CA725-9F4E-4618-9435-6B8AE05DA14D}.SkipTests|Any CPU.Build.0 = Debug|Any CPU {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -346,6 +376,8 @@ Global {2B47AD9F-FFF1-448A-88F1-D4F568811738} = {F2BF84D9-0A14-40AF-A0F3-B9BBBBC16A44} {25AECCB5-B187-4406-844B-91B8FF0FCB37} = {2B47AD9F-FFF1-448A-88F1-D4F568811738} {EA628ABD-624E-4AF3-B548-6710D4D66531} = {2B47AD9F-FFF1-448A-88F1-D4F568811738} + {17E2067E-9D0F-4891-B6BA-CD828886B968} = {995D9EFA-8BB1-4333-80AD-C525A06FD984} + {C55CA725-9F4E-4618-9435-6B8AE05DA14D} = {995D9EFA-8BB1-4333-80AD-C525A06FD984} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}