From 558bde8596e95a0d6766e69d5ab7e158e674ac4b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 20 May 2020 16:43:06 +1000 Subject: [PATCH] Migrates back office variables and a couple methods for the BackOfficeController --- src/Umbraco.Core/Editors/BackOfficeModel.cs | 4 +- .../Editors/BackOfficePreviewModel.cs | 4 +- src/Umbraco.Core/EmailSender.cs | 2 +- src/Umbraco.Core/Features/DisabledFeatures.cs | 2 +- src/Umbraco.Core/Features/EnabledFeatures.cs | 2 +- src/Umbraco.Core/Features/UmbracoFeatures.cs | 4 +- src/Umbraco.Core/Trees/CoreTreeAttribute.cs | 2 +- .../Controllers/BackOfficeController.cs | 31 +- .../Controllers/BackOfficeServerVariables.cs | 502 ++++++++++++++++++ .../Controllers/PreviewController.cs | 5 +- .../Runtime/BackOfficeComposer.cs | 2 + .../Editors/BackOfficeController.cs | 39 +- .../Editors/BackOfficeServerVariables.cs | 3 - 13 files changed, 558 insertions(+), 44 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs diff --git a/src/Umbraco.Core/Editors/BackOfficeModel.cs b/src/Umbraco.Core/Editors/BackOfficeModel.cs index 779e290aad..be91654d9e 100644 --- a/src/Umbraco.Core/Editors/BackOfficeModel.cs +++ b/src/Umbraco.Core/Editors/BackOfficeModel.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Editors public class BackOfficeModel { public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, - IContentSettings contentSettings, TreeCollection treeCollection, + IContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings) { @@ -19,7 +19,6 @@ namespace Umbraco.Web.Editors GlobalSettings = globalSettings; UmbracoVersion = umbracoVersion; ContentSettings = contentSettings; - TreeCollection = treeCollection; HostingEnvironment = hostingEnvironment; RuntimeSettings = runtimeSettings; SecuritySettings = securitySettings; @@ -30,7 +29,6 @@ namespace Umbraco.Web.Editors public IGlobalSettings GlobalSettings { get; } public IUmbracoVersion UmbracoVersion { get; } public IContentSettings ContentSettings { get; } - public TreeCollection TreeCollection { get; } public IHostingEnvironment HostingEnvironment { get; } public IRuntimeSettings RuntimeSettings { get; set; } public ISecuritySettings SecuritySettings { get; set; } diff --git a/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs index db37bba9ce..b18896295a 100644 --- a/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs @@ -14,8 +14,8 @@ namespace Umbraco.Web.Editors private readonly UmbracoFeatures _features; public IEnumerable Languages { get; } - public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable languages, IContentSettings contentSettings, TreeCollection treeCollection, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings) - : base(features, globalSettings, umbracoVersion, contentSettings, treeCollection, hostingEnvironment, runtimeSettings, securitySettings) + public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable languages, IContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings) + : base(features, globalSettings, umbracoVersion, contentSettings, hostingEnvironment, runtimeSettings, securitySettings) { _features = features; Languages = languages; diff --git a/src/Umbraco.Core/EmailSender.cs b/src/Umbraco.Core/EmailSender.cs index 5cfdd765bc..7b07a4bdf7 100644 --- a/src/Umbraco.Core/EmailSender.cs +++ b/src/Umbraco.Core/EmailSender.cs @@ -83,7 +83,7 @@ namespace Umbraco.Core /// /// We assume this is possible if either an event handler is registered or an smtp server is configured /// - internal static bool CanSendRequiredEmail(IGlobalSettings globalSettings) => EventHandlerRegistered || globalSettings.IsSmtpServerConfigured; + public static bool CanSendRequiredEmail(IGlobalSettings globalSettings) => EventHandlerRegistered || globalSettings.IsSmtpServerConfigured; /// /// returns true if an event handler has been registered diff --git a/src/Umbraco.Core/Features/DisabledFeatures.cs b/src/Umbraco.Core/Features/DisabledFeatures.cs index 9bd091a570..1b54691365 100644 --- a/src/Umbraco.Core/Features/DisabledFeatures.cs +++ b/src/Umbraco.Core/Features/DisabledFeatures.cs @@ -5,7 +5,7 @@ namespace Umbraco.Web.Features /// /// Represents disabled features. /// - internal class DisabledFeatures + public class DisabledFeatures { /// /// Initializes a new instance of the class. diff --git a/src/Umbraco.Core/Features/EnabledFeatures.cs b/src/Umbraco.Core/Features/EnabledFeatures.cs index 7e734f6a84..fe9c496298 100644 --- a/src/Umbraco.Core/Features/EnabledFeatures.cs +++ b/src/Umbraco.Core/Features/EnabledFeatures.cs @@ -3,7 +3,7 @@ namespace Umbraco.Web.Features /// /// Represents enabled features. /// - internal class EnabledFeatures + public class EnabledFeatures { /// /// This allows us to inject a razor view into the Umbraco preview view to extend it diff --git a/src/Umbraco.Core/Features/UmbracoFeatures.cs b/src/Umbraco.Core/Features/UmbracoFeatures.cs index c25c98fbb7..1dacf01494 100644 --- a/src/Umbraco.Core/Features/UmbracoFeatures.cs +++ b/src/Umbraco.Core/Features/UmbracoFeatures.cs @@ -19,12 +19,12 @@ namespace Umbraco.Web.Features /// /// Gets the disabled features. /// - internal DisabledFeatures Disabled { get; } + public DisabledFeatures Disabled { get; } /// /// Gets the enabled features. /// - internal EnabledFeatures Enabled { get; } + public EnabledFeatures Enabled { get; } /// /// Determines whether a controller is enabled. diff --git a/src/Umbraco.Core/Trees/CoreTreeAttribute.cs b/src/Umbraco.Core/Trees/CoreTreeAttribute.cs index 2d6ffe6a15..c1d8d3726a 100644 --- a/src/Umbraco.Core/Trees/CoreTreeAttribute.cs +++ b/src/Umbraco.Core/Trees/CoreTreeAttribute.cs @@ -9,6 +9,6 @@ namespace Umbraco.Web.Trees /// This ensures that umbraco will look in the umbraco folders for views for this tree. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - internal class CoreTreeAttribute : Attribute + public class CoreTreeAttribute : Attribute { } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 3055ad72d6..e08bb3f632 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.BackOffice; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Hosting; @@ -35,6 +36,8 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILocalizedTextService _textService; private readonly IGridConfig _gridConfig; + private readonly BackOfficeServerVariables _backOfficeServerVariables; + private readonly AppCaches _appCaches; public BackOfficeController( BackOfficeUserManager userManager, @@ -44,7 +47,9 @@ namespace Umbraco.Web.BackOffice.Controllers IUmbracoApplicationLifetime umbracoApplicationLifetime, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, - IGridConfig gridConfig) + IGridConfig gridConfig, + BackOfficeServerVariables backOfficeServerVariables, + AppCaches appCaches) { _userManager = userManager; _runtimeMinifier = runtimeMinifier; @@ -54,6 +59,8 @@ namespace Umbraco.Web.BackOffice.Controllers _umbracoContextAccessor = umbracoContextAccessor; _textService = textService; _gridConfig = gridConfig ?? throw new ArgumentNullException(nameof(gridConfig)); + _backOfficeServerVariables = backOfficeServerVariables; + _appCaches = appCaches; } [HttpGet] @@ -117,13 +124,33 @@ namespace Umbraco.Web.BackOffice.Controllers return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.None }; } - //[UmbracoAuthorize(Order = 0)] TODO: Re-implement UmbracoAuthorizeAttribute + [UmbracoAuthorize(Order = 0)] // TODO: Re-implement UmbracoAuthorizeAttribute [HttpGet] public JsonNetResult GetGridConfig() { return new JsonNetResult { Data = _gridConfig.EditorsConfig.Editors, Formatting = Formatting.None }; } + /// + /// Returns the JavaScript object representing the static server variables javascript object + /// + /// + [UmbracoAuthorize(Order = 0)] + [MinifyJavaScriptResult(Order = 1)] + public async Task ServerVariables() + { + //cache the result if debugging is disabled + var serverVars = ServerVariablesParser.Parse(await _backOfficeServerVariables.GetServerVariablesAsync()); + var result = _hostingEnvironment.IsDebugMode + ? serverVars + : _appCaches.RuntimeCache.GetCacheItem( + typeof(BackOfficeController) + "ServerVariables", + () => serverVars, + new TimeSpan(0, 10, 0)); + + return new JavaScriptResult(result); + } + [HttpGet] public async Task ValidatePasswordResetCode([Bind(Prefix = "u")]int userId, [Bind(Prefix = "r")]string resetCode) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs new file mode 100644 index 0000000000..051193c290 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -0,0 +1,502 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.WebAssets; +using Umbraco.Extensions; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Features; +using Umbraco.Web.Trees; +using Umbraco.Web.WebApi; + +namespace Umbraco.Web.BackOffice.Controllers +{ + /// + /// Used to collect the server variables for use in the back office angular app + /// + public class BackOfficeServerVariables + { + private readonly LinkGenerator _linkGenerator; + private readonly IRuntimeState _runtimeState; + private readonly UmbracoFeatures _features; + private readonly IGlobalSettings _globalSettings; + private readonly IUmbracoVersion _umbracoVersion; + private readonly IContentSettings _contentSettings; + //private readonly TreeCollection _treeCollection; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IRuntimeSettings _settings; + private readonly ISecuritySettings _securitySettings; + private readonly IRuntimeMinifier _runtimeMinifier; + private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider; + private readonly UmbracoApiControllerTypeCollection _apiControllers; + + public BackOfficeServerVariables( + LinkGenerator linkGenerator, + IRuntimeState runtimeState, + UmbracoFeatures features, + IGlobalSettings globalSettings, + IUmbracoVersion umbracoVersion, + IContentSettings contentSettings, + //TreeCollection treeCollection, // TODO: If we need this we need to migrate trees + IHostingEnvironment hostingEnvironment, + IRuntimeSettings settings, + ISecuritySettings securitySettings, + IRuntimeMinifier runtimeMinifier, + IAuthenticationSchemeProvider authenticationSchemeProvider, + UmbracoApiControllerTypeCollection apiControllers) + { + _linkGenerator = linkGenerator; + _runtimeState = runtimeState; + _features = features; + _globalSettings = globalSettings; + _umbracoVersion = umbracoVersion; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + //_treeCollection = treeCollection ?? throw new ArgumentNullException(nameof(treeCollection)); + _hostingEnvironment = hostingEnvironment; + _settings = settings; + _securitySettings = securitySettings; + _runtimeMinifier = runtimeMinifier; + _authenticationSchemeProvider = authenticationSchemeProvider; + _apiControllers = apiControllers; + } + + /// + /// Returns the server variables for non-authenticated users + /// + /// + internal async Task> BareMinimumServerVariablesAsync() + { + //this is the filter for the keys that we'll keep based on the full version of the server vars + var keepOnlyKeys = new Dictionary + { + {"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl"}}, + {"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "canSendRequiredEmail", "usernameIsEmail"}}, + {"application", new[] {"applicationPath", "cacheBuster"}}, + {"isDebuggingEnabled", new string[] { }}, + {"features", new [] {"disabledFeatures"}} + }; + //now do the filtering... + var defaults = await GetServerVariablesAsync(); + foreach (var key in defaults.Keys.ToArray()) + { + if (keepOnlyKeys.ContainsKey(key) == false) + { + defaults.Remove(key); + } + else + { + if (defaults[key] is System.Collections.IDictionary asDictionary) + { + var toKeep = keepOnlyKeys[key]; + foreach (var k in asDictionary.Keys.Cast().ToArray()) + { + if (toKeep.Contains(k) == false) + { + asDictionary.Remove(k); + } + } + } + } + } + + // TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address + // so based on compat and how things are currently working we need to replace the serverVarsJs one + ((Dictionary)defaults["umbracoUrls"])["serverVarsJs"] = _linkGenerator.GetPathByAction("ServerVariables", "BackOffice"); + + return defaults; + } + + /// + /// Returns the server variables for authenticated users + /// + /// + internal async Task> GetServerVariablesAsync() + { + var globalSettings = _globalSettings; + var defaultVals = new Dictionary + { + { + "umbracoUrls", new Dictionary + { + // TODO: Add 'umbracoApiControllerBaseUrl' which people can use in JS + // to prepend their URL. We could then also use this in our own resources instead of + // having each url defined here explicitly - we can do that in v8! for now + // for umbraco services we'll stick to explicitly defining the endpoints. + + {"externalLoginsUrl", _linkGenerator.GetPathByAction("ExternalLogin", "BackOffice")}, + {"externalLinkLoginsUrl", _linkGenerator.GetPathByAction("LinkLogin", "BackOffice")}, + {"gridConfig", _linkGenerator.GetPathByAction("GetGridConfig", "BackOffice")}, + // TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address + {"serverVarsJs", _linkGenerator.GetPathByAction("Application", "BackOffice")}, + //API URLs + { + "packagesRestApiBaseUrl", Constants.PackageRepository.RestApiBaseUrl + }, + //{ + // "redirectUrlManagementApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetEnableState()) + //}, + //{ + // "tourApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetTours()) + //}, + //{ + // "embedApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetEmbed("", 0, 0)) + //}, + //{ + // "userApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostSaveUser(null)) + //}, + //{ + // "userGroupsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostSaveUserGroup(null)) + //}, + //{ + // "contentApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostSave(null)) + //}, + //{ + // "mediaApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetRootMedia()) + //}, + //{ + // "imagesApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetBigThumbnail("")) + //}, + //{ + // "sectionApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetSections()) + //}, + //{ + // "treeApplicationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetApplicationTrees(null, null, null, TreeUse.None)) + //}, + //{ + // "contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllowedChildren(0)) + //}, + //{ + // "mediaTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllowedChildren(0)) + //}, + //{ + // "macroRenderingApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetMacroParameters(0)) + //}, + //{ + // "macroApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.Create(null)) + //}, + //{ + // "authenticationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostLogin(null)) + //}, + //{ + // "currentUserApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostChangePassword(null)) + //}, + //{ + // "entityApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetById(0, UmbracoEntityTypes.Media)) + //}, + //{ + // "dataTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetById(0)) + //}, + //{ + // "dashboardApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetDashboard(null)) + //}, + //{ + // "logApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetPagedEntityLog(0, 0, 0, Direction.Ascending, null)) + //}, + //{ + // "memberApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetByKey(Guid.Empty)) + //}, + //{ + // "packageInstallApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.Fetch(string.Empty)) + //}, + //{ + // "packageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetCreatedPackages()) + //}, + //{ + // "relationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetById(0)) + //}, + //{ + // "rteApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetConfiguration()) + //}, + //{ + // "stylesheetApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAll()) + //}, + //{ + // "memberTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllTypes()) + //}, + //{ + // "memberGroupApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllGroups()) + //}, + //{ + // "updateCheckApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetCheck()) + //}, + //{ + // "templateApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetById(0)) + //}, + //{ + // "memberTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetNodes("-1", null)) + //}, + //{ + // "mediaTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetNodes("-1", null)) + //}, + //{ + // "contentTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetNodes("-1", null)) + //}, + //{ + // "tagsDataBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetTags("", "", null)) + //}, + //{ + // "examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetIndexerDetails()) + //}, + //{ + // "healthCheckBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllHealthChecks()) + //}, + //{ + // "templateQueryApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.PostTemplateQuery(null)) + //}, + //{ + // "codeFileApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetByPath("", "")) + //}, + //{ + // "publishedStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetPublishedStatusUrl()) + //}, + //{ + // "dictionaryApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.DeleteById(int.MaxValue)) + //}, + //{ + // "publishedSnapshotCacheStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetStatus()) + //}, + //{ + // "helpApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetContextHelpForPage("","","")) + //}, + //{ + // "backOfficeAssetsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetSupportedLocales()) + //}, + //{ + // "languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetAllLanguages()) + //}, + //{ + // "relationTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetById(1)) + //}, + //{ + // "logViewerApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetNumberOfErrors(null, null)) + //}, + //{ + // "webProfilingBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetStatus()) + //}, + //{ + // "tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.UploadImage()) + //}, + //{ + // "imageUrlGeneratorApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // _apiControllers, controller => controller.GetCropUrl(null, null, null, null, null)) + //}, + } + }, + { + "umbracoSettings", new Dictionary + { + {"umbracoPath", _globalSettings.GetBackOfficePath(_hostingEnvironment)}, + {"mediaPath", _hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath).TrimEnd('/')}, + {"appPluginsPath", _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.AppPlugins).TrimEnd('/')}, + { + "imageFileTypes", + string.Join(",", _contentSettings.ImageFileTypes) + }, + { + "disallowedUploadFiles", + string.Join(",", _contentSettings.DisallowedUploadFiles) + }, + { + "allowedUploadFiles", + string.Join(",", _contentSettings.AllowedUploadFiles) + }, + { + "maxFileSize", + GetMaxRequestLength() + }, + {"keepUserLoggedIn", _securitySettings.KeepUserLoggedIn}, + {"usernameIsEmail", _securitySettings.UsernameIsEmail}, + {"cssPath", _hostingEnvironment.ToAbsolute(globalSettings.UmbracoCssPath).TrimEnd('/')}, + {"allowPasswordReset", _securitySettings.AllowPasswordReset}, + {"loginBackgroundImage", _contentSettings.LoginBackgroundImage}, + {"showUserInvite", EmailSender.CanSendRequiredEmail(globalSettings)}, + {"canSendRequiredEmail", EmailSender.CanSendRequiredEmail(globalSettings)}, + {"showAllowSegmentationForDocumentTypes", false}, + } + }, + { + "umbracoPlugins", new Dictionary + { + // for each tree that is [PluginController], get + // alias -> areaName + // so that routing (route.js) can look for views + { "trees", GetPluginTrees().ToArray() } + } + }, + { + "isDebuggingEnabled", _hostingEnvironment.IsDebugMode + }, + { + "application", GetApplicationState() + }, + { + "externalLogins", new Dictionary + { + { + "providers", (await _authenticationSchemeProvider.GetAllSchemesAsync()) + // TODO: We need to filter only back office enabled schemes. + // Before we used to have a property bag to check, now we don't so need to investigate the easiest/best + // way to do this. We have the type so maybe we check for a marker interface, but maybe there's another way, + // just need to investigate. + //.Where(p => p.Properties.ContainsKey("UmbracoBackOffice")) + .Select(p => new + { + authType = p.Name, caption = p.DisplayName, + // TODO: See above, if we need this property bag in the vars then we'll need to figure something out + // properties = p.Properties + }) + .ToArray() + } + } + }, + { + "features", new Dictionary + { + { + "disabledFeatures", new Dictionary + { + { "disableTemplates", _features.Disabled.DisableTemplates} + } + } + + } + } + }; + return defaultVals; + } + + [DataContract] + private class PluginTree + { + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "packageFolder")] + public string PackageFolder { get; set; } + } + + private IEnumerable GetPluginTrees() + { + // TODO: Use the code below once we migrate trees + return Enumerable.Empty(); + + // used to be (cached) + //var treeTypes = Current.TypeLoader.GetAttributedTreeControllers(); + // + // ie inheriting from TreeController and marked with TreeAttribute + // + // do this instead + // inheriting from TreeControllerBase and marked with TreeAttribute + + //foreach (var tree in _treeCollection) + //{ + // var treeType = tree.TreeControllerType; + + // // exclude anything marked with CoreTreeAttribute + // var coreTree = treeType.GetCustomAttribute(false); + // if (coreTree != null) continue; + + // // exclude anything not marked with PluginControllerAttribute + // var pluginController = treeType.GetCustomAttribute(false); + // if (pluginController == null) continue; + + // yield return new PluginTree { Alias = tree.TreeAlias, PackageFolder = pluginController.AreaName }; + //} + } + + /// + /// Returns the server variables regarding the application state + /// + /// + private Dictionary GetApplicationState() + { + var app = new Dictionary + { + // add versions - see UmbracoVersion for details & differences + + // the complete application version (eg "8.1.2-alpha.25") + { "version", _umbracoVersion.SemanticVersion.ToSemanticString() }, + + // the assembly version (eg "8.0.0") + { "assemblyVersion", _umbracoVersion.AssemblyVersion.ToString() } + }; + + var version = _runtimeState.SemanticVersion.ToSemanticString(); + + //the value is the hash of the version, cdf version and the configured state + app.Add("cacheBuster", $"{version}.{_runtimeState.Level}.{_runtimeMinifier.CacheBuster}".GenerateHash()); + + //useful for dealing with virtual paths on the client side when hosted in virtual directories especially + app.Add("applicationPath", _httpContextAccessor.GetRequiredHttpContext().Request.PathBase.ToString().EnsureEndsWith('/')); + + //add the server's GMT time offset in minutes + app.Add("serverTimeOffset", Convert.ToInt32(DateTimeOffset.Now.Offset.TotalMinutes)); + + return app; + } + + private string GetMaxRequestLength() + { + return _settings.MaxRequestLength.HasValue ? _settings.MaxRequestLength.Value.ToString() : string.Empty; + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index 3472302a24..9000dfd911 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -32,7 +32,6 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly ILocalizationService _localizationService; private readonly IUmbracoVersion _umbracoVersion; private readonly IContentSettings _contentSettings; - private readonly TreeCollection _treeCollection; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHostingEnvironment _hostingEnvironment; private readonly ICookieManager _cookieManager; @@ -49,7 +48,6 @@ namespace Umbraco.Web.BackOffice.Controllers ILocalizationService localizationService, IUmbracoVersion umbracoVersion, IContentSettings contentSettings, - TreeCollection treeCollection, IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, ICookieManager cookieManager, @@ -65,7 +63,6 @@ namespace Umbraco.Web.BackOffice.Controllers _localizationService = localizationService; _umbracoVersion = umbracoVersion; _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); - _treeCollection = treeCollection; _httpContextAccessor = httpContextAccessor; _hostingEnvironment = hostingEnvironment; _cookieManager = cookieManager; @@ -81,7 +78,7 @@ namespace Umbraco.Web.BackOffice.Controllers { var availableLanguages = _localizationService.GetAllLanguages(); - var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings); + var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _hostingEnvironment, _runtimeSettings, _securitySettings); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { diff --git a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs index 33b6fc5f2e..8d1192a3dd 100644 --- a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs +++ b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs @@ -2,6 +2,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Extensions; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Routing; namespace Umbraco.Web.BackOffice.Runtime @@ -11,6 +12,7 @@ namespace Umbraco.Web.BackOffice.Runtime public void Compose(Composition composition) { composition.RegisterUnique(); + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 64b2f70e42..ad05f404b3 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -98,9 +98,9 @@ namespace Umbraco.Web.Editors { return await RenderDefaultOrProcessExternalLoginAsync( () => - View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings)), + View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _hostingEnvironment, _runtimeSettings, _securitySettings)), () => - View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings)) + View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _hostingEnvironment, _runtimeSettings, _securitySettings)) ); } @@ -178,7 +178,7 @@ namespace Umbraco.Web.Editors { return await RenderDefaultOrProcessExternalLoginAsync( //The default view to render when there is no external login info or errors - () => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings)), + () => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _hostingEnvironment, _runtimeSettings, _securitySettings)), //The ActionResult to perform if external login is successful () => Redirect("/")); } @@ -270,7 +270,9 @@ namespace Umbraco.Web.Editors } - + // TODO: for converting to netcore, some examples: + // * https://github.com/dotnet/aspnetcore/blob/master/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs + // * https://github.com/dotnet/aspnetcore/blob/master/src/MusicStore/samples/MusicStore/Controllers/AccountController.cs [HttpPost] public ActionResult ExternalLogin(string provider, string redirectUrl = null) { @@ -283,6 +285,9 @@ namespace Umbraco.Web.Editors return new ChallengeResult(provider, redirectUrl); } + // TODO: for converting to netcore, some examples: + // * https://github.com/dotnet/aspnetcore/blob/master/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs + // * https://github.com/dotnet/aspnetcore/blob/master/src/MusicStore/samples/MusicStore/Controllers/AccountController.cs [UmbracoAuthorize] [HttpPost] public ActionResult LinkLogin(string provider) @@ -293,26 +298,9 @@ namespace Umbraco.Web.Editors User.Identity.GetUserId()); } - //[HttpGet] - //public async Task ValidatePasswordResetCode([Bind(Prefix = "u")]int userId, [Bind(Prefix = "r")]string resetCode) - //{ - // var user = await UserManager.FindByIdAsync(userId.ToString()); - // if (user != null) - // { - // var result = await UserManager.VerifyUserTokenAsync(user, "ResetPassword", "ResetPassword", resetCode); - // if (result) - // { - // //Add a flag and redirect for it to be displayed - // TempData[ViewDataExtensions.TokenPasswordResetCode] = new ValidatePasswordResetCodeModel { UserId = userId, ResetCode = resetCode }; - // return RedirectToLocal(Url.Action("Default", "BackOffice")); - // } - // } - - // //Add error and redirect for it to be displayed - // TempData[ViewDataExtensions.TokenPasswordResetCode] = new[] { Services.TextService.Localize("login/resetCodeExpired") }; - // return RedirectToLocal(Url.Action("Default", "BackOffice")); - //} - + // TODO: for converting to netcore, some examples: + // * https://github.com/dotnet/aspnetcore/blob/master/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs + // * https://github.com/dotnet/aspnetcore/blob/master/src/MusicStore/samples/MusicStore/Controllers/AccountController.cs [HttpGet] public async Task ExternalLinkLoginCallback() { @@ -374,6 +362,9 @@ namespace Umbraco.Web.Editors return await ExternalSignInAsync(loginInfo, externalSignInResponse); } + // TODO: for converting to netcore, some examples: + // * https://github.com/dotnet/aspnetcore/blob/master/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs + // * https://github.com/dotnet/aspnetcore/blob/master/src/MusicStore/samples/MusicStore/Controllers/AccountController.cs private async Task ExternalSignInAsync(ExternalLoginInfo loginInfo, Func response) { if (loginInfo == null) throw new ArgumentNullException("loginInfo"); diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index cf63fc2131..e1122145a2 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Runtime.Serialization; using System.Web; using System.Web.Mvc; -using Microsoft.Owin.Security; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Web.Features; @@ -18,8 +17,6 @@ using Umbraco.Web.Trees; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Runtime; using Umbraco.Core.WebAssets; using Umbraco.Web.Security;