diff --git a/.github/BUILD.md b/.github/BUILD.md index c6e870f396..ad33872423 100644 --- a/.github/BUILD.md +++ b/.github/BUILD.md @@ -43,6 +43,8 @@ If you only see a build.bat-file, you're probably on the wrong branch. If you sw You might run into [Powershell quirks](#powershell-quirks). +If it runs without errors; Hooray! Now you can continue with [the next step](CONTRIBUTING.md#how-do-i-begin) and open the solution and build it. + ### Build Infrastructure The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with: diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cea5859486..0101ac9d16 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -28,7 +28,7 @@ This project and everyone participating in it, is governed by the [our Code of C [Working with the code](#working-with-the-code) * [Building Umbraco from source code](#building-umbraco-from-source-code) * [Working with the source code](#working-with-the-source-code) - * [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened) + * [Making changes after the PR is open](#making-changes-after-the-pr-is-open) * [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions) * [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository) @@ -65,7 +65,7 @@ Great question! The short version goes like this: * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions) * **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/contrib`, create a new branch first. * **Push** - great, now you can push the changes up to your fork on GitHub - * **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress - you can now make use of GitHub's draft pull request status, detailed [here] (https://github.blog/2019-02-14-introducing-draft-pull-requests/)). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go. + * **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress - you can now make use of GitHub's draft pull request status, detailed [here](https://github.blog/2019-02-14-introducing-draft-pull-requests/)). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go. ![Create a pull request](img/createpullrequest.png) diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index c09dc7b9f6..3979720f9b 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -1,4 +1,5 @@ using System.Configuration; +using Umbraco.Configuration; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -10,6 +11,7 @@ namespace Umbraco.Core.Configuration public IHostingSettings HostingSettings { get; } = new HostingSettings(); public ICoreDebug CoreDebug { get; } = new CoreDebug(); + public IMachineKeyConfig MachineKeyConfig { get; } = new MachineKeyConfig(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -27,6 +29,7 @@ namespace Umbraco.Core.Configuration configs.AddPasswordConfigurations(); configs.Add(() => CoreDebug); + configs.Add(() => MachineKeyConfig); configs.Add(() => new ConnectionStrings(ioHelper)); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/MachineKeyConfig.cs b/src/Umbraco.Configuration/MachineKeyConfig.cs new file mode 100644 index 0000000000..4e3401e015 --- /dev/null +++ b/src/Umbraco.Configuration/MachineKeyConfig.cs @@ -0,0 +1,20 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class MachineKeyConfig : IMachineKeyConfig + { + //TODO all the machineKey stuff should be replaced: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/replacing-machinekey?view=aspnetcore-3.1 + + public bool HasMachineKey + { + get + { + var machineKeySection = + ConfigurationManager.GetSection("system.web/machineKey") as ConfigurationSection; + return !(machineKeySection?.ElementInformation?.Source is null); + } + } + } +} diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 0863577487..009e9f38d0 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web.Cache { macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); } - }; + } base.Refresh(json); } diff --git a/src/Umbraco.Core/Collections/TopoGraph.cs b/src/Umbraco.Core/Collections/TopoGraph.cs index b8ded4a458..955a210465 100644 --- a/src/Umbraco.Core/Collections/TopoGraph.cs +++ b/src/Umbraco.Core/Collections/TopoGraph.cs @@ -126,7 +126,7 @@ namespace Umbraco.Core.Collections if (_items.TryGetValue(key, out value)) yield return value; else if (throwOnMissing) - throw new Exception(MissingDependencyError); + throw new Exception($"{MissingDependencyError} Error in type {typeof(TItem).Name}, with key {key}"); } } } diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 41038ea4e9..0d398be83b 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -79,9 +79,12 @@ namespace Umbraco.Core.Composing foreach (var type in types) EnsureType(type, "register"); - // register them + // register them - ensuring that each item is registered with the same lifetime as the collection. + // NOTE: Previously each one was not registered with the same lifetime which would mean that if there + // was a dependency on an individual item, it would resolve a brand new transient instance which isn't what + // we would expect to happen. The same item should be resolved from the container as the collection. foreach (var type in types) - register.Register(type); + register.Register(type, CollectionLifetime); _registeredTypes = types; } diff --git a/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs b/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs new file mode 100644 index 0000000000..35969e668a --- /dev/null +++ b/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface IMachineKeyConfig + { + bool HasMachineKey { get;} + } +} diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ContentDashboard.cs b/src/Umbraco.Core/Dashboards/ContentDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ContentDashboard.cs rename to src/Umbraco.Core/Dashboards/ContentDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ExamineDashboard.cs b/src/Umbraco.Core/Dashboards/ExamineDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ExamineDashboard.cs rename to src/Umbraco.Core/Dashboards/ExamineDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/FormsDashboard.cs b/src/Umbraco.Core/Dashboards/FormsDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/FormsDashboard.cs rename to src/Umbraco.Core/Dashboards/FormsDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/HealthCheckDashboard.cs b/src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/HealthCheckDashboard.cs rename to src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/MediaDashboard.cs b/src/Umbraco.Core/Dashboards/MediaDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/MediaDashboard.cs rename to src/Umbraco.Core/Dashboards/MediaDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/MembersDashboard.cs b/src/Umbraco.Core/Dashboards/MembersDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/MembersDashboard.cs rename to src/Umbraco.Core/Dashboards/MembersDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ProfilerDashboard.cs b/src/Umbraco.Core/Dashboards/ProfilerDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ProfilerDashboard.cs rename to src/Umbraco.Core/Dashboards/ProfilerDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/PublishedStatusDashboard.cs b/src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/PublishedStatusDashboard.cs rename to src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/RedirectUrlDashboard.cs b/src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/RedirectUrlDashboard.cs rename to src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/SettingsDashboards.cs b/src/Umbraco.Core/Dashboards/SettingsDashboards.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/SettingsDashboards.cs rename to src/Umbraco.Core/Dashboards/SettingsDashboards.cs diff --git a/src/Umbraco.Core/HashGenerator.cs b/src/Umbraco.Core/HashGenerator.cs index 62be0dacdd..c7fad08089 100644 --- a/src/Umbraco.Core/HashGenerator.cs +++ b/src/Umbraco.Core/HashGenerator.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core public void AddDateTime(DateTime d) { - _writer.Write(d.Ticks);; + _writer.Write(d.Ticks); } public void AddString(string s) diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index 1662879cf2..41560292c7 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -22,11 +22,6 @@ namespace Umbraco.Core.Hosting string MapPath(string path); string ToAbsolute(string virtualPath, string root); - /// - /// Terminates the current application. The application restarts the next time a request is received for it. - /// - void LazyRestartApplication(); - void RegisterObject(IRegisteredObject registeredObject); void UnregisterObject(IRegisteredObject registeredObject); } diff --git a/src/Umbraco.Core/Manifest/ManifestWatcher.cs b/src/Umbraco.Core/Manifest/ManifestWatcher.cs index 8d81ac3de5..23caac3a1b 100644 --- a/src/Umbraco.Core/Manifest/ManifestWatcher.cs +++ b/src/Umbraco.Core/Manifest/ManifestWatcher.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Net; namespace Umbraco.Core.Manifest { @@ -13,13 +14,13 @@ namespace Umbraco.Core.Manifest private static volatile bool _isRestarting; private readonly ILogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly List _fws = new List(); - public ManifestWatcher(ILogger logger, IHostingEnvironment hostingEnvironment) + public ManifestWatcher(ILogger logger, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _hostingEnvironment = hostingEnvironment; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } public void Start(params string[] packageFolders) @@ -57,7 +58,7 @@ namespace Umbraco.Core.Manifest _isRestarting = true; _logger.Info("Manifest has changed, app pool is restarting ({Path})", e.FullPath); - _hostingEnvironment.LazyRestartApplication(); + _umbracoApplicationLifetime.Restart(); Dispose(); // uh? if the app restarts then this should be disposed anyways? } } diff --git a/src/Umbraco.Core/Models/PartialViewMacroModel.cs b/src/Umbraco.Core/Models/PartialViewMacroModel.cs index 9964877cba..f3272644f4 100644 --- a/src/Umbraco.Core/Models/PartialViewMacroModel.cs +++ b/src/Umbraco.Core/Models/PartialViewMacroModel.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.Models /// /// The model used when rendering Partial View Macros /// - public class PartialViewMacroModel + public class PartialViewMacroModel : IContentModel { public PartialViewMacroModel(IPublishedContent page, diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs new file mode 100644 index 0000000000..10d5b10955 --- /dev/null +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Net +{ + public interface IUmbracoApplicationLifetime + { + /// + /// A value indicating whether the application is restarting after the current request. + /// + bool IsRestarting { get; } + /// + /// Terminates the current application. The application restarts the next time a request is received for it. + /// + void Restart(); + } +} diff --git a/src/Umbraco.Core/Net/IUserAgentProvider.cs b/src/Umbraco.Core/Net/IUserAgentProvider.cs new file mode 100644 index 0000000000..14246ea99e --- /dev/null +++ b/src/Umbraco.Core/Net/IUserAgentProvider.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Net +{ + public interface IUserAgentProvider + { + string GetUserAgent(); + } +} diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 7425f64727..9a976bea41 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Services; namespace Umbraco.Core.Packaging { - public class ConflictingPackageData + public class ConflictingPackageData { private readonly IMacroService _macroService; private readonly IFileService _fileService; @@ -23,7 +23,7 @@ namespace Umbraco.Core.Packaging return stylesheetNodes .Select(n => { - var xElement = n.Element("Name") ?? n.Element("name"); ; + var xElement = n.Element("Name") ?? n.Element("name"); if (xElement == null) throw new FormatException("Missing \"Name\" element"); diff --git a/src/Umbraco.Core/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Packaging/PackageDefinition.cs index 11bf26c579..29a1919a2b 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinition.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Packaging /// /// This is used only for conversions and will not 'get' a PackageDefinition from the repository with a valid ID /// - internal static PackageDefinition FromCompiledPackage(CompiledPackage compiled) + public static PackageDefinition FromCompiledPackage(CompiledPackage compiled) { return new PackageDefinition { diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index a2a30f3a06..2c5b964518 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -90,7 +90,7 @@ namespace Umbraco.Core.Packaging { var packagesXml = EnsureStorage(out _); if (packagesXml?.Root == null) - yield break;; + yield break; foreach (var packageXml in packagesXml.Root.Elements("package")) yield return _parser.ToPackageDefinition(packageXml); @@ -518,7 +518,6 @@ namespace Umbraco.Core.Packaging private XElement GetStylesheetXml(string name, bool includeProperties) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); -; var sts = _fileService.GetStylesheetByName(name); if (sts == null) return null; var stylesheetXml = new XElement("Stylesheet"); diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 82e5c6f171..4c818aa87c 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -51,7 +51,10 @@ namespace Umbraco.Core.Services IEnumerable GetComposedOf(int id); // composition axis IEnumerable GetChildren(int id); + IEnumerable GetChildren(Guid id); + bool HasChildren(int id); + bool HasChildren(Guid id); void Save(TItem item, int userId = Constants.Security.SuperUserId); void Save(IEnumerable items, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs b/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs new file mode 100644 index 0000000000..74e2dd7380 --- /dev/null +++ b/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs @@ -0,0 +1,19 @@ +using System; +using Umbraco.Web; + +namespace Umbraco.Core +{ + public static class UmbracoContextAccessorExtensions + { + public static IUmbracoContext GetRequiredUmbracoContext(this IUmbracoContextAccessor umbracoContextAccessor) + { + if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); + + var umbracoContext = umbracoContextAccessor.UmbracoContext; + + if(umbracoContext is null) throw new InvalidOperationException("UmbracoContext is null"); + + return umbracoContext; + } + } +} diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 099b73ef36..4b76dc3799 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -336,7 +336,7 @@ namespace Umbraco.Core.Xml var child = parent.SelectSingleNode(name); if (child != null) { - child.InnerXml = ""; ; + child.InnerXml = ""; return child; } return AddCDataNode(xd, name, value); diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs index 334183a48c..d4e22bbfde 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; +using Umbraco.Net; namespace Umbraco.Core.Compose { @@ -12,18 +13,18 @@ namespace Umbraco.Core.Compose private readonly IRuntimeState _runtimeState; private readonly ILogger _logger; private readonly IIOHelper _ioHelper; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for // package.manifest chances and restarts the application on any change private ManifestWatcher _mw; - public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment) + public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger, IIOHelper ioHelper, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _runtimeState = runtimeState; _logger = logger; _ioHelper = ioHelper; - _hostingEnvironment = hostingEnvironment; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } public void Initialize() @@ -36,7 +37,7 @@ namespace Umbraco.Core.Compose var appPlugins = _ioHelper.MapPath("~/App_Plugins/"); if (Directory.Exists(appPlugins) == false) return; - _mw = new ManifestWatcher(_logger, _hostingEnvironment); + _mw = new ManifestWatcher(_logger, _umbracoApplicationLifetime); _mw.Start(Directory.GetDirectories(appPlugins)); } diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs similarity index 80% rename from src/Umbraco.Web/Install/InstallHelper.cs rename to src/Umbraco.Infrastructure/Intall/InstallHelper.cs index f7344d0b0b..a5b4f71d8e 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Cookie; @@ -11,9 +10,9 @@ using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Serialization; using Umbraco.Core.Services; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install @@ -22,25 +21,28 @@ namespace Umbraco.Web.Install { private static HttpClient _httpClient; private readonly DatabaseBuilder _databaseBuilder; - private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; private readonly IUmbracoVersion _umbracoVersion; private readonly IConnectionStrings _connectionStrings; private readonly IInstallationService _installationService; private readonly ICookieManager _cookieManager; + private readonly IUserAgentProvider _userAgentProvider; + private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; + private readonly IJsonSerializer _jsonSerializer; private InstallationType? _installationType; - public InstallHelper(IHttpContextAccessor httpContextAccessor, - DatabaseBuilder databaseBuilder, + public InstallHelper(DatabaseBuilder databaseBuilder, ILogger logger, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IConnectionStrings connectionStrings, IInstallationService installationService, - ICookieManager cookieManager) + ICookieManager cookieManager, + IUserAgentProvider userAgentProvider, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IJsonSerializer jsonSerializer) { - _httpContextAccessor = httpContextAccessor; _logger = logger; _globalSettings = globalSettings; _umbracoVersion = umbracoVersion; @@ -48,6 +50,9 @@ namespace Umbraco.Web.Install _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); _installationService = installationService; _cookieManager = cookieManager; + _userAgentProvider = userAgentProvider; + _umbracoDatabaseFactory = umbracoDatabaseFactory; + _jsonSerializer = jsonSerializer; } public InstallationType GetInstallationType() @@ -57,11 +62,9 @@ namespace Umbraco.Web.Install public async Task InstallStatus(bool isCompleted, string errorMsg) { - - var httpContext = _httpContextAccessor.GetRequiredHttpContext(); try { - var userAgent = httpContext.Request.UserAgent; + var userAgent = _userAgentProvider.GetUserAgent(); // Check for current install Id var installId = Guid.NewGuid(); @@ -88,7 +91,7 @@ namespace Umbraco.Web.Install { // we don't have DatabaseProvider anymore... doing it differently //dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString(); - dbProvider = GetDbProviderString(Current.SqlContext); + dbProvider = _umbracoDatabaseFactory.SqlContext.SqlSyntax.DbProvider; } var installLog = new InstallLog(installId: installId, isUpgrade: IsBrandNewInstall == false, @@ -105,23 +108,6 @@ namespace Umbraco.Web.Install } } - internal static string GetDbProviderString(ISqlContext sqlContext) - { - var dbProvider = string.Empty; - - // we don't have DatabaseProvider anymore... - //dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString(); - // - // doing it differently - var syntax = sqlContext.SqlSyntax; - if (syntax is SqlCeSyntaxProvider) - dbProvider = "SqlServerCE"; - else if (syntax is SqlServerSyntaxProvider) - dbProvider = (syntax as SqlServerSyntaxProvider).ServerVersion.IsAzure ? "SqlAzure" : "SqlServer"; - - return dbProvider; - } - /// /// Checks if this is a brand new install meaning that there is no configured version and there is no configured database connection /// @@ -162,7 +148,10 @@ namespace Umbraco.Web.Install using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) { var response = _httpClient.SendAsync(request).Result; - packages = response.Content.ReadAsAsync>().Result.ToList(); + + + var json = response.Content.ReadAsStringAsync().Result; + packages = _jsonSerializer.Deserialize>(json).ToList(); } } catch (AggregateException ex) diff --git a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs similarity index 85% rename from src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs index 164c142e0c..fb8201600c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading.Tasks; -using System.Web.Configuration; using System.Xml.Linq; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Security; using Umbraco.Web.Install.Models; @@ -12,13 +12,15 @@ namespace Umbraco.Web.Install.InstallSteps "ConfigureMachineKey", "machinekey", 2, "Updating some security settings...", PerformsAppRestart = true)] - internal class ConfigureMachineKey : InstallSetupStep + public class ConfigureMachineKey : InstallSetupStep { private readonly IIOHelper _ioHelper; + private readonly IMachineKeyConfig _machineKeyConfig; - public ConfigureMachineKey(IIOHelper ioHelper) + public ConfigureMachineKey(IIOHelper ioHelper, IMachineKeyConfig machineKeyConfig) { _ioHelper = ioHelper; + _machineKeyConfig = machineKeyConfig; } public override string View => HasMachineKey() == false ? base.View : ""; @@ -27,10 +29,9 @@ namespace Umbraco.Web.Install.InstallSteps /// Don't display the view or execute if a machine key already exists /// /// - private static bool HasMachineKey() + private bool HasMachineKey() { - var section = (MachineKeySection) WebConfigurationManager.GetSection("system.web/machineKey"); - return section.ElementInformation.Source != null; + return _machineKeyConfig.HasMachineKey; } /// diff --git a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs similarity index 69% rename from src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs index dcf115082f..17043f18a5 100644 --- a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs @@ -1,44 +1,31 @@ using System.Threading.Tasks; -using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Services; -using Umbraco.Web.Cache; -using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web.Security; - namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, - "UmbracoVersion", 50, "Installation is complete!, get ready to be redirected to your new CMS.", + "UmbracoVersion", 50, "Installation is complete! Get ready to be redirected to your new CMS.", PerformsAppRestart = true)] - internal class SetUmbracoVersionStep : InstallSetupStep + public class SetUmbracoVersionStep : InstallSetupStep { - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly InstallHelper _installHelper; private readonly IGlobalSettings _globalSettings; - private readonly IUserService _userService; private readonly IUmbracoVersion _umbracoVersion; - private readonly IIOHelper _ioHelper; - public SetUmbracoVersionStep(IHttpContextAccessor httpContextAccessor, InstallHelper installHelper, IGlobalSettings globalSettings, IUserService userService, IUmbracoVersion umbracoVersion, IIOHelper ioHelper) + public SetUmbracoVersionStep(IUmbracoContextAccessor umbracoContextAccessor, InstallHelper installHelper, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) { - _httpContextAccessor = httpContextAccessor; + _umbracoContextAccessor = umbracoContextAccessor; _installHelper = installHelper; _globalSettings = globalSettings; - _userService = userService; _umbracoVersion = umbracoVersion; - _ioHelper = ioHelper; } public override Task ExecuteAsync(object model) { - var security = new WebSecurity(_httpContextAccessor, _userService, _globalSettings, _ioHelper); - + var security = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; if (security.IsAuthenticated() == false && _globalSettings.ConfigurationStatus.IsNullOrWhiteSpace()) { security.PerformLogin(-1); diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs similarity index 93% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs index 624f5897c7..d8986cacb7 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Web; using Umbraco.Core.Services; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Packaging; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -20,14 +18,16 @@ namespace Umbraco.Web.Install.InstallSteps private readonly InstallHelper _installHelper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IUmbracoVersion _umbracoVersion; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IContentService _contentService; private readonly IPackagingService _packageService; - public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoVersion umbracoVersion) + public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoVersion umbracoVersion, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _installHelper = installHelper; _umbracoContextAccessor = umbracoContextAccessor; _umbracoVersion = umbracoVersion; + _umbracoApplicationLifetime = umbracoApplicationLifetime; _contentService = contentService; _packageService = packageService; } @@ -54,7 +54,7 @@ namespace Umbraco.Web.Install.InstallSteps var (packageFile, packageId) = await DownloadPackageFilesAsync(starterKitId.Value); - UmbracoApplication.Restart(); + _umbracoApplicationLifetime.Restart(); return new InstallSetupResult(new Dictionary { diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs similarity index 83% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs index c9688fed4c..cc269408f4 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs @@ -2,9 +2,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Web; using Umbraco.Core.Services; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -14,13 +13,13 @@ namespace Umbraco.Web.Install.InstallSteps PerformsAppRestart = true)] internal class StarterKitInstallStep : InstallSetupStep { - private readonly IHttpContextAccessor _httContextAccessor; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IPackagingService _packagingService; - public StarterKitInstallStep(IHttpContextAccessor httContextAccessor, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) + public StarterKitInstallStep(IUmbracoApplicationLifetime umbracoApplicationLifetime, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) { - _httContextAccessor = httContextAccessor; + _umbracoApplicationLifetime = umbracoApplicationLifetime; _umbracoContextAccessor = umbracoContextAccessor; _packagingService = packagingService; } @@ -34,7 +33,9 @@ namespace Umbraco.Web.Install.InstallSteps InstallBusinessLogic(packageId); - UmbracoApplication.Restart(_httContextAccessor.GetRequiredHttpContext()); + _umbracoApplicationLifetime.Restart(); + + return Task.FromResult(null); } diff --git a/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs b/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs index 86bd12c15e..1dcae62acd 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.Media.Exif public ExifBitConverter(ByteOrder from, ByteOrder to) : base(from, to) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs b/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs index 63c1ce3365..8889a13e1d 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.Media.Exif public ExifEnumProperty(ExifTag tag, T value) : this(tag, value, false) { - ; + } public override ExifInterOperability Interoperability @@ -210,13 +210,13 @@ namespace Umbraco.Web.Media.Exif public ExifPointSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y) - : base(tag, new ushort[] { x, y }) + : base(tag, new ushort[] {x, y}) { - ; + } } @@ -239,13 +239,13 @@ namespace Umbraco.Web.Media.Exif public ExifCircularSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d) : base(tag, new ushort[] { x, y, d }) { - ; + } } @@ -269,13 +269,13 @@ namespace Umbraco.Web.Media.Exif public ExifRectangularSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h) : base(tag, new ushort[] { x, y, w, h }) { - ; + } } @@ -303,13 +303,13 @@ namespace Umbraco.Web.Media.Exif public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value) : base(tag, value) { - ; + } public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s) : base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) { - ; + } } @@ -331,13 +331,13 @@ namespace Umbraco.Web.Media.Exif public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value) : base(tag, value) { - ; + } public GPSTimeStamp(ExifTag tag, float h, float m, float s) : base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) { - ; + } } diff --git a/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs b/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs index 94b255f4d1..24b3ac74be 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Media.Exif public JFIFVersion(ExifTag tag, ushort value) : base(tag, value) { - ; + } public override string ToString() diff --git a/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs b/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs index a1bc420fe4..78565d2bfa 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs @@ -45,7 +45,7 @@ public JPEGSection(JPEGMarker marker) : this(marker, new byte[0], new byte[0]) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs b/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs index 735358c40a..94cbccfbda 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs @@ -403,37 +403,37 @@ namespace Umbraco.Web.Media.Exif public Fraction32(int numerator, int denominator) : this(numerator, denominator, 0) { - ; + } public Fraction32(int numerator) : this(numerator, (int)1) { - ; + } public Fraction32(Fraction32 f) : this(f.Numerator, f.Denominator, f.Error) { - ; + } public Fraction32(float value) : this((double)value) { - ; + } public Fraction32(double value) : this(FromDouble(value)) { - ; + } public Fraction32(string s) : this(FromString(s)) { - ; + } #endregion @@ -1033,37 +1033,37 @@ namespace Umbraco.Web.Media.Exif public UFraction32(uint numerator, uint denominator) : this(numerator, denominator, 0) { - ; + } public UFraction32(uint numerator) : this(numerator, (uint)1) { - ; + } public UFraction32(UFraction32 f) : this(f.Numerator, f.Denominator, f.Error) { - ; + } public UFraction32(float value) : this((double)value) { - ; + } public UFraction32(double value) : this(FromDouble(value)) { - ; + } public UFraction32(string s) : this(FromString(s)) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs index 06a7f9aeb6..4ab5d386b0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs @@ -36,7 +36,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 if (string.IsNullOrEmpty(dataType.Configuration)) { config.Format = "YYYY-MM-DD"; - }; + } } catch (Exception ex) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs index 6e5dcc6b76..ef5a41b21d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs @@ -158,7 +158,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistUpdatedItem(IMacro entity) { entity.UpdatingEntity(); -; var dto = MacroFactory.BuildDto(entity); Database.Update(dto); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 3db94b026e..c02329aac4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -219,7 +219,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //Save updated entity to db template.UpdateDate = DateTime.Now; - ; var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); Database.Update(dto.NodeDto); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 7ae001bf24..58f9a4b68a 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -77,12 +77,13 @@ namespace Umbraco.Core.Persistence.SqlSyntax string ConvertIntegerToOrderableString { get; } string ConvertDateToOrderableString { get; } string ConvertDecimalToOrderableString { get; } - + /// /// Returns the default isolation level for the database /// IsolationLevel DefaultIsolationLevel { get; } + string DbProvider { get; } IEnumerable GetTablesInSchema(IDatabase db); IEnumerable GetColumnsInSchema(IDatabase db); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index a478c88412..8d3a41e5bf 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -175,6 +175,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(x => new Tuple(x.TableName, x.ColumnName, x.Name, x.Definition)); } + public override string DbProvider => ServerVersion.IsAzure ? "SqlAzure" : "SqlServer"; + public override IEnumerable GetTablesInSchema(IDatabase db) { var items = db.Fetch("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = (SELECT SCHEMA_NAME())"); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index b2e03df96e..3df97c0b4f 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -202,6 +202,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } public abstract IsolationLevel DefaultIsolationLevel { get; } + public abstract string DbProvider { get; } public virtual IEnumerable GetTablesInSchema(IDatabase db) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 64e4312b3f..0043eeed72 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -48,7 +48,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters value = new ImageCropperValue { Src = sourceString }; } - value.ApplyConfiguration(propertyType.DataType.ConfigurationAs()); + value?.ApplyConfiguration(propertyType.DataType.ConfigurationAs()); return value; } diff --git a/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs b/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs index 2ed0fb878c..7abcc60c86 100644 --- a/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs @@ -55,6 +55,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.RepeatableRead; + public override string DbProvider => "SqlServerCE"; public override string FormatColumnRename(string tableName, string oldName, string newName) { diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 68b7ee596a..3ce60aa2b5 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -1309,7 +1309,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var member = args.Entity; // refresh the edited data - OnRepositoryRefreshed(db, member, true); + OnRepositoryRefreshed(db, member, false); } private void OnRepositoryRefreshed(IUmbracoDatabase db, IContentBase content, bool published) diff --git a/src/Umbraco.TestData/UmbracoTestDataController.cs b/src/Umbraco.TestData/UmbracoTestDataController.cs index 522d4f6a40..dad9e72c73 100644 --- a/src/Umbraco.TestData/UmbracoTestDataController.cs +++ b/src/Umbraco.TestData/UmbracoTestDataController.cs @@ -223,7 +223,7 @@ namespace Umbraco.TestData { var content = Services.ContentService.Create(faker.Commerce.ProductName(), currParent, docType.Alias); content.SetValue("review", faker.Rant.Review()); - content.SetValue("desc", string.Join(", ", Enumerable.Range(0, 5).Select(x => faker.Commerce.ProductAdjective()))); ; + content.SetValue("desc", string.Join(", ", Enumerable.Range(0, 5).Select(x => faker.Commerce.ProductAdjective()))); content.SetValue("media", imageIds[random.Next(0, imageIds.Count - 1)]); Services.ContentService.Save(content); diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index 575bd869ba..2d977e89c7 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -357,7 +357,7 @@ namespace Umbraco.Tests.Composing var col2 = factory.GetInstance(); AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); - AssertSameCollection(col1, col2); + AssertSameCollection(factory, col1, col2); } } @@ -416,11 +416,11 @@ namespace Umbraco.Tests.Composing { col1A = factory.GetInstance(); col1B = factory.GetInstance(); - } - AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); - AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); - AssertSameCollection(col1A, col1B); + AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); + AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); + AssertSameCollection(factory, col1A, col1B); + } TestCollection col2; @@ -455,7 +455,7 @@ namespace Umbraco.Tests.Composing Assert.IsInstanceOf(expected[i], colA[i]); } - private static void AssertSameCollection(IEnumerable col1, IEnumerable col2) + private static void AssertSameCollection(IFactory factory, IEnumerable col1, IEnumerable col2) { Assert.AreSame(col1, col2); @@ -463,8 +463,19 @@ namespace Umbraco.Tests.Composing var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); + + // Ensure each item in each collection is the same but also + // resolve each item from the factory to ensure it's also the same since + // it should have the same lifespan. for (var i = 0; i < col1A.Length; i++) + { Assert.AreSame(col1A[i], col2A[i]); + + var itemA = factory.GetInstance(col1A[i].GetType()); + var itemB = factory.GetInstance(col2A[i].GetType()); + + Assert.AreSame(itemA, itemB); + } } private static void AssertNotSameCollection(IEnumerable col1, IEnumerable col2) @@ -475,8 +486,11 @@ namespace Umbraco.Tests.Composing var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); + for (var i = 0; i < col1A.Length; i++) + { Assert.AreNotSame(col1A[i], col2A[i]); + } } #endregion diff --git a/src/Umbraco.Tests/Logging/LogviewerTests.cs b/src/Umbraco.Tests/Logging/LogviewerTests.cs index 6e0e82827c..87cc19a2c6 100644 --- a/src/Umbraco.Tests/Logging/LogviewerTests.cs +++ b/src/Umbraco.Tests/Logging/LogviewerTests.cs @@ -164,7 +164,7 @@ namespace Umbraco.Tests.Logging //Query @Level='Warning' BUT we pass in array of LogLevels for Debug & Info (Expect to get 0 results) string[] logLevelMismatch = { "Debug", "Information" }; - var filterLevelQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: "@Level='Warning'", logLevels: logLevelMismatch); ; + var filterLevelQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: "@Level='Warning'", logLevels: logLevelMismatch); Assert.AreEqual(0, filterLevelQuery.TotalItems); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js index b8ee797c82..a33796ab6d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js @@ -133,6 +133,17 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se } }; + scope.currentSectionInOverflow = function () { + if (scope.overflowingSections === 0) { + return false; + } + + var currentSection = scope.sections.filter(s => s.alias === scope.currentSection); + + return (scope.sections.indexOf(currentSection[0]) >= scope.maxSections); + + }; + loadSections(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js index 2dc0ebdf93..381ecc76c3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js @@ -18,9 +18,9 @@ function link(scope, element, attrs, ctrl) { - scope.close = function() { - if(scope.onClose) { - scope.onClose(); + scope.close = function () { + if (scope.onClose) { + scope.onClose(); } } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 7429f60e0d..91b0ba8754 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -42,7 +42,7 @@ $scope.page.isNew = Object.toBoolean(newVal); //We fetch all ancestors of the node to generate the footer breadcrumb navigation - if (content.parentId && content.parentId !== -1) { + if (content.parentId && content.parentId !== -1 && content.parentId !== -20) { loadBreadcrumb(); if (!watchingCulture) { $scope.$watch('culture', @@ -287,6 +287,8 @@ $scope.page.showSaveButton = true; // add ellipsis to the save button if it opens the variant overlay $scope.page.saveButtonEllipsis = content.variants && content.variants.length > 1 ? "true" : "false"; + } else { + $scope.page.showSaveButton = false; } // create the pubish combo button diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 0ed1b12000..95b2a520d1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -328,6 +328,9 @@ // invariant nodes scope.currentUrls = scope.node.urls; } + + // figure out if multiple cultures apply across the content urls + scope.currentUrlsHaveMultipleCultures = _.keys(_.groupBy(scope.currentUrls, url => url.culture)).length > 1; } // load audit trail and redirects when on the info tab diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js index a33fd4be53..96ce8735eb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js @@ -99,13 +99,18 @@ Use this directive to render a ui component for selecting child items to a paren @param {string} parentName (binding): The parent name. @param {string} parentIcon (binding): The parent icon. @param {number} parentId (binding): The parent id. -@param {callback} onRemove (binding): Callback when the remove button is clicked on an item. +@param {callback} onRemove (binding): Callback when removing an item.

The callback returns:

  • child: The selected item.
  • $index: The selected item index.
-@param {callback} onAdd (binding): Callback when the add button is clicked. +@param {callback} onAdd (binding): Callback when adding an item. +

The callback returns:

+
    +
  • $event: The select event.
  • +
+@param {callback} onSort (binding): Callback when sorting an item.

The callback returns:

  • $event: The select event.
  • @@ -174,16 +179,15 @@ Use this directive to render a ui component for selecting child items to a paren eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){ if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } + if (oldValue === undefined || newValue === undefined) { return; } syncParentName(); - })); eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){ if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } + if (oldValue === undefined || newValue === undefined) { return; } syncParentIcon(); })); @@ -191,6 +195,7 @@ Use this directive to render a ui component for selecting child items to a paren // sortable options for allowed child content types scope.sortableOptions = { axis: "y", + cancel: ".unsortable", containment: "parent", distance: 10, opacity: 0.7, @@ -199,7 +204,7 @@ Use this directive to render a ui component for selecting child items to a paren zIndex: 6000, update: function (e, ui) { if(scope.onSort) { - scope.onSort(); + scope.onSort(); } } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index 0369b4bd2e..149c2b5087 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -24,8 +24,7 @@ function valPropertyMsg(serverValidationManager, localizationService) { var hasError = false; //create properties on our custom scope so we can use it in our template - scope.errorMsg = ""; - + scope.errorMsg = ""; //the property form controller api var formCtrl = ctrl[0]; @@ -34,11 +33,15 @@ function valPropertyMsg(serverValidationManager, localizationService) { //the property controller api var umbPropCtrl = ctrl[2]; //the variants controller api - var umbVariantCtrl = ctrl[3]; + var umbVariantCtrl = ctrl[3]; var currentProperty = umbPropCtrl.property; scope.currentProperty = currentProperty; + var currentCulture = currentProperty.culture; + + // validation object won't exist when editor loads outside the content form (ie in settings section when modifying a content type) + var isMandatory = currentProperty.validation ? currentProperty.validation.mandatory : undefined; var labels = {}; localizationService.localize("errors_propertyHasErrors").then(function (data) { @@ -91,23 +94,25 @@ function valPropertyMsg(serverValidationManager, localizationService) { if (!watcher) { watcher = scope.$watch("currentProperty.value", function (newValue, oldValue) { - if (angular.equals(newValue, oldValue)) { return; } var errCount = 0; + for (var e in formCtrl.$error) { if (angular.isArray(formCtrl.$error[e])) { errCount++; } - } + } //we are explicitly checking for valServer errors here, since we shouldn't auto clear // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg // is the only one, then we'll clear. - if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { + if (errCount === 0 + || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) + || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { scope.errorMsg = ""; formCtrl.$setValidity('valPropertyMsg', true); } else if (showValidation && scope.errorMsg === "") { @@ -136,6 +141,21 @@ function valPropertyMsg(serverValidationManager, localizationService) { } //if there are any errors in the current property form that are not valPropertyMsg else if (_.without(_.keys(formCtrl.$error), "valPropertyMsg").length > 0) { + + // errors exist, but if the property is NOT mandatory and has no value, the errors should be cleared + if (isMandatory !== undefined && isMandatory === false && !currentProperty.value) { + hasError = false; + showValidation = false; + scope.errorMsg = ""; + + // if there's no value, the controls can be reset, which clears the error state on formCtrl + for (let control of formCtrl.$getControls()) { + control.$setValidity(); + } + + return; + } + hasError = true; //update the validation message if we don't already have one assigned. if (showValidation && scope.errorMsg === "") { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index d481205657..5bfe66b25f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -525,7 +525,7 @@ When building a custom infinite editor view you can use the same components as a function rollback(editor) { editor.view = "views/common/infiniteeditors/rollback/rollback.html"; - if (!editor.size) editor.size = "small"; + if (!editor.size) editor.size = "medium"; open(editor); } @@ -784,7 +784,7 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the user group picker in infinite editing, the submit callback returns the saved template + * Opens the template editor in infinite editing, the submit callback returns the saved template * @param {Object} editor rendering options * @param {String} editor.id The template id * @param {Callback} editor.submit Submits the editor diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index 456601a7bd..ff4122b258 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -103,14 +103,21 @@ - +.umb-toggle.umb-toggle--disabled.umb-toggle--checked, .umb-toggle.umb-toggle--disabled { .umb-toggle__toggle { cursor: not-allowed; - border-color: @gray-5; + background-color: @gray-9; + border-color: @gray-9; + } + .umb-toggle__icon--left { + color: @gray-6; + } + .umb-toggle__icon--right { + color: @gray-6; } .umb-toggle__handler { - background-color: @gray-5; + background-color: @gray-10; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less index da690663d0..937f746c56 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less @@ -16,23 +16,24 @@ .umb-child-selector__child.-placeholder { border: 1px dashed @gray-8; background: none; - cursor: pointer; text-align: center; justify-content: center; - - color:@ui-action-type; + width: 100%; + color: @ui-action-type; + &:hover { - color:@ui-action-type-hover; - border-color:@ui-action-type-hover; - text-decoration:none; + color: @ui-action-type-hover; + border-color: @ui-action-type-hover; + text-decoration: none; } } .umb-child-selector__children-container { - margin-left: 30px; - .umb-child-selector__child { - cursor: move; - } + margin-left: 30px; + + .umb-child-selector__child.ui-sortable-handle { + cursor: move; + } } .umb-child-selector__child-description { @@ -65,5 +66,6 @@ } .umb-child-selector__child-remove { - cursor: pointer; + background: none; + border: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less index b9efd8f7d8..1c5c275642 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less @@ -162,5 +162,6 @@ &.umb-form-check--disabled { cursor: not-allowed !important; opacity: 0.5; + pointer-events: none; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less index 68cb9b2e58..ec598c17eb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less @@ -23,6 +23,7 @@ background-color: transparent; border-color: @ui-action-discreet-border; transition: background-color .1s linear, border-color .1s linear, color .1s linear, width .1s ease-in-out, padding-left .1s ease-in-out; + cursor: pointer; } &:focus-within, &:hover { @@ -39,6 +40,7 @@ background-color: white; color: @ui-action-discreet-border-hover; border-color: @ui-action-discreet-border-hover; + cursor: unset; } input:focus, &:focus-within input, &.--has-value input { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index 4168ab3c39..716693c778 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -135,6 +135,8 @@ .umb-nested-content__header-bar:hover .umb-nested-content__icons, +.umb-nested-content__header-bar:focus .umb-nested-content__icons, +.umb-nested-content__header-bar:focus-within .umb-nested-content__icons, .umb-nested-content__item--active > .umb-nested-content__header-bar .umb-nested-content__icons { opacity: 1; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index f754a09368..939fd79826 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -29,7 +29,8 @@ .umb-node-preview__icon { display: flex; width: 25px; - height: 25px; + min-height: 25px; + height: 100%; justify-content: center; align-items: center; font-size: 20px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less index 3f0b981ac6..17c4bf1a55 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less @@ -61,6 +61,7 @@ opacity: 0; transition: opacity 120ms; } +.umb-property:focus-within .umb-property-actions__toggle, .umb-property:hover .umb-property-actions__toggle, .umb-property .umb-property-actions__toggle:focus { opacity: 1; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less index 08b1a1b5e1..9550acfb1b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less @@ -1,6 +1,7 @@ .umb-property-file-upload { .umb-upload-button-big { + max-width: (@propertyEditorLimitedWidth - 40); display: block; padding: 20px; opacity: 1; diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index efc0178ca2..f5c499cbfc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -418,7 +418,7 @@ // -------------------------------------------------- // Limit width of specific property editors .umb-property-editor--limit-width { - max-width: 800px; + max-width: @propertyEditorLimitedWidth; } // Horizontal dividers diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index c25941514e..764b73c593 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -722,6 +722,10 @@ // // File upload // -------------------------------------------------- +.umb-fileupload { + display: flex; +} + .umb-fileupload .preview { border-radius: 5px; border: 1px solid @gray-6; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index d6d38f540a..0e43f428ae 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -164,4 +164,13 @@ .mce-fullscreen { position: absolute; + + .mce-in { + position: fixed; + top: 35px !important; + } + + umb-editor__overlay, .umb-editor { + position: fixed; + } } diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index 27b11b1c3b..1f19786b3c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -55,7 +55,7 @@ ul.sections { transition: opacity .1s linear, box-shadow .1s; } - &.current a { + &.current > a { color: @ui-active; &::after { @@ -76,6 +76,13 @@ ul.sections { transition: opacity .1s linear; } + &.current { + i { + opacity: 1; + background: @ui-active; + } + } + &:hover i { opacity: 1; } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index a906bc0eed..5c0e6f7729 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -244,6 +244,7 @@ @paddingSmall: 2px 10px; // 26px @paddingMini: 0 6px; // 22px +@propertyEditorLimitedWidth: 800px; // Disabled this to keep consistency throughout the backoffice UI. Untill a better solution is thought up, this will do. @baseBorderRadius: 3px; // 2px; diff --git a/src/Umbraco.Web.UI.Client/src/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/navigation.controller.js index 194c45afe6..281be2d331 100644 --- a/src/Umbraco.Web.UI.Client/src/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/navigation.controller.js @@ -257,6 +257,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar evts.push(eventsService.on("app.ready", function (evt, data) { $scope.authenticated = true; ensureInit(); + ensureMainCulture(); })); // event for infinite editors @@ -279,8 +280,22 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } })); - - + /** + * For multi language sites, this ensures that mculture is set to either the last selected language or the default one + */ + function ensureMainCulture() { + if ($location.search().mculture) { + return; + } + var language = lastLanguageOrDefault(); + if (!language) { + return; + } + // trigger a language selection in the next digest cycle + $timeout(function () { + $scope.selectLanguage(language); + }); + } /** * Based on the current state of the application, this configures the scope variables that control the main tree and language drop down @@ -385,28 +400,19 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if ($scope.languages.length > 1) { //if there's already one set, check if it exists - var currCulture = null; + var language = null; var mainCulture = $location.search().mculture; if (mainCulture) { - currCulture = _.find($scope.languages, function (l) { + language = _.find($scope.languages, function (l) { return l.culture.toLowerCase() === mainCulture.toLowerCase(); }); } - if (!currCulture) { - // no culture in the request, let's look for one in the cookie that's set when changing language - var defaultCulture = $cookies.get("UMB_MCULTURE"); - if (!defaultCulture || !_.find($scope.languages, function (l) { - return l.culture.toLowerCase() === defaultCulture.toLowerCase(); - })) { - // no luck either, look for the default language - var defaultLang = _.find($scope.languages, function (l) { - return l.isDefault; - }); - if (defaultLang) { - defaultCulture = defaultLang.culture; - } + if (!language) { + language = lastLanguageOrDefault(); + + if (language) { + $location.search("mculture", language.culture); } - $location.search("mculture", defaultCulture ? defaultCulture : null); } } @@ -431,6 +437,25 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); }); } + + function lastLanguageOrDefault() { + if (!$scope.languages || $scope.languages.length <= 1) { + return null; + } + // see if we can find a culture in the cookie set when changing language + var lastCulture = $cookies.get("UMB_MCULTURE"); + var language = lastCulture ? _.find($scope.languages, function (l) { + return l.culture.toLowerCase() === lastCulture.toLowerCase(); + }) : null; + if (!language) { + // no luck, look for the default language + language = _.find($scope.languages, function (l) { + return l.isDefault; + }); + } + return language; + } + function nodeExpandedHandler(args) { //store the reference to the expanded node path if (args.node) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js index b8581d28d0..9640f2eba2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js @@ -56,7 +56,8 @@ "validation_validateAsEmail", "validation_validateAsNumber", "validation_validateAsUrl", - "validation_enterCustomValidation" + "validation_enterCustomValidation", + "validation_fieldIsMandatory" ]; localizationService.localizeMany(labels) @@ -66,6 +67,7 @@ vm.labels.validateAsNumber = data[1]; vm.labels.validateAsUrl = data[2]; vm.labels.customValidation = data[3]; + vm.labels.fieldIsMandatory = data[4]; vm.validationTypes = [ { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index 4474390199..77ee276e3e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -84,14 +84,15 @@
    - - + on-click="vm.toggleValidation()" + label-on="{{vm.labels.fieldIsMandatory}}" + label-off="{{vm.labels.fieldIsMandatory}}" + show-labels="true" + label-position="right" focus-when="{{vm.focusOnMandatoryField}}" + class="mb1"> -
  • +
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html index e358d75b9e..064dcf6945 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html @@ -70,7 +70,7 @@
    - +

    Oh, we got lost!

    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html index a2caff4ff2..9e161d6b4e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html @@ -1,8 +1,7 @@
    -
    -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html index 32aa5ed1a2..014297ae51 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html @@ -1,12 +1,11 @@
    - +
    @@ -15,7 +14,6 @@
    - Remove -
    - -
    \ No newline at end of file + +
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index 64f601f8b7..1a1254fcf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -22,10 +22,14 @@ function contentCreateController($scope, function initialize() { $scope.loading = true; $scope.allowedTypes = null; - $scope.countTypes = contentTypeResource.getCount; var getAllowedTypes = contentTypeResource.getAllowedTypes($scope.currentNode.id).then(function (data) { $scope.allowedTypes = iconHelper.formatContentTypeIcons(data); + if ($scope.allowedTypes.length === 0) { + contentTypeResource.getCount().then(function(count) { + $scope.countTypes = count; + }); + } }); var getCurrentUser = authResource.getCurrentUser().then(function (currentUser) { if (currentUser.allowedSections.indexOf("settings") > -1) { diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index a35a404c24..cdabb05fd4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -220,7 +220,7 @@ function startupLatestEditsController($scope) { } angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController); -function MediaFolderBrowserDashboardController($rootScope, $scope, $location, contentTypeResource, userService) { +function MediaFolderBrowserDashboardController($scope, $routeParams, $location, contentTypeResource, userService) { var currentUser = {}; @@ -251,6 +251,8 @@ function MediaFolderBrowserDashboardController($rootScope, $scope, $location, co view: dt.view }; + // tell the list view to list content at root + $routeParams.id = -1; }); } else if (currentUser.startMediaIds.length > 0){ diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html index a7f7d45087..f1388753a0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html @@ -4,7 +4,7 @@ -

    Hours of Umbraco training videos are only a click away

    +

    Hours of Umbraco training videos are only a click away

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js index a5884c2355..a7ce67fc0b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js @@ -73,14 +73,14 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, var content = $scope.content; - // we need to check wether an app is present in the current data, if not we will present the default app. + // we need to check whether an app is present in the current data, if not we will present the default app. var isAppPresent = false; // on first init, we dont have any apps. but if we are re-initializing, we do, but ... if ($scope.app) { // lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.) - _.forEach(content.apps, function(app) { + content.apps.forEach(app => { if (app === $scope.app) { isAppPresent = true; } @@ -88,7 +88,7 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, // if we did reload our DocType, but still have the same app we will try to find it by the alias. if (isAppPresent === false) { - _.forEach(content.apps, function(app) { + content.apps.forEach(app => { if (app.alias === $scope.app.alias) { isAppPresent = true; app.active = true; @@ -182,24 +182,26 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, formHelper.resetForm({ scope: $scope }); - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - savedContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - editorState.set($scope.content); - - syncTreeNode($scope.content, data.path); - - init(); - - $scope.page.saveButtonState = "success"; - // close the editor if it's infinite mode + // submit function manages rebinding changes if(infiniteMode && $scope.model.submit) { $scope.model.mediaNode = $scope.content; $scope.model.submit($scope.model); + } else { + // if not infinite mode, rebind changed props etc + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + savedContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + editorState.set($scope.content); + + syncTreeNode($scope.content, data.path); + + $scope.page.saveButtonState = "success"; + + init(); } }, function(err) { @@ -245,7 +247,7 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, syncTreeNode($scope.content, data.path, true); } - if ($scope.content.parentId && $scope.content.parentId != -1) { + if ($scope.content.parentId && $scope.content.parentId !== -1 && $scope.content.parentId !== -21) { //We fetch all ancestors of the node to generate the footer breadcrump navigation entityResource.getAncestors(nodeId, "media") .then(function (anc) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 3c954b9316..139a2515b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -276,6 +276,9 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time } $scope.reloadView = function (id, reloadActiveNode) { + if (!id) { + return; + } $scope.viewLoaded = false; $scope.folders = []; @@ -713,45 +716,12 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time } function initView() { - //default to root id if the id is undefined var id = $routeParams.id; if (id === undefined) { - id = -1; + // no ID found in route params - don't list anything as we don't know for sure where we are + return; } - getContentTypesCallback(id).then(function (listViewAllowedTypes) { - $scope.listViewAllowedTypes = listViewAllowedTypes; - - var blueprints = false; - _.each(listViewAllowedTypes, function (allowedType) { - if (_.isEmpty(allowedType.blueprints)) { - // this helps the view understand that there are no blueprints available - allowedType.blueprints = null; - } - else { - blueprints = true; - // turn the content type blueprints object into an array of sortable objects for the view - allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { - return { - id: pair[0], - name: pair[1] - }; - }); - } - }); - - if (listViewAllowedTypes.length === 1 && blueprints === false) { - $scope.createAllowedButtonSingle = true; - } - if (listViewAllowedTypes.length === 1 && blueprints === true) { - $scope.createAllowedButtonSingleWithBlueprints = true; - } - if (listViewAllowedTypes.length > 1) { - $scope.createAllowedButtonMultiWithBlueprints = true; - } - }); - - $scope.contentId = id; $scope.isTrashed = editorState.current ? editorState.current.trashed : id === "-20" || id === "-21"; @@ -765,6 +735,40 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time $scope.options.allowBulkMove || $scope.options.allowBulkDelete; + if ($scope.isTrashed === false) { + getContentTypesCallback(id).then(function (listViewAllowedTypes) { + $scope.listViewAllowedTypes = listViewAllowedTypes; + + var blueprints = false; + _.each(listViewAllowedTypes, function (allowedType) { + if (_.isEmpty(allowedType.blueprints)) { + // this helps the view understand that there are no blueprints available + allowedType.blueprints = null; + } + else { + blueprints = true; + // turn the content type blueprints object into an array of sortable objects for the view + allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { + return { + id: pair[0], + name: pair[1] + }; + }); + } + }); + + if (listViewAllowedTypes.length === 1 && blueprints === false) { + $scope.createAllowedButtonSingle = true; + } + if (listViewAllowedTypes.length === 1 && blueprints === true) { + $scope.createAllowedButtonSingleWithBlueprints = true; + } + if (listViewAllowedTypes.length > 1) { + $scope.createAllowedButtonMultiWithBlueprints = true; + } + }); + } + $scope.reloadView($scope.contentId); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 8c6194d638..cde99d7b9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -48,49 +48,45 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders // when there is no match for a selected id. This will ensure that the values being set on save, are the same as before. - medias = _.map(ids, - function (id) { - var found = _.find(medias, - function (m) { - // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and - // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() - // compares and be completely sure it works. - return m.udi.toString() === id.toString() || m.id.toString() === id.toString(); - }); - if (found) { - return found; - } else { - return { - name: vm.labels.deletedItem, - id: $scope.model.config.idType !== "udi" ? id : null, - udi: $scope.model.config.idType === "udi" ? id : null, - icon: "icon-picture", - thumbnail: null, - trashed: true - }; - } - }); + medias = ids.map(id => { + var found = medias.find(m => + // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and + // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() + // compares and be completely sure it works. + m.udi.toString() === id.toString() || m.id.toString() === id.toString()); + + if (found) { + return found; + } else { + return { + name: vm.labels.deletedItem, + id: $scope.model.config.idType !== "udi" ? id : null, + udi: $scope.model.config.idType === "udi" ? id : null, + icon: "icon-picture", + thumbnail: null, + trashed: true + }; + } + }); - _.each(medias, - function (media, i) { + medias.forEach(media => { + if (!media.extension && media.id && media.metaData) { + media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); + } - if (!media.extension && media.id && media.metaData) { - media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); - } + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id && media.metaData) { + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); + } - // if there is no thumbnail, try getting one if the media is not a placeholder item - if (!media.thumbnail && media.id && media.metaData) { - media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); - } + $scope.mediaItems.push(media); - $scope.mediaItems.push(media); - - if ($scope.model.config.idType === "udi") { - $scope.ids.push(media.udi); - } else { - $scope.ids.push(media.id); - } - }); + if ($scope.model.config.idType === "udi") { + $scope.ids.push(media.udi); + } else { + $scope.ids.push(media.id); + } + }); sync(); }); @@ -100,7 +96,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl function sync() { $scope.model.value = $scope.ids.join(); removeAllEntriesAction.isDisabled = $scope.ids.length === 0; - }; + } function setDirty() { angularHelper.getCurrentForm($scope).$setDirty(); @@ -111,18 +107,17 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // reload. We only reload the images that is already picked but has been updated. // We have to get the entities from the server because the media // can be edited without being selected - _.each($scope.images, - function (image, i) { - if (updatedMediaNodes.indexOf(image.udi) !== -1) { - image.loading = true; - entityResource.getById(image.udi, "media") - .then(function (mediaEntity) { - angular.extend(image, mediaEntity); - image.thumbnail = mediaHelper.resolveFileFromEntity(image, true); - image.loading = false; - }); - } - }); + $scope.mediaItems.forEach(media => { + if (updatedMediaNodes.indexOf(media.udi) !== -1) { + media.loading = true; + entityResource.getById(media.udi, "Media") + .then(function (mediaEntity) { + angular.extend(media, mediaEntity); + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); + media.loading = false; + }); + } + }); } function init() { @@ -177,20 +172,20 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // the media picker is using media entities so we get the // entity so we easily can format it for use in the media grid if (model && model.mediaNode) { - entityResource.getById(model.mediaNode.id, "media") + entityResource.getById(model.mediaNode.id, "Media") .then(function (mediaEntity) { // if an image is selecting more than once // we need to update all the media items - angular.forEach($scope.images, function (image) { - if (image.id === model.mediaNode.id) { - angular.extend(image, mediaEntity); - image.thumbnail = mediaHelper.resolveFileFromEntity(image, true); + $scope.mediaItems.forEach(media => { + if (media.id === model.mediaNode.id) { + angular.extend(media, mediaEntity); + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } }); }); } }, - close: function (model) { + close: function () { editorService.close(); } }; @@ -210,7 +205,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl editorService.close(); - _.each(model.selection, function (media, i) { + model.selection.forEach(media => { // if there is no thumbnail, try getting one if the media is not a placeholder item if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); @@ -280,16 +275,14 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl disabled: !multiPicker, items: "li:not(.add-wrapper)", cancel: ".unsortable", - update: function (e, ui) { + update: function () { setDirty(); $timeout(function() { // TODO: Instead of doing this with a timeout would be better to use a watch like we do in the // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the // watch do all the rest. - $scope.ids = _.map($scope.mediaItems, - function (item) { - return $scope.model.config.idType === "udi" ? item.udi : item.id; - }); + $scope.ids = $scope.mediaItems.map(media => $scope.model.config.idType === "udi" ? media.udi : media.id); + sync(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index b17906272d..c4dba4d373 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -36,16 +36,16 @@
    - -
  • -
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 7de3a5b567..f2538c66c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -221,6 +221,7 @@ if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) { // only one scaffold type - no need to display the picker addNode(vm.scaffolds[0].contentTypeAlias); + vm.overlayMenu = null; return; } @@ -276,6 +277,9 @@ }; vm.getName = function (idx) { + if (!model.value || !model.value.length) { + return ""; + } var name = ""; @@ -325,6 +329,10 @@ }; vm.getIcon = function (idx) { + if (!model.value || !model.value.length) { + return ""; + } + var scaffold = getScaffold(model.value[idx].ncContentTypeAlias); return scaffold && scaffold.icon ? iconHelper.convertFromLegacyIcon(scaffold.icon) : "icon-folder"; } @@ -480,10 +488,12 @@ } // Enforce min items if we only have one scaffold type + var modelWasChanged = false; if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) { for (var i = vm.nodes.length; i < model.config.minItems; i++) { addNode(vm.scaffolds[0].contentTypeAlias); } + modelWasChanged = true; } // If there is only one item, set it as current node @@ -495,6 +505,10 @@ vm.inited = true; + if (modelWasChanged) { + updateModel(); + } + updatePropertyActionStates(); checkAbilityToPasteContent(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index a20289d076..f5f0f7f2a2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -56,7 +56,7 @@ return value.toFixed(stepDecimalPlaces); }, from: function (value) { - return value; + return Number(value); } }, "range": { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/group.html b/src/Umbraco.Web.UI.Client/src/views/users/group.html index 0244819655..eae6dbd75c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/group.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/group.html @@ -40,13 +40,12 @@ on-remove="vm.removeSelectedItem($index, vm.userGroup.sections)"> - + @@ -61,14 +60,14 @@ on-remove="vm.clearStartNode('content')"> - + + @@ -85,14 +84,14 @@ on-remove="vm.clearStartNode('media')"> - + + @@ -127,13 +126,12 @@ on-edit="vm.setPermissionsForNode(node)"> - + @@ -155,13 +153,11 @@ on-remove="vm.removeSelectedItem($index, vm.userGroup.users)"> - + diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index e0cf09da50..bb3efaede2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -75,13 +75,11 @@ on-remove="model.removeSelectedItem($index, model.user.userGroups)"> - + @@ -100,13 +98,12 @@ name="model.labels.noStartNodes"> - + @@ -125,13 +122,12 @@ name="model.labels.noStartNodes"> - + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml index 7962d17898..8bcb535bd6 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml @@ -31,13 +31,13 @@ @* a single image *@ if (media.IsDocumentType("Image")) { - @Render(media); + @Render(media) } @* a folder with images under it *@ foreach (var image in media.Children(Current.VariationContextAccessor)) { - @Render(image); + @Render(image) } } diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml index defe59d808..131b0515ae 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml @@ -2,31 +2,31 @@ @using Umbraco.Web.Templates @using Newtonsoft.Json.Linq -@* +@* Razor helpers located at the bottom of this file *@ @if (Model != null && Model.sections != null) { var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; - +
    @if (oneColumn) { foreach (var section in Model.sections) {
    @foreach (var row in section.rows) { - @renderRow(row); + @renderRow(row) }
    - } - }else { + } + }else {
    @foreach (var s in Model.sections) {
    @foreach (var row in s.rows) { - @renderRow(row); + @renderRow(row) }
    @@ -85,4 +85,4 @@ return new MvcHtmlString(string.Join(" ", attrs)); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml index 9333628ed6..23fee33043 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml @@ -12,7 +12,7 @@ foreach (var section in Model.sections) {
    @foreach (var row in section.rows) { - @renderRow(row, true); + @renderRow(row, true) }
    } @@ -23,7 +23,7 @@
    @foreach (var row in s.rows) { - @renderRow(row, false); + @renderRow(row, false) }
    diff --git a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs index f9b1f1019b..75c4bcb660 100644 --- a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs +++ b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs @@ -49,11 +49,6 @@ namespace Umbraco.Web.Hosting } public string ToAbsolute(string virtualPath, string root) => VirtualPathUtility.ToAbsolute(virtualPath, root); - public void LazyRestartApplication() - { - HttpRuntime.UnloadAppDomain(); - } - public void RegisterObject(IRegisteredObject registeredObject) { var wrapped = new RegisteredObjectWrapper(registeredObject); diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs new file mode 100644 index 0000000000..245e8ea374 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Web; +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public bool IsRestarting { get; set; } + + public void Restart() + { + IsRestarting = true; + + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext != null) + { + // unload app domain - we must null out all identities otherwise we get serialization errors + // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue + httpContext.User = null; + } + + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } + } +} diff --git a/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs b/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs new file mode 100644 index 0000000000..bd37b62531 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs @@ -0,0 +1,19 @@ +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetUserAgentProvider : IUserAgentProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetUserAgentProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public string GetUserAgent() + { + return _httpContextAccessor.GetRequiredHttpContext().Request.UserAgent; + } + } +} diff --git a/src/Umbraco.Web/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs index 1879e8b69b..087d393d3c 100644 --- a/src/Umbraco.Web/Cache/WebCachingAppCache.cs +++ b/src/Umbraco.Web/Cache/WebCachingAppCache.cs @@ -89,7 +89,7 @@ namespace Umbraco.Web.Cache protected override void EnterWriteLock() { - _locker.EnterWriteLock();; + _locker.EnterWriteLock(); } protected override void ExitReadLock() diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 081b808cac..47c36177fe 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -263,7 +263,8 @@ namespace Umbraco.Web.Composing public static IUmbracoVersion UmbracoVersion => Factory.GetInstance(); public static IPublishedUrlProvider PublishedUrlProvider => Factory.GetInstance(); public static IMenuItemCollectionFactory MenuItemCollectionFactory => Factory.GetInstance(); - public static MembershipHelper MembershipHelper => Factory.GetInstance(); + public static MembershipHelper MembershipHelper => Factory.GetInstance(); + public static IUmbracoApplicationLifetime UmbracoApplicationLifetime => Factory.GetInstance(); #endregion } diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 3d205dcaed..835bda19b6 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -446,7 +446,7 @@ namespace Umbraco.Web.Editors { var propertyEditor = propertyEditors.SingleOrDefault(x => x.Alias == dataType.Alias); if (propertyEditor != null) - dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); ; + dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); } var grouped = dataTypes diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 4563547346..ed37bc9fbd 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Net; using Umbraco.Web.JavaScript; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; @@ -39,6 +40,7 @@ namespace Umbraco.Web.Editors private readonly IUmbracoVersion _umbracoVersion; private readonly IIOHelper _ioHelper; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; public PackageInstallController( IGlobalSettings globalSettings, @@ -53,11 +55,13 @@ namespace Umbraco.Web.Editors IUmbracoVersion umbracoVersion, UmbracoMapper umbracoMapper, IIOHelper ioHelper, - IPublishedUrlProvider publishedUrlProvider) + IPublishedUrlProvider publishedUrlProvider, + IUmbracoApplicationLifetime umbracoApplicationLifetime) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper, umbracoMapper, publishedUrlProvider) { _umbracoVersion = umbracoVersion; _ioHelper = ioHelper; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } /// @@ -121,8 +125,8 @@ namespace Umbraco.Web.Editors model.LicenseUrl = ins.LicenseUrl; model.Readme = ins.Readme; model.ConflictingMacroAliases = ins.Warnings.ConflictingMacros.ToDictionary(x => x.Name, x => x.Alias); - model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); ; - model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); ; + model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); + model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); model.ContainsUnsecureFiles = ins.Warnings.UnsecureFiles.Any(); model.Url = ins.Url; model.Version = ins.Version; @@ -325,7 +329,7 @@ namespace Umbraco.Web.Editors var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool - UmbracoApplication.Restart(Request.TryGetHttpContext().Result); + _umbracoApplicationLifetime.Restart(); model.IsRestarting = true; @@ -337,12 +341,8 @@ namespace Umbraco.Web.Editors { if (model.IsRestarting == false) return model; - //check for the key, if it's not there we're are restarted - if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting") == false) - { - //reset it - model.IsRestarting = false; - } + model.IsRestarting = _umbracoApplicationLifetime.IsRestarting; + return model; } diff --git a/src/Umbraco.Web/HttpContextAccessorExtensions.cs b/src/Umbraco.Web/HttpContextAccessorExtensions.cs index 2076510ce0..8901cf692c 100644 --- a/src/Umbraco.Web/HttpContextAccessorExtensions.cs +++ b/src/Umbraco.Web/HttpContextAccessorExtensions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Web { public static HttpContextBase GetRequiredHttpContext(this IHttpContextAccessor httpContextAccessor) { + if (httpContextAccessor == null) throw new ArgumentNullException(nameof(httpContextAccessor)); var httpContext = httpContextAccessor.HttpContext; if(httpContext is null) throw new InvalidOperationException("HttpContext is null"); diff --git a/src/Umbraco.Web/JavaScript/AssetInitialization.cs b/src/Umbraco.Web/JavaScript/AssetInitialization.cs index 00605ece1f..42c750ffd3 100644 --- a/src/Umbraco.Web/JavaScript/AssetInitialization.cs +++ b/src/Umbraco.Web/JavaScript/AssetInitialization.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Web; using ClientDependency.Core; using ClientDependency.Core.Config; +using Umbraco.Core; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; @@ -35,7 +36,7 @@ namespace Umbraco.Web.JavaScript var requestUrl = httpContext.Request.Url; if (requestUrl == null) throw new ArgumentException("HttpContext.Request.Url is null.", nameof(httpContext)); - var dependencies = assets.Select(x => + var dependencies = assets.Where(x => x.IsNullOrWhiteSpace() == false).Select(x => { // most declarations with be made relative to the /umbraco folder, so things // like lib/blah/blah.js so we need to turn them into absolutes here diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 8518293241..462748fae0 100644 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -340,7 +340,7 @@ namespace Umbraco.Web.Macros /// The text output of the macro execution. private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) { - var engine = new PartialViewMacroEngine(); + var engine = new PartialViewMacroEngine(_umbracoContextAccessor, _httpContextAccessor, _ioHelper); return engine.Execute(macro, content); } diff --git a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs index ecfda80399..0099ff220a 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs @@ -5,6 +5,7 @@ using System.Web.Routing; using System.Web.WebPages; using Umbraco.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Composing; @@ -15,37 +16,24 @@ namespace Umbraco.Web.Macros /// public class PartialViewMacroEngine { - private readonly Func _getHttpContext; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IIOHelper _ioHelper; private readonly Func _getUmbracoContext; - public PartialViewMacroEngine() + public PartialViewMacroEngine(IUmbracoContextAccessor umbracoContextAccessor, IHttpContextAccessor httpContextAccessor, IIOHelper ioHelper) { - _getHttpContext = () => - { - if (HttpContext.Current == null) - throw new InvalidOperationException($"The {GetType()} cannot execute with a null HttpContext.Current reference."); - return new HttpContextWrapper(HttpContext.Current); - }; + _httpContextAccessor = httpContextAccessor; + _ioHelper = ioHelper; _getUmbracoContext = () => { - if (Current.UmbracoContext == null) + var context = umbracoContextAccessor.UmbracoContext; + if (context == null) throw new InvalidOperationException($"The {GetType()} cannot execute with a null UmbracoContext.Current reference."); - return Current.UmbracoContext; + return context; }; } - /// - /// Constructor generally used for unit testing - /// - /// - /// - internal PartialViewMacroEngine(HttpContextBase httpContext, IUmbracoContext umbracoContext) - { - _getHttpContext = () => httpContext; - _getUmbracoContext = () => umbracoContext; - } - public bool Validate(string code, string tempFileName, IPublishedContent currentPage, out string errorMessage) { var temp = GetVirtualPathFromPhysicalPath(tempFileName); @@ -68,7 +56,7 @@ namespace Umbraco.Web.Macros if (content == null) throw new ArgumentNullException(nameof(content)); if (macro.MacroSource.IsNullOrWhiteSpace()) throw new ArgumentException("The MacroSource property of the macro object cannot be null or empty"); - var http = _getHttpContext(); + var httpContext = _httpContextAccessor.GetRequiredHttpContext(); var umbCtx = _getUmbracoContext(); var routeVals = new RouteData(); routeVals.Values.Add("controller", "PartialViewMacro"); @@ -79,13 +67,14 @@ namespace Umbraco.Web.Macros var viewContext = new ViewContext { ViewData = new ViewDataDictionary() }; //try and extract the current view context from the route values, this would be set in the UmbracoViewPage or in // the UmbracoPageResult if POSTing to an MVC controller but rendering in Webforms - if (http.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext)) + if (httpContext.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext)) { - viewContext = (ViewContext)http.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext]; + viewContext = (ViewContext)httpContext.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext]; } routeVals.DataTokens.Add("ParentActionViewContext", viewContext); - var request = new RequestContext(http, routeVals); + var request = new RequestContext(httpContext, routeVals); + string output; using (var controller = new PartialViewMacroController(macro, content)) { @@ -103,7 +92,7 @@ namespace Umbraco.Web.Macros private string GetVirtualPathFromPhysicalPath(string physicalPath) { - var rootpath = _getHttpContext().Server.MapPath("~/"); + var rootpath = _ioHelper.MapPath("~/"); physicalPath = physicalPath.Replace(rootpath, ""); physicalPath = physicalPath.Replace("\\", "/"); return "~/" + physicalPath; diff --git a/src/Umbraco.Web/Mvc/ContentModelBinder.cs b/src/Umbraco.Web/Mvc/ContentModelBinder.cs index 052938807a..a66f8e9089 100644 --- a/src/Umbraco.Web/Mvc/ContentModelBinder.cs +++ b/src/Umbraco.Web/Mvc/ContentModelBinder.cs @@ -151,9 +151,15 @@ namespace Umbraco.Web.Mvc var context = HttpContext.Current; if (context == null) + { AppDomain.Unload(AppDomain.CurrentDomain); + } else - UmbracoApplication.Restart(new HttpContextWrapper(context)); + { + Current.UmbracoApplicationLifetime.Restart(); + } + + } throw new ModelBindingException(msg.ToString()); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 0c8f663e38..6da8e815ca 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -32,6 +32,31 @@ namespace Umbraco.Web private static IExamineManager ExamineManager => Current.Factory.GetInstance(); private static IUserService UserService => Current.Services.UserService; + + #region Creator/Writer Names + + public static string CreatorName(this IPublishedContent content, IUserService userService) + { + return userService.GetProfileById(content.CreatorId)?.Name; + } + + public static string WriterName(this IPublishedContent content, IUserService userService) + { + return userService.GetProfileById(content.WriterId)?.Name; + } + + public static string CreatorName(this IPublishedContent content) + { + return content.GetCreatorName(UserService); + } + + public static string WriterName(this IPublishedContent content) + { + return content.GetWriterName(UserService); + } + + #endregion + #region Template /// @@ -684,19 +709,6 @@ namespace Umbraco.Web #endregion - #region Writer and creator - - public static string CreatorName(this IPublishedContent content) - { - return content.GetCreatorName(UserService); - } - - public static string WriterName(this IPublishedContent content) - { - return content.GetWriterName(UserService); - } - - #endregion #region Url diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 420e4b5daa..345d847365 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -49,6 +49,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Request; using Umbraco.Core.Session; using Umbraco.Web.AspNet; +using Umbraco.Web.AspNet; using Umbraco.Web.Models; namespace Umbraco.Web.Runtime @@ -65,7 +66,7 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(); - + composition.Register(); composition.Register(Lifetime.Singleton); composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); @@ -74,6 +75,7 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(); + composition.Register(Lifetime.Singleton); composition.Register(); composition.Register(Lifetime.Singleton); diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index afd89f967a..4fac87e226 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -86,15 +86,18 @@ namespace Umbraco.Web.Trees .OrderBy(entity => entity.Name) .Select(dt => { + // get the content type here so we can get the icon from it to use when we create the tree node + // and we can enrich the result with content type data that's not available in the entity service output + var contentType = contentTypes[dt.Id]; + // since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to. // need this check to keep supporting sites where children have already been created. var hasChildren = dt.HasChildren; - var node = CreateTreeNode(dt, Constants.ObjectTypes.DocumentType, id, queryStrings, Constants.Icons.ContentType, hasChildren); + var node = CreateTreeNode(dt, Constants.ObjectTypes.DocumentType, id, queryStrings, contentType?.Icon ?? Constants.Icons.ContentType, hasChildren); node.Path = dt.Path; - // enrich the result with content type data that's not available in the entity service output - var contentType = contentTypes[dt.Id]; + // now we can enrich the result with content type data that's not available in the entity service output node.Alias = contentType.Alias; node.AdditionalData["isElement"] = contentType.IsElement; diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index d89e93bb49..c366791d2b 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; @@ -28,6 +27,7 @@ namespace Umbraco.Web.Trees { private readonly UmbracoTreeSearcher _treeSearcher; private readonly IMenuItemCollectionFactory _menuItemCollectionFactory; + private readonly IMediaTypeService _mediaTypeService; public MediaTypeTreeController( UmbracoTreeSearcher treeSearcher, @@ -41,11 +41,13 @@ namespace Umbraco.Web.Trees UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider, - IMenuItemCollectionFactory menuItemCollectionFactory) + IMenuItemCollectionFactory menuItemCollectionFactory, + IMediaTypeService mediaTypeService) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper, publishedUrlProvider) { _treeSearcher = treeSearcher; _menuItemCollectionFactory = menuItemCollectionFactory; + _mediaTypeService = mediaTypeService; } protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) @@ -71,6 +73,8 @@ namespace Umbraco.Web.Trees // if the request is for folders only then just return if (queryStrings["foldersonly"].IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") return nodes; + var mediaTypes = _mediaTypeService.GetAll(); + nodes.AddRange( Services.EntityService.GetChildren(intId.Result, UmbracoObjectTypes.MediaType) .OrderBy(entity => entity.Name) @@ -79,7 +83,8 @@ namespace Umbraco.Web.Trees // since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to. // need this check to keep supporting sites where children have already been created. var hasChildren = dt.HasChildren; - var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaType, id, queryStrings, Constants.Icons.MediaType, hasChildren); + var mt = mediaTypes.FirstOrDefault(x => x.Id == dt.Id); + var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaType, id, queryStrings, mt?.Icon ?? Constants.Icons.MediaType, hasChildren); node.Path = dt.Path; return node; diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs index 2046baf2d3..5db9088f20 100644 --- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Trees { return Services.MemberTypeService.GetAll() .OrderBy(x => x.Name) - .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberType, id, queryStrings, Constants.Icons.MemberType, false)); + .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberType, id, queryStrings, dt?.Icon ?? Constants.Icons.MemberType, false)); } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8c06da4894..cd0c4152a0 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -136,7 +136,9 @@ + + @@ -160,8 +162,6 @@ - - @@ -273,7 +273,6 @@ - @@ -360,7 +359,6 @@ - @@ -456,7 +454,6 @@ - diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 90d575bf73..098fc94724 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -36,46 +36,5 @@ namespace Umbraco.Web return new WebRuntime(this, configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom); } - - /// - /// Restarts the Umbraco application. - /// - public static void Restart() - { - // see notes in overload - - var httpContext = HttpContext.Current; - if (httpContext != null) - { - httpContext.Application.Add("AppPoolRestarting", true); - httpContext.User = null; - } - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } - - /// - /// Restarts the Umbraco application. - /// - public static void Restart(HttpContextBase httpContext) - { - if (httpContext != null) - { - // we're going to put an application wide flag to show that the application is about to restart. - // we're doing this because if there is a script checking if the app pool is fully restarted, then - // it can check if this flag exists... if it does it means the app pool isn't restarted yet. - httpContext.Application.Add("AppPoolRestarting", true); - - // unload app domain - we must null out all identities otherwise we get serialization errors - // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue - httpContext.User = null; - } - - if (HttpContext.Current != null) - HttpContext.Current.User = null; - - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } } } diff --git a/src/Umbraco.Web/UmbracoContextFactory.cs b/src/Umbraco.Web/UmbracoContextFactory.cs index 466bf68576..4090da5a6d 100644 --- a/src/Umbraco.Web/UmbracoContextFactory.cs +++ b/src/Umbraco.Web/UmbracoContextFactory.cs @@ -70,7 +70,6 @@ namespace Umbraco.Web _variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture); } - var webSecurity = new WebSecurity(_httpContextAccessor, _userService, _globalSettings, _ioHelper); return new UmbracoContext(_httpContextAccessor, _publishedSnapshotService, webSecurity, _globalSettings, _variationContextAccessor, _ioHelper, _uriUtility, _cookieManager); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index e1e74ead5e..679fbb789c 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml.XPath; +using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; -using Umbraco.Web.Composing; using Umbraco.Web.Mvc; namespace Umbraco.Web