diff --git a/.editorconfig b/.editorconfig index 5a35b71ce6..29ad74b4d6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -25,16 +25,22 @@ dotnet_naming_rule.private_members_with_underscore.severity = suggestion dotnet_naming_symbols.private_fields.applicable_kinds = field dotnet_naming_symbols.private_fields.applicable_accessibilities = private +# dotnet_naming_symbols.private_fields.required_modifiers = abstract,async,readonly,static # all except const dotnet_naming_style.prefix_underscore.capitalization = camel_case dotnet_naming_style.prefix_underscore.required_prefix = _ + + + + + # https://github.com/MicrosoftDocs/visualstudio-docs/blob/master/docs/ide/editorconfig-code-style-settings-reference.md [*.cs] csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = true:suggestion -csharp_prefer_braces = false : none +csharp_prefer_braces = false : none -[*.{js,less}] -trim_trailing_whitespace = false +[*.{js,less}] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index a664be3a85..c8987ade67 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,7 @@ *.png binary *.gif binary -*.cs text=auto diff=csharp +*.cs text=auto diff=csharp *.vb text=auto *.c text=auto *.cpp text=auto @@ -41,9 +41,13 @@ *.fs text=auto *.fsx text=auto *.hs text=auto +*.json text=auto +*.xml text=auto -*.csproj text=auto merge=union -*.vbproj text=auto merge=union -*.fsproj text=auto merge=union -*.dbproj text=auto merge=union -*.sln text=auto eol=crlf merge=union +*.csproj text=auto merge=union +*.vbproj text=auto merge=union +*.fsproj text=auto merge=union +*.dbproj text=auto merge=union +*.sln text=auto eol=crlf merge=union + +*.gitattributes text=auto 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 e009ee2294..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) @@ -60,12 +60,12 @@ Great question! The short version goes like this: ![Clone the fork](img/clonefork.png) - * **Switch to the correct branch** - switch to the v8-dev branch + * **Switch to the correct branch** - switch to the `v8/contrib` branch * **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md) * **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/dev`, create a new branch first. + * **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) @@ -158,7 +158,7 @@ To find the general areas for something you're looking to fix or improve, have a ### Which branch should I target for my contributions? -We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/dev`. If you are working on v8, this is the branch you should be targetting. For v7 contributions, please target 'v7/dev'. +We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/contrib`. If you are working on v8, this is the branch you should be targetting. For v7 contributions, please target 'v7/dev'. Please note: we are no longer accepting features for v7 but will continue to merge bug fixes as and when they arise. @@ -184,10 +184,10 @@ Then when you want to get the changes from the main repository: ``` git fetch upstream -git rebase upstream/v8/dev +git rebase upstream/v8/contrib ``` -In this command we're syncing with the `v8/dev` branch, but you can of course choose another one if needed. +In this command we're syncing with the `v8/contrib` branch, but you can of course choose another one if needed. (More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated)) diff --git a/.github/README.md b/.github/README.md index d6d978c3d6..467ca6e5e6 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,4 +1,4 @@ -# [Umbraco CMS](https://umbraco.com) · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/dev)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![Twitter](https://img.shields.io/twitter/follow/umbraco.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=umbraco) +# [Umbraco CMS](https://umbraco.com) · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/contrib)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![Twitter](https://img.shields.io/twitter/follow/umbraco.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=umbraco) Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social. diff --git a/.github/img/defaultbranch.png b/.github/img/defaultbranch.png index f3a5b9efbc..3550b5c34c 100644 Binary files a/.github/img/defaultbranch.png and b/.github/img/defaultbranch.png differ diff --git a/.gitignore b/.gitignore index a0ff4d5b27..12ad3299ad 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ App_Data/TEMP/* src/Umbraco.Web.UI/[Cc]ss/* src/Umbraco.Web.UI/App_Code/* src/Umbraco.Web.UI/App_Data/* +src/Umbraco.Web.UI/data/* src/Umbraco.Tests/App_Data/* src/Umbraco.Web.UI/[Mm]edia/* src/Umbraco.Web.UI/[Mm]aster[Pp]ages/* diff --git a/NuGet.Config b/NuGet.Config index 7d786702f4..64425091dc 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -7,6 +7,6 @@ --> - + diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index 658d2f0672..7199f414b1 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -27,8 +27,7 @@ - - + diff --git a/build/NuSpecs/tools/serilog.config.install.xdt b/build/NuSpecs/tools/serilog.config.install.xdt index e0df2985c7..b4a10b7bc2 100644 --- a/build/NuSpecs/tools/serilog.config.install.xdt +++ b/build/NuSpecs/tools/serilog.config.install.xdt @@ -2,7 +2,7 @@ > - + diff --git a/build/build-bootstrap.ps1 b/build/build-bootstrap.ps1 index 71a25bfd7e..82c789ff22 100644 --- a/build/build-bootstrap.ps1 +++ b/build/build-bootstrap.ps1 @@ -22,6 +22,8 @@ # get NuGet $cache = 4 $nuget = "$scriptTemp\nuget.exe" + # ensure the correct NuGet-source is used. This one is used by Umbraco + $nugetsourceUmbraco = "https://www.myget.org/F/umbracocore/api/v3/index.json" if (-not $local) { $source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" @@ -61,7 +63,7 @@ # get the build system if (-not $local) { - $params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease" + $params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease", "-Source", $nugetsourceUmbraco &$nuget install Umbraco.Build @params if (-not $?) { throw "Failed to download Umbraco.Build." } } diff --git a/build/build.ps1 b/build/build.ps1 index ea07e4516f..6e124d1508 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -375,11 +375,14 @@ }) + $nugetsourceUmbraco = "https://api.nuget.org/v3/index.json" + $ubuild.DefineMethod("RestoreNuGet", { Write-Host "Restore NuGet" Write-Host "Logging to $($this.BuildTemp)\nuget.restore.log" - &$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\src\Umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log" + $params = "-Source", $nugetsourceUmbraco + &$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\src\Umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log" @params if (-not $?) { throw "Failed to restore NuGet packages." } }) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index a9f5dcf352..4c2d72fb8c 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -2,7 +2,7 @@ using System.Resources; [assembly: AssemblyCompany("Umbraco")] -[assembly: AssemblyCopyright("Copyright © Umbraco 2019")] +[assembly: AssemblyCopyright("Copyright © Umbraco 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/Umbraco.Abstractions/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Abstractions/Cache/JsonCacheRefresherBase.cs deleted file mode 100644 index d9d9644a36..0000000000 --- a/src/Umbraco.Abstractions/Cache/JsonCacheRefresherBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Umbraco.Core.Sync; - -namespace Umbraco.Core.Cache -{ - /// - /// A base class for "json" cache refreshers. - /// - /// The actual cache refresher type. - /// The actual cache refresher type is used for strongly typed events. - public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher - where TInstanceType : class, ICacheRefresher - { - /// - /// Initializes a new instance of the . - /// - /// A cache helper. - protected JsonCacheRefresherBase(AppCaches appCaches) : base(appCaches) - { } - - /// - /// Refreshes as specified by a json payload. - /// - /// The json payload. - public virtual void Refresh(string json) - { - OnCacheUpdated(This, new CacheRefresherEventArgs(json, MessageType.RefreshByJson)); - } - } -} diff --git a/src/Umbraco.Abstractions/Composing/Current.cs b/src/Umbraco.Abstractions/Composing/Current.cs deleted file mode 100644 index f99a32a585..0000000000 --- a/src/Umbraco.Abstractions/Composing/Current.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Umbraco.Core.Logging; - -namespace Umbraco.Composing -{ - public static class Current - { - - public static ILogger Logger { get; set; } = new NullLogger(); - } -} diff --git a/src/Umbraco.Abstractions/CompositionExtensions.cs b/src/Umbraco.Abstractions/CompositionExtensions.cs deleted file mode 100644 index c65cff50d6..0000000000 --- a/src/Umbraco.Abstractions/CompositionExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Umbraco.Core.Composing; - -namespace Umbraco.Core -{ - public static partial class CompositionExtensions - { - - #region Collection Builders - - /// - /// Gets the components collection builder. - /// - public static ComponentCollectionBuilder Components(this Composition composition) - => composition.WithCollectionBuilder(); - - #endregion - } -} diff --git a/src/Umbraco.Abstractions/Configuration/Configs.cs b/src/Umbraco.Abstractions/Configuration/Configs.cs deleted file mode 100644 index abb06d525f..0000000000 --- a/src/Umbraco.Abstractions/Configuration/Configs.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Configuration -{ - /// - /// Represents Umbraco configurations. - /// - /// - /// During composition, use composition.Configs. When running, either inject the required configuration, - /// or use Current.Configs. - /// - public class Configs - { - private readonly Func _configSectionResolver; - - public Configs(Func configSectionResolver) - { - _configSectionResolver = configSectionResolver ?? throw new ArgumentNullException(nameof(configSectionResolver)); - } - - private readonly Dictionary> _configs = new Dictionary>(); - private Dictionary> _registerings = new Dictionary>(); - private Lazy _factory; - - /// - /// Gets a configuration. - /// - public TConfig GetConfig() - where TConfig : class - { - if (!_configs.TryGetValue(typeof(TConfig), out var configFactory)) - throw new InvalidOperationException($"No configuration of type {typeof(TConfig)} has been added."); - - return (TConfig) configFactory.Value; - } - - /// - /// Adds a configuration, provided by a factory. - /// - public void Add(Func configFactory) - where TConfig : class - { - // make sure it is not too late - if (_registerings == null) - throw new InvalidOperationException("Configurations have already been registered."); - - var typeOfConfig = typeof(TConfig); - - var lazyConfigFactory = _configs[typeOfConfig] = new Lazy(configFactory); - _registerings[typeOfConfig] = register => register.Register(_ => (TConfig) lazyConfigFactory.Value, Lifetime.Singleton); - } - - /// - /// Adds a configuration, provided by a factory. - /// - public void Add(Func configFactory) - where TConfig : class - { - // make sure it is not too late - if (_registerings == null) - throw new InvalidOperationException("Configurations have already been registered."); - - var typeOfConfig = typeof(TConfig); - - _configs[typeOfConfig] = new Lazy(() => - { - if (!(_factory is null)) return _factory.Value.GetInstance(); - throw new InvalidOperationException($"Cannot get configuration of type {typeOfConfig} during composition."); - }); - _registerings[typeOfConfig] = register => register.Register(configFactory, Lifetime.Singleton); - } - - /// - /// Adds a configuration, provided by a configuration section. - /// - public void Add(string sectionName) - where TConfig : class - { - Add(() => GetConfig(sectionName)); - } - - private TConfig GetConfig(string sectionName) - where TConfig : class - { - // note: need to use SafeCallContext here because ConfigurationManager.GetSection ends up getting AppDomain.Evidence - // which will want to serialize the call context including anything that is in there - what a mess! - - using (new SafeCallContext()) - { - if ((_configSectionResolver(sectionName) is TConfig config)) - return config; - var ex = new InvalidOperationException($"Could not get configuration section \"{sectionName}\" from config files."); - throw ex; - } - } - - /// - /// Registers configurations in a register. - /// - public void RegisterWith(IRegister register, Func factory) - { - // do it only once - if (_registerings == null) - throw new InvalidOperationException("Configurations have already been registered."); - - _factory = new Lazy(factory); - - register.Register(this); - - foreach (var registering in _registerings.Values) - registering(register); - - // no need to keep them around - _registerings = null; - } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs b/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs deleted file mode 100644 index 1723785069..0000000000 --- a/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.IO; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.Grid; -using Umbraco.Core.Configuration.HealthChecks; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Manifest; - -namespace Umbraco.Core -{ - /// - /// Provides extension methods for the class. - /// - public static class ConfigsExtensions - { - public static IGlobalSettings Global(this Configs configs) - => configs.GetConfig(); - - public static IHostingSettings Hosting(this Configs configs) - => configs.GetConfig(); - - public static IConnectionStrings ConnectionStrings(this Configs configs) - => configs.GetConfig(); - - public static IUmbracoSettingsSection Settings(this Configs configs) - => configs.GetConfig(); - - public static IHealthChecks HealthChecks(this Configs configs) - => configs.GetConfig(); - - public static IGridConfig Grids(this Configs configs) - => configs.GetConfig(); - - public static ICoreDebug CoreDebug(this Configs configs) - => configs.GetConfig(); - - public static IUserPasswordConfiguration UserPasswordConfiguration(this Configs configs) - => configs.GetConfig(); - - public static IMemberPasswordConfiguration MemberPasswordConfiguration(this Configs configs) - => configs.GetConfig(); - - public static void AddPasswordConfigurations(this Configs configs) - { - configs.Add(() => - { - return new UserPasswordConfiguration(configs.Settings().Security.UserPasswordConfiguration); - }); - configs.Add(() => - { - return new MemberPasswordConfiguration(configs.Settings().Security.MemberPasswordConfiguration); - }); - } - - public static void AddCoreConfigs(this Configs configs, IIOHelper ioHelper) - { - var configDir = new DirectoryInfo(ioHelper.MapPath(Constants.SystemDirectories.Config)); - - // GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition - configs.Add(factory => new GridConfig( - factory.GetInstance(), - factory.GetInstance(), - configDir, - factory.GetInstance(), - factory.GetInstance().Debug)); - } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/GlobalSettingsExtensions.cs b/src/Umbraco.Abstractions/Configuration/GlobalSettingsExtensions.cs deleted file mode 100644 index 4d8039dfbb..0000000000 --- a/src/Umbraco.Abstractions/Configuration/GlobalSettingsExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Umbraco.Core.IO; - -namespace Umbraco.Core.Configuration -{ - public static class GlobalSettingsExtensions - { - private static string _mvcArea; - - - /// - /// This returns the string of the MVC Area route. - /// - /// - /// This will return the MVC area that we will route all custom routes through like surface controllers, etc... - /// We will use the 'Path' (default ~/umbraco) to create it but since it cannot contain '/' and people may specify a path of ~/asdf/asdf/admin - /// we will convert the '/' to '-' and use that as the path. its a bit lame but will work. - /// - /// We also make sure that the virtual directory (SystemDirectories.Root) is stripped off first, otherwise we'd end up with something - /// like "MyVirtualDirectory-Umbraco" instead of just "Umbraco". - /// - public static string GetUmbracoMvcArea(this IGlobalSettings globalSettings, IIOHelper ioHelper) - { - if (_mvcArea != null) return _mvcArea; - - _mvcArea = GetUmbracoMvcAreaNoCache(globalSettings, ioHelper); - - return _mvcArea; - } - - internal static string GetUmbracoMvcAreaNoCache(this IGlobalSettings globalSettings, IIOHelper ioHelper) - { - if (globalSettings.Path.IsNullOrWhiteSpace()) - { - throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified"); - } - - var path = globalSettings.Path; - if (path.StartsWith(ioHelper.Root)) // beware of TrimStart, see U4-2518 - path = path.Substring(ioHelper.Root.Length); - return path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower(); - } - - } -} diff --git a/src/Umbraco.Abstractions/Configuration/Grid/GridConfig.cs b/src/Umbraco.Abstractions/Configuration/Grid/GridConfig.cs deleted file mode 100644 index de4f7ccd5a..0000000000 --- a/src/Umbraco.Abstractions/Configuration/Grid/GridConfig.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.IO; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Manifest; - -namespace Umbraco.Core.Configuration.Grid -{ - public class GridConfig : IGridConfig - { - public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, IManifestParser manifestParser, bool isDebug) - { - EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, manifestParser, isDebug); - } - - public IGridEditorsConfig EditorsConfig { get; } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/IConnectionStrings.cs b/src/Umbraco.Abstractions/Configuration/IConnectionStrings.cs deleted file mode 100644 index 0d33378669..0000000000 --- a/src/Umbraco.Abstractions/Configuration/IConnectionStrings.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Umbraco.Core.Configuration -{ - public interface IConnectionStrings - { - ConfigConnectionString this[string key] - { - get; - } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs b/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs deleted file mode 100644 index 9edf1a462e..0000000000 --- a/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Umbraco.Core.Configuration.UmbracoSettings; - -namespace Umbraco.Core.Configuration -{ - public abstract class PasswordConfiguration : IPasswordConfiguration - { - protected PasswordConfiguration(IPasswordConfigurationSection configSection) - { - if (configSection == null) - { - throw new ArgumentNullException(nameof(configSection)); - } - - RequiredLength = configSection.RequiredLength; - RequireNonLetterOrDigit = configSection.RequireNonLetterOrDigit; - RequireDigit = configSection.RequireDigit; - RequireLowercase = configSection.RequireLowercase; - RequireUppercase = configSection.RequireUppercase; - UseLegacyEncoding = configSection.UseLegacyEncoding; - HashAlgorithmType = configSection.HashAlgorithmType; - MaxFailedAccessAttemptsBeforeLockout = configSection.MaxFailedAccessAttemptsBeforeLockout; - } - - public int RequiredLength { get; } - - public bool RequireNonLetterOrDigit { get; } - - public bool RequireDigit { get; } - - public bool RequireLowercase { get; } - - public bool RequireUppercase { get; } - - public bool UseLegacyEncoding { get; } - - public string HashAlgorithmType { get; } - - public int MaxFailedAccessAttemptsBeforeLockout { get; } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs deleted file mode 100644 index cbbb933857..0000000000 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IMemberPasswordConfigurationSection : IPasswordConfigurationSection - { - } -} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs deleted file mode 100644 index acd9c92588..0000000000 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IUmbracoSettingsSection : IUmbracoConfigurationSection - { - IBackOfficeSection BackOffice { get; } - - IContentSection Content { get; } - - ISecuritySection Security { get; } - - IRequestHandlerSection RequestHandler { get; } - - ILoggingSection Logging { get; } - - IWebRoutingSection WebRouting { get; } - - IKeepAliveSection KeepAlive { get; } - } -} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs deleted file mode 100644 index d80dd2b7e5..0000000000 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IUserPasswordConfigurationSection : IPasswordConfigurationSection - { - } -} diff --git a/src/Umbraco.Abstractions/Constants-Web.cs b/src/Umbraco.Abstractions/Constants-Web.cs deleted file mode 100644 index 64216ba571..0000000000 --- a/src/Umbraco.Abstractions/Constants-Web.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Umbraco.Core -{ - public static partial class Constants - { - /// - /// Defines the identifiers for Umbraco system nodes. - /// - public static class Web - { - public const string UmbracoContextDataToken = "umbraco-context"; - public const string UmbracoDataToken = "umbraco"; - public const string PublishedDocumentRequestDataToken = "umbraco-doc-request"; - public const string CustomRouteDataToken = "umbraco-custom-route"; - public const string UmbracoRouteDefinitionDataToken = "umbraco-route-def"; - - /// - /// The preview cookie name - /// - public const string PreviewCookieName = "UMB_PREVIEW"; - - public const string InstallerCookieName = "umb_installId"; - } - } -} diff --git a/src/Umbraco.Abstractions/EnumExtensions.cs b/src/Umbraco.Abstractions/EnumExtensions.cs deleted file mode 100644 index b2e5f32c9a..0000000000 --- a/src/Umbraco.Abstractions/EnumExtensions.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; - -namespace Umbraco.Core -{ - /// - /// Provides extension methods to enums. - /// - public static class EnumExtensions - { - // note: - // - no need to HasFlagExact, that's basically an == test - // - HasFlagAll cannot be named HasFlag because ext. methods never take priority over instance methods - - /// - /// Determines whether a flag enum has all the specified values. - /// - /// - /// True when all bits set in are set in , though other bits may be set too. - /// This is the behavior of the original method. - /// - public static bool HasFlagAll(this T use, T uses) - where T : Enum - { - var num = Convert.ToUInt64(use); - var nums = Convert.ToUInt64(uses); - - return (num & nums) == nums; - } - - /// - /// Determines whether a flag enum has any of the specified values. - /// - /// - /// True when at least one of the bits set in is set in . - /// - public static bool HasFlagAny(this T use, T uses) - where T : Enum - { - var num = Convert.ToUInt64(use); - var nums = Convert.ToUInt64(uses); - - return (num & nums) > 0; - } - - /// - /// Sets a flag of the given input enum - /// - /// - /// Enum to set flag of - /// Flag to set - /// A new enum with the flag set - public static T SetFlag(this T input, T flag) - where T : Enum - { - var i = Convert.ToUInt64(input); - var f = Convert.ToUInt64(flag); - - // bitwise OR to set flag f of enum i - var result = i | f; - - return (T)Enum.ToObject(typeof(T), result); - } - - /// - /// Unsets a flag of the given input enum - /// - /// - /// Enum to unset flag of - /// Flag to unset - /// A new enum with the flag unset - public static T UnsetFlag(this T input, T flag) - where T : Enum - { - var i = Convert.ToUInt64(input); - var f = Convert.ToUInt64(flag); - - // bitwise AND combined with bitwise complement to unset flag f of enum i - var result = i & ~f; - - return (T)Enum.ToObject(typeof(T), result); - } - } -} diff --git a/src/Umbraco.Abstractions/Exceptions/ArgumentNullOrEmptyException.cs b/src/Umbraco.Abstractions/Exceptions/ArgumentNullOrEmptyException.cs deleted file mode 100644 index 037d35d0ee..0000000000 --- a/src/Umbraco.Abstractions/Exceptions/ArgumentNullOrEmptyException.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Exceptions -{ - /// - /// The exception that is thrown when a null reference, or an empty argument, is passed to a method that does not accept it as a valid argument. - /// - /// - [Obsolete("Throw an ArgumentNullException when the parameter is null or an ArgumentException when its empty instead.")] - [Serializable] - public class ArgumentNullOrEmptyException : ArgumentNullException - { - /// - /// Initializes a new instance of the class. - /// - public ArgumentNullOrEmptyException() - { } - - /// - /// Initializes a new instance of the class with the name of the parameter that caused this exception. - /// - /// The named of the parameter that caused the exception. - public ArgumentNullOrEmptyException(string paramName) - : base(paramName) - { } - - /// - /// Initializes a new instance of the class with a specified error message and the name of the parameter that caused this exception. - /// - /// The named of the parameter that caused the exception. - /// A message that describes the error. - public ArgumentNullOrEmptyException(string paramName, string message) - : base(paramName, message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for this exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public ArgumentNullOrEmptyException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The object that holds the serialized object data. - /// An object that describes the source or destination of the serialized data. - protected ArgumentNullOrEmptyException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/src/Umbraco.Abstractions/Exceptions/WontImplementException.cs b/src/Umbraco.Abstractions/Exceptions/WontImplementException.cs deleted file mode 100644 index e354bc4c3d..0000000000 --- a/src/Umbraco.Abstractions/Exceptions/WontImplementException.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Exceptions -{ - /// - /// The exception that is thrown when a requested method or operation is not, and will not be, implemented. - /// - /// - /// The is to be used when some code is not implemented, - /// but should eventually be implemented (i.e. work in progress) and is reported by tools such as ReSharper. - /// This exception is to be used when some code is not implemented, and is not meant to be, for whatever - /// reason. - /// - /// - [Serializable] - [Obsolete("If a method or operation is not, and will not be, implemented, it is invalid or not supported, so we should throw either an InvalidOperationException or NotSupportedException instead.")] - public class WontImplementException : NotImplementedException - { - /// - /// Initializes a new instance of the class. - /// - public WontImplementException() - { } - - /// - /// Initializes a new instance of the class with a specified reason message. - /// - /// The error message that explains the reason for the exception. - public WontImplementException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. If the parameter is not , the current exception is raised in a block that handles the inner exception. - public WontImplementException(string message, Exception inner) - : base(message, inner) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected WontImplementException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/src/Umbraco.Abstractions/IO/FileSecurityException.cs b/src/Umbraco.Abstractions/IO/FileSecurityException.cs deleted file mode 100644 index 8ce9ab34a5..0000000000 --- a/src/Umbraco.Abstractions/IO/FileSecurityException.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.IO -{ - /// - /// The exception that is thrown when the caller does not have the required permission to access a file. - /// - /// - [Obsolete("Throw an UnauthorizedAccessException instead.")] - [Serializable] - public class FileSecurityException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public FileSecurityException() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public FileSecurityException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public FileSecurityException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected FileSecurityException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs b/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs deleted file mode 100644 index e8af1b0ac3..0000000000 --- a/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Must be implemented by property editors that store media and return media paths - /// - /// - /// Currently there are only 2x core editors that do this: upload and image cropper. - /// It would be possible for developers to know implement their own media property editors whereas previously this was not possible. - /// - public interface IDataEditorWithMediaPath - { - /// - /// Returns the media path for the value stored for a property - /// - /// - /// - string GetMediaPath(object value); - } -} diff --git a/src/Umbraco.Abstractions/Models/MacroTypes.cs b/src/Umbraco.Abstractions/Models/MacroTypes.cs deleted file mode 100644 index 5f8440845d..0000000000 --- a/src/Umbraco.Abstractions/Models/MacroTypes.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Enum for the various types of Macros - /// - [Serializable] - [DataContract(IsReference = true)] - public enum MacroTypes - { - [EnumMember] - Unknown = 4, - [EnumMember] - PartialView = 7 - } -} diff --git a/src/Umbraco.Abstractions/Properties/AssemblyInfo.cs b/src/Umbraco.Abstractions/Properties/AssemblyInfo.cs deleted file mode 100644 index 6d5520f975..0000000000 --- a/src/Umbraco.Abstractions/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] -[assembly: Guid("DA322714-FB89-4943-92BD-BB122B82F66B")] - -// Umbraco Cms -[assembly: InternalsVisibleTo("Umbraco.Web")] -[assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("Umbraco.Examine")] -[assembly: InternalsVisibleTo("Umbraco.ModelsBuilder.Embedded")] - -[assembly: InternalsVisibleTo("Umbraco.Tests")] -[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] - -// Allow this to be mocked in our unit tests -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] - -// Umbraco Deploy -[assembly: InternalsVisibleTo("Umbraco.Deploy")] -[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")] -[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")] - -// Umbraco Forms -[assembly: InternalsVisibleTo("Umbraco.Forms.Core")] -[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")] -[assembly: InternalsVisibleTo("Umbraco.Forms.Web")] - -// Umbraco Headless -[assembly: InternalsVisibleTo("Umbraco.Cloud.Headless")] - -// code analysis -// IDE1006 is broken, wants _value syntax for consts, etc - and it's even confusing ppl at MS, kill it -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "~_~")] diff --git a/src/Umbraco.Configuration/ActiveDirectorySettings.cs b/src/Umbraco.Configuration/ActiveDirectorySettings.cs new file mode 100644 index 0000000000..d85def7f33 --- /dev/null +++ b/src/Umbraco.Configuration/ActiveDirectorySettings.cs @@ -0,0 +1,15 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class ActiveDirectorySettings : IActiveDirectorySettings + { + public ActiveDirectorySettings() + { + ActiveDirectoryDomain = ConfigurationManager.AppSettings["ActiveDirectoryDomain"]; + } + + public string ActiveDirectoryDomain { get; } + } +} diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index fea0c23f29..5c6eade265 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -1,39 +1,63 @@ -using System.Configuration; +using Umbraco.Configuration; +using Umbraco.Configuration.Implementations; using Umbraco.Core.Configuration.HealthChecks; +using Umbraco.Core.Configuration.Implementations; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; namespace Umbraco.Core.Configuration { public class ConfigsFactory : IConfigsFactory { - - public ConfigsFactory() - { - } - public IHostingSettings HostingSettings { get; } = new HostingSettings(); - public ICoreDebug CoreDebug { get; } = new CoreDebug(); + public IMachineKeyConfig MachineKeyConfig { get; } = new MachineKeyConfig(); + public IIndexCreatorSettings IndexCreatorSettings { get; } = new IndexCreatorSettings(); + public INuCacheSettings NuCacheSettings { get; } = new NuCacheSettings(); + public ITypeFinderSettings TypeFinderSettings { get; } = new TypeFinderSettings(); + public IRuntimeSettings RuntimeSettings { get; } = new RuntimeSettings(); + public IActiveDirectorySettings ActiveDirectorySettings { get; } = new ActiveDirectorySettings(); + public IExceptionFilterSettings ExceptionFilterSettings { get; } = new ExceptionFilterSettings(); + public ITourSettings TourSettings { get; } = new TourSettings(); + public ILoggingSettings LoggingSettings { get; } = new LoggingSettings(); + public IKeepAliveSettings KeepAliveSettings { get; } = new KeepAliveSettings(); + public IWebRoutingSettings WebRoutingSettings { get; } = new WebRoutingSettings(); + public IRequestHandlerSettings RequestHandlerSettings { get; } = new RequestHandlerSettings(); + public ISecuritySettings SecuritySettings { get; } = new SecuritySettings(); + public IUserPasswordConfiguration UserPasswordConfigurationSettings { get; } = new UserPasswordConfigurationSettings(); + public IMemberPasswordConfiguration MemberPasswordConfigurationSettings { get; } = new MemberPasswordConfigurationSettings(); + public IContentSettings ContentSettings { get; } = new ContentSettings(); + public IGlobalSettings GlobalSettings { get; } = new GlobalSettings(); + public IHealthChecks HealthChecksSettings { get; } = new HealthChecksSettings(); + public IConnectionStrings ConnectionStrings { get; } = new ConnectionStrings(); + public IModelsBuilderConfig ModelsBuilderConfig { get; } = new ModelsBuilderConfig(); - public IUmbracoSettingsSection UmbracoSettings { get; } - - public Configs Create(IIOHelper ioHelper) + public Configs Create() { - var configs = new Configs(section => ConfigurationManager.GetSection(section)); - configs.Add(() => new GlobalSettings(ioHelper)); - configs.Add(() => HostingSettings); + var configs = new Configs(); - configs.Add("umbracoConfiguration/settings"); - configs.Add("umbracoConfiguration/HealthChecks"); + configs.Add(() => GlobalSettings); + configs.Add(() => HostingSettings); + configs.Add(() => HealthChecksSettings); + configs.Add(() => CoreDebug); + configs.Add(() => MachineKeyConfig); + configs.Add(() => ConnectionStrings); + configs.Add(() => ModelsBuilderConfig); + configs.Add(() => IndexCreatorSettings); + configs.Add(() => NuCacheSettings); + configs.Add(() => TypeFinderSettings); + configs.Add(() => RuntimeSettings); + configs.Add(() => ActiveDirectorySettings); + configs.Add(() => ExceptionFilterSettings); + configs.Add(() => TourSettings); + configs.Add(() => LoggingSettings); + configs.Add(() => KeepAliveSettings); + configs.Add(() => WebRoutingSettings); + configs.Add(() => RequestHandlerSettings); + configs.Add(() => SecuritySettings); + configs.Add(() => UserPasswordConfigurationSettings); + configs.Add(() => MemberPasswordConfigurationSettings); + configs.Add(() => ContentSettings); - // Password configuration is held within IUmbracoSettingsSection from umbracoConfiguration/settings but we'll add explicitly - // so it can be independently retrieved in classes that need it. - configs.AddPasswordConfigurations(); - - configs.Add(() => CoreDebug); - configs.Add(() => new ConnectionStrings()); - configs.AddCoreConfigs(ioHelper); return configs; } } diff --git a/src/Umbraco.Configuration/ConnectionStrings.cs b/src/Umbraco.Configuration/ConnectionStrings.cs index 707f58c7b7..66fdb33d5b 100644 --- a/src/Umbraco.Configuration/ConnectionStrings.cs +++ b/src/Umbraco.Configuration/ConnectionStrings.cs @@ -1,13 +1,17 @@ using System; using System.Configuration; +using System.IO; using System.Linq; using System.Xml.Linq; +using Umbraco.Composing; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Configuration { public class ConnectionStrings : IConnectionStrings { + public ConfigConnectionString this[string key] { get @@ -17,5 +21,94 @@ namespace Umbraco.Core.Configuration return new ConfigConnectionString(settings.ConnectionString, settings.ProviderName, settings.Name); } } + + public void RemoveConnectionString(string key, IIOHelper ioHelper) + { + var fileName = ioHelper.MapPath(string.Format("{0}/web.config", ioHelper.Root)); + var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); + + var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single(); + var setting = appSettings.Descendants("add").FirstOrDefault(s => s.Attribute("key").Value == key); + + if (setting != null) + { + setting.Remove(); + xml.Save(fileName, SaveOptions.DisableFormatting); + ConfigurationManager.RefreshSection("appSettings"); + } + var settings = ConfigurationManager.ConnectionStrings[key]; + } + + /// + /// Saves the connection string as a proper .net connection string in web.config. + /// + /// Saves the ConnectionString in the very nasty 'medium trust'-supportive way. + /// The connection string. + /// The provider name. + public void SaveConnectionString(string connectionString, string providerName, IIOHelper ioHelper) + { + + if (connectionString == null) throw new ArgumentNullException(nameof(connectionString)); + if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionString)); + if (providerName == null) throw new ArgumentNullException(nameof(providerName)); + if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(providerName)); + + + var fileSource = "web.config"; + var fileName = ioHelper.MapPath(ioHelper.Root +"/" + fileSource); + + var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); + if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root)."); + + var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault(); + if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings)."); + + // handle configSource + var configSourceAttribute = connectionStrings.Attribute("configSource"); + if (configSourceAttribute != null) + { + fileSource = configSourceAttribute.Value; + fileName = ioHelper.MapPath(ioHelper.Root + "/" + fileSource); + + if (!File.Exists(fileName)) + throw new Exception($"Invalid configSource \"{fileSource}\" (no such file)."); + + xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); + if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root)."); + + connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault(); + if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings)."); + } + + // create or update connection string + var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name")?.Value == Constants.System.UmbracoConnectionName); + if (setting == null) + { + connectionStrings.Add(new XElement("add", + new XAttribute("name", Constants.System.UmbracoConnectionName), + new XAttribute("connectionString", connectionString), + new XAttribute("providerName", providerName))); + } + else + { + AddOrUpdateAttribute(setting, "connectionString", connectionString); + AddOrUpdateAttribute(setting, "providerName", providerName); + } + + // save + Current.Logger.Info("Saving connection string to {ConfigFile}.", fileSource); + xml.Save(fileName, SaveOptions.DisableFormatting); + Current.Logger.Info("Saved connection string to {ConfigFile}.", fileSource); + } + + private static void AddOrUpdateAttribute(XElement element, string name, string value) + { + var attribute = element.Attribute(name); + if (attribute == null) + element.Add(new XAttribute(name, value)); + else + attribute.Value = value; + } + } } diff --git a/src/Umbraco.Configuration/ExceptionFilterSettings.cs b/src/Umbraco.Configuration/ExceptionFilterSettings.cs new file mode 100644 index 0000000000..628b8755cc --- /dev/null +++ b/src/Umbraco.Configuration/ExceptionFilterSettings.cs @@ -0,0 +1,18 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class ExceptionFilterSettings : IExceptionFilterSettings + { + public ExceptionFilterSettings() + { + if (bool.TryParse(ConfigurationManager.AppSettings["Umbraco.Web.DisableModelBindingExceptionFilter"], + out var disabled)) + { + Disabled = disabled; + } + } + public bool Disabled { get; } + } +} diff --git a/src/Umbraco.Configuration/GlobalSettings.cs b/src/Umbraco.Configuration/GlobalSettings.cs index 499ff787a8..6fda5fa9ba 100644 --- a/src/Umbraco.Configuration/GlobalSettings.cs +++ b/src/Umbraco.Configuration/GlobalSettings.cs @@ -1,7 +1,10 @@ using System; using System.Configuration; using System.Linq; +using System.Net.Mail; using System.Xml.Linq; +using Umbraco.Composing; +using Umbraco.Configuration; using Umbraco.Core.IO; namespace Umbraco.Core.Configuration @@ -62,7 +65,6 @@ namespace Umbraco.Core.Configuration /// public class GlobalSettings : IGlobalSettings { - private readonly IIOHelper _ioHelper; // TODO these should not be static private static string _reservedPaths; @@ -72,11 +74,6 @@ namespace Umbraco.Core.Configuration internal const string StaticReservedPaths = "~/app_plugins/,~/install/,~/mini-profiler-resources/,"; //must end with a comma! internal const string StaticReservedUrls = "~/config/splashes/noNodes.aspx,~/.well-known,"; //must end with a comma! - public GlobalSettings(IIOHelper ioHelper) - { - _ioHelper = ioHelper; - } - /// /// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config) /// @@ -84,7 +81,6 @@ namespace Umbraco.Core.Configuration { _reservedPaths = null; _reservedUrls = null; - HasSmtpServer = null; } /// @@ -95,29 +91,39 @@ namespace Umbraco.Core.Configuration { ResetInternal(); } + + public bool IsSmtpServerConfigured { get { - var smtpSection = ConfigurationManager.GetSection("system.net/mailSettings/smtp") as ConfigurationSection; - if (smtpSection is null) return false; + var smtpSettings = SmtpSettings; + if (smtpSettings is null) return false; + + if (!(smtpSettings.From is null)) return true; + if (!(smtpSettings.Host is null)) return true; + if (!(smtpSettings.PickupDirectoryLocation is null)) return true; + + return false; + } + } + + public ISmtpSettings SmtpSettings + { + get + { + var smtpSection = ConfigurationManager.GetSection("system.net/mailSettings/smtp") as ConfigurationSection; + if (smtpSection is null) return null; + + var result = new SmtpSettings(); var from = smtpSection.ElementInformation.Properties["from"]; if (@from != null && @from.Value is string fromPropValue && string.IsNullOrEmpty(fromPropValue) == false && !string.Equals("noreply@example.com", fromPropValue, StringComparison.OrdinalIgnoreCase)) { - return true; - } - - var networkSection = ConfigurationManager.GetSection("system.net/mailSettings/smtp/network") as ConfigurationSection; - var host = networkSection?.ElementInformation.Properties["host"]; - if (host != null - && host.Value is string hostPropValue - && string.IsNullOrEmpty(hostPropValue) == false) - { - return true; + result.From = fromPropValue; } var specifiedPickupDirectorySection = ConfigurationManager.GetSection("system.net/mailSettings/smtp/specifiedPickupDirectory") as ConfigurationSection; @@ -126,18 +132,21 @@ namespace Umbraco.Core.Configuration && pickupDirectoryLocation.Value is string pickupDirectoryLocationPropValue && string.IsNullOrEmpty(pickupDirectoryLocationPropValue) == false) { - return true; + result.PickupDirectoryLocation = pickupDirectoryLocationPropValue; } - return false; + // SmtpClient can magically read the section system.net/mailSettings/smtp/network, witch is always + // null if we use ConfigurationManager.GetSection. SmtpSection does not exist in .Net Standard + var smtpClient = new SmtpClient(); + + result.Host = smtpClient.Host; + result.Port = smtpClient.Port; + + return result; + } } - /// - /// For testing only - /// - internal static bool? HasSmtpServer { get; set; } - /// /// Gets the reserved urls from web.config. /// @@ -185,36 +194,11 @@ namespace Umbraco.Core.Configuration } } - /// - /// Gets the name of the content XML file. - /// - /// The content XML. - /// - /// Defaults to ~/App_Data/umbraco.config - /// - public string ContentXmlFile - { - get - { - return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ContentXML) - ? ConfigurationManager.AppSettings[Constants.AppSettings.ContentXML] - : "~/App_Data/umbraco.config"; - } - } - /// /// Gets the path to umbraco's root directory (/umbraco by default). /// /// The path. - public string Path - { - get - { - return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.Path) - ? _ioHelper.ResolveUrl(ConfigurationManager.AppSettings[Constants.AppSettings.Path]) - : string.Empty; - } - } + public string Path => ConfigurationManager.AppSettings[Constants.AppSettings.Path]; /// /// Gets or sets the configuration status. This will return the version number of the currently installed umbraco instance. @@ -230,7 +214,7 @@ namespace Umbraco.Core.Configuration } set { - SaveSetting(Constants.AppSettings.ConfigurationStatus, value, _ioHelper); + SaveSetting(Constants.AppSettings.ConfigurationStatus, value, Current.IOHelper); //TODO remove } } @@ -257,26 +241,6 @@ namespace Umbraco.Core.Configuration ConfigurationManager.RefreshSection("appSettings"); } - /// - /// Removes a setting from the configuration file. - /// - /// Key of the setting to be removed. - public static void RemoveSetting(string key, IIOHelper ioHelper) - { - var fileName = ioHelper.MapPath(string.Format("{0}/web.config", ioHelper.Root)); - var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); - - var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single(); - var setting = appSettings.Descendants("add").FirstOrDefault(s => s.Attribute("key").Value == key); - - if (setting != null) - { - setting.Remove(); - xml.Save(fileName, SaveOptions.DisableFormatting); - ConfigurationManager.RefreshSection("appSettings"); - } - } - /// /// Gets the time out in minutes. @@ -405,6 +369,10 @@ namespace Umbraco.Core.Configuration private string _databaseFactoryServerVersion; public string DatabaseFactoryServerVersion => GetterWithDefaultValue(Constants.AppSettings.Debug.DatabaseFactoryServerVersion, string.Empty, ref _databaseFactoryServerVersion); + private string _mainDomLock; + + public string MainDomLock => GetterWithDefaultValue(Constants.AppSettings.MainDomLock, string.Empty, ref _mainDomLock); + private T GetterWithDefaultValue(string appSettingKey, T defaultValue, ref T backingField) { if (backingField != null) return backingField; @@ -430,5 +398,22 @@ namespace Umbraco.Core.Configuration return backingField; } + + /// + /// Gets the path to the razor file used when no published content is available. + /// + public string NoNodesViewPath + { + get + { + var configuredValue = ConfigurationManager.AppSettings[Constants.AppSettings.NoNodesViewPath]; + if (!string.IsNullOrWhiteSpace(configuredValue)) + { + return configuredValue; + } + + return "~/config/splashes/NoNodes.cshtml"; + } + } } } diff --git a/src/Umbraco.Configuration/HealthChecks/HealthChecksSection.cs b/src/Umbraco.Configuration/HealthChecks/HealthChecksSection.cs index 90a7d8c567..373d846567 100644 --- a/src/Umbraco.Configuration/HealthChecks/HealthChecksSection.cs +++ b/src/Umbraco.Configuration/HealthChecks/HealthChecksSection.cs @@ -1,10 +1,9 @@ using System; -using System.Collections.Generic; using System.Configuration; namespace Umbraco.Core.Configuration.HealthChecks { - public class HealthChecksSection : ConfigurationSection, IHealthChecks + public class HealthChecksSection : ConfigurationSection { private const string DisabledChecksKey = "disabledChecks"; private const string NotificationSettingsKey = "notificationSettings"; @@ -21,14 +20,5 @@ namespace Umbraco.Core.Configuration.HealthChecks get { return ((HealthCheckNotificationSettingsElement)(base[NotificationSettingsKey])); } } - IEnumerable IHealthChecks.DisabledChecks - { - get { return DisabledChecks; } - } - - IHealthCheckNotificationSettings IHealthChecks.NotificationSettings - { - get { return NotificationSettings; } - } } } diff --git a/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs b/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs new file mode 100644 index 0000000000..0302a7ea31 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs @@ -0,0 +1,22 @@ +using System.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal abstract class ConfigurationManagerConfigBase + { + private UmbracoSettingsSection _umbracoSettingsSection; + + protected UmbracoSettingsSection UmbracoSettingsSection + { + get + { + if (_umbracoSettingsSection is null) + { + _umbracoSettingsSection = ConfigurationManager.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; + } + return _umbracoSettingsSection; + } + } + } +} diff --git a/src/Umbraco.Configuration/Implementations/ContentSettings.cs b/src/Umbraco.Configuration/Implementations/ContentSettings.cs new file mode 100644 index 0000000000..1c3f543bfe --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/ContentSettings.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Macros; + +namespace Umbraco.Configuration.Implementations +{ + internal class ContentSettings : ConfigurationManagerConfigBase, IContentSettings + { + public string NotificationEmailAddress => UmbracoSettingsSection.Content.Notifications.NotificationEmailAddress; + public bool DisableHtmlEmail => UmbracoSettingsSection.Content.Notifications.DisableHtmlEmail; + public IEnumerable ImageFileTypes => UmbracoSettingsSection.Content.Imaging.ImageFileTypes; + public IEnumerable ImageAutoFillProperties => UmbracoSettingsSection.Content.Imaging.ImageAutoFillProperties; + public bool ResolveUrlsFromTextString => UmbracoSettingsSection.Content.ResolveUrlsFromTextString; + public IEnumerable Error404Collection => UmbracoSettingsSection.Content.Error404Collection; + public string PreviewBadge => UmbracoSettingsSection.Content.PreviewBadge; + public MacroErrorBehaviour MacroErrorBehaviour => UmbracoSettingsSection.Content.MacroErrors; + public IEnumerable DisallowedUploadFiles => UmbracoSettingsSection.Content.DisallowedUploadFiles; + public IEnumerable AllowedUploadFiles => UmbracoSettingsSection.Content.AllowedUploadFiles; + public bool ShowDeprecatedPropertyEditors => UmbracoSettingsSection.Content.ShowDeprecatedPropertyEditors; + public string LoginBackgroundImage => UmbracoSettingsSection.Content.LoginBackgroundImage; + } +} diff --git a/src/Umbraco.Configuration/Implementations/HealthChecksSettings.cs b/src/Umbraco.Configuration/Implementations/HealthChecksSettings.cs new file mode 100644 index 0000000000..656a3ffc82 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/HealthChecksSettings.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Configuration; +using Umbraco.Core.Configuration.HealthChecks; + +namespace Umbraco.Core.Configuration.Implementations +{ + public class HealthChecksSettings : IHealthChecks + { + private HealthChecksSection _healthChecksSection; + + private HealthChecksSection HealthChecksSection + { + get + { + if (_healthChecksSection is null) + { + _healthChecksSection = ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks") as HealthChecksSection; + } + return _healthChecksSection; + } + } + + public IEnumerable DisabledChecks => HealthChecksSection.DisabledChecks; + public IHealthCheckNotificationSettings NotificationSettings => HealthChecksSection.NotificationSettings; + } +} diff --git a/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs b/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs new file mode 100644 index 0000000000..0b8315d447 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs @@ -0,0 +1,10 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class KeepAliveSettings : ConfigurationManagerConfigBase, IKeepAliveSettings + { + public bool DisableKeepAliveTask => UmbracoSettingsSection.KeepAlive.DisableKeepAliveTask; + public string KeepAlivePingUrl => UmbracoSettingsSection.KeepAlive.KeepAlivePingUrl; + } +} diff --git a/src/Umbraco.Configuration/Implementations/LoggingSettings.cs b/src/Umbraco.Configuration/Implementations/LoggingSettings.cs new file mode 100644 index 0000000000..020b0c0e64 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/LoggingSettings.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class LoggingSettings : ConfigurationManagerConfigBase, ILoggingSettings + { + public int MaxLogAge => UmbracoSettingsSection.Logging.MaxLogAge; + } +} diff --git a/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs b/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs new file mode 100644 index 0000000000..e42b02de73 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration.Implementations +{ + internal class MemberPasswordConfigurationSettings : ConfigurationManagerConfigBase, IMemberPasswordConfiguration + { + public int RequiredLength => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequiredLength; + public bool RequireNonLetterOrDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit; + public bool RequireDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireDigit; + public bool RequireLowercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireLowercase; + public bool RequireUppercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireUppercase; + public bool UseLegacyEncoding=> UmbracoSettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding; + public string HashAlgorithmType=> UmbracoSettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType; + public int MaxFailedAccessAttemptsBeforeLockout => UmbracoSettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout; + } +} diff --git a/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs b/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs new file mode 100644 index 0000000000..0cd011c863 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class RequestHandlerSettings : ConfigurationManagerConfigBase, IRequestHandlerSettings + { + public bool AddTrailingSlash => UmbracoSettingsSection.RequestHandler.AddTrailingSlash; + public bool ConvertUrlsToAscii => UmbracoSettingsSection.RequestHandler.UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); + public bool TryConvertUrlsToAscii => UmbracoSettingsSection.RequestHandler.UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); + public IEnumerable CharCollection => UmbracoSettingsSection.RequestHandler.UrlReplacing.CharCollection; + } +} diff --git a/src/Umbraco.Configuration/Implementations/SecuritySettings.cs b/src/Umbraco.Configuration/Implementations/SecuritySettings.cs new file mode 100644 index 0000000000..b7e39b0608 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/SecuritySettings.cs @@ -0,0 +1,14 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class SecuritySettings : ConfigurationManagerConfigBase, ISecuritySettings + { + public bool KeepUserLoggedIn => UmbracoSettingsSection.Security.KeepUserLoggedIn; + public bool HideDisabledUsersInBackoffice => UmbracoSettingsSection.Security.HideDisabledUsersInBackoffice; + public bool AllowPasswordReset => UmbracoSettingsSection.Security.AllowPasswordReset; + public string AuthCookieName => UmbracoSettingsSection.Security.AuthCookieName; + public string AuthCookieDomain => UmbracoSettingsSection.Security.AuthCookieDomain; + public bool UsernameIsEmail => UmbracoSettingsSection.Security.UsernameIsEmail; + } +} diff --git a/src/Umbraco.Configuration/Implementations/TourSettings.cs b/src/Umbraco.Configuration/Implementations/TourSettings.cs new file mode 100644 index 0000000000..134c3c48d5 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/TourSettings.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class TourSettings : ConfigurationManagerConfigBase, ITourSettings + { + public bool EnableTours => UmbracoSettingsSection.BackOffice.Tours.EnableTours; + } +} diff --git a/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs b/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs new file mode 100644 index 0000000000..51dd645c42 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Configuration; +namespace Umbraco.Configuration.Implementations +{ + internal class UserPasswordConfigurationSettings : ConfigurationManagerConfigBase, IUserPasswordConfiguration + { + public int RequiredLength => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequiredLength; + public bool RequireNonLetterOrDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit; + public bool RequireDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireDigit; + public bool RequireLowercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireLowercase; + public bool RequireUppercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireUppercase; + public bool UseLegacyEncoding=> UmbracoSettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding; + public string HashAlgorithmType=> UmbracoSettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType; + public int MaxFailedAccessAttemptsBeforeLockout => UmbracoSettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout; + } +} diff --git a/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs b/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs new file mode 100644 index 0000000000..5da3de20a0 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class WebRoutingSettings : ConfigurationManagerConfigBase, IWebRoutingSettings + { + public bool TrySkipIisCustomErrors => UmbracoSettingsSection.WebRouting.TrySkipIisCustomErrors; + public bool InternalRedirectPreservesTemplate => UmbracoSettingsSection.WebRouting.InternalRedirectPreservesTemplate; + public bool DisableAlternativeTemplates => UmbracoSettingsSection.WebRouting.DisableAlternativeTemplates; + public bool ValidateAlternativeTemplates => UmbracoSettingsSection.WebRouting.ValidateAlternativeTemplates; + public bool DisableFindContentByIdPath => UmbracoSettingsSection.WebRouting.DisableFindContentByIdPath; + public bool DisableRedirectUrlTracking => UmbracoSettingsSection.WebRouting.DisableRedirectUrlTracking; + public string UrlProviderMode => UmbracoSettingsSection.WebRouting.UrlProviderMode; + public string UmbracoApplicationUrl => UmbracoSettingsSection.WebRouting.UmbracoApplicationUrl; + } +} diff --git a/src/Umbraco.Configuration/IndexCreatorSettings.cs b/src/Umbraco.Configuration/IndexCreatorSettings.cs new file mode 100644 index 0000000000..00d1a29dba --- /dev/null +++ b/src/Umbraco.Configuration/IndexCreatorSettings.cs @@ -0,0 +1,15 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class IndexCreatorSettings : IIndexCreatorSettings + { + public IndexCreatorSettings() + { + LuceneDirectoryFactory = ConfigurationManager.AppSettings["Umbraco.Examine.LuceneDirectoryFactory"]; + } + + public string LuceneDirectoryFactory { get; } + } +} 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.ModelsBuilder.Embedded/Configuration/ModelsBuilderConfig.cs b/src/Umbraco.Configuration/ModelsBuilderConfig.cs similarity index 56% rename from src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsBuilderConfig.cs rename to src/Umbraco.Configuration/ModelsBuilderConfig.cs index 4fb46facaf..79f4b065a0 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsBuilderConfig.cs +++ b/src/Umbraco.Configuration/ModelsBuilderConfig.cs @@ -1,110 +1,81 @@ using System; using System.Configuration; using System.IO; -using System.Web.Configuration; +using System.Threading; +using Umbraco.Core.Configuration; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; -namespace Umbraco.ModelsBuilder.Embedded.Configuration +namespace Umbraco.Configuration { /// /// Represents the models builder configuration. /// public class ModelsBuilderConfig : IModelsBuilderConfig { - private readonly IIOHelper _ioHelper; - public const string DefaultModelsNamespace = "Umbraco.Web.PublishedModels"; - public string DefaultModelsDirectory => _ioHelper.MapPath("~/App_Data/Models"); + private const string Prefix = "Umbraco.ModelsBuilder."; + private object _modelsModelLock; + private bool _modelsModelConfigured; + private ModelsMode _modelsMode; + private object _flagOutOfDateModelsLock; + private bool _flagOutOfDateModelsConfigured; + private bool _flagOutOfDateModels; + + + public string DefaultModelsDirectory => "~/App_Data/Models"; /// /// Initializes a new instance of the class. /// - public ModelsBuilderConfig(IIOHelper ioHelper) + public ModelsBuilderConfig() { - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - const string prefix = "Umbraco.ModelsBuilder."; - // giant kill switch, default: false // must be explicitely set to true for anything else to happen - Enable = ConfigurationManager.AppSettings[prefix + "Enable"] == "true"; + Enable = ConfigurationManager.AppSettings[Prefix + "Enable"] == "true"; // ensure defaults are initialized for tests - ModelsNamespace = DefaultModelsNamespace; + ModelsNamespace = Constants.ModelsBuilder.DefaultModelsNamespace; ModelsDirectory = DefaultModelsDirectory; DebugLevel = 0; // stop here, everything is false if (!Enable) return; - // mode - var modelsMode = ConfigurationManager.AppSettings[prefix + "ModelsMode"]; - if (!string.IsNullOrWhiteSpace(modelsMode)) - { - switch (modelsMode) - { - case nameof(ModelsMode.Nothing): - ModelsMode = ModelsMode.Nothing; - break; - case nameof(ModelsMode.PureLive): - ModelsMode = ModelsMode.PureLive; - break; - case nameof(ModelsMode.AppData): - ModelsMode = ModelsMode.AppData; - break; - case nameof(ModelsMode.LiveAppData): - ModelsMode = ModelsMode.LiveAppData; - break; - default: - throw new ConfigurationErrorsException($"ModelsMode \"{modelsMode}\" is not a valid mode." - + " Note that modes are case-sensitive. Possible values are: " + string.Join(", ", Enum.GetNames(typeof(ModelsMode)))); - } - } - // default: false - AcceptUnsafeModelsDirectory = ConfigurationManager.AppSettings[prefix + "AcceptUnsafeModelsDirectory"].InvariantEquals("true"); + AcceptUnsafeModelsDirectory = ConfigurationManager.AppSettings[Prefix + "AcceptUnsafeModelsDirectory"].InvariantEquals("true"); // default: true - EnableFactory = !ConfigurationManager.AppSettings[prefix + "EnableFactory"].InvariantEquals("false"); - FlagOutOfDateModels = !ConfigurationManager.AppSettings[prefix + "FlagOutOfDateModels"].InvariantEquals("false"); + EnableFactory = !ConfigurationManager.AppSettings[Prefix + "EnableFactory"].InvariantEquals("false"); // default: initialized above with DefaultModelsNamespace const - var value = ConfigurationManager.AppSettings[prefix + "ModelsNamespace"]; + var value = ConfigurationManager.AppSettings[Prefix + "ModelsNamespace"]; if (!string.IsNullOrWhiteSpace(value)) ModelsNamespace = value; // default: initialized above with DefaultModelsDirectory const - value = ConfigurationManager.AppSettings[prefix + "ModelsDirectory"]; + value = ConfigurationManager.AppSettings[Prefix + "ModelsDirectory"]; if (!string.IsNullOrWhiteSpace(value)) { - var root = Current.IOHelper.MapPath("~/"); - if (root == null) - throw new ConfigurationErrorsException("Could not determine root directory."); - // GetModelsDirectory will ensure that the path is safe - ModelsDirectory = GetModelsDirectory(root, value, AcceptUnsafeModelsDirectory); + ModelsDirectory = value; } // default: 0 - value = ConfigurationManager.AppSettings[prefix + "DebugLevel"]; + value = ConfigurationManager.AppSettings[Prefix + "DebugLevel"]; if (!string.IsNullOrWhiteSpace(value)) { - int debugLevel; - if (!int.TryParse(value, out debugLevel)) + if (!int.TryParse(value, out var debugLevel)) throw new ConfigurationErrorsException($"Invalid debug level \"{value}\"."); DebugLevel = debugLevel; } - // not flagging if not generating, or live (incl. pure) - if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsLive()) - FlagOutOfDateModels = false; } /// /// Initializes a new instance of the class. /// - public ModelsBuilderConfig(IIOHelper ioHelper, + public ModelsBuilderConfig( bool enable = false, ModelsMode modelsMode = ModelsMode.Nothing, string modelsNamespace = null, @@ -114,48 +85,18 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration bool acceptUnsafeModelsDirectory = false, int debugLevel = 0) { - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); Enable = enable; - ModelsMode = modelsMode; + _modelsMode = modelsMode; - ModelsNamespace = string.IsNullOrWhiteSpace(modelsNamespace) ? DefaultModelsNamespace : modelsNamespace; + ModelsNamespace = string.IsNullOrWhiteSpace(modelsNamespace) ? Constants.ModelsBuilder.DefaultModelsNamespace : modelsNamespace; EnableFactory = enableFactory; - FlagOutOfDateModels = flagOutOfDateModels; + _flagOutOfDateModels = flagOutOfDateModels; ModelsDirectory = string.IsNullOrWhiteSpace(modelsDirectory) ? DefaultModelsDirectory : modelsDirectory; AcceptUnsafeModelsDirectory = acceptUnsafeModelsDirectory; DebugLevel = debugLevel; } - // internal for tests - internal static string GetModelsDirectory(string root, string config, bool acceptUnsafe) - { - // making sure it is safe, ie under the website root, - // unless AcceptUnsafeModelsDirectory and then everything is OK. - if (!Path.IsPathRooted(root)) - throw new ConfigurationErrorsException($"Root is not rooted \"{root}\"."); - - if (config.StartsWith("~/")) - { - var dir = Path.Combine(root, config.TrimStart("~/")); - - // sanitize - GetFullPath will take care of any relative - // segments in path, eg '../../foo.tmp' - it may throw a SecurityException - // if the combined path reaches illegal parts of the filesystem - dir = Path.GetFullPath(dir); - root = Path.GetFullPath(root); - - if (!dir.StartsWith(root) && !acceptUnsafe) - throw new ConfigurationErrorsException($"Invalid models directory \"{config}\"."); - - return dir; - } - - if (acceptUnsafe) - return Path.GetFullPath(config); - - throw new ConfigurationErrorsException($"Invalid models directory \"{config}\"."); - } /// /// Gets a value indicating whether the whole models experience is enabled. @@ -169,7 +110,26 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration /// /// Gets the models mode. /// - public ModelsMode ModelsMode { get; } + public ModelsMode ModelsMode => + LazyInitializer.EnsureInitialized(ref _modelsMode, ref _modelsModelConfigured, ref _modelsModelLock, () => + { + // mode + var modelsMode = ConfigurationManager.AppSettings[Prefix + "ModelsMode"]; + if (string.IsNullOrWhiteSpace(modelsMode)) return ModelsMode.Nothing; //default + switch (modelsMode) + { + case nameof(ModelsMode.Nothing): + return ModelsMode.Nothing; + case nameof(ModelsMode.PureLive): + return ModelsMode.PureLive; + case nameof(ModelsMode.AppData): + return ModelsMode.AppData; + case nameof(ModelsMode.LiveAppData): + return ModelsMode.LiveAppData; + default: + throw new ConfigurationErrorsException($"ModelsMode \"{modelsMode}\" is not a valid mode." + " Note that modes are case-sensitive. Possible values are: " + string.Join(", ", Enum.GetNames(typeof(ModelsMode)))); + } + }); /// /// Gets a value indicating whether system.web/compilation/@debug is true. @@ -178,8 +138,13 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration { get { - var section = (CompilationSection)ConfigurationManager.GetSection("system.web/compilation"); - return section != null && section.Debug; + if (ConfigurationManager.GetSection("system.web/compilation") is ConfigurationSection section && + bool.TryParse(section.ElementInformation.Properties["debug"].Value.ToString(), out var isDebug)) + { + return isDebug; + } + + return false; } } @@ -201,7 +166,17 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration /// Models become out-of-date when data types or content types are updated. When this /// setting is activated the ~/App_Data/Models/ood.txt file is then created. When models are /// generated through the dashboard, the files is cleared. Default value is false. - public bool FlagOutOfDateModels { get; } + public bool FlagOutOfDateModels + => LazyInitializer.EnsureInitialized(ref _flagOutOfDateModels, ref _flagOutOfDateModelsConfigured, ref _flagOutOfDateModelsLock, () => + { + var flagOutOfDateModels = !ConfigurationManager.AppSettings[Prefix + "FlagOutOfDateModels"].InvariantEquals("false"); + if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsLive()) + { + flagOutOfDateModels = false; + } + + return flagOutOfDateModels; + }); /// /// Gets the models directory. diff --git a/src/Umbraco.Configuration/NuCacheSettings.cs b/src/Umbraco.Configuration/NuCacheSettings.cs new file mode 100644 index 0000000000..c3a286d33d --- /dev/null +++ b/src/Umbraco.Configuration/NuCacheSettings.cs @@ -0,0 +1,14 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class NuCacheSettings : INuCacheSettings + { + public NuCacheSettings() + { + BTreeBlockSize = ConfigurationManager.AppSettings["Umbraco.Web.PublishedCache.NuCache.BTree.BlockSize"]; + } + public string BTreeBlockSize { get; } + } +} diff --git a/src/Umbraco.Configuration/Properties/AssemblyInfo.cs b/src/Umbraco.Configuration/Properties/AssemblyInfo.cs index d10dd929da..b77c087e22 100644 --- a/src/Umbraco.Configuration/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Configuration/Properties/AssemblyInfo.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; // Umbraco Cms [assembly: InternalsVisibleTo("Umbraco.Tests")] +[assembly: InternalsVisibleTo("Umbraco.Tests.Common")] [assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] // Allow this to be mocked in our unit tests diff --git a/src/Umbraco.Configuration/RuntimeSettings.cs b/src/Umbraco.Configuration/RuntimeSettings.cs new file mode 100644 index 0000000000..6dc8d6f832 --- /dev/null +++ b/src/Umbraco.Configuration/RuntimeSettings.cs @@ -0,0 +1,29 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class RuntimeSettings : IRuntimeSettings + { + public RuntimeSettings() + { + if (ConfigurationManager.GetSection("system.web/httpRuntime") is ConfigurationSection section) + { + var maxRequestLengthProperty = section.ElementInformation.Properties["maxRequestLength"]; + if (maxRequestLengthProperty != null && maxRequestLengthProperty.Value is int requestLength) + { + MaxRequestLength = requestLength; + } + + var maxQueryStringProperty = section.ElementInformation.Properties["maxQueryStringLength"]; + if (maxQueryStringProperty != null && maxQueryStringProperty.Value is int maxQueryStringLength) + { + MaxQueryStringLength = maxQueryStringLength; + } + } + } + public int? MaxQueryStringLength { get; } + public int? MaxRequestLength { get; } + + } +} diff --git a/src/Umbraco.Configuration/SmtpSettings.cs b/src/Umbraco.Configuration/SmtpSettings.cs new file mode 100644 index 0000000000..7e3ff80690 --- /dev/null +++ b/src/Umbraco.Configuration/SmtpSettings.cs @@ -0,0 +1,12 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class SmtpSettings : ISmtpSettings + { + public string From { get; set; } + public string Host { get; set; } + public int Port { get; set; } + public string PickupDirectoryLocation { get; set; } + } +} diff --git a/src/Umbraco.Configuration/TypeFinderSettings.cs b/src/Umbraco.Configuration/TypeFinderSettings.cs new file mode 100644 index 0000000000..bb3063d7bf --- /dev/null +++ b/src/Umbraco.Configuration/TypeFinderSettings.cs @@ -0,0 +1,17 @@ +using System.Configuration; +using Umbraco.Core.Configuration; +using Umbraco.Core; + +namespace Umbraco.Configuration +{ + public class TypeFinderSettings : ITypeFinderSettings + { + public TypeFinderSettings() + { + AssembliesAcceptingLoadExceptions = ConfigurationManager.AppSettings[ + Constants.AppSettings.AssembliesAcceptingLoadExceptions]; + } + + public string AssembliesAcceptingLoadExceptions { get; } + } +} diff --git a/src/Umbraco.Configuration/Umbraco.Configuration.csproj b/src/Umbraco.Configuration/Umbraco.Configuration.csproj index 15c6ac263f..57fca1dfd6 100644 --- a/src/Umbraco.Configuration/Umbraco.Configuration.csproj +++ b/src/Umbraco.Configuration/Umbraco.Configuration.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 8 @@ -25,7 +26,7 @@ - + diff --git a/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs b/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs index 79bff51d05..46b9bf32a9 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs @@ -7,6 +7,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("tours")] internal TourConfigElement Tours => (TourConfigElement)this["tours"]; - ITourSection IBackOfficeSection.Tours => Tours; + ITourSettings IBackOfficeSection.Tours => Tours; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Configuration/UmbracoSettings/ContentElement.cs index 8e4f0edd8f..28b4314c9a 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/ContentElement.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Macros; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class ContentElement : UmbracoConfigurationElement, IContentSection + internal class ContentElement : UmbracoConfigurationElement, IContentSettings { private const string DefaultPreviewBadge = @"
Preview modeClick to end
"; @@ -40,26 +40,26 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("loginBackgroundImage")] internal InnerTextConfigurationElement LoginBackgroundImage => GetOptionalTextElement("loginBackgroundImage", string.Empty); - string IContentSection.NotificationEmailAddress => Notifications.NotificationEmailAddress; + string IContentSettings.NotificationEmailAddress => Notifications.NotificationEmailAddress; - bool IContentSection.DisableHtmlEmail => Notifications.DisableHtmlEmail; + bool IContentSettings.DisableHtmlEmail => Notifications.DisableHtmlEmail; - IEnumerable IContentSection.ImageFileTypes => Imaging.ImageFileTypes; + IEnumerable IContentSettings.ImageFileTypes => Imaging.ImageFileTypes; - IEnumerable IContentSection.ImageAutoFillProperties => Imaging.ImageAutoFillProperties; + IEnumerable IContentSettings.ImageAutoFillProperties => Imaging.ImageAutoFillProperties; - bool IContentSection.ResolveUrlsFromTextString => ResolveUrlsFromTextString; + bool IContentSettings.ResolveUrlsFromTextString => ResolveUrlsFromTextString; - string IContentSection.PreviewBadge => PreviewBadge; + string IContentSettings.PreviewBadge => PreviewBadge; - MacroErrorBehaviour IContentSection.MacroErrorBehaviour => MacroErrors; + MacroErrorBehaviour IContentSettings.MacroErrorBehaviour => MacroErrors; - IEnumerable IContentSection.DisallowedUploadFiles => DisallowedUploadFiles; + IEnumerable IContentSettings.DisallowedUploadFiles => DisallowedUploadFiles; - IEnumerable IContentSection.AllowedUploadFiles => AllowedUploadFiles; + IEnumerable IContentSettings.AllowedUploadFiles => AllowedUploadFiles; - bool IContentSection.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; + bool IContentSettings.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; - string IContentSection.LoginBackgroundImage => LoginBackgroundImage; + string IContentSettings.LoginBackgroundImage => LoginBackgroundImage; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs index 89ba9be54d..2297fb4e20 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class KeepAliveElement : ConfigurationElement, IKeepAliveSection + internal class KeepAliveElement : ConfigurationElement, IKeepAliveSettings { [ConfigurationProperty("disableKeepAliveTask", DefaultValue = "false")] public bool DisableKeepAliveTask => (bool)base["disableKeepAliveTask"]; diff --git a/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs b/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs index 106b6cc134..2fdd61e169 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs @@ -3,12 +3,12 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class LoggingElement : UmbracoConfigurationElement, ILoggingSection + internal class LoggingElement : UmbracoConfigurationElement, ILoggingSettings { [ConfigurationProperty("maxLogAge")] internal InnerTextConfigurationElement MaxLogAge => GetOptionalTextElement("maxLogAge", -1); - int ILoggingSection.MaxLogAge => MaxLogAge; + int ILoggingSettings.MaxLogAge => MaxLogAge; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs index 93c7c20159..92cd112630 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class MemberPasswordConfigurationElement : PasswordConfigurationElement, IMemberPasswordConfigurationSection + internal class MemberPasswordConfigurationElement : PasswordConfigurationElement, IMemberPasswordConfiguration { } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs b/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs index 80fcb6ca1a..f959a56e71 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSection + internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSettings { [ConfigurationProperty("addTrailingSlash")] public InnerTextConfigurationElement AddTrailingSlash => GetOptionalTextElement("addTrailingSlash", true); @@ -85,12 +85,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings return collection; } - bool IRequestHandlerSection.AddTrailingSlash => AddTrailingSlash; + bool IRequestHandlerSettings.AddTrailingSlash => AddTrailingSlash; - bool IRequestHandlerSection.ConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); + bool IRequestHandlerSettings.ConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); - bool IRequestHandlerSection.TryConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); + bool IRequestHandlerSettings.TryConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); - IEnumerable IRequestHandlerSection.CharCollection => UrlReplacing.CharCollection; + IEnumerable IRequestHandlerSettings.CharCollection => UrlReplacing.CharCollection; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs index 82012cfd0f..aec6809298 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class SecurityElement : UmbracoConfigurationElement, ISecuritySection + internal class SecurityElement : UmbracoConfigurationElement, ISecuritySettings { [ConfigurationProperty("keepUserLoggedIn")] internal InnerTextConfigurationElement KeepUserLoggedIn => GetOptionalTextElement("keepUserLoggedIn", true); @@ -38,14 +38,14 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("memberPasswordConfiguration")] public MemberPasswordConfigurationElement MemberPasswordConfiguration => (MemberPasswordConfigurationElement)this["memberPasswordConfiguration"]; - bool ISecuritySection.KeepUserLoggedIn => KeepUserLoggedIn; + bool ISecuritySettings.KeepUserLoggedIn => KeepUserLoggedIn; - bool ISecuritySection.HideDisabledUsersInBackoffice => HideDisabledUsersInBackoffice; + bool ISecuritySettings.HideDisabledUsersInBackoffice => HideDisabledUsersInBackoffice; /// /// Used to enable/disable the forgot password functionality on the back office login screen /// - bool ISecuritySection.AllowPasswordReset => AllowPasswordReset; + bool ISecuritySettings.AllowPasswordReset => AllowPasswordReset; /// /// A boolean indicating that by default the email address will be the username @@ -54,14 +54,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings /// Even if this is true and the username is different from the email in the database, the username field will still be shown. /// When this is false, the username and email fields will be shown in the user section. /// - bool ISecuritySection.UsernameIsEmail => UsernameIsEmail; + bool ISecuritySettings.UsernameIsEmail => UsernameIsEmail; - string ISecuritySection.AuthCookieName => AuthCookieName; + string ISecuritySettings.AuthCookieName => AuthCookieName; - string ISecuritySection.AuthCookieDomain => AuthCookieDomain; - - IUserPasswordConfigurationSection ISecuritySection.UserPasswordConfiguration => UserPasswordConfiguration; - - IMemberPasswordConfigurationSection ISecuritySection.MemberPasswordConfiguration => MemberPasswordConfiguration; + string ISecuritySettings.AuthCookieDomain => AuthCookieDomain; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs b/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs index dab69f3da0..f75b71fc57 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class TourConfigElement : UmbracoConfigurationElement, ITourSection + internal class TourConfigElement : UmbracoConfigurationElement, ITourSettings { //disabled by default so that upgraders don't get it enabled by default // TODO: we probably just want to disable the initial one from automatically loading ? diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index e605b86edf..781d00b979 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection + internal class UmbracoSettingsSection : ConfigurationSection { [ConfigurationProperty("backOffice")] public BackOfficeElement BackOffice => (BackOfficeElement)this["backOffice"]; @@ -24,19 +24,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("keepAlive")] internal KeepAliveElement KeepAlive => (KeepAliveElement)this["keepAlive"]; - - IContentSection IUmbracoSettingsSection.Content => Content; - - ISecuritySection IUmbracoSettingsSection.Security => Security; - - IRequestHandlerSection IUmbracoSettingsSection.RequestHandler => RequestHandler; - - IBackOfficeSection IUmbracoSettingsSection.BackOffice => BackOffice; - - ILoggingSection IUmbracoSettingsSection.Logging => Logging; - - IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting; - - IKeepAliveSection IUmbracoSettingsSection.KeepAlive => KeepAlive; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs index 8128f3d8e7..a1d2aa8842 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class UserPasswordConfigurationElement : PasswordConfigurationElement, IUserPasswordConfigurationSection + internal class UserPasswordConfigurationElement : PasswordConfigurationElement, IUserPasswordConfiguration { } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs index 7b7102f2e7..206fc213d2 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class WebRoutingElement : ConfigurationElement, IWebRoutingSection + internal class WebRoutingElement : ConfigurationElement, IWebRoutingSettings { [ConfigurationProperty("trySkipIisCustomErrors", DefaultValue = "false")] public bool TrySkipIisCustomErrors => (bool) base["trySkipIisCustomErrors"]; diff --git a/src/Umbraco.Abstractions/Actions/ActionAssignDomain.cs b/src/Umbraco.Core/Actions/ActionAssignDomain.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionAssignDomain.cs rename to src/Umbraco.Core/Actions/ActionAssignDomain.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionBrowse.cs b/src/Umbraco.Core/Actions/ActionBrowse.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionBrowse.cs rename to src/Umbraco.Core/Actions/ActionBrowse.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionChangeDocType.cs b/src/Umbraco.Core/Actions/ActionChangeDocType.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionChangeDocType.cs rename to src/Umbraco.Core/Actions/ActionChangeDocType.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionCollection.cs b/src/Umbraco.Core/Actions/ActionCollection.cs similarity index 97% rename from src/Umbraco.Abstractions/Actions/ActionCollection.cs rename to src/Umbraco.Core/Actions/ActionCollection.cs index 89ac8a59f4..f6a3d92995 100644 --- a/src/Umbraco.Abstractions/Actions/ActionCollection.cs +++ b/src/Umbraco.Core/Actions/ActionCollection.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Actions : base(items) { } - internal T GetAction() + public T GetAction() where T : IAction { return this.OfType().FirstOrDefault(); diff --git a/src/Umbraco.Abstractions/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Core/Actions/ActionCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionCollectionBuilder.cs rename to src/Umbraco.Core/Actions/ActionCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionCopy.cs b/src/Umbraco.Core/Actions/ActionCopy.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionCopy.cs rename to src/Umbraco.Core/Actions/ActionCopy.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionCreateBlueprintFromContent.cs b/src/Umbraco.Core/Actions/ActionCreateBlueprintFromContent.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionCreateBlueprintFromContent.cs rename to src/Umbraco.Core/Actions/ActionCreateBlueprintFromContent.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionDelete.cs b/src/Umbraco.Core/Actions/ActionDelete.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionDelete.cs rename to src/Umbraco.Core/Actions/ActionDelete.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionMove.cs b/src/Umbraco.Core/Actions/ActionMove.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionMove.cs rename to src/Umbraco.Core/Actions/ActionMove.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionNew.cs b/src/Umbraco.Core/Actions/ActionNew.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionNew.cs rename to src/Umbraco.Core/Actions/ActionNew.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionProtect.cs b/src/Umbraco.Core/Actions/ActionProtect.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionProtect.cs rename to src/Umbraco.Core/Actions/ActionProtect.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionPublish.cs b/src/Umbraco.Core/Actions/ActionPublish.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionPublish.cs rename to src/Umbraco.Core/Actions/ActionPublish.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionRestore.cs b/src/Umbraco.Core/Actions/ActionRestore.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionRestore.cs rename to src/Umbraco.Core/Actions/ActionRestore.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionRights.cs b/src/Umbraco.Core/Actions/ActionRights.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionRights.cs rename to src/Umbraco.Core/Actions/ActionRights.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionRollback.cs b/src/Umbraco.Core/Actions/ActionRollback.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionRollback.cs rename to src/Umbraco.Core/Actions/ActionRollback.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionSort.cs b/src/Umbraco.Core/Actions/ActionSort.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionSort.cs rename to src/Umbraco.Core/Actions/ActionSort.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionToPublish.cs b/src/Umbraco.Core/Actions/ActionToPublish.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionToPublish.cs rename to src/Umbraco.Core/Actions/ActionToPublish.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionUnpublish.cs b/src/Umbraco.Core/Actions/ActionUnpublish.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionUnpublish.cs rename to src/Umbraco.Core/Actions/ActionUnpublish.cs diff --git a/src/Umbraco.Abstractions/Actions/ActionUpdate.cs b/src/Umbraco.Core/Actions/ActionUpdate.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/ActionUpdate.cs rename to src/Umbraco.Core/Actions/ActionUpdate.cs diff --git a/src/Umbraco.Abstractions/Actions/IAction.cs b/src/Umbraco.Core/Actions/IAction.cs similarity index 100% rename from src/Umbraco.Abstractions/Actions/IAction.cs rename to src/Umbraco.Core/Actions/IAction.cs diff --git a/src/Umbraco.Abstractions/AssemblyExtensions.cs b/src/Umbraco.Core/AssemblyExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/AssemblyExtensions.cs rename to src/Umbraco.Core/AssemblyExtensions.cs diff --git a/src/Umbraco.Abstractions/Attempt.cs b/src/Umbraco.Core/Attempt.cs similarity index 100% rename from src/Umbraco.Abstractions/Attempt.cs rename to src/Umbraco.Core/Attempt.cs diff --git a/src/Umbraco.Abstractions/AttemptOfTResult.cs b/src/Umbraco.Core/AttemptOfTResult.cs similarity index 100% rename from src/Umbraco.Abstractions/AttemptOfTResult.cs rename to src/Umbraco.Core/AttemptOfTResult.cs diff --git a/src/Umbraco.Abstractions/AttemptOfTResultTStatus.cs b/src/Umbraco.Core/AttemptOfTResultTStatus.cs similarity index 100% rename from src/Umbraco.Abstractions/AttemptOfTResultTStatus.cs rename to src/Umbraco.Core/AttemptOfTResultTStatus.cs diff --git a/src/Umbraco.Abstractions/Cache/AppCacheExtensions.cs b/src/Umbraco.Core/Cache/AppCacheExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/AppCacheExtensions.cs rename to src/Umbraco.Core/Cache/AppCacheExtensions.cs diff --git a/src/Umbraco.Abstractions/Cache/AppCaches.cs b/src/Umbraco.Core/Cache/AppCaches.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/AppCaches.cs rename to src/Umbraco.Core/Cache/AppCaches.cs diff --git a/src/Umbraco.Abstractions/Cache/AppPolicedCacheDictionary.cs b/src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/AppPolicedCacheDictionary.cs rename to src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs diff --git a/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs rename to src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs diff --git a/src/Umbraco.Abstractions/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs similarity index 84% rename from src/Umbraco.Abstractions/Cache/CacheKeys.cs rename to src/Umbraco.Core/Cache/CacheKeys.cs index b8ee0e97c4..ec57d633b9 100644 --- a/src/Umbraco.Abstractions/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -12,5 +12,7 @@ public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers public const string MacroFromAliasCacheKey = "macroFromAlias_"; + + public const string UserGroupGetByAliasCacheKeyPrefix = "UserGroupRepository_GetByAlias_"; } } diff --git a/src/Umbraco.Abstractions/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/CacheRefresherBase.cs rename to src/Umbraco.Core/Cache/CacheRefresherBase.cs diff --git a/src/Umbraco.Abstractions/Cache/CacheRefresherCollection.cs b/src/Umbraco.Core/Cache/CacheRefresherCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/CacheRefresherCollection.cs rename to src/Umbraco.Core/Cache/CacheRefresherCollection.cs diff --git a/src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs b/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs rename to src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Cache/CacheRefresherEventArgs.cs b/src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/CacheRefresherEventArgs.cs rename to src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs diff --git a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs similarity index 95% rename from src/Umbraco.Web/Cache/ContentCacheRefresher.cs rename to src/Umbraco.Core/Cache/ContentCacheRefresher.cs index 5c997efeaf..6e04a06b95 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs @@ -1,17 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; -using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Cache @@ -19,14 +15,14 @@ namespace Umbraco.Web.Cache public sealed class ContentCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; private readonly IDomainService _domainService; - public ContentCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IdkMap idkMap, IDomainService domainService) + public ContentCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IDomainService domainService) : base(appCaches, serializer) { _publishedSnapshotService = publishedSnapshotService; - _idkMap = idkMap; + _idKeyMap = idKeyMap; _domainService = domainService; } @@ -58,7 +54,7 @@ namespace Umbraco.Web.Cache //By GUID Key isolatedCache.Clear(RepositoryCacheKeys.GetKey(payload.Key)); - _idkMap.ClearCache(payload.Id); + _idKeyMap.ClearCache(payload.Id); // remove those that are in the branch if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.RefreshBranch | TreeChangeTypes.Remove)) @@ -141,7 +137,6 @@ namespace Umbraco.Web.Cache public class JsonPayload { - [JsonConstructor] public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes) { Id = id; diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs similarity index 94% rename from src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs rename to src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs index 655baf18bf..8681f2626e 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs @@ -17,14 +17,14 @@ namespace Umbraco.Web.Cache private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IPublishedModelFactory _publishedModelFactory; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; - public ContentTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository) + public ContentTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository) : base(appCaches, serializer) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; - _idkMap = idkMap; + _idKeyMap = idKeyMap; _contentTypeCommonRepository = contentTypeCommonRepository; } @@ -70,7 +70,7 @@ namespace Umbraco.Web.Cache foreach (var id in payloads.Select(x => x.Id)) { - _idkMap.ClearCache(id); + _idKeyMap.ClearCache(id); } if (payloads.Any(x => x.ItemType == typeof(IContentType).Name)) diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs similarity index 94% rename from src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs rename to src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index 3c83ff3027..7455722ef7 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -15,14 +15,14 @@ namespace Umbraco.Web.Cache { private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IPublishedModelFactory _publishedModelFactory; - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; - public DataTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IdkMap idkMap) + public DataTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap) : base(appCaches, serializer) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; - _idkMap = idkMap; + _idKeyMap = idKeyMap; } #region Define @@ -56,7 +56,7 @@ namespace Umbraco.Web.Cache foreach (var payload in payloads) { - _idkMap.ClearCache(payload.Id); + _idKeyMap.ClearCache(payload.Id); } // TODO: not sure I like these? diff --git a/src/Umbraco.Abstractions/Cache/DeepCloneAppCache.cs b/src/Umbraco.Core/Cache/DeepCloneAppCache.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/DeepCloneAppCache.cs rename to src/Umbraco.Core/Cache/DeepCloneAppCache.cs diff --git a/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs b/src/Umbraco.Core/Cache/DictionaryAppCache.cs similarity index 94% rename from src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs rename to src/Umbraco.Core/Cache/DictionaryAppCache.cs index fd360b303d..d372916240 100644 --- a/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs +++ b/src/Umbraco.Core/Cache/DictionaryAppCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text.RegularExpressions; @@ -101,5 +102,9 @@ namespace Umbraco.Core.Cache var compiled = new Regex(regex, RegexOptions.Compiled); _items.RemoveAll(kvp => compiled.IsMatch(kvp.Key)); } + + public IEnumerator> GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs rename to src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Core/Cache/DistributedCache.cs similarity index 83% rename from src/Umbraco.Web/Cache/DistributedCache.cs rename to src/Umbraco.Core/Cache/DistributedCache.cs index 1e0e33ebd7..698e97c610 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Core/Cache/DistributedCache.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Web.Composing; +using Umbraco.Core.Sync; namespace Umbraco.Web.Cache { @@ -21,6 +21,15 @@ namespace Umbraco.Web.Cache /// public sealed class DistributedCache { + private readonly IServerMessenger _serverMessenger; + private readonly CacheRefresherCollection _cacheRefreshers; + + public DistributedCache(IServerMessenger serverMessenger, CacheRefresherCollection cacheRefreshers) + { + _serverMessenger = serverMessenger; + _cacheRefreshers = cacheRefreshers; + } + #region Core notification methods /// @@ -37,7 +46,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || instances.Length == 0 || getNumericId == null) return; - Current.ServerMessenger.PerformRefresh( + _serverMessenger.PerformRefresh( GetRefresherById(refresherGuid), getNumericId, instances); @@ -52,7 +61,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || id == default(int)) return; - Current.ServerMessenger.PerformRefresh( + _serverMessenger.PerformRefresh( GetRefresherById(refresherGuid), id); } @@ -66,7 +75,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || id == Guid.Empty) return; - Current.ServerMessenger.PerformRefresh( + _serverMessenger.PerformRefresh( GetRefresherById(refresherGuid), id); } @@ -77,7 +86,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || payload == null) return; - Current.ServerMessenger.PerformRefresh( + _serverMessenger.PerformRefresh( GetRefresherById(refresherGuid), payload); } @@ -88,23 +97,10 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || payloads == null) return; - Current.ServerMessenger.PerformRefresh( + _serverMessenger.PerformRefresh( GetRefresherById(refresherGuid), payloads.ToArray()); } - /// - /// Notifies the distributed cache, for a specified . - /// - /// The unique identifier of the ICacheRefresher. - /// The notification content. - public void RefreshByJson(Guid refresherGuid, string jsonPayload) - { - if (refresherGuid == Guid.Empty || jsonPayload.IsNullOrWhiteSpace()) return; - - Current.ServerMessenger.PerformRefresh( - GetRefresherById(refresherGuid), - jsonPayload); - } ///// ///// Notifies the distributed cache, for a specified . @@ -115,7 +111,7 @@ namespace Umbraco.Web.Cache //{ // if (refresherId == Guid.Empty || payload == null) return; - // Current.ServerMessenger.Notify( + // _serverMessenger.Notify( // Current.ServerRegistrar.Registrations, // GetRefresherById(refresherId), // json); @@ -129,7 +125,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty) return; - Current.ServerMessenger.PerformRefreshAll( + _serverMessenger.PerformRefreshAll( GetRefresherById(refresherGuid)); } @@ -142,7 +138,7 @@ namespace Umbraco.Web.Cache { if (refresherGuid == Guid.Empty || id == default(int)) return; - Current.ServerMessenger.PerformRemove( + _serverMessenger.PerformRemove( GetRefresherById(refresherGuid), id); } @@ -159,7 +155,7 @@ namespace Umbraco.Web.Cache /// public void Remove(Guid refresherGuid, Func getNumericId, params T[] instances) { - Current.ServerMessenger.PerformRemove( + _serverMessenger.PerformRemove( GetRefresherById(refresherGuid), getNumericId, instances); @@ -168,9 +164,9 @@ namespace Umbraco.Web.Cache #endregion // helper method to get an ICacheRefresher by its unique identifier - private static ICacheRefresher GetRefresherById(Guid refresherGuid) + private ICacheRefresher GetRefresherById(Guid refresherGuid) { - return Current.CacheRefreshers[refresherGuid]; + return _cacheRefreshers[refresherGuid]; } } } diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs b/src/Umbraco.Core/Cache/DistributedCacheBinderComponent.cs similarity index 100% rename from src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs rename to src/Umbraco.Core/Cache/DistributedCacheBinderComponent.cs diff --git a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/DomainCacheRefresher.cs rename to src/Umbraco.Core/Cache/DomainCacheRefresher.cs diff --git a/src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs b/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs rename to src/Umbraco.Core/Cache/FastDictionaryAppCache.cs diff --git a/src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs b/src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs rename to src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs diff --git a/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs b/src/Umbraco.Core/Cache/HttpRequestAppCache.cs similarity index 92% rename from src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs rename to src/Umbraco.Core/Cache/HttpRequestAppCache.cs index 87d87ad1c9..e698d93ebe 100644 --- a/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs +++ b/src/Umbraco.Core/Cache/HttpRequestAppCache.cs @@ -171,5 +171,20 @@ namespace Umbraco.Core.Cache } #endregion + + public IEnumerator> GetEnumerator() + { + if (!TryGetContextItems(out var items)) + { + yield break; + } + + foreach (DictionaryEntry item in items) + { + yield return new KeyValuePair(item.Key.ToString(), item.Value); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Abstractions/Cache/IAppCache.cs b/src/Umbraco.Core/Cache/IAppCache.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IAppCache.cs rename to src/Umbraco.Core/Cache/IAppCache.cs diff --git a/src/Umbraco.Abstractions/Cache/IAppPolicyCache.cs b/src/Umbraco.Core/Cache/IAppPolicyCache.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IAppPolicyCache.cs rename to src/Umbraco.Core/Cache/IAppPolicyCache.cs diff --git a/src/Umbraco.Abstractions/Cache/ICacheRefresher.cs b/src/Umbraco.Core/Cache/ICacheRefresher.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/ICacheRefresher.cs rename to src/Umbraco.Core/Cache/ICacheRefresher.cs diff --git a/src/Umbraco.Web/Cache/IDistributedCacheBinder.cs b/src/Umbraco.Core/Cache/IDistributedCacheBinder.cs similarity index 96% rename from src/Umbraco.Web/Cache/IDistributedCacheBinder.cs rename to src/Umbraco.Core/Cache/IDistributedCacheBinder.cs index e4237fea94..cb83799f85 100644 --- a/src/Umbraco.Web/Cache/IDistributedCacheBinder.cs +++ b/src/Umbraco.Core/Cache/IDistributedCacheBinder.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Umbraco.Core.Events; -using Umbraco.Core.Services.Implement; namespace Umbraco.Web.Cache { diff --git a/src/Umbraco.Abstractions/Cache/IJsonCacheRefresher.cs b/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IJsonCacheRefresher.cs rename to src/Umbraco.Core/Cache/IJsonCacheRefresher.cs diff --git a/src/Umbraco.Abstractions/Cache/IPayloadCacheRefresher.cs b/src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IPayloadCacheRefresher.cs rename to src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs diff --git a/src/Umbraco.Abstractions/Cache/IRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IRepositoryCachePolicy.cs rename to src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs diff --git a/src/Umbraco.Abstractions/Cache/IRequestCache.cs b/src/Umbraco.Core/Cache/IRequestCache.cs similarity index 68% rename from src/Umbraco.Abstractions/Cache/IRequestCache.cs rename to src/Umbraco.Core/Cache/IRequestCache.cs index 5ed32b5ba0..7ed7f8251c 100644 --- a/src/Umbraco.Abstractions/Cache/IRequestCache.cs +++ b/src/Umbraco.Core/Cache/IRequestCache.cs @@ -1,6 +1,8 @@ +using System.Collections.Generic; + namespace Umbraco.Core.Cache { - public interface IRequestCache : IAppCache + public interface IRequestCache : IAppCache, IEnumerable> { bool Set(string key, object value); bool Remove(string key); diff --git a/src/Umbraco.Abstractions/Cache/IsolatedCaches.cs b/src/Umbraco.Core/Cache/IsolatedCaches.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/IsolatedCaches.cs rename to src/Umbraco.Core/Cache/IsolatedCaches.cs diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs new file mode 100644 index 0000000000..6826aab6ad --- /dev/null +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -0,0 +1,53 @@ +using Umbraco.Core.Serialization; +using Umbraco.Core.Sync; + +namespace Umbraco.Core.Cache +{ + /// + /// A base class for "json" cache refreshers. + /// + /// The actual cache refresher type. + /// The actual cache refresher type is used for strongly typed events. + public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher + where TInstanceType : class, ICacheRefresher + { + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Initializes a new instance of the . + /// + /// A cache helper. + protected JsonCacheRefresherBase(AppCaches appCaches, IJsonSerializer jsonSerializer) : base(appCaches) + { + JsonSerializer = jsonSerializer; + } + + /// + /// Refreshes as specified by a json payload. + /// + /// The json payload. + public virtual void Refresh(string json) + { + OnCacheUpdated(This, new CacheRefresherEventArgs(json, MessageType.RefreshByJson)); + } + + #region Json + /// + /// Deserializes a json payload into an object payload. + /// + /// The json payload. + /// The deserialized object payload. + public TJsonPayload[] Deserialize(string json) + { + return JsonSerializer.Deserialize(json); + } + + + public string Serialize(params TJsonPayload[] jsonPayloads) + { + return JsonSerializer.Serialize(jsonPayloads); + } + #endregion + + } +} diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs similarity index 99% rename from src/Umbraco.Web/Cache/LanguageCacheRefresher.cs rename to src/Umbraco.Core/Cache/LanguageCacheRefresher.cs index 9d0c8c8450..b66e35f843 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs @@ -11,8 +11,6 @@ using static Umbraco.Web.Cache.LanguageCacheRefresher.JsonPayload; namespace Umbraco.Web.Cache { public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase - - //CacheRefresherBase { public LanguageCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService) : base(appCaches, serializer) diff --git a/src/Umbraco.Web/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs similarity index 73% rename from src/Umbraco.Web/Cache/MacroCacheRefresher.cs rename to src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 24479b8415..009e9f38d0 100644 --- a/src/Umbraco.Web/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -1,19 +1,19 @@ using System; -using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; using System.Linq; -using Newtonsoft.Json; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.Serialization; namespace Umbraco.Web.Cache { - public sealed class MacroCacheRefresher : JsonCacheRefresherBase + public sealed class MacroCacheRefresher : PayloadCacheRefresherBase { - public MacroCacheRefresher(AppCaches appCaches) - : base(appCaches) - { } + public MacroCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer) + : base(appCaches, jsonSerializer) + { + + } #region Define @@ -53,11 +53,10 @@ namespace Umbraco.Web.Cache { macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); } - }; + } base.Refresh(json); } - #endregion #region Json @@ -75,21 +74,6 @@ namespace Umbraco.Web.Cache public string Alias { get; } } - private static JsonPayload[] Deserialize(string json) - { - return JsonConvert.DeserializeObject(json); - } - - internal static string Serialize(params Macro[] macros) - { - return JsonConvert.SerializeObject(macros.Select(x => new JsonPayload(x.Id, x.Alias)).ToArray()); - } - - internal static string Serialize(params IMacro[] macros) - { - return JsonConvert.SerializeObject(macros.Select(x => new JsonPayload(x.Id, x.Alias)).ToArray()); - } - #endregion #region Helpers diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs similarity index 91% rename from src/Umbraco.Web/Cache/MediaCacheRefresher.cs rename to src/Umbraco.Core/Cache/MediaCacheRefresher.cs index 8da643c279..9e62ed61fe 100644 --- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs @@ -2,14 +2,10 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using System.Linq; -using System.Xml.Linq; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; -using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Cache @@ -17,13 +13,13 @@ namespace Umbraco.Web.Cache public sealed class MediaCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; - public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IdkMap idkMap) + public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap) : base(appCaches, serializer) { _publishedSnapshotService = publishedSnapshotService; - _idkMap = idkMap; + _idKeyMap = idKeyMap; } #region Define @@ -48,14 +44,14 @@ namespace Umbraco.Web.Cache if (anythingChanged) { - Current.AppCaches.ClearPartialViewCache(); + AppCaches.ClearPartialViewCache(); var mediaCache = AppCaches.IsolatedCaches.Get(); foreach (var payload in payloads) { if (payload.ChangeTypes == TreeChangeTypes.Remove) - _idkMap.ClearCache(payload.Id); + _idKeyMap.ClearCache(payload.Id); if (!mediaCache) continue; diff --git a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs similarity index 89% rename from src/Umbraco.Web/Cache/MemberCacheRefresher.cs rename to src/Umbraco.Core/Cache/MemberCacheRefresher.cs index 1565b1c849..68c89c0bc7 100644 --- a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; @@ -9,12 +10,12 @@ namespace Umbraco.Web.Cache { public sealed class MemberCacheRefresher : TypedCacheRefresherBase { - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; - public MemberCacheRefresher(AppCaches appCaches, IdkMap idkMap) + public MemberCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap) : base(appCaches) { - _idkMap = idkMap; + _idKeyMap = idKeyMap; } #region Define @@ -57,7 +58,7 @@ namespace Umbraco.Web.Cache private void ClearCache(int id) { - _idkMap.ClearCache(id); + _idKeyMap.ClearCache(id); AppCaches.ClearPartialViewCache(); var memberCache = AppCaches.IsolatedCaches.Get(); diff --git a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs similarity index 74% rename from src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs rename to src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs index 3e195cec5e..213ca11302 100644 --- a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs @@ -1,16 +1,18 @@ using System; using System.Linq; -using Newtonsoft.Json; using Umbraco.Core.Cache; using Umbraco.Core.Models; +using Umbraco.Core.Serialization; namespace Umbraco.Web.Cache { - public sealed class MemberGroupCacheRefresher : JsonCacheRefresherBase + public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase { - public MemberGroupCacheRefresher(AppCaches appCaches) - : base(appCaches) - { } + public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer) + : base(appCaches, jsonSerializer) + { + + } #region Define @@ -68,15 +70,6 @@ namespace Umbraco.Web.Cache public int Id { get; } } - private JsonPayload[] Deserialize(string json) - { - return JsonConvert.DeserializeObject(json); - } - - internal static string Serialize(params IMemberGroup[] groups) - { - return JsonConvert.SerializeObject(groups.Select(x => new JsonPayload(x.Id, x.Name)).ToArray()); - } #endregion } diff --git a/src/Umbraco.Abstractions/Cache/NoAppCache.cs b/src/Umbraco.Core/Cache/NoAppCache.cs similarity index 91% rename from src/Umbraco.Abstractions/Cache/NoAppCache.cs rename to src/Umbraco.Core/Cache/NoAppCache.cs index 5ca8f47059..60bc6fb8b8 100644 --- a/src/Umbraco.Abstractions/Cache/NoAppCache.cs +++ b/src/Umbraco.Core/Cache/NoAppCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; @@ -84,5 +85,9 @@ namespace Umbraco.Core.Cache /// public virtual void ClearByRegex(string regex) { } + + public IEnumerator> GetEnumerator() => new Dictionary().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Abstractions/Cache/NoCacheRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/NoCacheRepositoryCachePolicy.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/NoCacheRepositoryCachePolicy.cs rename to src/Umbraco.Core/Cache/NoCacheRepositoryCachePolicy.cs diff --git a/src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs rename to src/Umbraco.Core/Cache/ObjectCacheAppCache.cs diff --git a/src/Umbraco.Abstractions/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs similarity index 67% rename from src/Umbraco.Abstractions/Cache/PayloadCacheRefresherBase.cs rename to src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs index 1ee804bc82..7d3fd417d4 100644 --- a/src/Umbraco.Abstractions/Cache/PayloadCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -10,34 +10,19 @@ namespace Umbraco.Core.Cache /// The actual cache refresher type. /// The payload type. /// The actual cache refresher type is used for strongly typed events. - public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher + public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher where TInstanceType : class, ICacheRefresher { - private readonly IJsonSerializer _serializer; /// /// Initializes a new instance of the . /// /// A cache helper. /// - protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer) : base(appCaches) + protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer) : base(appCaches, serializer) { - _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); } - #region Json - - /// - /// Deserializes a json payload into an object payload. - /// - /// The json payload. - /// The deserialized object payload. - protected virtual TPayload[] Deserialize(string json) - { - return _serializer.Deserialize(json); - } - - #endregion #region Refresher diff --git a/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs rename to src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs diff --git a/src/Umbraco.Web/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/RelationTypeCacheRefresher.cs rename to src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs diff --git a/src/Umbraco.Abstractions/Cache/RepositoryCachePolicyOptions.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/RepositoryCachePolicyOptions.cs rename to src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs diff --git a/src/Umbraco.Abstractions/Cache/SafeLazy.cs b/src/Umbraco.Core/Cache/SafeLazy.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/SafeLazy.cs rename to src/Umbraco.Core/Cache/SafeLazy.cs diff --git a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs similarity index 88% rename from src/Umbraco.Web/Cache/TemplateCacheRefresher.cs rename to src/Umbraco.Core/Cache/TemplateCacheRefresher.cs index 2c93a6f754..27e727f73a 100644 --- a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs @@ -8,13 +8,13 @@ namespace Umbraco.Web.Cache { public sealed class TemplateCacheRefresher : CacheRefresherBase { - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; - public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository) + public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository) : base(appCaches) { - _idkMap = idkMap; + _idKeyMap = idKeyMap; _contentTypeCommonRepository = contentTypeCommonRepository; } @@ -55,7 +55,7 @@ namespace Umbraco.Web.Cache private void RemoveFromCache(int id) { - _idkMap.ClearCache(id); + _idKeyMap.ClearCache(id); AppCaches.RuntimeCache.Clear($"{CacheKeys.TemplateFrontEndCacheKey}{id}"); //need to clear the runtime cache for templates diff --git a/src/Umbraco.Abstractions/Cache/TypedCacheRefresherBase.cs b/src/Umbraco.Core/Cache/TypedCacheRefresherBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Cache/TypedCacheRefresherBase.cs rename to src/Umbraco.Core/Cache/TypedCacheRefresherBase.cs diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/UserCacheRefresher.cs similarity index 100% rename from src/Umbraco.Web/Cache/UserCacheRefresher.cs rename to src/Umbraco.Core/Cache/UserCacheRefresher.cs diff --git a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs similarity index 91% rename from src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs rename to src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs index cfdf8f3669..3fd34abfcd 100644 --- a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.Cache var userGroupCache = AppCaches.IsolatedCaches.Get(); if (userGroupCache) { - userGroupCache.Result.ClearByKey(UserGroupRepository.GetByAliasCacheKeyPrefix); + userGroupCache.Result.ClearByKey(CacheKeys.UserGroupGetByAliasCacheKeyPrefix); } //We'll need to clear all user cache too @@ -59,7 +59,7 @@ namespace Umbraco.Web.Cache if (userGroupCache) { userGroupCache.Result.Clear(RepositoryCacheKeys.GetKey(id)); - userGroupCache.Result.ClearByKey(UserGroupRepository.GetByAliasCacheKeyPrefix); + userGroupCache.Result.ClearByKey(CacheKeys.UserGroupGetByAliasCacheKeyPrefix); } //we don't know what user's belong to this group without doing a look up so we'll need to just clear them all diff --git a/src/Umbraco.Core/CacheHelperExtensions.cs b/src/Umbraco.Core/CacheHelperExtensions.cs new file mode 100644 index 0000000000..2b01ae1028 --- /dev/null +++ b/src/Umbraco.Core/CacheHelperExtensions.cs @@ -0,0 +1,23 @@ +using Umbraco.Core.Cache; + +namespace Umbraco.Core +{ + + /// + /// Extension methods for the cache helper + /// + public static class CacheHelperExtensions + { + + public const string PartialViewCacheKey = "Umbraco.Web.PartialViewCacheKey"; + + /// + /// Clears the cache for partial views + /// + /// + public static void ClearPartialViewCache(this AppCaches appCaches) + { + appCaches.RuntimeCache.ClearByKey(PartialViewCacheKey); + } + } +} diff --git a/src/Umbraco.Abstractions/CodeAnnotations/FriendlyNameAttribute.cs b/src/Umbraco.Core/CodeAnnotations/FriendlyNameAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/CodeAnnotations/FriendlyNameAttribute.cs rename to src/Umbraco.Core/CodeAnnotations/FriendlyNameAttribute.cs diff --git a/src/Umbraco.Abstractions/CodeAnnotations/UmbracoObjectTypeAttribute.cs b/src/Umbraco.Core/CodeAnnotations/UmbracoObjectTypeAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/CodeAnnotations/UmbracoObjectTypeAttribute.cs rename to src/Umbraco.Core/CodeAnnotations/UmbracoObjectTypeAttribute.cs diff --git a/src/Umbraco.Abstractions/CodeAnnotations/UmbracoUdiTypeAttribute.cs b/src/Umbraco.Core/CodeAnnotations/UmbracoUdiTypeAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/CodeAnnotations/UmbracoUdiTypeAttribute.cs rename to src/Umbraco.Core/CodeAnnotations/UmbracoUdiTypeAttribute.cs diff --git a/src/Umbraco.Abstractions/Collections/CompositeIntStringKey.cs b/src/Umbraco.Core/Collections/CompositeIntStringKey.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/CompositeIntStringKey.cs rename to src/Umbraco.Core/Collections/CompositeIntStringKey.cs diff --git a/src/Umbraco.Abstractions/Collections/CompositeNStringNStringKey.cs b/src/Umbraco.Core/Collections/CompositeNStringNStringKey.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/CompositeNStringNStringKey.cs rename to src/Umbraco.Core/Collections/CompositeNStringNStringKey.cs diff --git a/src/Umbraco.Abstractions/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/CompositeStringStringKey.cs rename to src/Umbraco.Core/Collections/CompositeStringStringKey.cs diff --git a/src/Umbraco.Abstractions/Collections/CompositeTypeTypeKey.cs b/src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/CompositeTypeTypeKey.cs rename to src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs diff --git a/src/Umbraco.Abstractions/Collections/ConcurrentHashSet.cs b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/ConcurrentHashSet.cs rename to src/Umbraco.Core/Collections/ConcurrentHashSet.cs diff --git a/src/Umbraco.Abstractions/Collections/DeepCloneableList.cs b/src/Umbraco.Core/Collections/DeepCloneableList.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/DeepCloneableList.cs rename to src/Umbraco.Core/Collections/DeepCloneableList.cs diff --git a/src/Umbraco.Abstractions/Collections/ListCloneBehavior.cs b/src/Umbraco.Core/Collections/ListCloneBehavior.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/ListCloneBehavior.cs rename to src/Umbraco.Core/Collections/ListCloneBehavior.cs diff --git a/src/Umbraco.Abstractions/Collections/ObservableDictionary.cs b/src/Umbraco.Core/Collections/ObservableDictionary.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/ObservableDictionary.cs rename to src/Umbraco.Core/Collections/ObservableDictionary.cs diff --git a/src/Umbraco.Abstractions/Collections/OrderedHashSet.cs b/src/Umbraco.Core/Collections/OrderedHashSet.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/OrderedHashSet.cs rename to src/Umbraco.Core/Collections/OrderedHashSet.cs diff --git a/src/Umbraco.Abstractions/Collections/TopoGraph.cs b/src/Umbraco.Core/Collections/TopoGraph.cs similarity index 97% rename from src/Umbraco.Abstractions/Collections/TopoGraph.cs rename to src/Umbraco.Core/Collections/TopoGraph.cs index b8ded4a458..955a210465 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Collections/TypeList.cs b/src/Umbraco.Core/Collections/TypeList.cs similarity index 100% rename from src/Umbraco.Abstractions/Collections/TypeList.cs rename to src/Umbraco.Core/Collections/TypeList.cs diff --git a/src/Umbraco.Abstractions/Composing/BuilderCollectionBase.cs b/src/Umbraco.Core/Composing/BuilderCollectionBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/BuilderCollectionBase.cs rename to src/Umbraco.Core/Composing/BuilderCollectionBase.cs diff --git a/src/Umbraco.Abstractions/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs similarity index 91% rename from src/Umbraco.Abstractions/Composing/CollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 41038ea4e9..0d398be83b 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Composing/ComponentCollection.cs b/src/Umbraco.Core/Composing/ComponentCollection.cs similarity index 93% rename from src/Umbraco.Abstractions/Composing/ComponentCollection.cs rename to src/Umbraco.Core/Composing/ComponentCollection.cs index fa4a1849b6..62b240f10f 100644 --- a/src/Umbraco.Abstractions/Composing/ComponentCollection.cs +++ b/src/Umbraco.Core/Composing/ComponentCollection.cs @@ -51,7 +51,7 @@ namespace Umbraco.Core.Composing } catch (Exception ex) { - _logger.Error(componentType, ex, "Error while terminating component {ComponentType}.", componentType.FullName); + _logger.Error(ex, "Error while terminating component {ComponentType}.", componentType.FullName); } } } diff --git a/src/Umbraco.Abstractions/Composing/ComponentCollectionBuilder.cs b/src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ComponentCollectionBuilder.cs rename to src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Composing/ComponentComposer.cs b/src/Umbraco.Core/Composing/ComponentComposer.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ComponentComposer.cs rename to src/Umbraco.Core/Composing/ComponentComposer.cs diff --git a/src/Umbraco.Abstractions/Composing/ComposeAfterAttribute.cs b/src/Umbraco.Core/Composing/ComposeAfterAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ComposeAfterAttribute.cs rename to src/Umbraco.Core/Composing/ComposeAfterAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/ComposeBeforeAttribute.cs b/src/Umbraco.Core/Composing/ComposeBeforeAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ComposeBeforeAttribute.cs rename to src/Umbraco.Core/Composing/ComposeBeforeAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/Composers.cs b/src/Umbraco.Core/Composing/Composers.cs similarity index 93% rename from src/Umbraco.Abstractions/Composing/Composers.cs rename to src/Umbraco.Core/Composing/Composers.cs index b2e6c9d068..004c2527e6 100644 --- a/src/Umbraco.Abstractions/Composing/Composers.cs +++ b/src/Umbraco.Core/Composing/Composers.cs @@ -22,28 +22,6 @@ namespace Umbraco.Core.Composing private const int LogThresholdMilliseconds = 100; - /// - /// Initializes a new instance of the class. - /// - /// The composition. - /// The types. - /// The profiling logger. - [Obsolete("This overload only gets the EnableComposer/DisableComposer attributes from the composerTypes assemblies.")] - public Composers(Composition composition, IEnumerable composerTypes, IProfilingLogger logger) - : this(composition, composerTypes, Enumerable.Empty(), logger) - { - var enableDisableAttributes = new List(); - - var assemblies = composerTypes.Select(t => t.Assembly).Distinct(); - foreach (var assembly in assemblies) - { - enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(EnableComposerAttribute))); - enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(DisableComposerAttribute))); - } - - _enableDisableAttributes = enableDisableAttributes; - } - /// /// Initializes a new instance of the class. /// diff --git a/src/Umbraco.Abstractions/Composing/Composition.cs b/src/Umbraco.Core/Composing/Composition.cs similarity index 99% rename from src/Umbraco.Abstractions/Composing/Composition.cs rename to src/Umbraco.Core/Composing/Composition.cs index a186a1f00a..9a696c2dc0 100644 --- a/src/Umbraco.Abstractions/Composing/Composition.cs +++ b/src/Umbraco.Core/Composing/Composition.cs @@ -135,7 +135,7 @@ namespace Umbraco.Core.Composing IFactory factory = null; - Configs.RegisterWith(_register, () => factory); + Configs.RegisterWith(_register); // ReSharper disable once AccessToModifiedClosure -- on purpose _register.Register(_ => factory, Lifetime.Singleton); diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs new file mode 100644 index 0000000000..262c2a903c --- /dev/null +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -0,0 +1,41 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Infrastructure.PublishedCache +{ + public static class CompositionExtensions + { + /// + /// Sets the published snapshot service. + /// + /// The composition. + /// A function creating a published snapshot service. + public static void SetPublishedSnapshotService(this Composition composition, Func factory) + { + composition.RegisterUnique(factory); + } + + /// + /// Sets the published snapshot service. + /// + /// The type of the published snapshot service. + /// The composition. + public static void SetPublishedSnapshotService(this Composition composition) + where T : IPublishedSnapshotService + { + composition.RegisterUnique(); + } + + /// + /// Sets the published snapshot service. + /// + /// The composition. + /// A published snapshot service. + public static void SetPublishedSnapshotService(this Composition composition, IPublishedSnapshotService service) + { + composition.RegisterUnique(_ => service); + } + } +} diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index a50bb89558..41e1ea2c21 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -1,223 +1,61 @@ -using System; -using Umbraco.Core.Cache; +using System; +using System.Runtime.CompilerServices; +using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.Dictionary; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using Umbraco.Core.Mapping; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PackageActions; -using Umbraco.Core.Packaging; -using Umbraco.Core.Persistence; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Scoping; -using Umbraco.Core.Security; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Core.Sync; -using Umbraco.Net; -namespace Umbraco.Core.Composing +namespace Umbraco.Composing { - /// - /// Provides a static service locator for most singletons. - /// - /// - /// This class is initialized with the container in UmbracoApplicationBase, - /// right after the container is created in UmbracoApplicationBase.HandleApplicationStart. - /// Obviously, this is a service locator, which some may consider an anti-pattern. And yet, - /// practically, it works. - /// public static class Current { - private static IFactory _factory; - - // TODO: get rid of these oddities - // we don't want Umbraco tests to die because the container has not been properly initialized, - // for some too-important things such as IShortStringHelper or loggers, so if it's not - // registered we setup a default one. We should really refactor our tests so that it does - // not happen. - - private static IShortStringHelper _shortStringHelper; - private static ILogger _logger; - private static IProfiler _profiler; - private static IProfilingLogger _profilingLogger; - private static IPublishedValueFallback _publishedValueFallback; + private static ILogger _logger = new NullLogger(); private static Configs _configs; + private static IIOHelper _ioHelper; + private static IHostingEnvironment _hostingEnvironment; + private static IBackOfficeInfo _backOfficeInfo; + private static IProfiler _profiler; - /// - /// Gets or sets the factory. - /// - public static IFactory Factory + public static ILogger Logger => EnsureInitialized(_logger); + public static Configs Configs => EnsureInitialized(_configs); + public static IIOHelper IOHelper => EnsureInitialized(_ioHelper); + public static IHostingEnvironment HostingEnvironment => EnsureInitialized(_hostingEnvironment); + public static IBackOfficeInfo BackOfficeInfo => EnsureInitialized(_backOfficeInfo); + public static IProfiler Profiler => EnsureInitialized(_profiler); + + public static bool IsInitialized { get; private set; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static T EnsureInitialized(T returnValue) + where T : class { - get + if (returnValue is null && !IsInitialized) + throw new InvalidOperationException("Current cannot be used before initialize"); + return returnValue; + } + + public static void Initialize( + ILogger logger, + Configs configs, + IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + IBackOfficeInfo backOfficeInfo, + IProfiler profiler) + { + if (IsInitialized) { - if (_factory == null) throw new InvalidOperationException("No factory has been set."); - return _factory; - } - set - { - if (_factory != null) throw new InvalidOperationException("A factory has already been set."); - if (_configs != null) throw new InvalidOperationException("Configs are unlocked."); - _factory = value; + throw new InvalidOperationException("Current cannot be initialized more than once"); } + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _configs = configs ?? throw new ArgumentNullException(nameof(configs)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); + _backOfficeInfo = backOfficeInfo ?? throw new ArgumentNullException(nameof(backOfficeInfo)); + _profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); + + IsInitialized = true; } - - public static bool HasFactory => _factory != null; - - /// - /// Resets . Indented for testing only, and not supported in production code. - /// - /// - /// For UNIT TESTS exclusively. - /// Resets everything that is 'current'. - /// - public static void Reset() - { - _factory.DisposeIfDisposable(); - _factory = null; - - _configs = null; - _shortStringHelper = null; - _logger = null; - _profiler = null; - _profilingLogger = null; - _publishedValueFallback = null; - - Resetted?.Invoke(null, EventArgs.Empty); - } - - /// - /// Unlocks . Intended for testing only, and not supported in production code. - /// - /// - /// For UNIT TESTS exclusively. - /// Unlocks so that it is possible to add configurations - /// directly to without having to wire composition. - /// - public static void UnlockConfigs(IConfigsFactory configsFactory, IIOHelper ioHelper) - { - if (_factory != null) - throw new InvalidOperationException("Cannot unlock configs when a factory has been set."); - _configs = configsFactory.Create(ioHelper); - } - - internal static event EventHandler Resetted; - - #region Getters - - public static UmbracoMapper Mapper - => _factory.GetInstance(); - - public static IShortStringHelper ShortStringHelper - => _shortStringHelper ?? (_shortStringHelper = _factory?.TryGetInstance() - ?? new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(Configs.Settings()))); - - public static ILogger Logger - => _logger ?? (_logger = _factory?.TryGetInstance() - ?? new DebugDiagnosticsLogger(new MessageTemplates())); - - public static IProfiler Profiler - => _profiler ?? (_profiler = _factory?.TryGetInstance() - ?? new LogProfiler(Logger)); - - public static IProfilingLogger ProfilingLogger - => _profilingLogger ?? (_profilingLogger = _factory?.TryGetInstance()) - ?? new ProfilingLogger(Logger, Profiler); - - public static IRuntimeState RuntimeState - => Factory.GetInstance(); - - public static TypeLoader TypeLoader - => Factory.GetInstance(); - - public static Configs Configs - { - get - { - if (_configs != null) return _configs; - if (_factory == null) throw new InvalidOperationException("Can not get Current.Config during composition. Use composition.Config."); - return _factory.GetInstance(); - } - } - - public static IFileSystems FileSystems - => Factory.GetInstance(); - - public static IMediaFileSystem MediaFileSystem - => Factory.GetInstance(); - - public static UrlSegmentProviderCollection UrlSegmentProviders - => Factory.GetInstance(); - - public static CacheRefresherCollection CacheRefreshers - => Factory.GetInstance(); - - public static DataEditorCollection DataEditors - => Factory.GetInstance(); - - public static DataValueReferenceFactoryCollection DataValueReferenceFactories - => Factory.GetInstance(); - - public static PropertyEditorCollection PropertyEditors - => Factory.GetInstance(); - - public static ParameterEditorCollection ParameterEditors - => Factory.GetInstance(); - - internal static ManifestValueValidatorCollection ManifestValidators - => Factory.GetInstance(); - - internal static PackageActionCollection PackageActions - => Factory.GetInstance(); - - internal static IPackageActionRunner PackageActionRunner - => Factory.GetInstance(); - - internal static PropertyValueConverterCollection PropertyValueConverters - => Factory.GetInstance(); - - internal static IPublishedModelFactory PublishedModelFactory - => Factory.GetInstance(); - - public static IServerMessenger ServerMessenger - => Factory.GetInstance(); - - public static IServerRegistrar ServerRegistrar - => Factory.GetInstance(); - - public static ICultureDictionaryFactory CultureDictionaryFactory - => Factory.GetInstance(); - - public static AppCaches AppCaches - => Factory.GetInstance(); - - public static ServiceContext Services - => Factory.GetInstance(); - - public static IScopeProvider ScopeProvider - => Factory.GetInstance(); - - public static ISqlContext SqlContext - => Factory.GetInstance(); - - public static IPublishedContentTypeFactory PublishedContentTypeFactory - => Factory.GetInstance(); - - public static IPublishedValueFallback PublishedValueFallback - => _publishedValueFallback ?? Factory.GetInstance() ?? new NoopPublishedValueFallback(); - - public static IVariationContextAccessor VariationContextAccessor - => Factory.GetInstance(); - - public static IIOHelper IOHelper => Factory.GetInstance(); - - - public static IHostingEnvironment HostingEnvironment => Factory.GetInstance(); - public static IBackOfficeInfo BackOfficeInfo => Factory.GetInstance(); - public static ISessionIdResolver SessionIdResolver => Factory.GetInstance(); - - #endregion } } diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs new file mode 100644 index 0000000000..61d7cff240 --- /dev/null +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Returns a list of scannable assemblies based on an entry point assembly and it's references + /// + /// + /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies and their references + /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. + /// + public class DefaultUmbracoAssemblyProvider : IAssemblyProvider + { + private readonly Assembly _entryPointAssembly; + private static readonly string[] UmbracoCoreAssemblyNames = new[] + { + "Umbraco.Core", + "Umbraco.Web", + "Umbraco.Infrastructure", + "Umbraco.PublishedCache.NuCache", + "Umbraco.ModelsBuilder.Embedded", + "Umbraco.Examine.Lucene", + }; + + public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) + { + _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); + } + + // TODO: It would be worth investigating a netcore3 version of this which would use + // var allAssemblies = System.Runtime.Loader.AssemblyLoadContext.All.SelectMany(x => x.Assemblies); + // that will still only resolve Assemblies that are already loaded but it would also make it possible to + // query dynamically generated assemblies once they are added. It would also provide the ability to probe + // assembly locations that are not in the same place as the entry point assemblies. + + public IEnumerable Assemblies + { + get + { + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); + return finder.Find(); + } + } + } +} diff --git a/src/Umbraco.Abstractions/Composing/DisableAttribute.cs b/src/Umbraco.Core/Composing/DisableAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/DisableAttribute.cs rename to src/Umbraco.Core/Composing/DisableAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/DisableComposerAttribute.cs b/src/Umbraco.Core/Composing/DisableComposerAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/DisableComposerAttribute.cs rename to src/Umbraco.Core/Composing/DisableComposerAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/EnableAttribute.cs b/src/Umbraco.Core/Composing/EnableAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/EnableAttribute.cs rename to src/Umbraco.Core/Composing/EnableAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/EnableComposerAttribute.cs b/src/Umbraco.Core/Composing/EnableComposerAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/EnableComposerAttribute.cs rename to src/Umbraco.Core/Composing/EnableComposerAttribute.cs diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs new file mode 100644 index 0000000000..91225ff973 --- /dev/null +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames + /// + /// + /// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs + /// + internal class FindAssembliesWithReferencesTo + { + private readonly Assembly[] _referenceAssemblies; + private readonly string[] _targetAssemblies; + private readonly bool _includeTargets; + + /// + /// Constructor + /// + /// Entry point assemblies + /// Used to check if the entry point or it's transitive assemblies reference these assembly names + /// If true will also use the target assembly names as entry point assemblies + public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets) + { + _referenceAssemblies = referenceAssemblies; + _targetAssemblies = targetAssemblyNames; + _includeTargets = includeTargets; + } + + public IEnumerable Find() + { + var referenceItems = new List(); + foreach (var assembly in _referenceAssemblies) + { + referenceItems.Add(assembly); + } + + if (_includeTargets) + { + foreach(var target in _targetAssemblies) + { + referenceItems.Add(Assembly.Load(target)); + } + } + + var provider = new ReferenceResolver(_targetAssemblies, referenceItems); + var assemblyNames = provider.ResolveAssemblies(); + return assemblyNames.ToList(); + } + + } +} diff --git a/src/Umbraco.Abstractions/Composing/HideFromTypeFinderAttribute.cs b/src/Umbraco.Core/Composing/HideFromTypeFinderAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/HideFromTypeFinderAttribute.cs rename to src/Umbraco.Core/Composing/HideFromTypeFinderAttribute.cs diff --git a/src/Umbraco.Core/Composing/IAssemblyProvider.cs b/src/Umbraco.Core/Composing/IAssemblyProvider.cs new file mode 100644 index 0000000000..bde97a9556 --- /dev/null +++ b/src/Umbraco.Core/Composing/IAssemblyProvider.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Provides a list of assemblies that can be scanned + /// + public interface IAssemblyProvider + { + IEnumerable Assemblies { get; } + } +} diff --git a/src/Umbraco.Abstractions/Composing/IBuilderCollection.cs b/src/Umbraco.Core/Composing/IBuilderCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IBuilderCollection.cs rename to src/Umbraco.Core/Composing/IBuilderCollection.cs diff --git a/src/Umbraco.Abstractions/Composing/ICollectionBuilder.cs b/src/Umbraco.Core/Composing/ICollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ICollectionBuilder.cs rename to src/Umbraco.Core/Composing/ICollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Composing/IComponent.cs b/src/Umbraco.Core/Composing/IComponent.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IComponent.cs rename to src/Umbraco.Core/Composing/IComponent.cs diff --git a/src/Umbraco.Abstractions/Composing/IComposer.cs b/src/Umbraco.Core/Composing/IComposer.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IComposer.cs rename to src/Umbraco.Core/Composing/IComposer.cs diff --git a/src/Umbraco.Abstractions/Composing/ICoreComposer.cs b/src/Umbraco.Core/Composing/ICoreComposer.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ICoreComposer.cs rename to src/Umbraco.Core/Composing/ICoreComposer.cs diff --git a/src/Umbraco.Abstractions/Composing/IDiscoverable.cs b/src/Umbraco.Core/Composing/IDiscoverable.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IDiscoverable.cs rename to src/Umbraco.Core/Composing/IDiscoverable.cs diff --git a/src/Umbraco.Abstractions/Composing/IFactory.cs b/src/Umbraco.Core/Composing/IFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IFactory.cs rename to src/Umbraco.Core/Composing/IFactory.cs diff --git a/src/Umbraco.Core/Composing/IPublishedCacheComposer .cs b/src/Umbraco.Core/Composing/IPublishedCacheComposer .cs new file mode 100644 index 0000000000..d88eb44ea3 --- /dev/null +++ b/src/Umbraco.Core/Composing/IPublishedCacheComposer .cs @@ -0,0 +1,5 @@ +namespace Umbraco.Core.Composing +{ + public interface IPublishedCacheComposer : ICoreComposer + { } +} diff --git a/src/Umbraco.Abstractions/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IRegister.cs rename to src/Umbraco.Core/Composing/IRegister.cs diff --git a/src/Umbraco.Abstractions/Composing/ITypeFinder.cs b/src/Umbraco.Core/Composing/ITypeFinder.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/ITypeFinder.cs rename to src/Umbraco.Core/Composing/ITypeFinder.cs diff --git a/src/Umbraco.Abstractions/Composing/IUserComposer.cs b/src/Umbraco.Core/Composing/IUserComposer.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/IUserComposer.cs rename to src/Umbraco.Core/Composing/IUserComposer.cs diff --git a/src/Umbraco.Abstractions/Composing/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/LazyCollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs diff --git a/src/Umbraco.Abstractions/Composing/Lifetime.cs b/src/Umbraco.Core/Composing/Lifetime.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/Lifetime.cs rename to src/Umbraco.Core/Composing/Lifetime.cs diff --git a/src/Umbraco.Abstractions/Composing/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/OrderedCollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs new file mode 100644 index 0000000000..65dba8bf23 --- /dev/null +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. + /// + /// + /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs + /// + internal class ReferenceResolver + { + private readonly HashSet _umbracoAssemblies; + private readonly IReadOnlyList _assemblies; + private readonly Dictionary _classifications; + private readonly List _lookup = new List(); + + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList entryPointAssemblies) + { + _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _assemblies = entryPointAssemblies; + _classifications = new Dictionary(); + + foreach (var item in entryPointAssemblies) + { + _lookup.Add(item); + } + } + + /// + /// Returns a list of assemblies that directly reference or transitively reference the targetAssemblies + /// + /// + /// + /// This includes all assemblies in the same location as the entry point assemblies + /// + public IEnumerable ResolveAssemblies() + { + var applicationParts = new List(); + + var assemblies = new HashSet(_assemblies); + + // Get the unique directories of the assemblies + var assemblyLocations = GetAssemblyFolders(assemblies).ToList(); + + // Load in each assembly in the directory of the entry assembly to be included in the search + // for Umbraco dependencies/transitive dependencies + foreach(var dir in assemblyLocations) + { + foreach(var dll in Directory.EnumerateFiles(dir, "*.dll")) + { + var assemblyName = AssemblyName.GetAssemblyName(dll); + + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assemblyName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + continue; + + // don't include this item if it's Umbraco + // TODO: We should maybe pass an explicit list of these names in? + if (assemblyName.FullName.StartsWith("Umbraco.")) + continue; + + var assembly = Assembly.Load(assemblyName); + assemblies.Add(assembly); + } + } + + foreach (var item in assemblies) + { + var classification = Resolve(item); + if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) + { + applicationParts.Add(item); + } + } + + return applicationParts; + } + + + private IEnumerable GetAssemblyFolders(IEnumerable assemblies) + { + return assemblies.Select(x => Path.GetDirectoryName(GetAssemblyLocation(x)).ToLowerInvariant()).Distinct(); + } + + // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs + private string GetAssemblyLocation(Assembly assembly) + { + if (Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out var result) && + result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) + { + return result.LocalPath; + } + + return assembly.Location; + } + + private Classification Resolve(Assembly assembly) + { + if (_classifications.TryGetValue(assembly, out var classification)) + { + return classification; + } + + // Initialize the dictionary with a value to short-circuit recursive references. + classification = Classification.Unknown; + _classifications[assembly] = classification; + + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assembly.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + { + // if its part of the filter it doesn't reference umbraco + classification = Classification.DoesNotReferenceUmbraco; + } + else if (_umbracoAssemblies.Contains(assembly.GetName().Name)) + { + classification = Classification.IsUmbraco; + } + else + { + classification = Classification.DoesNotReferenceUmbraco; + foreach (var reference in GetReferences(assembly)) + { + // recurse + var referenceClassification = Resolve(reference); + + if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) + { + classification = Classification.ReferencesUmbraco; + break; + } + } + } + + Debug.Assert(classification != Classification.Unknown); + _classifications[assembly] = classification; + return classification; + } + + protected virtual IEnumerable GetReferences(Assembly assembly) + { + foreach (var referenceName in assembly.GetReferencedAssemblies()) + { + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + continue; + + var reference = Assembly.Load(referenceName); + + if (!_lookup.Contains(reference)) + { + // A dependency references an item that isn't referenced by this project. + // We'll add this reference so that we can calculate the classification. + + _lookup.Add(reference); + } + yield return reference; + } + } + + protected enum Classification + { + Unknown, + DoesNotReferenceUmbraco, + ReferencesUmbraco, + IsUmbraco, + } + } +} diff --git a/src/Umbraco.Abstractions/Composing/RuntimeLevelAttribute.cs b/src/Umbraco.Core/Composing/RuntimeLevelAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/RuntimeLevelAttribute.cs rename to src/Umbraco.Core/Composing/RuntimeLevelAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/SetCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/SetCollectionBuilderBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/SetCollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/SetCollectionBuilderBase.cs diff --git a/src/Umbraco.Abstractions/Composing/TargetedServiceFactory.cs b/src/Umbraco.Core/Composing/TargetedServiceFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/TargetedServiceFactory.cs rename to src/Umbraco.Core/Composing/TargetedServiceFactory.cs diff --git a/src/Umbraco.Abstractions/Composing/TypeCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/TypeCollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs diff --git a/src/Umbraco.Abstractions/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs similarity index 77% rename from src/Umbraco.Abstractions/Composing/TypeFinder.cs rename to src/Umbraco.Core/Composing/TypeFinder.cs index 9d88153b0a..79fddad1ca 100644 --- a/src/Umbraco.Abstractions/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,106 +1,35 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Configuration; -using System.IO; using System.Linq; using System.Reflection; using System.Security; using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing { + /// public class TypeFinder : ITypeFinder { private readonly ILogger _logger; - - public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); - _allAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = GetRootDirectorySafe(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; - //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - - //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - return assemblies; - }); - } - - //Lazy access to the all assemblies list - private readonly Lazy> _allAssemblies; + private readonly IAssemblyProvider _assemblyProvider; private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); - private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); - private string _rootDir = ""; + private static readonly ConcurrentDictionary TypeNamesCache = new ConcurrentDictionary(); private readonly string[] _assembliesAcceptingLoadExceptions; - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() + // used for benchmark tests + internal bool QueryWithReferencingAssemblies = true; + + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { - if (string.IsNullOrEmpty(_rootDir) == false) - { - return _rootDir; - } - - var codeBase = Assembly.GetExecutingAssembly().CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - var baseDirectory = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(baseDirectory)) - throw new PanicException("No root directory could be resolved."); - - _rootDir = baseDirectory.Contains("bin") - ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) - : baseDirectory; - - return _rootDir; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assemblyProvider = assemblyProvider; + _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); } private bool AcceptsLoadExceptions(Assembly a) @@ -119,22 +48,8 @@ namespace Umbraco.Core.Composing }); } - /// - /// lazily load a reference to all assemblies and only local assemblies. - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - private IEnumerable GetAllAssemblies() - { - return _allAssemblies.Value; - } + + private IEnumerable GetAllAssemblies() => _assemblyProvider.Assemblies; /// public IEnumerable AssembliesToScan @@ -181,7 +96,10 @@ namespace Umbraco.Core.Composing /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// - private static readonly string[] KnownAssemblyExclusionFilter = { + internal static readonly string[] KnownAssemblyExclusionFilter = { + "mscorlib,", + "netstandard,", + "System,", "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -228,7 +146,14 @@ namespace Umbraco.Core.Composing "WebDriver,", "itextsharp,", "mscorlib,", - "nunit.framework,", + "NUnit,", + "NUnit.", + "NUnit3.", + "Selenium.", + "ImageProcessor", + "MiniProfiler.", + "Owin,", + "SQLite", }; /// @@ -290,6 +215,11 @@ namespace Umbraco.Core.Composing /// public virtual Type GetTypeByName(string name) { + + //NOTE: This will not find types in dynamic assemblies unless those assemblies are already loaded + //into the appdomain. + + // This is exactly what the BuildManager does, if the type is an assembly qualified type // name it will find it. if (TypeNameContainsAssembly(name)) @@ -340,18 +270,24 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(attributeType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate) { // get all assembly types that can be assigned to baseType try { assemblyTypes = GetTypesWithFormattedException(assembly) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -371,10 +307,13 @@ namespace Umbraco.Core.Composing if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } @@ -405,19 +344,25 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(baseType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); // get all assembly types that can be assigned to baseType - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate) { try { assemblyTypes = GetTypesWithFormattedException(assembly) .Where(baseType.IsAssignableFrom) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -437,10 +382,13 @@ namespace Umbraco.Core.Composing if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed)) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } @@ -522,6 +470,5 @@ namespace Umbraco.Core.Composing #endregion - } } diff --git a/src/Umbraco.Core/Composing/TypeFinderConfig.cs b/src/Umbraco.Core/Composing/TypeFinderConfig.cs new file mode 100644 index 0000000000..3dc672b27c --- /dev/null +++ b/src/Umbraco.Core/Composing/TypeFinderConfig.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Composing +{ + /// + /// TypeFinder config via appSettings + /// + internal class TypeFinderConfig : ITypeFinderConfig + { + private readonly ITypeFinderSettings _settings; + private IEnumerable _assembliesAcceptingLoadExceptions; + + public TypeFinderConfig(ITypeFinderSettings settings) + { + _settings = settings; + } + + public IEnumerable AssembliesAcceptingLoadExceptions + { + get + { + if (_assembliesAcceptingLoadExceptions != null) + return _assembliesAcceptingLoadExceptions; + + var s = _settings.AssembliesAcceptingLoadExceptions; + return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) + ? Array.Empty() + : s.Split(',').Select(x => x.Trim()).ToArray(); + } + } + } +} diff --git a/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs b/src/Umbraco.Core/Composing/TypeFinderExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs rename to src/Umbraco.Core/Composing/TypeFinderExtensions.cs diff --git a/src/Umbraco.Abstractions/Composing/TypeHelper.cs b/src/Umbraco.Core/Composing/TypeHelper.cs similarity index 98% rename from src/Umbraco.Abstractions/Composing/TypeHelper.cs rename to src/Umbraco.Core/Composing/TypeHelper.cs index 28eab6a5ec..1987a4059c 100644 --- a/src/Umbraco.Abstractions/Composing/TypeHelper.cs +++ b/src/Umbraco.Core/Composing/TypeHelper.cs @@ -82,9 +82,9 @@ namespace Umbraco.Core.Composing /// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot /// reference that assembly, same with the global.asax assembly. /// - public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) + public static IReadOnlyList GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) { - if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) + if (assembly.IsDynamic || assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) return EmptyAssemblies; @@ -92,7 +92,7 @@ namespace Umbraco.Core.Composing // should only be scanning those assemblies because any other assembly will definitely not // contain sub type's of the one we're currently looking for var name = assembly.GetName().Name; - return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray(); + return assemblies.Where(x => x == assembly || HasReference(x, name)).ToList(); } /// diff --git a/src/Umbraco.Abstractions/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs similarity index 97% rename from src/Umbraco.Abstractions/Composing/TypeLoader.cs rename to src/Umbraco.Core/Composing/TypeLoader.cs index 76d00c472d..4d8b5c984c 100644 --- a/src/Umbraco.Abstractions/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -516,29 +516,29 @@ namespace Umbraco.Core.Composing #region Get Assembly Attributes - /// - /// Gets the assembly attributes of the specified type . - /// - /// The attribute type. - /// - /// The assembly attributes of the specified type . - /// - public IEnumerable GetAssemblyAttributes() - where T : Attribute - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets the assembly attributes of the specified type . + ///// + ///// The attribute type. + ///// + ///// The assembly attributes of the specified type . + ///// + //public IEnumerable GetAssemblyAttributes() + // where T : Attribute + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} - /// - /// Gets all the assembly attributes. - /// - /// - /// All assembly attributes. - /// - public IEnumerable GetAssemblyAttributes() - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets all the assembly attributes. + ///// + ///// + ///// All assembly attributes. + ///// + //public IEnumerable GetAssemblyAttributes() + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} /// /// Gets the assembly attributes of the specified . diff --git a/src/Umbraco.Abstractions/Composing/WeightAttribute.cs b/src/Umbraco.Core/Composing/WeightAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/WeightAttribute.cs rename to src/Umbraco.Core/Composing/WeightAttribute.cs diff --git a/src/Umbraco.Abstractions/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Composing/WeightedCollectionBuilderBase.cs rename to src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs diff --git a/src/Umbraco.Core/CompositionExtensions.cs b/src/Umbraco.Core/CompositionExtensions.cs new file mode 100644 index 0000000000..bea78f82ed --- /dev/null +++ b/src/Umbraco.Core/CompositionExtensions.cs @@ -0,0 +1,37 @@ +using Umbraco.Core.Composing; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Dashboards; + +namespace Umbraco.Core +{ + public static partial class CompositionExtensions + { + + #region Collection Builders + + /// + /// Gets the components collection builder. + /// + public static ComponentCollectionBuilder Components(this Composition composition) + => composition.WithCollectionBuilder(); + + + /// + /// Gets the backoffice dashboards collection builder. + /// + /// The composition. + public static DashboardCollectionBuilder Dashboards(this Composition composition) + => composition.WithCollectionBuilder(); + + + /// + /// Gets the content finders collection builder. + /// + /// The composition. + /// + public static MediaUrlGeneratorCollectionBuilder MediaUrlGenerators(this Composition composition) + => composition.WithCollectionBuilder(); + + #endregion + } +} diff --git a/src/Umbraco.Abstractions/CompositionExtensions_Uniques.cs b/src/Umbraco.Core/CompositionExtensions_Uniques.cs similarity index 100% rename from src/Umbraco.Abstractions/CompositionExtensions_Uniques.cs rename to src/Umbraco.Core/CompositionExtensions_Uniques.cs diff --git a/src/Umbraco.Abstractions/ConfigConnectionStringExtensions.cs b/src/Umbraco.Core/ConfigConnectionStringExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/ConfigConnectionStringExtensions.cs rename to src/Umbraco.Core/ConfigConnectionStringExtensions.cs diff --git a/src/Umbraco.Abstractions/Configuration/ConfigConnectionString.cs b/src/Umbraco.Core/Configuration/ConfigConnectionString.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/ConfigConnectionString.cs rename to src/Umbraco.Core/Configuration/ConfigConnectionString.cs diff --git a/src/Umbraco.Core/Configuration/Configs.cs b/src/Umbraco.Core/Configuration/Configs.cs new file mode 100644 index 0000000000..821ee308f0 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Configs.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; + +namespace Umbraco.Core.Configuration +{ + /// + /// Represents Umbraco configurations. + /// + /// + /// During composition, use composition.Configs. When running, either inject the required configuration, + /// or use Current.Configs. + /// + public class Configs + { + private readonly Dictionary> _configs = new Dictionary>(); + private Dictionary> _registerings = new Dictionary>(); + + /// + /// Gets a configuration. + /// + public TConfig GetConfig() + where TConfig : class + { + if (!_configs.TryGetValue(typeof(TConfig), out var configFactory)) + throw new InvalidOperationException($"No configuration of type {typeof(TConfig)} has been added."); + + return (TConfig) configFactory.Value; + } + + /// + /// Adds a configuration, provided by a factory. + /// + public void Add(Func configFactory) + where TConfig : class + { + // make sure it is not too late + if (_registerings == null) + throw new InvalidOperationException("Configurations have already been registered."); + + var typeOfConfig = typeof(TConfig); + + var lazyConfigFactory = _configs[typeOfConfig] = new Lazy(configFactory); + _registerings[typeOfConfig] = register => register.Register(_ => (TConfig) lazyConfigFactory.Value, Lifetime.Singleton); + } + + /// + /// Registers configurations in a register. + /// + public void RegisterWith(IRegister register) + { + // do it only once + if (_registerings == null) + throw new InvalidOperationException("Configurations have already been registered."); + + register.Register(this); + + foreach (var registering in _registerings.Values) + registering(register); + + // no need to keep them around + _registerings = null; + } + } +} diff --git a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs new file mode 100644 index 0000000000..5019282d97 --- /dev/null +++ b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs @@ -0,0 +1,46 @@ +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.Grid; +using Umbraco.Core.Configuration.HealthChecks; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for the class. + /// + public static class ConfigsExtensions + { + public static IGlobalSettings Global(this Configs configs) + => configs.GetConfig(); + + public static IHostingSettings Hosting(this Configs configs) + => configs.GetConfig(); + + public static IConnectionStrings ConnectionStrings(this Configs configs) + => configs.GetConfig(); + + public static IContentSettings Content(this Configs configs) + => configs.GetConfig(); + + public static ISecuritySettings Security(this Configs configs) + => configs.GetConfig(); + + + public static IUserPasswordConfiguration UserPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + public static IMemberPasswordConfiguration MemberPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + + public static IRequestHandlerSettings RequestHandler(this Configs configs) + => configs.GetConfig(); + + public static IWebRoutingSettings WebRouting(this Configs configs) + => configs.GetConfig(); + + public static IHealthChecks HealthChecks(this Configs configs) + => configs.GetConfig(); + public static ICoreDebug CoreDebug(this Configs configs) + => configs.GetConfig(); + + } +} diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs new file mode 100644 index 0000000000..72c720e3d6 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -0,0 +1,20 @@ +using System.IO; +using Umbraco.Core.Cache; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Manifest; +using Umbraco.Core.Serialization; + +namespace Umbraco.Core.Configuration.Grid +{ + public class GridConfig : IGridConfig + { + public GridConfig(AppCaches appCaches, IIOHelper ioHelper, IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment) + { + EditorsConfig = new GridEditorsConfig(appCaches, ioHelper, manifestParser, jsonSerializer, hostingEnvironment.IsDebugMode); + } + + public IGridEditorsConfig EditorsConfig { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs similarity index 66% rename from src/Umbraco.Abstractions/Configuration/Grid/GridEditorsConfig.cs rename to src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index a1ebf008fc..410c83ff1a 100644 --- a/src/Umbraco.Abstractions/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -1,27 +1,30 @@ using System; using System.Collections.Generic; using System.IO; +using Umbraco.Composing; using Umbraco.Core.Cache; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Serialization; namespace Umbraco.Core.Configuration.Grid { internal class GridEditorsConfig : IGridEditorsConfig { - private readonly ILogger _logger; private readonly AppCaches _appCaches; - private readonly DirectoryInfo _configFolder; + private readonly IIOHelper _ioHelper; private readonly IManifestParser _manifestParser; private readonly bool _isDebug; + private readonly IJsonSerializer _jsonSerializer; - public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, IManifestParser manifestParser, bool isDebug) + public GridEditorsConfig(AppCaches appCaches, IIOHelper ioHelper, IManifestParser manifestParser,IJsonSerializer jsonSerializer, bool isDebug) { - _logger = logger; _appCaches = appCaches; - _configFolder = configFolder; + _ioHelper = ioHelper; _manifestParser = manifestParser; + _jsonSerializer = jsonSerializer; _isDebug = isDebug; } @@ -31,19 +34,20 @@ namespace Umbraco.Core.Configuration.Grid { List GetResult() { + var configFolder = new DirectoryInfo(_ioHelper.MapPath(Constants.SystemDirectories.Config)); var editors = new List(); - var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js"); + var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js"); if (File.Exists(gridConfig)) { var sourceString = File.ReadAllText(gridConfig); try { - editors.AddRange(_manifestParser.ParseGridEditors(sourceString)); + editors.AddRange(_jsonSerializer.Deserialize>(sourceString)); } catch (Exception ex) { - _logger.Error(ex, "Could not parse the contents of grid.editors.config.js into a JSON array '{Json}", sourceString); + Current.Logger.Error(ex, "Could not parse the contents of grid.editors.config.js into a JSON array '{Json}", sourceString); } } @@ -63,7 +67,6 @@ namespace Umbraco.Core.Configuration.Grid return result; } - } } } diff --git a/src/Umbraco.Abstractions/Configuration/Grid/IGridConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridConfig.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/Grid/IGridConfig.cs rename to src/Umbraco.Core/Configuration/Grid/IGridConfig.cs diff --git a/src/Umbraco.Abstractions/Configuration/Grid/IGridEditorConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/Grid/IGridEditorConfig.cs rename to src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs diff --git a/src/Umbraco.Abstractions/Configuration/Grid/IGridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridEditorsConfig.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/Grid/IGridEditorsConfig.cs rename to src/Umbraco.Core/Configuration/Grid/IGridEditorsConfig.cs diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/AbstractConfigCheck.cs similarity index 96% rename from src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/AbstractConfigCheck.cs index bac4fb4c47..6f665902c4 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/AbstractConfigCheck.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Umbraco.Core.Composing; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -13,6 +13,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config private readonly ConfigurationService _configurationService; protected ILocalizedTextService TextService { get; } + protected IIOHelper IOHelper { get; } + protected ILogger Logger { get; } /// /// Gets the config file path. @@ -52,10 +54,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { return false; } } - protected AbstractConfigCheck(ILocalizedTextService textService) + protected AbstractConfigCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) { TextService = textService; - _configurationService = new ConfigurationService(AbsoluteFilePath, XPath, textService); + IOHelper = ioHelper; + Logger = logger; + _configurationService = new ConfigurationService(AbsoluteFilePath, XPath, textService, logger); } /// @@ -66,7 +70,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config /// /// Gets the absolute file path. /// - private string AbsoluteFilePath => Current.IOHelper.MapPath(FilePath); + private string AbsoluteFilePath => IOHelper.MapPath(FilePath); /// /// Gets the message for when the check has succeeded. diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/AcceptableConfiguration.cs b/src/Umbraco.Core/Configuration/HealthChecks/AcceptableConfiguration.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/Checks/Config/AcceptableConfiguration.cs rename to src/Umbraco.Core/Configuration/HealthChecks/AcceptableConfiguration.cs diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/CompilationDebugCheck.cs similarity index 90% rename from src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/CompilationDebugCheck.cs index 951609f91f..b34c600342 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/CompilationDebugCheck.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -8,8 +10,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config Group = "Live Environment")] public class CompilationDebugCheck : AbstractConfigCheck { - public CompilationDebugCheck(ILocalizedTextService textService) - : base(textService) + public CompilationDebugCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) + : base(textService, ioHelper, logger) { } public override string FilePath => "~/Web.config"; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs b/src/Umbraco.Core/Configuration/HealthChecks/ConfigurationService.cs similarity index 90% rename from src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs rename to src/Umbraco.Core/Configuration/HealthChecks/ConfigurationService.cs index 95b458b142..a4554a52fd 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/ConfigurationService.cs @@ -3,7 +3,6 @@ using System.IO; using System.Xml; using Umbraco.Core.Logging; using Umbraco.Core.Services; -using Umbraco.Web.Composing; namespace Umbraco.Web.HealthCheck.Checks.Config { @@ -14,15 +13,19 @@ namespace Umbraco.Web.HealthCheck.Checks.Config private readonly string _configFilePath; private readonly string _xPath; private readonly ILocalizedTextService _textService; + private readonly ILogger _logger; /// The absolute file location of the configuration file /// The XPath to select the value + /// + /// /// - public ConfigurationService(string configFilePath, string xPath, ILocalizedTextService textService) + public ConfigurationService(string configFilePath, string xPath, ILocalizedTextService textService, ILogger logger) { _configFilePath = configFilePath; _xPath = xPath; _textService = textService; + _logger = logger; } /// @@ -58,7 +61,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config } catch (Exception ex) { - Current.Logger.Error(ex, "Error trying to get configuration value"); + _logger.Error(ex, "Error trying to get configuration value"); return new ConfigurationServiceResult { Success = false, @@ -104,7 +107,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config } catch (Exception ex) { - Current.Logger.Error(ex, "Error trying to update configuration"); + _logger.Error(ex, "Error trying to update configuration"); return new ConfigurationServiceResult { Success = false, diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationServiceResult.cs b/src/Umbraco.Core/Configuration/HealthChecks/ConfigurationServiceResult.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationServiceResult.cs rename to src/Umbraco.Core/Configuration/HealthChecks/ConfigurationServiceResult.cs diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/CustomErrorsCheck.cs similarity index 91% rename from src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/CustomErrorsCheck.cs index 63986b6c62..57bf36b2b9 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/CustomErrorsCheck.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; -using System.Web.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -10,8 +11,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config Group = "Live Environment")] public class CustomErrorsCheck : AbstractConfigCheck { - public CustomErrorsCheck(ILocalizedTextService textService) - : base(textService) + public CustomErrorsCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) + : base(textService, ioHelper, logger) { } public override string FilePath => "~/Web.config"; @@ -22,7 +23,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config public override IEnumerable Values => new List { - new AcceptableConfiguration { IsRecommended = true, Value = CustomErrorsMode.RemoteOnly.ToString() }, + new AcceptableConfiguration { IsRecommended = true, Value = "RemoteOnly" }, new AcceptableConfiguration { IsRecommended = false, Value = "On" } }; diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/HealthCheckNotificationVerbosity.cs b/src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationVerbosity.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/HealthCheckNotificationVerbosity.cs rename to src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationVerbosity.cs diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/IDisabledHealthCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/IDisabledHealthCheck.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/IDisabledHealthCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/IDisabledHealthCheck.cs diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/IHealthCheckNotificationSettings.cs b/src/Umbraco.Core/Configuration/HealthChecks/IHealthCheckNotificationSettings.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/IHealthCheckNotificationSettings.cs rename to src/Umbraco.Core/Configuration/HealthChecks/IHealthCheckNotificationSettings.cs diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/IHealthChecks.cs b/src/Umbraco.Core/Configuration/HealthChecks/IHealthChecks.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/IHealthChecks.cs rename to src/Umbraco.Core/Configuration/HealthChecks/IHealthChecks.cs diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/INotificationMethod.cs b/src/Umbraco.Core/Configuration/HealthChecks/INotificationMethod.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/INotificationMethod.cs rename to src/Umbraco.Core/Configuration/HealthChecks/INotificationMethod.cs diff --git a/src/Umbraco.Abstractions/Configuration/HealthChecks/INotificationMethodSettings.cs b/src/Umbraco.Core/Configuration/HealthChecks/INotificationMethodSettings.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/HealthChecks/INotificationMethodSettings.cs rename to src/Umbraco.Core/Configuration/HealthChecks/INotificationMethodSettings.cs diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/MacroErrorsCheck.cs similarity index 94% rename from src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/MacroErrorsCheck.cs index 09c041998e..2c152907d9 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/MacroErrorsCheck.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -9,8 +11,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config Group = "Configuration")] public class MacroErrorsCheck : AbstractConfigCheck { - public MacroErrorsCheck(ILocalizedTextService textService) - : base(textService) + public MacroErrorsCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) + : base(textService, ioHelper, logger) { } public override string FilePath => "~/Config/umbracoSettings.config"; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/NotificationEmailCheck.cs similarity index 90% rename from src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/NotificationEmailCheck.cs index 8c52a5686b..a46ec5d8a1 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/NotificationEmailCheck.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -10,8 +12,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { private const string DefaultFromEmail = "your@email.here"; - public NotificationEmailCheck(ILocalizedTextService textService) - : base(textService) + public NotificationEmailCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) + : base(textService, ioHelper, logger) { } public override string FilePath => "~/Config/umbracoSettings.config"; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/TraceCheck.cs similarity index 86% rename from src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/TraceCheck.cs index b8e4e51eb9..ffd359f497 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/TraceCheck.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -9,8 +11,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config public class TraceCheck : AbstractConfigCheck { - public TraceCheck(ILocalizedTextService textService) - : base(textService) + public TraceCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger) + : base(textService, ioHelper, logger) { } public override string FilePath => "~/Web.config"; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs b/src/Umbraco.Core/Configuration/HealthChecks/TrySkipIisCustomErrorsCheck.cs similarity index 79% rename from src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs rename to src/Umbraco.Core/Configuration/HealthChecks/TrySkipIisCustomErrorsCheck.cs index 4a467d7120..e6ec49a1e7 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/TrySkipIisCustomErrorsCheck.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config @@ -11,11 +13,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Config Group = "Configuration")] public class TrySkipIisCustomErrorsCheck : AbstractConfigCheck { - private readonly Version _serverVersion = HttpRuntime.IISVersion; + private readonly Version _iisVersion; - public TrySkipIisCustomErrorsCheck(ILocalizedTextService textService) - : base(textService) - { } + public TrySkipIisCustomErrorsCheck(ILocalizedTextService textService, IIOHelper ioHelper, ILogger logger, + IHostingEnvironment hostingEnvironment) + : base(textService, ioHelper, logger) + { + _iisVersion = hostingEnvironment.IISVersion; + } public override string FilePath => "~/Config/umbracoSettings.config"; @@ -28,7 +33,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { // beware! 7.5 and 7.5.0 are not the same thing! - var recommendedValue = _serverVersion >= new Version("7.5") + var recommendedValue = _iisVersion >= new Version("7.5") ? bool.TrueString.ToLower() : bool.FalseString.ToLower(); return new List { new AcceptableConfiguration { IsRecommended = true, Value = recommendedValue } }; @@ -40,7 +45,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckSuccessMessage", - new[] { Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); + new[] { Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() }); } } @@ -49,7 +54,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckErrorMessage", - new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); + new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() }); } } @@ -58,7 +63,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckRectifySuccessMessage", - new[] { Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); + new[] { Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() }); } } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/ValueComparisonType.cs b/src/Umbraco.Core/Configuration/HealthChecks/ValueComparisonType.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/Checks/Config/ValueComparisonType.cs rename to src/Umbraco.Core/Configuration/HealthChecks/ValueComparisonType.cs diff --git a/src/Umbraco.Core/Configuration/IActiveDirectorySettings.cs b/src/Umbraco.Core/Configuration/IActiveDirectorySettings.cs new file mode 100644 index 0000000000..e6b9202c06 --- /dev/null +++ b/src/Umbraco.Core/Configuration/IActiveDirectorySettings.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface IActiveDirectorySettings + { + string ActiveDirectoryDomain { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/IConfigsFactory.cs b/src/Umbraco.Core/Configuration/IConfigsFactory.cs similarity index 67% rename from src/Umbraco.Abstractions/Configuration/IConfigsFactory.cs rename to src/Umbraco.Core/Configuration/IConfigsFactory.cs index 98a24ca37f..dd2459b88c 100644 --- a/src/Umbraco.Abstractions/Configuration/IConfigsFactory.cs +++ b/src/Umbraco.Core/Configuration/IConfigsFactory.cs @@ -1,9 +1,10 @@ using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Configuration { public interface IConfigsFactory { - Configs Create(IIOHelper ioHelper); + Configs Create(); } } diff --git a/src/Umbraco.Core/Configuration/IConnectionStrings.cs b/src/Umbraco.Core/Configuration/IConnectionStrings.cs new file mode 100644 index 0000000000..f8d17d6794 --- /dev/null +++ b/src/Umbraco.Core/Configuration/IConnectionStrings.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.IO; + +namespace Umbraco.Core.Configuration +{ + public interface IConnectionStrings + { + ConfigConnectionString this[string key] + { + get; + } + + void RemoveConnectionString(string umbracoConnectionName, IIOHelper ioHelper); + void SaveConnectionString(string connectionString, string providerName, IIOHelper ioHelper); + } +} diff --git a/src/Umbraco.Abstractions/Configuration/ICoreDebug.cs b/src/Umbraco.Core/Configuration/ICoreDebug.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/ICoreDebug.cs rename to src/Umbraco.Core/Configuration/ICoreDebug.cs diff --git a/src/Umbraco.Core/Configuration/IExceptionFilterSettings.cs b/src/Umbraco.Core/Configuration/IExceptionFilterSettings.cs new file mode 100644 index 0000000000..169c04da5f --- /dev/null +++ b/src/Umbraco.Core/Configuration/IExceptionFilterSettings.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface IExceptionFilterSettings + { + bool Disabled { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/IGlobalSettings.cs b/src/Umbraco.Core/Configuration/IGlobalSettings.cs similarity index 92% rename from src/Umbraco.Abstractions/Configuration/IGlobalSettings.cs rename to src/Umbraco.Core/Configuration/IGlobalSettings.cs index 212e6f1843..3cb211a7a7 100644 --- a/src/Umbraco.Abstractions/Configuration/IGlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/IGlobalSettings.cs @@ -21,7 +21,7 @@ string ReservedPaths { get; } /// - /// Gets the path to umbraco's root directory (/umbraco by default). + /// Gets the path to umbraco's root directory. /// string Path { get; } @@ -67,6 +67,7 @@ string UmbracoMediaPath { get; } bool IsSmtpServerConfigured { get; } + ISmtpSettings SmtpSettings { get; } /// /// Gets a value indicating whether the runtime should enter Install level when the database is missing. @@ -94,5 +95,11 @@ bool DisableElectionForSingleServer { get; } string RegisterType { get; } string DatabaseFactoryServerVersion { get; } + string MainDomLock { get; } + + /// + /// Gets the path to the razor file used when no published content is available. + /// + string NoNodesViewPath { get; } } } diff --git a/src/Umbraco.Abstractions/Configuration/IHostingSettings.cs b/src/Umbraco.Core/Configuration/IHostingSettings.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IHostingSettings.cs rename to src/Umbraco.Core/Configuration/IHostingSettings.cs diff --git a/src/Umbraco.Core/Configuration/IIndexCreatorSettings.cs b/src/Umbraco.Core/Configuration/IIndexCreatorSettings.cs new file mode 100644 index 0000000000..b3e2854a0d --- /dev/null +++ b/src/Umbraco.Core/Configuration/IIndexCreatorSettings.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface IIndexCreatorSettings + { + string LuceneDirectoryFactory { get; } + } +} 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.Abstractions/Configuration/IMemberPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/IMemberPasswordConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IMemberPasswordConfiguration.cs rename to src/Umbraco.Core/Configuration/IMemberPasswordConfiguration.cs diff --git a/src/Umbraco.ModelsBuilder.Embedded/Configuration/IModelsBuilderConfig.cs b/src/Umbraco.Core/Configuration/IModelsBuilderConfig.cs similarity index 87% rename from src/Umbraco.ModelsBuilder.Embedded/Configuration/IModelsBuilderConfig.cs rename to src/Umbraco.Core/Configuration/IModelsBuilderConfig.cs index 7e96aec60e..6a071ac277 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Configuration/IModelsBuilderConfig.cs +++ b/src/Umbraco.Core/Configuration/IModelsBuilderConfig.cs @@ -1,4 +1,4 @@ -namespace Umbraco.ModelsBuilder.Embedded.Configuration +namespace Umbraco.Core.Configuration { public interface IModelsBuilderConfig { diff --git a/src/Umbraco.Core/Configuration/INuCacheSettings.cs b/src/Umbraco.Core/Configuration/INuCacheSettings.cs new file mode 100644 index 0000000000..c6524297a6 --- /dev/null +++ b/src/Umbraco.Core/Configuration/INuCacheSettings.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface INuCacheSettings + { + string BTreeBlockSize { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/IPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/IPasswordConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IPasswordConfiguration.cs rename to src/Umbraco.Core/Configuration/IPasswordConfiguration.cs diff --git a/src/Umbraco.Core/Configuration/IRuntimeSettings.cs b/src/Umbraco.Core/Configuration/IRuntimeSettings.cs new file mode 100644 index 0000000000..915e774186 --- /dev/null +++ b/src/Umbraco.Core/Configuration/IRuntimeSettings.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Configuration +{ + public interface IRuntimeSettings + { + int? MaxQueryStringLength { get; } + int? MaxRequestLength { get; } + } +} diff --git a/src/Umbraco.Core/Configuration/ISmtpSettings.cs b/src/Umbraco.Core/Configuration/ISmtpSettings.cs new file mode 100644 index 0000000000..c2fb4b2dbe --- /dev/null +++ b/src/Umbraco.Core/Configuration/ISmtpSettings.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Configuration +{ + public interface ISmtpSettings + { + string From { get; } + string Host { get; } + int Port{ get; } + string PickupDirectoryLocation { get; } + } +} diff --git a/src/Umbraco.Core/Configuration/ITypeFinderSettings.cs b/src/Umbraco.Core/Configuration/ITypeFinderSettings.cs new file mode 100644 index 0000000000..15e72a1f40 --- /dev/null +++ b/src/Umbraco.Core/Configuration/ITypeFinderSettings.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface ITypeFinderSettings + { + string AssembliesAcceptingLoadExceptions { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/IUmbracoConfigurationSection.cs b/src/Umbraco.Core/Configuration/IUmbracoConfigurationSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IUmbracoConfigurationSection.cs rename to src/Umbraco.Core/Configuration/IUmbracoConfigurationSection.cs diff --git a/src/Umbraco.Abstractions/Configuration/IUmbracoVersion.cs b/src/Umbraco.Core/Configuration/IUmbracoVersion.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IUmbracoVersion.cs rename to src/Umbraco.Core/Configuration/IUmbracoVersion.cs diff --git a/src/Umbraco.Abstractions/Configuration/IUserPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/IUserPasswordConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/IUserPasswordConfiguration.cs rename to src/Umbraco.Core/Configuration/IUserPasswordConfiguration.cs diff --git a/src/Umbraco.Abstractions/Configuration/LocalTempStorage.cs b/src/Umbraco.Core/Configuration/LocalTempStorage.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/LocalTempStorage.cs rename to src/Umbraco.Core/Configuration/LocalTempStorage.cs diff --git a/src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs similarity index 80% rename from src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs rename to src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs index 58c907c31f..8e7cd97f35 100644 --- a/src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs +++ b/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs @@ -7,9 +7,9 @@ namespace Umbraco.Core.Configuration /// public class MemberPasswordConfiguration : PasswordConfiguration, IMemberPasswordConfiguration { - public MemberPasswordConfiguration(IMemberPasswordConfigurationSection configSection) - : base(configSection) - { + public MemberPasswordConfiguration(IMemberPasswordConfiguration configSettings) + : base(configSettings) + { } } } diff --git a/src/Umbraco.Core/Configuration/ModelsBuilderConfigExtensions.cs b/src/Umbraco.Core/Configuration/ModelsBuilderConfigExtensions.cs new file mode 100644 index 0000000000..3e3b116395 --- /dev/null +++ b/src/Umbraco.Core/Configuration/ModelsBuilderConfigExtensions.cs @@ -0,0 +1,57 @@ +using System.Configuration; +using System.IO; +using Umbraco.Core.IO; + +namespace Umbraco.Core.Configuration +{ + public static class ModelsBuilderConfigExtensions + { + private static string _modelsDirectoryAbsolute = null; + + public static string ModelsDirectoryAbsolute(this IModelsBuilderConfig modelsBuilderConfig, IIOHelper ioHelper) + { + + if (_modelsDirectoryAbsolute is null) + { + var modelsDirectory = modelsBuilderConfig.ModelsDirectory; + var root = ioHelper.MapPath("~/"); + + _modelsDirectoryAbsolute = GetModelsDirectory(root, modelsDirectory, + modelsBuilderConfig.AcceptUnsafeModelsDirectory); + } + + return _modelsDirectoryAbsolute; + } + + // internal for tests + internal static string GetModelsDirectory(string root, string config, bool acceptUnsafe) + { + // making sure it is safe, ie under the website root, + // unless AcceptUnsafeModelsDirectory and then everything is OK. + + if (!Path.IsPathRooted(root)) + throw new ConfigurationErrorsException($"Root is not rooted \"{root}\"."); + + if (config.StartsWith("~/")) + { + var dir = Path.Combine(root, config.TrimStart("~/")); + + // sanitize - GetFullPath will take care of any relative + // segments in path, eg '../../foo.tmp' - it may throw a SecurityException + // if the combined path reaches illegal parts of the filesystem + dir = Path.GetFullPath(dir); + root = Path.GetFullPath(root); + + if (!dir.StartsWith(root) && !acceptUnsafe) + throw new ConfigurationErrorsException($"Invalid models directory \"{config}\"."); + + return dir; + } + + if (acceptUnsafe) + return Path.GetFullPath(config); + + throw new ConfigurationErrorsException($"Invalid models directory \"{config}\"."); + } + } +} diff --git a/src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsMode.cs b/src/Umbraco.Core/Configuration/ModelsMode.cs similarity index 92% rename from src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsMode.cs rename to src/Umbraco.Core/Configuration/ModelsMode.cs index e0286fdab1..2483367394 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsMode.cs +++ b/src/Umbraco.Core/Configuration/ModelsMode.cs @@ -1,4 +1,4 @@ -namespace Umbraco.ModelsBuilder.Embedded.Configuration +namespace Umbraco.Core.Configuration { /// /// Defines the models generation modes. @@ -8,7 +8,7 @@ /// /// Do not generate models. /// - Nothing = 0, // default value + Nothing = 0, // default value /// /// Generate models in memory. diff --git a/src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsModeExtensions.cs b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs similarity index 94% rename from src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsModeExtensions.cs rename to src/Umbraco.Core/Configuration/ModelsModeExtensions.cs index be638729ea..8d1b51cd28 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Configuration/ModelsModeExtensions.cs +++ b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs @@ -1,4 +1,6 @@ -namespace Umbraco.ModelsBuilder.Embedded.Configuration +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration { /// /// Provides extensions for the enumeration. diff --git a/src/Umbraco.Core/Configuration/PasswordConfiguration.cs b/src/Umbraco.Core/Configuration/PasswordConfiguration.cs new file mode 100644 index 0000000000..6827695b35 --- /dev/null +++ b/src/Umbraco.Core/Configuration/PasswordConfiguration.cs @@ -0,0 +1,41 @@ +using System; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Configuration +{ + public abstract class PasswordConfiguration : IPasswordConfiguration + { + protected PasswordConfiguration(IPasswordConfiguration configSettings) + { + if (configSettings == null) + { + throw new ArgumentNullException(nameof(configSettings)); + } + + RequiredLength = configSettings.RequiredLength; + RequireNonLetterOrDigit = configSettings.RequireNonLetterOrDigit; + RequireDigit = configSettings.RequireDigit; + RequireLowercase = configSettings.RequireLowercase; + RequireUppercase = configSettings.RequireUppercase; + UseLegacyEncoding = configSettings.UseLegacyEncoding; + HashAlgorithmType = configSettings.HashAlgorithmType; + MaxFailedAccessAttemptsBeforeLockout = configSettings.MaxFailedAccessAttemptsBeforeLockout; + } + + public int RequiredLength { get; } + + public bool RequireNonLetterOrDigit { get; } + + public bool RequireDigit { get; } + + public bool RequireLowercase { get; } + + public bool RequireUppercase { get; } + + public bool UseLegacyEncoding { get; } + + public string HashAlgorithmType { get; } + + public int MaxFailedAccessAttemptsBeforeLockout { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ContentSectionExtensions.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentSectionExtensions.cs similarity index 73% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/ContentSectionExtensions.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ContentSectionExtensions.cs index 82cc5928cf..d100eb0a74 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ContentSectionExtensions.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentSectionExtensions.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings /// The file extension. /// /// A value indicating whether the file extension corresponds to an image. - public static bool IsImageFile(this IContentSection contentConfig, string extension) + public static bool IsImageFile(this IContentSettings contentConfig, string extension) { if (contentConfig == null) throw new ArgumentNullException(nameof(contentConfig)); if (extension == null) return false; @@ -24,22 +24,22 @@ namespace Umbraco.Core.Configuration.UmbracoSettings /// held in settings. /// Allow upload if extension is whitelisted OR if there is no whitelist and extension is NOT blacklisted. /// - public static bool IsFileAllowedForUpload(this IContentSection contentSection, string extension) + public static bool IsFileAllowedForUpload(this IContentSettings contentSettings, string extension) { - return contentSection.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)) || - (contentSection.AllowedUploadFiles.Any() == false && - contentSection.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)) == false); + return contentSettings.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)) || + (contentSettings.AllowedUploadFiles.Any() == false && + contentSettings.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)) == false); } /// /// Gets the auto-fill configuration for a specified property alias. /// - /// + /// /// The property type alias. /// The auto-fill configuration for the specified property alias, or null. - public static IImagingAutoFillUploadField GetConfig(this IContentSection contentSection, string propertyTypeAlias) + public static IImagingAutoFillUploadField GetConfig(this IContentSettings contentSettings, string propertyTypeAlias) { - var autoFillConfigs = contentSection.ImageAutoFillProperties; + var autoFillConfigs = contentSettings.ImageAutoFillProperties; return autoFillConfigs?.FirstOrDefault(x => x.Alias == propertyTypeAlias); } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IBackOfficeSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs similarity index 75% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IBackOfficeSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs index 36dd6a22ed..85df4540c0 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IBackOfficeSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs @@ -2,6 +2,6 @@ { public interface IBackOfficeSection { - ITourSection Tours { get; } + ITourSettings Tours { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IChar.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IChar.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IContentErrorPage.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IContentErrorPage.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSettings.cs similarity index 93% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IContentSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IContentSettings.cs index 228b0923dc..08f6231309 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSettings.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Macros; namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IContentSection : IUmbracoConfigurationSection + public interface IContentSettings : IUmbracoConfigurationSection { string NotificationEmailAddress { get; } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs similarity index 68% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs index 3a0ad258c5..58a8151474 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IKeepAliveSection : IUmbracoConfigurationSection + public interface IKeepAliveSettings : IUmbracoConfigurationSection { bool DisableKeepAliveTask { get; } string KeepAlivePingUrl { get; } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ILoggingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs similarity index 66% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/ILoggingSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs index 6c1be8ade5..ee5647ee27 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ILoggingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ILoggingSection : IUmbracoConfigurationSection + public interface ILoggingSettings : IUmbracoConfigurationSection { int MaxLogAge { get; } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IRequestHandlerSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs similarity index 78% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IRequestHandlerSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs index 2393c5af63..11fdaa8310 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IRequestHandlerSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IRequestHandlerSection : IUmbracoConfigurationSection + public interface IRequestHandlerSettings : IUmbracoConfigurationSection { bool AddTrailingSlash { get; } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs similarity index 78% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs index a6ed188713..6ab520fefd 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs @@ -1,9 +1,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ISecuritySection : IUmbracoConfigurationSection + public interface ISecuritySettings : IUmbracoConfigurationSection { bool KeepUserLoggedIn { get; } - + bool HideDisabledUsersInBackoffice { get; } /// @@ -23,9 +23,5 @@ /// When this is false, the username and email fields will be shown in the user section. /// bool UsernameIsEmail { get; } - - IUserPasswordConfigurationSection UserPasswordConfiguration { get; } - - IMemberPasswordConfigurationSection MemberPasswordConfiguration { get; } } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITourSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs similarity index 75% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITourSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs index 938642521e..d3d8293140 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITourSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ITourSection + public interface ITourSettings { bool EnableTours { get; } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ITypeFinderConfig.cs similarity index 72% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ITypeFinderConfig.cs index fd5b18ed39..a290c26d15 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ITypeFinderConfig.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; namespace Umbraco.Core.Configuration.UmbracoSettings { diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs similarity index 86% rename from src/Umbraco.Abstractions/Configuration/UmbracoSettings/IWebRoutingSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs index f0a986efe2..f7f6a94d30 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IWebRoutingSection : IUmbracoConfigurationSection + public interface IWebRoutingSettings : IUmbracoConfigurationSection { bool TrySkipIisCustomErrors { get; } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs similarity index 100% rename from src/Umbraco.Abstractions/Configuration/UmbracoVersion.cs rename to src/Umbraco.Core/Configuration/UmbracoVersion.cs diff --git a/src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs similarity index 81% rename from src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs rename to src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs index 4cfee5280b..07e6603cee 100644 --- a/src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs +++ b/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs @@ -7,9 +7,9 @@ namespace Umbraco.Core.Configuration /// public class UserPasswordConfiguration : PasswordConfiguration, IUserPasswordConfiguration { - public UserPasswordConfiguration(IUserPasswordConfigurationSection configSection) - : base(configSection) - { + public UserPasswordConfiguration(IUserPasswordConfiguration configSettings) + : base(configSettings) + { } } } diff --git a/src/Umbraco.Abstractions/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs similarity index 95% rename from src/Umbraco.Abstractions/Constants-AppSettings.cs rename to src/Umbraco.Core/Constants-AppSettings.cs index beaffef5b4..c55b4b0314 100644 --- a/src/Umbraco.Abstractions/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -9,6 +9,8 @@ namespace Umbraco.Core /// public static class AppSettings { + public const string MainDomLock = "Umbraco.Core.MainDom.Lock"; + // TODO: Kill me - still used in Umbraco.Core.IO.SystemFiles:27 [Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")] public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML @@ -113,6 +115,11 @@ namespace Umbraco.Core /// public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer"; + /// + /// Gets the path to the razor file used when no published content is available. + /// + public const string NoNodesViewPath = "Umbraco.Core.NoNodesViewPath"; + /// /// Debug specific web.config AppSetting keys for Umbraco /// diff --git a/src/Umbraco.Abstractions/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-Applications.cs rename to src/Umbraco.Core/Constants-Applications.cs diff --git a/src/Umbraco.Abstractions/Constants-Composing.cs b/src/Umbraco.Core/Constants-Composing.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-Composing.cs rename to src/Umbraco.Core/Constants-Composing.cs diff --git a/src/Umbraco.Abstractions/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs similarity index 99% rename from src/Umbraco.Abstractions/Constants-Conventions.cs rename to src/Umbraco.Core/Constants-Conventions.cs index 37275b156a..0bfb890abd 100644 --- a/src/Umbraco.Abstractions/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -137,7 +137,7 @@ namespace Umbraco.Core public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider"; public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider"; - + /// /// Property alias for the Comments on a Member /// @@ -285,6 +285,7 @@ namespace Umbraco.Core //TODO: return a list of built in types so we can use that to prevent deletion in the uI } + } } } diff --git a/src/Umbraco.Abstractions/Constants-DataTypes.cs b/src/Umbraco.Core/Constants-DataTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-DataTypes.cs rename to src/Umbraco.Core/Constants-DataTypes.cs diff --git a/src/Umbraco.Abstractions/Constants-DatabaseProviders.cs b/src/Umbraco.Core/Constants-DatabaseProviders.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-DatabaseProviders.cs rename to src/Umbraco.Core/Constants-DatabaseProviders.cs diff --git a/src/Umbraco.Abstractions/Constants-DeploySelector.cs b/src/Umbraco.Core/Constants-DeploySelector.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-DeploySelector.cs rename to src/Umbraco.Core/Constants-DeploySelector.cs diff --git a/src/Umbraco.Abstractions/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-Icons.cs rename to src/Umbraco.Core/Constants-Icons.cs diff --git a/src/Umbraco.Abstractions/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-Indexes.cs rename to src/Umbraco.Core/Constants-Indexes.cs diff --git a/src/Umbraco.Core/Constants-ModelsBuilder.cs b/src/Umbraco.Core/Constants-ModelsBuilder.cs new file mode 100644 index 0000000000..28e70ed383 --- /dev/null +++ b/src/Umbraco.Core/Constants-ModelsBuilder.cs @@ -0,0 +1,17 @@ +namespace Umbraco.Core +{ + /// + /// Defines constants. + /// + public static partial class Constants + { + /// + /// Defines constants for ModelsBuilder. + /// + public static class ModelsBuilder + { + + public const string DefaultModelsNamespace = "Umbraco.Web.PublishedModels"; + } + } +} diff --git a/src/Umbraco.Abstractions/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs similarity index 97% rename from src/Umbraco.Abstractions/Constants-ObjectTypes.cs rename to src/Umbraco.Core/Constants-ObjectTypes.cs index 51adc69963..dacd7d9fc5 100644 --- a/src/Umbraco.Abstractions/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -68,9 +68,6 @@ namespace Umbraco.Core public const string IdReservation = "92849B1E-3904-4713-9356-F646F87C25F4"; - [Obsolete("This no longer exists in the database")] - public const string Stylesheet = "9F68DA4F-A3A8-44C2-8226-DCBD125E4840"; - // ReSharper restore MemberHidesStaticFromOuterClass } diff --git a/src/Umbraco.Abstractions/Constants-PackageRepository.cs b/src/Umbraco.Core/Constants-PackageRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-PackageRepository.cs rename to src/Umbraco.Core/Constants-PackageRepository.cs diff --git a/src/Umbraco.Abstractions/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-PropertyEditors.cs rename to src/Umbraco.Core/Constants-PropertyEditors.cs diff --git a/src/Umbraco.Abstractions/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-PropertyTypeGroups.cs rename to src/Umbraco.Core/Constants-PropertyTypeGroups.cs diff --git a/src/Umbraco.Abstractions/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-Security.cs rename to src/Umbraco.Core/Constants-Security.cs diff --git a/src/Umbraco.Abstractions/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-System.cs rename to src/Umbraco.Core/Constants-System.cs diff --git a/src/Umbraco.Abstractions/Constants-SystemDirectories.cs b/src/Umbraco.Core/Constants-SystemDirectories.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-SystemDirectories.cs rename to src/Umbraco.Core/Constants-SystemDirectories.cs diff --git a/src/Umbraco.Abstractions/Constants-UdiEntityType.cs b/src/Umbraco.Core/Constants-UdiEntityType.cs similarity index 100% rename from src/Umbraco.Abstractions/Constants-UdiEntityType.cs rename to src/Umbraco.Core/Constants-UdiEntityType.cs diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs new file mode 100644 index 0000000000..b1efd782fa --- /dev/null +++ b/src/Umbraco.Core/Constants-Web.cs @@ -0,0 +1,44 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the identifiers for Umbraco system nodes. + /// + public static class Web + { + public const string UmbracoContextDataToken = "umbraco-context"; + public const string UmbracoDataToken = "umbraco"; + public const string PublishedDocumentRequestDataToken = "umbraco-doc-request"; + public const string CustomRouteDataToken = "umbraco-custom-route"; + public const string UmbracoRouteDefinitionDataToken = "umbraco-route-def"; + + /// + /// The preview cookie name + /// + public const string PreviewCookieName = "UMB_PREVIEW"; + + public const string InstallerCookieName = "umb_installId"; + + /// + /// The cookie name that is used to store the validation value + /// + public const string CsrfValidationCookieName = "UMB-XSRF-V"; + + /// + /// The cookie name that is set for angular to use to pass in to the header value for "X-UMB-XSRF-TOKEN" + /// + public const string AngularCookieName = "UMB-XSRF-TOKEN"; + + /// + /// The header name that angular uses to pass in the token to validate the cookie + /// + public const string AngularHeadername = "X-UMB-XSRF-TOKEN"; + + /// + /// The route name of the page shown when Umbraco has no published content. + /// + public const string NoContentRouteName = "umbraco-no-content"; + } + } +} diff --git a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollection.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs similarity index 85% rename from src/Umbraco.Web/ContentApps/ContentAppFactoryCollection.cs rename to src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs index 07987aea3e..4e85e51af3 100644 --- a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollection.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs @@ -11,17 +11,18 @@ namespace Umbraco.Web.ContentApps public class ContentAppFactoryCollection : BuilderCollectionBase { private readonly ILogger _logger; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; - public ContentAppFactoryCollection(IEnumerable items, ILogger logger) + public ContentAppFactoryCollection(IEnumerable items, ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(items) { _logger = logger; + _umbracoContextAccessor = umbracoContextAccessor; } private IEnumerable GetCurrentUserGroups() { - var umbracoContext = Composing.Current.UmbracoContext; - var currentUser = umbracoContext?.Security?.CurrentUser; + var currentUser = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser; return currentUser == null ? Enumerable.Empty() : currentUser.Groups; diff --git a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs similarity index 85% rename from src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs rename to src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs index 0561cc06f0..f3d3a149d5 100644 --- a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs @@ -6,6 +6,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Identity; namespace Umbraco.Web.ContentApps { @@ -18,8 +19,8 @@ namespace Umbraco.Web.ContentApps { // get the logger just-in-time - see note below for manifest parser var logger = factory.GetInstance(); - - return new ContentAppFactoryCollection(CreateItems(factory), logger); + var umbracoContextAccessor = factory.GetInstance(); + return new ContentAppFactoryCollection(CreateItems(factory), logger, umbracoContextAccessor); } protected override IEnumerable CreateItems(IFactory factory) @@ -28,8 +29,8 @@ namespace Umbraco.Web.ContentApps // simply getting the builder in order to configure the collection, would require // its dependencies too, and that can create cycles or other oddities var manifestParser = factory.GetInstance(); - - return base.CreateItems(factory).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x, Current.IOHelper))); + var ioHelper = factory.GetInstance(); + return base.CreateItems(factory).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x, ioHelper))); } } } diff --git a/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs b/src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs similarity index 100% rename from src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs rename to src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs diff --git a/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs b/src/Umbraco.Core/ContentApps/ContentInfoContentAppFactory.cs similarity index 100% rename from src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs rename to src/Umbraco.Core/ContentApps/ContentInfoContentAppFactory.cs diff --git a/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs b/src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs similarity index 100% rename from src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs rename to src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs diff --git a/src/Umbraco.Abstractions/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/ContentExtensions.cs rename to src/Umbraco.Core/ContentExtensions.cs diff --git a/src/Umbraco.Abstractions/ContentVariationExtensions.cs b/src/Umbraco.Core/ContentVariationExtensions.cs similarity index 53% rename from src/Umbraco.Abstractions/ContentVariationExtensions.cs rename to src/Umbraco.Core/ContentVariationExtensions.cs index f3e8943172..1c34a61c3a 100644 --- a/src/Umbraco.Abstractions/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/ContentVariationExtensions.cs @@ -12,121 +12,260 @@ namespace Umbraco.Core /// /// Determines whether the content type is invariant. /// + /// The content type. + /// + /// A value indicating whether the content type is invariant. + /// public static bool VariesByNothing(this ISimpleContentType contentType) => contentType.Variations.VariesByNothing(); - /// - /// Determines whether the content type varies by culture. - /// - public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture(); - /// /// Determines whether the content type is invariant. /// + /// The content type. + /// + /// A value indicating whether the content type is invariant. + /// public static bool VariesByNothing(this IContentTypeBase contentType) => contentType.Variations.VariesByNothing(); - /// - /// Determines whether the content type varies by culture. - /// - /// And then it could also vary by segment. - public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture(); - - /// - /// Determines whether the content type varies by segment. - /// - /// And then it could also vary by culture. - public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment(); - - /// - /// Determines whether the content type varies by culture and segment. - /// - public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment(); - - /// - /// Determines whether the property type is invariant. - /// - public static bool VariesByNothing(this IPropertyType propertyType) => propertyType.Variations.VariesByNothing(); - - /// - /// Determines whether the property type varies by culture. - /// - /// And then it could also vary by segment. - public static bool VariesByCulture(this IPropertyType propertyType) => propertyType.Variations.VariesByCulture(); - - /// - /// Determines whether the property type varies by segment. - /// - /// And then it could also vary by culture. - public static bool VariesBySegment(this IPropertyType propertyType) => propertyType.Variations.VariesBySegment(); - - /// - /// Determines whether the property type varies by culture and segment. - /// - public static bool VariesByCultureAndSegment(this IPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); - /// /// Determines whether the content type is invariant. /// + /// The content type. + /// + /// A value indicating whether the content type is invariant. + /// public static bool VariesByNothing(this IPublishedContentType contentType) => contentType.Variations.VariesByNothing(); /// - /// Determines whether the content type varies by culture. + /// Determines whether the property type is invariant. /// - /// And then it could also vary by segment. - public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture(); - - /// - /// Determines whether the content type varies by segment. - /// - /// And then it could also vary by culture. - public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment(); - - /// - /// Determines whether the content type varies by culture and segment. - /// - public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); + /// The property type. + /// + /// A value indicating whether the property type is invariant. + /// + public static bool VariesByNothing(this IPropertyType propertyType) => propertyType.Variations.VariesByNothing(); /// /// Determines whether the property type is invariant. /// + /// The property type. + /// + /// A value indicating whether the property type is invariant. + /// public static bool VariesByNothing(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing(); - /// - /// Determines whether the property type varies by culture. - /// - public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture(); - - /// - /// Determines whether the property type varies by segment. - /// - public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment(); - - /// - /// Determines whether the property type varies by culture and segment. - /// - public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); - /// /// Determines whether a variation is invariant. /// + /// The variation. + /// + /// A value indicating whether the variation is invariant. + /// public static bool VariesByNothing(this ContentVariation variation) => variation == ContentVariation.Nothing; + /// + /// Determines whether the content type varies by culture. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture. + /// + public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture(); + + /// + /// Determines whether the content type varies by culture. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture. + /// + public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture(); + + /// + /// Determines whether the content type varies by culture. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture. + /// + public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture(); + + /// + /// Determines whether the property type varies by culture. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by culture. + /// + public static bool VariesByCulture(this IPropertyType propertyType) => propertyType.Variations.VariesByCulture(); + + /// + /// Determines whether the property type varies by culture. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by culture. + /// + public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture(); + /// /// Determines whether a variation varies by culture. /// - /// And then it could also vary by segment. + /// The variation. + /// + /// A value indicating whether the variation varies by culture. + /// public static bool VariesByCulture(this ContentVariation variation) => (variation & ContentVariation.Culture) > 0; + /// + /// Determines whether the content type varies by segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by segment. + /// + public static bool VariesBySegment(this ISimpleContentType contentType) => contentType.Variations.VariesBySegment(); + + /// + /// Determines whether the content type varies by segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by segment. + /// + public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment(); + + /// + /// Determines whether the content type varies by segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by segment. + /// + public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment(); + + /// + /// Determines whether the property type varies by segment. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by segment. + /// + public static bool VariesBySegment(this IPropertyType propertyType) => propertyType.Variations.VariesBySegment(); + + /// + /// Determines whether the property type varies by segment. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by segment. + /// + public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment(); + /// /// Determines whether a variation varies by segment. /// - /// And then it could also vary by culture. + /// The variation. + /// + /// A value indicating whether the variation varies by segment. + /// public static bool VariesBySegment(this ContentVariation variation) => (variation & ContentVariation.Segment) > 0; + /// + /// Determines whether the content type varies by culture and segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this ISimpleContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); + + /// + /// Determines whether the content type varies by culture and segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment(); + + /// + /// Determines whether the content type varies by culture and segment. + /// + /// The content type. + /// + /// A value indicating whether the content type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); + + /// + /// Determines whether the property type varies by culture and segment. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this IPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); + + /// + /// Determines whether the property type varies by culture and segment. + /// + /// The property type. + /// + /// A value indicating whether the property type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); + /// /// Determines whether a variation varies by culture and segment. /// + /// The variation. + /// + /// A value indicating whether the variation varies by culture and segment. + /// public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) == ContentVariation.CultureAndSegment; + /// + /// Sets or removes the content type variation depending on the specified value. + /// + /// The content type. + /// The variation to set or remove. + /// If set to true sets the variation; otherwise, removes the variation. + /// + /// This method does not support setting the variation to nothing. + /// + public static void SetVariesBy(this IContentTypeBase contentType, ContentVariation variation, bool value = true) => contentType.Variations = contentType.Variations.SetFlag(variation, value); + + /// + /// Sets or removes the property type variation depending on the specified value. + /// + /// The property type. + /// The variation to set or remove. + /// If set to true sets the variation; otherwise, removes the variation. + /// + /// This method does not support setting the variation to nothing. + /// + public static void SetVariesBy(this IPropertyType propertyType, ContentVariation variation, bool value = true) => propertyType.Variations = propertyType.Variations.SetFlag(variation, value); + + /// + /// Returns the variations with the variation set or removed depending on the specified value. + /// + /// The existing variations. + /// The variation to set or remove. + /// If set to true sets the variation; otherwise, removes the variation. + /// + /// The variations with the variation set or removed. + /// + /// + /// This method does not support setting the variation to nothing. + /// + public static ContentVariation SetFlag(this ContentVariation variations, ContentVariation variation, bool value = true) + { + return value + ? variations | variation // Set flag using bitwise logical OR + : variations & ~variation; // Remove flag using bitwise logical AND with bitwise complement (reversing the bit) + } + /// /// Validates that a combination of culture and segment is valid for the variation. /// @@ -135,16 +274,18 @@ namespace Umbraco.Core /// The segment. /// A value indicating whether to perform exact validation. /// A value indicating whether to support wildcards. - /// A value indicating whether to throw a when the combination is invalid. - /// True if the combination is valid; otherwise false. + /// A value indicating whether to throw a when the combination is invalid. + /// + /// true if the combination is valid; otherwise false. + /// + /// Occurs when the combination is invalid, and is true. /// /// When validation is exact, the combination must match the variation exactly. For instance, if the variation is Culture, then /// a culture is required. When validation is not strict, the combination must be equivalent, or more restrictive: if the variation is /// Culture, an invariant combination is ok. /// Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type. - /// Both and can be "*" to indicate "all of them". + /// Both and can be "*" to indicate "all of them". /// - /// Occurs when the combination is invalid, and is true. public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid) { culture = culture.NullOrWhiteSpaceAsNull(); @@ -166,6 +307,7 @@ namespace Umbraco.Core { if (throwIfInvalid) throw new NotSupportedException($"Culture may not be null because culture variation is enabled."); + return false; } } @@ -178,6 +320,7 @@ namespace Umbraco.Core { if (throwIfInvalid) throw new NotSupportedException($"Culture \"{culture}\" is invalid because culture variation is disabled."); + return false; } } @@ -190,6 +333,7 @@ namespace Umbraco.Core { if (throwIfInvalid) throw new NotSupportedException($"Segment \"{segment}\" is invalid because segment variation is disabled."); + return false; } diff --git a/src/Umbraco.Core/Cookie/ICookieManager.cs b/src/Umbraco.Core/Cookie/ICookieManager.cs new file mode 100644 index 0000000000..0eced07b37 --- /dev/null +++ b/src/Umbraco.Core/Cookie/ICookieManager.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Cookie +{ + public interface ICookieManager + { + void ExpireCookie(string cookieName); + string GetCookieValue(string cookieName); + void SetCookieValue(string cookieName, string value); + bool HasCookie(string cookieName); + } + +} diff --git a/src/Umbraco.Abstractions/CustomBooleanTypeConverter.cs b/src/Umbraco.Core/CustomBooleanTypeConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/CustomBooleanTypeConverter.cs rename to src/Umbraco.Core/CustomBooleanTypeConverter.cs diff --git a/src/Umbraco.Abstractions/Dashboards/AccessRule.cs b/src/Umbraco.Core/Dashboards/AccessRule.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/AccessRule.cs rename to src/Umbraco.Core/Dashboards/AccessRule.cs diff --git a/src/Umbraco.Abstractions/Dashboards/AccessRuleType.cs b/src/Umbraco.Core/Dashboards/AccessRuleType.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/AccessRuleType.cs rename to src/Umbraco.Core/Dashboards/AccessRuleType.cs diff --git a/src/Umbraco.Web/Dashboards/ContentDashboard.cs b/src/Umbraco.Core/Dashboards/ContentDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/ContentDashboard.cs rename to src/Umbraco.Core/Dashboards/ContentDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/DashboardCollection.cs b/src/Umbraco.Core/Dashboards/DashboardCollection.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/DashboardCollection.cs rename to src/Umbraco.Core/Dashboards/DashboardCollection.cs diff --git a/src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs rename to src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Dashboards/DashboardSlim.cs b/src/Umbraco.Core/Dashboards/DashboardSlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/DashboardSlim.cs rename to src/Umbraco.Core/Dashboards/DashboardSlim.cs diff --git a/src/Umbraco.Web/Dashboards/ExamineDashboard.cs b/src/Umbraco.Core/Dashboards/ExamineDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/ExamineDashboard.cs rename to src/Umbraco.Core/Dashboards/ExamineDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/FormsDashboard.cs b/src/Umbraco.Core/Dashboards/FormsDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/FormsDashboard.cs rename to src/Umbraco.Core/Dashboards/FormsDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/HealthCheckDashboard.cs b/src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/HealthCheckDashboard.cs rename to src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs diff --git a/src/Umbraco.Abstractions/Dashboards/IAccessRule.cs b/src/Umbraco.Core/Dashboards/IAccessRule.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/IAccessRule.cs rename to src/Umbraco.Core/Dashboards/IAccessRule.cs diff --git a/src/Umbraco.Abstractions/Dashboards/IDashboard.cs b/src/Umbraco.Core/Dashboards/IDashboard.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/IDashboard.cs rename to src/Umbraco.Core/Dashboards/IDashboard.cs diff --git a/src/Umbraco.Abstractions/Dashboards/IDashboardSlim.cs b/src/Umbraco.Core/Dashboards/IDashboardSlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Dashboards/IDashboardSlim.cs rename to src/Umbraco.Core/Dashboards/IDashboardSlim.cs diff --git a/src/Umbraco.Web/Dashboards/MediaDashboard.cs b/src/Umbraco.Core/Dashboards/MediaDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/MediaDashboard.cs rename to src/Umbraco.Core/Dashboards/MediaDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/MembersDashboard.cs b/src/Umbraco.Core/Dashboards/MembersDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/MembersDashboard.cs rename to src/Umbraco.Core/Dashboards/MembersDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/ProfilerDashboard.cs b/src/Umbraco.Core/Dashboards/ProfilerDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/ProfilerDashboard.cs rename to src/Umbraco.Core/Dashboards/ProfilerDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/PublishedStatusDashboard.cs b/src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/PublishedStatusDashboard.cs rename to src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/RedirectUrlDashboard.cs b/src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/RedirectUrlDashboard.cs rename to src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs diff --git a/src/Umbraco.Web/Dashboards/SettingsDashboards.cs b/src/Umbraco.Core/Dashboards/SettingsDashboards.cs similarity index 100% rename from src/Umbraco.Web/Dashboards/SettingsDashboards.cs rename to src/Umbraco.Core/Dashboards/SettingsDashboards.cs diff --git a/src/Umbraco.Abstractions/DataTableExtensions.cs b/src/Umbraco.Core/DataTableExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/DataTableExtensions.cs rename to src/Umbraco.Core/DataTableExtensions.cs diff --git a/src/Umbraco.Abstractions/DateTimeExtensions.cs b/src/Umbraco.Core/DateTimeExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/DateTimeExtensions.cs rename to src/Umbraco.Core/DateTimeExtensions.cs diff --git a/src/Umbraco.Abstractions/DecimalExtensions.cs b/src/Umbraco.Core/DecimalExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/DecimalExtensions.cs rename to src/Umbraco.Core/DecimalExtensions.cs diff --git a/src/Umbraco.Abstractions/DelegateEqualityComparer.cs b/src/Umbraco.Core/DelegateEqualityComparer.cs similarity index 100% rename from src/Umbraco.Abstractions/DelegateEqualityComparer.cs rename to src/Umbraco.Core/DelegateEqualityComparer.cs diff --git a/src/Umbraco.Abstractions/DelegateExtensions.cs b/src/Umbraco.Core/DelegateExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/DelegateExtensions.cs rename to src/Umbraco.Core/DelegateExtensions.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactBase.cs rename to src/Umbraco.Core/Deploy/ArtifactBase.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactDependency.cs b/src/Umbraco.Core/Deploy/ArtifactDependency.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactDependency.cs rename to src/Umbraco.Core/Deploy/ArtifactDependency.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactDependencyCollection.cs b/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactDependencyCollection.cs rename to src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactDependencyMode.cs b/src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactDependencyMode.cs rename to src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactDeployState.cs rename to src/Umbraco.Core/Deploy/ArtifactDeployState.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs rename to src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs diff --git a/src/Umbraco.Abstractions/Deploy/ArtifactSignature.cs b/src/Umbraco.Core/Deploy/ArtifactSignature.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ArtifactSignature.cs rename to src/Umbraco.Core/Deploy/ArtifactSignature.cs diff --git a/src/Umbraco.Abstractions/Deploy/Difference.cs b/src/Umbraco.Core/Deploy/Difference.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/Difference.cs rename to src/Umbraco.Core/Deploy/Difference.cs diff --git a/src/Umbraco.Abstractions/Deploy/Direction.cs b/src/Umbraco.Core/Deploy/Direction.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/Direction.cs rename to src/Umbraco.Core/Deploy/Direction.cs diff --git a/src/Umbraco.Abstractions/Deploy/IArtifact.cs b/src/Umbraco.Core/Deploy/IArtifact.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IArtifact.cs rename to src/Umbraco.Core/Deploy/IArtifact.cs diff --git a/src/Umbraco.Abstractions/Deploy/IArtifactSignature.cs b/src/Umbraco.Core/Deploy/IArtifactSignature.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IArtifactSignature.cs rename to src/Umbraco.Core/Deploy/IArtifactSignature.cs diff --git a/src/Umbraco.Abstractions/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IDataTypeConfigurationConnector.cs rename to src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs diff --git a/src/Umbraco.Abstractions/Deploy/IDeployContext.cs b/src/Umbraco.Core/Deploy/IDeployContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IDeployContext.cs rename to src/Umbraco.Core/Deploy/IDeployContext.cs diff --git a/src/Umbraco.Abstractions/Deploy/IFileSource.cs b/src/Umbraco.Core/Deploy/IFileSource.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IFileSource.cs rename to src/Umbraco.Core/Deploy/IFileSource.cs diff --git a/src/Umbraco.Abstractions/Deploy/IFileType.cs b/src/Umbraco.Core/Deploy/IFileType.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IFileType.cs rename to src/Umbraco.Core/Deploy/IFileType.cs diff --git a/src/Umbraco.Abstractions/Deploy/IFileTypeCollection.cs b/src/Umbraco.Core/Deploy/IFileTypeCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IFileTypeCollection.cs rename to src/Umbraco.Core/Deploy/IFileTypeCollection.cs diff --git a/src/Umbraco.Abstractions/Deploy/IImageSourceParser.cs b/src/Umbraco.Core/Deploy/IImageSourceParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IImageSourceParser.cs rename to src/Umbraco.Core/Deploy/IImageSourceParser.cs diff --git a/src/Umbraco.Abstractions/Deploy/ILocalLinkParser.cs b/src/Umbraco.Core/Deploy/ILocalLinkParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/ILocalLinkParser.cs rename to src/Umbraco.Core/Deploy/ILocalLinkParser.cs diff --git a/src/Umbraco.Abstractions/Deploy/IMacroParser.cs b/src/Umbraco.Core/Deploy/IMacroParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IMacroParser.cs rename to src/Umbraco.Core/Deploy/IMacroParser.cs diff --git a/src/Umbraco.Abstractions/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IServiceConnector.cs rename to src/Umbraco.Core/Deploy/IServiceConnector.cs diff --git a/src/Umbraco.Abstractions/Deploy/IUniqueIdentifyingServiceConnector.cs b/src/Umbraco.Core/Deploy/IUniqueIdentifyingServiceConnector.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IUniqueIdentifyingServiceConnector.cs rename to src/Umbraco.Core/Deploy/IUniqueIdentifyingServiceConnector.cs diff --git a/src/Umbraco.Abstractions/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs similarity index 100% rename from src/Umbraco.Abstractions/Deploy/IValueConnector.cs rename to src/Umbraco.Core/Deploy/IValueConnector.cs diff --git a/src/Umbraco.Abstractions/Dictionary/ICultureDictionary.cs b/src/Umbraco.Core/Dictionary/ICultureDictionary.cs similarity index 100% rename from src/Umbraco.Abstractions/Dictionary/ICultureDictionary.cs rename to src/Umbraco.Core/Dictionary/ICultureDictionary.cs diff --git a/src/Umbraco.Abstractions/Dictionary/ICultureDictionaryFactory.cs b/src/Umbraco.Core/Dictionary/ICultureDictionaryFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Dictionary/ICultureDictionaryFactory.cs rename to src/Umbraco.Core/Dictionary/ICultureDictionaryFactory.cs diff --git a/src/Umbraco.Abstractions/DictionaryExtensions.cs b/src/Umbraco.Core/DictionaryExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/DictionaryExtensions.cs rename to src/Umbraco.Core/DictionaryExtensions.cs diff --git a/src/Umbraco.Abstractions/Direction.cs b/src/Umbraco.Core/Direction.cs similarity index 100% rename from src/Umbraco.Abstractions/Direction.cs rename to src/Umbraco.Core/Direction.cs diff --git a/src/Umbraco.Abstractions/DisposableObjectSlim.cs b/src/Umbraco.Core/DisposableObjectSlim.cs similarity index 100% rename from src/Umbraco.Abstractions/DisposableObjectSlim.cs rename to src/Umbraco.Core/DisposableObjectSlim.cs diff --git a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs b/src/Umbraco.Core/Editors/EditorModelEventArgs.cs similarity index 81% rename from src/Umbraco.Web/Editors/EditorModelEventArgs.cs rename to src/Umbraco.Core/Editors/EditorModelEventArgs.cs index daf262fce5..24ee1a3d85 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs +++ b/src/Umbraco.Core/Editors/EditorModelEventArgs.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Editors Model = (T)baseArgs.Model; } - public EditorModelEventArgs(T model, UmbracoContext umbracoContext) + public EditorModelEventArgs(T model, IUmbracoContext umbracoContext) : base(model, umbracoContext) { Model = model; @@ -34,13 +34,13 @@ namespace Umbraco.Web.Editors public class EditorModelEventArgs : EventArgs { - public EditorModelEventArgs(object model, UmbracoContext umbracoContext) + public EditorModelEventArgs(object model, IUmbracoContext umbracoContext) { Model = model; UmbracoContext = umbracoContext; } public object Model { get; set; } - public UmbracoContext UmbracoContext { get; } + public IUmbracoContext UmbracoContext { get; } } } diff --git a/src/Umbraco.Web/Editors/EditorValidatorCollection.cs b/src/Umbraco.Core/Editors/EditorValidatorCollection.cs similarity index 100% rename from src/Umbraco.Web/Editors/EditorValidatorCollection.cs rename to src/Umbraco.Core/Editors/EditorValidatorCollection.cs diff --git a/src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs b/src/Umbraco.Core/Editors/EditorValidatorCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs rename to src/Umbraco.Core/Editors/EditorValidatorCollectionBuilder.cs diff --git a/src/Umbraco.Web/Editors/EditorValidatorOfT.cs b/src/Umbraco.Core/Editors/EditorValidatorOfT.cs similarity index 100% rename from src/Umbraco.Web/Editors/EditorValidatorOfT.cs rename to src/Umbraco.Core/Editors/EditorValidatorOfT.cs diff --git a/src/Umbraco.Web/Editors/IEditorValidator.cs b/src/Umbraco.Core/Editors/IEditorValidator.cs similarity index 100% rename from src/Umbraco.Web/Editors/IEditorValidator.cs rename to src/Umbraco.Core/Editors/IEditorValidator.cs diff --git a/src/Umbraco.Abstractions/EmailSender.cs b/src/Umbraco.Core/EmailSender.cs similarity index 100% rename from src/Umbraco.Abstractions/EmailSender.cs rename to src/Umbraco.Core/EmailSender.cs diff --git a/src/Umbraco.Abstractions/Enum.cs b/src/Umbraco.Core/Enum.cs similarity index 100% rename from src/Umbraco.Abstractions/Enum.cs rename to src/Umbraco.Core/Enum.cs diff --git a/src/Umbraco.Core/EnumExtensions.cs b/src/Umbraco.Core/EnumExtensions.cs new file mode 100644 index 0000000000..9097432f64 --- /dev/null +++ b/src/Umbraco.Core/EnumExtensions.cs @@ -0,0 +1,44 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods to . + /// + public static class EnumExtensions + { + /// + /// Determines whether all the flags/bits are set within the enum value. + /// + /// The enum type. + /// The enum value. + /// The flags. + /// + /// true if all the flags/bits are set within the enum value; otherwise, false. + /// + [Obsolete("Use Enum.HasFlag() or bitwise operations (if performance is important) instead.")] + public static bool HasFlagAll(this T value, T flags) + where T : Enum + { + return value.HasFlag(flags); + } + + /// + /// Determines whether any of the flags/bits are set within the enum value. + /// + /// The enum type. + /// The value. + /// The flags. + /// + /// true if any of the flags/bits are set within the enum value; otherwise, false. + /// + public static bool HasFlagAny(this T value, T flags) + where T : Enum + { + var v = Convert.ToUInt64(value); + var f = Convert.ToUInt64(flags); + + return (v & f) > 0; + } + } +} diff --git a/src/Umbraco.Abstractions/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/EnumerableExtensions.cs rename to src/Umbraco.Core/EnumerableExtensions.cs diff --git a/src/Umbraco.Abstractions/Events/CancellableEnumerableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableEnumerableObjectEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/CancellableEnumerableObjectEventArgs.cs rename to src/Umbraco.Core/Events/CancellableEnumerableObjectEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/CancellableEventArgs.cs b/src/Umbraco.Core/Events/CancellableEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/CancellableEventArgs.cs rename to src/Umbraco.Core/Events/CancellableEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/CancellableObjectEventArgs.cs rename to src/Umbraco.Core/Events/CancellableObjectEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/CancellableObjectEventArgsOfTEventObject.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgsOfTEventObject.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/CancellableObjectEventArgsOfTEventObject.cs rename to src/Umbraco.Core/Events/CancellableObjectEventArgsOfTEventObject.cs diff --git a/src/Umbraco.Abstractions/Events/ContentCacheEventArgs.cs b/src/Umbraco.Core/Events/ContentCacheEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ContentCacheEventArgs.cs rename to src/Umbraco.Core/Events/ContentCacheEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/ContentPublishedEventArgs.cs b/src/Umbraco.Core/Events/ContentPublishedEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ContentPublishedEventArgs.cs rename to src/Umbraco.Core/Events/ContentPublishedEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/ContentPublishingEventArgs.cs b/src/Umbraco.Core/Events/ContentPublishingEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ContentPublishingEventArgs.cs rename to src/Umbraco.Core/Events/ContentPublishingEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/ContentSavedEventArgs.cs b/src/Umbraco.Core/Events/ContentSavedEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ContentSavedEventArgs.cs rename to src/Umbraco.Core/Events/ContentSavedEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/ContentSavingEventArgs.cs b/src/Umbraco.Core/Events/ContentSavingEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ContentSavingEventArgs.cs rename to src/Umbraco.Core/Events/ContentSavingEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/CopyEventArgs.cs b/src/Umbraco.Core/Events/CopyEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/CopyEventArgs.cs rename to src/Umbraco.Core/Events/CopyEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/DatabaseCreationEventArgs.cs b/src/Umbraco.Core/Events/DatabaseCreationEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/DatabaseCreationEventArgs.cs rename to src/Umbraco.Core/Events/DatabaseCreationEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/DeleteEventArgs.cs b/src/Umbraco.Core/Events/DeleteEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/DeleteEventArgs.cs rename to src/Umbraco.Core/Events/DeleteEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/DeleteRevisionsEventArgs.cs b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/DeleteRevisionsEventArgs.cs rename to src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/EventDefinition.cs b/src/Umbraco.Core/Events/EventDefinition.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventDefinition.cs rename to src/Umbraco.Core/Events/EventDefinition.cs diff --git a/src/Umbraco.Abstractions/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventDefinitionBase.cs rename to src/Umbraco.Core/Events/EventDefinitionBase.cs diff --git a/src/Umbraco.Abstractions/Events/EventDefinitionFilter.cs b/src/Umbraco.Core/Events/EventDefinitionFilter.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventDefinitionFilter.cs rename to src/Umbraco.Core/Events/EventDefinitionFilter.cs diff --git a/src/Umbraco.Abstractions/Events/EventExtensions.cs b/src/Umbraco.Core/Events/EventExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventExtensions.cs rename to src/Umbraco.Core/Events/EventExtensions.cs diff --git a/src/Umbraco.Abstractions/Events/EventMessage.cs b/src/Umbraco.Core/Events/EventMessage.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventMessage.cs rename to src/Umbraco.Core/Events/EventMessage.cs diff --git a/src/Umbraco.Abstractions/Events/EventMessageType.cs b/src/Umbraco.Core/Events/EventMessageType.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventMessageType.cs rename to src/Umbraco.Core/Events/EventMessageType.cs diff --git a/src/Umbraco.Abstractions/Events/EventMessages.cs b/src/Umbraco.Core/Events/EventMessages.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventMessages.cs rename to src/Umbraco.Core/Events/EventMessages.cs diff --git a/src/Umbraco.Abstractions/Events/EventNameExtractor.cs b/src/Umbraco.Core/Events/EventNameExtractor.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventNameExtractor.cs rename to src/Umbraco.Core/Events/EventNameExtractor.cs diff --git a/src/Umbraco.Abstractions/Events/EventNameExtractorError.cs b/src/Umbraco.Core/Events/EventNameExtractorError.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventNameExtractorError.cs rename to src/Umbraco.Core/Events/EventNameExtractorError.cs diff --git a/src/Umbraco.Abstractions/Events/EventNameExtractorResult.cs b/src/Umbraco.Core/Events/EventNameExtractorResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/EventNameExtractorResult.cs rename to src/Umbraco.Core/Events/EventNameExtractorResult.cs diff --git a/src/Umbraco.Abstractions/Events/ExportedMemberEventArgs.cs b/src/Umbraco.Core/Events/ExportedMemberEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ExportedMemberEventArgs.cs rename to src/Umbraco.Core/Events/ExportedMemberEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/IDeletingMediaFilesEventArgs.cs b/src/Umbraco.Core/Events/IDeletingMediaFilesEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/IDeletingMediaFilesEventArgs.cs rename to src/Umbraco.Core/Events/IDeletingMediaFilesEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/IEventDefinition.cs b/src/Umbraco.Core/Events/IEventDefinition.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/IEventDefinition.cs rename to src/Umbraco.Core/Events/IEventDefinition.cs diff --git a/src/Umbraco.Abstractions/Events/IEventDispatcher.cs b/src/Umbraco.Core/Events/IEventDispatcher.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/IEventDispatcher.cs rename to src/Umbraco.Core/Events/IEventDispatcher.cs diff --git a/src/Umbraco.Abstractions/Events/IEventMessagesAccessor.cs b/src/Umbraco.Core/Events/IEventMessagesAccessor.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/IEventMessagesAccessor.cs rename to src/Umbraco.Core/Events/IEventMessagesAccessor.cs diff --git a/src/Umbraco.Abstractions/Events/IEventMessagesFactory.cs b/src/Umbraco.Core/Events/IEventMessagesFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/IEventMessagesFactory.cs rename to src/Umbraco.Core/Events/IEventMessagesFactory.cs diff --git a/src/Umbraco.Abstractions/Events/ImportPackageEventArgs.cs b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/ImportPackageEventArgs.cs rename to src/Umbraco.Core/Events/ImportPackageEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/MacroErrorEventArgs.cs b/src/Umbraco.Core/Events/MacroErrorEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/MacroErrorEventArgs.cs rename to src/Umbraco.Core/Events/MacroErrorEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/MoveEventArgs.cs b/src/Umbraco.Core/Events/MoveEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/MoveEventArgs.cs rename to src/Umbraco.Core/Events/MoveEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/MoveEventInfo.cs b/src/Umbraco.Core/Events/MoveEventInfo.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/MoveEventInfo.cs rename to src/Umbraco.Core/Events/MoveEventInfo.cs diff --git a/src/Umbraco.Abstractions/Events/NewEventArgs.cs b/src/Umbraco.Core/Events/NewEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/NewEventArgs.cs rename to src/Umbraco.Core/Events/NewEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/PassThroughEventDispatcher.cs b/src/Umbraco.Core/Events/PassThroughEventDispatcher.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/PassThroughEventDispatcher.cs rename to src/Umbraco.Core/Events/PassThroughEventDispatcher.cs diff --git a/src/Umbraco.Abstractions/Events/PublishEventArgs.cs b/src/Umbraco.Core/Events/PublishEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/PublishEventArgs.cs rename to src/Umbraco.Core/Events/PublishEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/QueuingEventDispatcherBase.cs b/src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/QueuingEventDispatcherBase.cs rename to src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs diff --git a/src/Umbraco.Abstractions/Events/RecycleBinEventArgs.cs b/src/Umbraco.Core/Events/RecycleBinEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/RecycleBinEventArgs.cs rename to src/Umbraco.Core/Events/RecycleBinEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/RefreshContentEventArgs.cs b/src/Umbraco.Core/Events/RefreshContentEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/RefreshContentEventArgs.cs rename to src/Umbraco.Core/Events/RefreshContentEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/RolesEventArgs.cs b/src/Umbraco.Core/Events/RolesEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/RolesEventArgs.cs rename to src/Umbraco.Core/Events/RolesEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/RollbackEventArgs.cs b/src/Umbraco.Core/Events/RollbackEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/RollbackEventArgs.cs rename to src/Umbraco.Core/Events/RollbackEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/SaveEventArgs.cs b/src/Umbraco.Core/Events/SaveEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/SaveEventArgs.cs rename to src/Umbraco.Core/Events/SaveEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/SendEmailEventArgs.cs b/src/Umbraco.Core/Events/SendEmailEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/SendEmailEventArgs.cs rename to src/Umbraco.Core/Events/SendEmailEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/SendToPublishEventArgs.cs b/src/Umbraco.Core/Events/SendToPublishEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/SendToPublishEventArgs.cs rename to src/Umbraco.Core/Events/SendToPublishEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/SupersedeEventAttribute.cs b/src/Umbraco.Core/Events/SupersedeEventAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/SupersedeEventAttribute.cs rename to src/Umbraco.Core/Events/SupersedeEventAttribute.cs diff --git a/src/Umbraco.Abstractions/Events/TransientEventMessagesFactory.cs b/src/Umbraco.Core/Events/TransientEventMessagesFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/TransientEventMessagesFactory.cs rename to src/Umbraco.Core/Events/TransientEventMessagesFactory.cs diff --git a/src/Umbraco.Abstractions/Events/TypedEventHandler.cs b/src/Umbraco.Core/Events/TypedEventHandler.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/TypedEventHandler.cs rename to src/Umbraco.Core/Events/TypedEventHandler.cs diff --git a/src/Umbraco.Abstractions/Events/UninstallPackageEventArgs.cs b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/UninstallPackageEventArgs.cs rename to src/Umbraco.Core/Events/UninstallPackageEventArgs.cs diff --git a/src/Umbraco.Abstractions/Events/UserGroupWithUsers.cs b/src/Umbraco.Core/Events/UserGroupWithUsers.cs similarity index 100% rename from src/Umbraco.Abstractions/Events/UserGroupWithUsers.cs rename to src/Umbraco.Core/Events/UserGroupWithUsers.cs diff --git a/src/Umbraco.Abstractions/Exceptions/AuthorizationException.cs b/src/Umbraco.Core/Exceptions/AuthorizationException.cs similarity index 100% rename from src/Umbraco.Abstractions/Exceptions/AuthorizationException.cs rename to src/Umbraco.Core/Exceptions/AuthorizationException.cs diff --git a/src/Umbraco.Abstractions/Exceptions/BootFailedException.cs b/src/Umbraco.Core/Exceptions/BootFailedException.cs similarity index 100% rename from src/Umbraco.Abstractions/Exceptions/BootFailedException.cs rename to src/Umbraco.Core/Exceptions/BootFailedException.cs diff --git a/src/Umbraco.Abstractions/Exceptions/DataOperationException.cs b/src/Umbraco.Core/Exceptions/DataOperationException.cs similarity index 100% rename from src/Umbraco.Abstractions/Exceptions/DataOperationException.cs rename to src/Umbraco.Core/Exceptions/DataOperationException.cs diff --git a/src/Umbraco.Abstractions/Exceptions/InvalidCompositionException.cs b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs similarity index 100% rename from src/Umbraco.Abstractions/Exceptions/InvalidCompositionException.cs rename to src/Umbraco.Core/Exceptions/InvalidCompositionException.cs diff --git a/src/Umbraco.Abstractions/Exceptions/PanicException.cs b/src/Umbraco.Core/Exceptions/PanicException.cs similarity index 100% rename from src/Umbraco.Abstractions/Exceptions/PanicException.cs rename to src/Umbraco.Core/Exceptions/PanicException.cs diff --git a/src/Umbraco.Abstractions/ExpressionExtensions.cs b/src/Umbraco.Core/ExpressionExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/ExpressionExtensions.cs rename to src/Umbraco.Core/ExpressionExtensions.cs diff --git a/src/Umbraco.Abstractions/ExpressionHelper.cs b/src/Umbraco.Core/ExpressionHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/ExpressionHelper.cs rename to src/Umbraco.Core/ExpressionHelper.cs diff --git a/src/Umbraco.Abstractions/FactoryExtensions.cs b/src/Umbraco.Core/FactoryExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/FactoryExtensions.cs rename to src/Umbraco.Core/FactoryExtensions.cs diff --git a/src/Umbraco.Web/Features/DisabledFeatures.cs b/src/Umbraco.Core/Features/DisabledFeatures.cs similarity index 82% rename from src/Umbraco.Web/Features/DisabledFeatures.cs rename to src/Umbraco.Core/Features/DisabledFeatures.cs index 62fb019c70..9bd091a570 100644 --- a/src/Umbraco.Web/Features/DisabledFeatures.cs +++ b/src/Umbraco.Core/Features/DisabledFeatures.cs @@ -1,5 +1,4 @@ using Umbraco.Core.Collections; -using Umbraco.Web.WebApi; namespace Umbraco.Web.Features { @@ -13,19 +12,19 @@ namespace Umbraco.Web.Features /// public DisabledFeatures() { - Controllers = new TypeList(); + Controllers = new TypeList(); } /// /// Gets the disabled controllers. /// - public TypeList Controllers { get; } + public TypeList Controllers { get; } /// /// Disables the device preview feature of previewing. /// public bool DisableDevicePreview { get; set; } - + /// /// If true, all references to templates will be removed in the back office and routing /// diff --git a/src/Umbraco.Web/Features/EnabledFeatures.cs b/src/Umbraco.Core/Features/EnabledFeatures.cs similarity index 100% rename from src/Umbraco.Web/Features/EnabledFeatures.cs rename to src/Umbraco.Core/Features/EnabledFeatures.cs diff --git a/src/Umbraco.Core/Features/IUmbracoFeature.cs b/src/Umbraco.Core/Features/IUmbracoFeature.cs new file mode 100644 index 0000000000..ccb80b0a9f --- /dev/null +++ b/src/Umbraco.Core/Features/IUmbracoFeature.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Web.Features +{ + /// + /// This is a marker interface to allow controllers to be disabled if also marked with FeatureAuthorizeAttribute. + /// + public interface IUmbracoFeature + { + + } +} diff --git a/src/Umbraco.Web/Features/UmbracoFeatures.cs b/src/Umbraco.Core/Features/UmbracoFeatures.cs similarity index 90% rename from src/Umbraco.Web/Features/UmbracoFeatures.cs rename to src/Umbraco.Core/Features/UmbracoFeatures.cs index d1c3899271..69fe58f76d 100644 --- a/src/Umbraco.Web/Features/UmbracoFeatures.cs +++ b/src/Umbraco.Core/Features/UmbracoFeatures.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Web.WebApi; namespace Umbraco.Web.Features { @@ -16,7 +15,7 @@ namespace Umbraco.Web.Features Disabled = new DisabledFeatures(); Enabled = new EnabledFeatures(); } - + /// /// Gets the disabled features. /// @@ -32,7 +31,7 @@ namespace Umbraco.Web.Features /// internal bool IsControllerEnabled(Type feature) { - if (typeof(UmbracoApiControllerBase).IsAssignableFrom(feature)) + if (typeof(IUmbracoFeature).IsAssignableFrom(feature)) return Disabled.Controllers.Contains(feature) == false; throw new NotSupportedException("Not a supported feature type."); diff --git a/src/Umbraco.Core/FileResources/BlockingWeb.config b/src/Umbraco.Core/FileResources/BlockingWeb.config deleted file mode 100644 index 80182af9a1..0000000000 --- a/src/Umbraco.Core/FileResources/BlockingWeb.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Core/FileResources/Files.Designer.cs b/src/Umbraco.Core/FileResources/Files.Designer.cs deleted file mode 100644 index afc88ff636..0000000000 --- a/src/Umbraco.Core/FileResources/Files.Designer.cs +++ /dev/null @@ -1,86 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Core.FileResources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Files { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Files() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Core.FileResources.Files", typeof(Files).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0"?> - /// - ///<!-- Blocks public downloading of anything in this folder and sub folders --> - /// - ///<configuration> - /// <system.web> - /// <httpHandlers> - /// <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/> - /// </httpHandlers> - /// </system.web> - /// <system.webServer> - /// <validation validateIntegratedModeConfiguration="false" /> - /// <handlers> - /// <remove name="BlockViewHandler"/> - /// <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.H [rest of string was truncated]";. - /// - internal static string BlockingWebConfig { - get { - return ResourceManager.GetString("BlockingWebConfig", resourceCulture); - } - } - } -} diff --git a/src/Umbraco.Core/FileResources/Files.resx b/src/Umbraco.Core/FileResources/Files.resx deleted file mode 100644 index 29c7feee77..0000000000 --- a/src/Umbraco.Core/FileResources/Files.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - blockingweb.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - diff --git a/src/Umbraco.Abstractions/GuidUdi.cs b/src/Umbraco.Core/GuidUdi.cs similarity index 100% rename from src/Umbraco.Abstractions/GuidUdi.cs rename to src/Umbraco.Core/GuidUdi.cs diff --git a/src/Umbraco.Abstractions/GuidUtils.cs b/src/Umbraco.Core/GuidUtils.cs similarity index 100% rename from src/Umbraco.Abstractions/GuidUtils.cs rename to src/Umbraco.Core/GuidUtils.cs diff --git a/src/Umbraco.Abstractions/HashCodeCombiner.cs b/src/Umbraco.Core/HashCodeCombiner.cs similarity index 100% rename from src/Umbraco.Abstractions/HashCodeCombiner.cs rename to src/Umbraco.Core/HashCodeCombiner.cs diff --git a/src/Umbraco.Abstractions/HashCodeHelper.cs b/src/Umbraco.Core/HashCodeHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/HashCodeHelper.cs rename to src/Umbraco.Core/HashCodeHelper.cs diff --git a/src/Umbraco.Abstractions/HashGenerator.cs b/src/Umbraco.Core/HashGenerator.cs similarity index 99% rename from src/Umbraco.Abstractions/HashGenerator.cs rename to src/Umbraco.Core/HashGenerator.cs index 62be0dacdd..c7fad08089 100644 --- a/src/Umbraco.Abstractions/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.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs similarity index 81% rename from src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs index b9604b48f3..6aeb83d61c 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Install; using Umbraco.Core.IO; using Umbraco.Core.Services; -using Umbraco.Web.Install; namespace Umbraco.Web.HealthCheck.Checks.Permissions { @@ -29,10 +29,16 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions public class FolderAndFilePermissionsCheck : HealthCheck { private readonly ILocalizedTextService _textService; + private readonly IGlobalSettings _globalSettings; + private readonly IFilePermissionHelper _filePermissionHelper; + private readonly IIOHelper _ioHelper; - public FolderAndFilePermissionsCheck(ILocalizedTextService textService) + public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IGlobalSettings globalSettings, IFilePermissionHelper filePermissionHelper, IIOHelper ioHelper) { _textService = textService; + _globalSettings = globalSettings; + _filePermissionHelper = filePermissionHelper; + _ioHelper = ioHelper; } /// @@ -66,10 +72,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions { Constants.SystemDirectories.Preview, PermissionCheckRequirement.Required }, { Constants.SystemDirectories.AppPlugins, PermissionCheckRequirement.Required }, { Constants.SystemDirectories.Config, PermissionCheckRequirement.Optional }, - { Current.Configs.Global().UmbracoCssPath, PermissionCheckRequirement.Optional }, - { Current.Configs.Global().UmbracoMediaPath, PermissionCheckRequirement.Optional }, - { Current.Configs.Global().UmbracoScriptsPath, PermissionCheckRequirement.Optional }, - { Current.Configs.Global().UmbracoPath, PermissionCheckRequirement.Optional }, + { _globalSettings.UmbracoCssPath, PermissionCheckRequirement.Optional }, + { _globalSettings.UmbracoMediaPath, PermissionCheckRequirement.Optional }, + { _globalSettings.UmbracoScriptsPath, PermissionCheckRequirement.Optional }, + { _globalSettings.UmbracoPath, PermissionCheckRequirement.Optional }, { Constants.SystemDirectories.MvcViews, PermissionCheckRequirement.Optional } }; @@ -82,15 +88,15 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions }; // Run checks for required and optional paths for modify permission - var requiredPathCheckResult = FilePermissionHelper.EnsureDirectories( + var requiredPathCheckResult = _filePermissionHelper.EnsureDirectories( GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out var requiredFailedPaths); - var optionalPathCheckResult = FilePermissionHelper.EnsureDirectories( + var optionalPathCheckResult = _filePermissionHelper.EnsureDirectories( GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out var optionalFailedPaths); //now check the special folders - var requiredPathCheckResult2 = FilePermissionHelper.EnsureDirectories( + var requiredPathCheckResult2 = _filePermissionHelper.EnsureDirectories( GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Required), out var requiredFailedPaths2, writeCausesRestart:true); - var optionalPathCheckResult2 = FilePermissionHelper.EnsureDirectories( + var optionalPathCheckResult2 = _filePermissionHelper.EnsureDirectories( GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Optional), out var optionalFailedPaths2, writeCausesRestart: true); requiredPathCheckResult = requiredPathCheckResult && requiredPathCheckResult2; @@ -115,18 +121,18 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions // Run checks for required and optional paths for modify permission IEnumerable requiredFailedPaths; IEnumerable optionalFailedPaths; - var requiredPathCheckResult = FilePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out requiredFailedPaths); - var optionalPathCheckResult = FilePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out optionalFailedPaths); + var requiredPathCheckResult = _filePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out requiredFailedPaths); + var optionalPathCheckResult = _filePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out optionalFailedPaths); return GetStatus(requiredPathCheckResult, requiredFailedPaths, optionalPathCheckResult, optionalFailedPaths, PermissionCheckFor.File); } - private static string[] GetPathsToCheck(Dictionary pathsToCheck, + private string[] GetPathsToCheck(Dictionary pathsToCheck, PermissionCheckRequirement requirement) { return pathsToCheck .Where(x => x.Value == requirement) - .Select(x => Current.IOHelper.MapPath(x.Key)) + .Select(x => _ioHelper.MapPath(x.Key)) .OrderBy(x => x) .ToArray(); } @@ -166,7 +172,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions private string GetMessageForPathCheckFailure(string messageKey, IEnumerable failedPaths) { - var rootFolder = Current.IOHelper.MapPath("/"); + var rootFolder = _ioHelper.MapPath("/"); var failedFolders = failedPaths .Select(x => ParseFolderFromFullPath(rootFolder, x)); return _textService.Localize(messageKey, diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs similarity index 97% rename from src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs index 13d95dfe04..f735c6100a 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs @@ -7,7 +7,6 @@ using System.Text.RegularExpressions; using System.Xml.Linq; using System.Xml.XPath; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Services; @@ -24,19 +23,21 @@ namespace Umbraco.Web.HealthCheck.Checks.Security private readonly string _value; private readonly string _localizedTextPrefix; private readonly bool _metaTagOptionAvailable; + private readonly IIOHelper _ioHelper; protected BaseHttpHeaderCheck( IRuntimeState runtime, ILocalizedTextService textService, - string header, string value, string localizedTextPrefix, bool metaTagOptionAvailable) + string header, string value, string localizedTextPrefix, bool metaTagOptionAvailable, IIOHelper ioHelper) { Runtime = runtime; TextService = textService ?? throw new ArgumentNullException(nameof(textService)); - + _ioHelper = ioHelper; _header = header; _value = value; _localizedTextPrefix = localizedTextPrefix; _metaTagOptionAvailable = metaTagOptionAvailable; + } /// @@ -169,7 +170,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security { // There don't look to be any useful classes defined in https://msdn.microsoft.com/en-us/library/system.web.configuration(v=vs.110).aspx // for working with the customHeaders section, so working with the XML directly. - var configFile = Current.IOHelper.MapPath("~/Web.config"); + var configFile = _ioHelper.MapPath("~/Web.config"); var doc = XDocument.Load(configFile); var systemWebServerElement = doc.XPathSelectElement("/configuration/system.webServer"); var httpProtocolElement = systemWebServerElement.Element("httpProtocol"); diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/ClickJackingCheck.cs similarity index 84% rename from src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/ClickJackingCheck.cs index 359bfa83cb..2a3a0a9dab 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/ClickJackingCheck.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Security @@ -10,8 +11,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security Group = "Security")] public class ClickJackingCheck : BaseHttpHeaderCheck { - public ClickJackingCheck(IRuntimeState runtime, ILocalizedTextService textService) - : base(runtime, textService, "X-Frame-Options", "sameorigin", "clickJacking", true) + public ClickJackingCheck(IRuntimeState runtime, ILocalizedTextService textService, IIOHelper ioHelper) + : base(runtime, textService, "X-Frame-Options", "sameorigin", "clickJacking", true, ioHelper) { } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs similarity index 93% rename from src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs index fd76b9d486..6fa6e8b37e 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs @@ -16,13 +16,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Security { private readonly ILocalizedTextService _textService; private readonly IRuntimeState _runtime; - private readonly IHttpContextAccessor _httpContextAccessor; - public ExcessiveHeadersCheck(ILocalizedTextService textService, IRuntimeState runtime, IHttpContextAccessor httpContextAccessor) + public ExcessiveHeadersCheck(ILocalizedTextService textService, IRuntimeState runtime) { _textService = textService; _runtime = runtime; - _httpContextAccessor = httpContextAccessor; } /// diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/HstsCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/HstsCheck.cs similarity index 91% rename from src/Umbraco.Web/HealthCheck/Checks/Security/HstsCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/HstsCheck.cs index d0da243ced..7ce7a80c93 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/HstsCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/HstsCheck.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Security @@ -15,8 +16,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security // and the blog post of Troy Hunt (https://www.troyhunt.com/understanding-http-strict-transport/) // If you want do to it perfectly, you have to submit it https://hstspreload.org/, // but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites. - public HstsCheck(IRuntimeState runtime, ILocalizedTextService textService) - : base(runtime, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true) + public HstsCheck(IRuntimeState runtime, ILocalizedTextService textService, IIOHelper ioHelper) + : base(runtime, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true, ioHelper) { } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/HttpsCheck.cs similarity index 94% rename from src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/HttpsCheck.cs index eec51eacf0..615748b93e 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/HttpsCheck.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Net; using System.Security.Cryptography.X509Certificates; -using System.Web; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Services; +using Umbraco.Core.Logging; using Umbraco.Web.HealthCheck.Checks.Config; namespace Umbraco.Web.HealthCheck.Checks.Security @@ -22,14 +21,18 @@ namespace Umbraco.Web.HealthCheck.Checks.Security private readonly ILocalizedTextService _textService; private readonly IRuntimeState _runtime; private readonly IGlobalSettings _globalSettings; + private readonly IIOHelper _ioHelper; + private readonly ILogger _logger; private const string FixHttpsSettingAction = "fixHttpsSetting"; - public HttpsCheck(ILocalizedTextService textService, IRuntimeState runtime, IGlobalSettings globalSettings) + public HttpsCheck(ILocalizedTextService textService, IRuntimeState runtime, IGlobalSettings globalSettings, IIOHelper ioHelper, ILogger logger) { _textService = textService; _runtime = runtime; _globalSettings = globalSettings; + _ioHelper = ioHelper; + _logger = logger; } /// @@ -76,7 +79,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security { // Got a valid response, check now for if certificate expiring within 14 days // Hat-tip: https://stackoverflow.com/a/15343898/489433 - const int NumberOfDaysForExpiryWarning = 14; + const int numberOfDaysForExpiryWarning = 14; var cert = request.ServicePoint.Certificate; var cert2 = new X509Certificate2(cert); var expirationDate = cert2.NotAfter; @@ -87,7 +90,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security result = StatusResultType.Error; message = _textService.Localize("healthcheck/httpsCheckExpiredCertificate"); } - else if (daysToExpiry < NumberOfDaysForExpiryWarning) + else if (daysToExpiry < numberOfDaysForExpiryWarning) { result = StatusResultType.Warning; message = _textService.Localize("healthcheck/httpsCheckExpiringCertificate", new[] { daysToExpiry.ToString() }); @@ -183,9 +186,9 @@ namespace Umbraco.Web.HealthCheck.Checks.Security private HealthCheckStatus FixHttpsSetting() { - var configFile = Current.IOHelper.MapPath("~/Web.config"); + var configFile = _ioHelper.MapPath("~/Web.config"); const string xPath = "/configuration/appSettings/add[@key='Umbraco.Core.UseHttps']/@value"; - var configurationService = new ConfigurationService(configFile, xPath, _textService); + var configurationService = new ConfigurationService(configFile, xPath, _textService, _logger); var updateConfigFile = configurationService.UpdateConfigFile("true"); if (updateConfigFile.Success) diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/NoSniffCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/NoSniffCheck.cs similarity index 84% rename from src/Umbraco.Web/HealthCheck/Checks/Security/NoSniffCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/NoSniffCheck.cs index ceeec152ad..392d8c94db 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/NoSniffCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/NoSniffCheck.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Security @@ -10,8 +11,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security Group = "Security")] public class NoSniffCheck : BaseHttpHeaderCheck { - public NoSniffCheck(IRuntimeState runtime, ILocalizedTextService textService) - : base(runtime, textService, "X-Content-Type-Options", "nosniff", "noSniff", false) + public NoSniffCheck(IRuntimeState runtime, ILocalizedTextService textService, IIOHelper ioHelper) + : base(runtime, textService, "X-Content-Type-Options", "nosniff", "noSniff", false, ioHelper) { } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/XssProtectionCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Security/XssProtectionCheck.cs similarity index 90% rename from src/Umbraco.Web/HealthCheck/Checks/Security/XssProtectionCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Security/XssProtectionCheck.cs index dc25aa6a65..8881221923 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/XssProtectionCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Security/XssProtectionCheck.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Security @@ -15,8 +16,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security // and the blog post of Troy Hunt (https://www.troyhunt.com/understanding-http-strict-transport/) // If you want do to it perfectly, you have to submit it https://hstspreload.appspot.com/, // but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites. - public XssProtectionCheck(IRuntimeState runtime, ILocalizedTextService textService) - : base(runtime, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true) + public XssProtectionCheck(IRuntimeState runtime, ILocalizedTextService textService, IIOHelper ioHelper) + : base(runtime, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true, ioHelper) { } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Services/SmtpCheck.cs similarity index 80% rename from src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs rename to src/Umbraco.Core/HealthCheck/Checks/Services/SmtpCheck.cs index d6f7cef497..7b57c01223 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Services/SmtpCheck.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net.Configuration; using System.Net.Sockets; -using System.Web.Configuration; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Services @@ -18,11 +17,13 @@ namespace Umbraco.Web.HealthCheck.Checks.Services { private readonly ILocalizedTextService _textService; private readonly IRuntimeState _runtime; + private readonly IGlobalSettings _globalSettings; - public SmtpCheck(ILocalizedTextService textService, IRuntimeState runtime) + public SmtpCheck(ILocalizedTextService textService, IRuntimeState runtime, IGlobalSettings globalSettings) { _textService = textService; _runtime = runtime; + _globalSettings = globalSettings; } /// @@ -47,31 +48,27 @@ namespace Umbraco.Web.HealthCheck.Checks.Services private HealthCheckStatus CheckSmtpSettings() { - const int DefaultSmtpPort = 25; var message = string.Empty; var success = false; - // appPath is the virtual application root path on the server - var config = WebConfigurationManager.OpenWebConfiguration(_runtime.ApplicationVirtualPath); - var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings"); - if (settings == null) + var smtpSettings = _globalSettings.SmtpSettings; + + if (smtpSettings == null) { message = _textService.Localize("healthcheck/smtpMailSettingsNotFound"); } else { - var host = settings.Smtp.Network.Host; - var port = settings.Smtp.Network.Port == 0 ? DefaultSmtpPort : settings.Smtp.Network.Port; - if (string.IsNullOrEmpty(host)) + if (string.IsNullOrEmpty(smtpSettings.Host)) { message = _textService.Localize("healthcheck/smtpMailSettingsHostNotConfigured"); } else { - success = CanMakeSmtpConnection(host, port); + success = CanMakeSmtpConnection(smtpSettings.Host, smtpSettings.Port); message = success ? _textService.Localize("healthcheck/smtpMailSettingsConnectionSuccess") - : _textService.Localize("healthcheck/smtpMailSettingsConnectionFail", new [] { host, port.ToString() }); + : _textService.Localize("healthcheck/smtpMailSettingsConnectionFail", new [] { smtpSettings.Host, smtpSettings.Port.ToString() }); } } diff --git a/src/Umbraco.Web/HealthCheck/HealthCheck.cs b/src/Umbraco.Core/HealthCheck/HealthCheck.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheck.cs rename to src/Umbraco.Core/HealthCheck/HealthCheck.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckAction.cs b/src/Umbraco.Core/HealthCheck/HealthCheckAction.cs similarity index 97% rename from src/Umbraco.Web/HealthCheck/HealthCheckAction.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckAction.cs index ce7d842e96..124068df72 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckAction.cs +++ b/src/Umbraco.Core/HealthCheck/HealthCheckAction.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; namespace Umbraco.Web.HealthCheck { diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckAttribute.cs b/src/Umbraco.Core/HealthCheck/HealthCheckAttribute.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckAttribute.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckAttribute.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckCollection.cs b/src/Umbraco.Core/HealthCheck/HealthCheckCollection.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckCollection.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckCollection.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckGroup.cs b/src/Umbraco.Core/HealthCheck/HealthCheckGroup.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckGroup.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckGroup.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodAttribute.cs b/src/Umbraco.Core/HealthCheck/HealthCheckNotificationMethodAttribute.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodAttribute.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckNotificationMethodAttribute.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckStatus.cs b/src/Umbraco.Core/HealthCheck/HealthCheckStatus.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckStatus.cs rename to src/Umbraco.Core/HealthCheck/HealthCheckStatus.cs diff --git a/src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs b/src/Umbraco.Core/HealthCheck/HeathCheckCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs rename to src/Umbraco.Core/HealthCheck/HeathCheckCollectionBuilder.cs diff --git a/src/Umbraco.Web/HealthCheck/StatusResultType.cs b/src/Umbraco.Core/HealthCheck/StatusResultType.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/StatusResultType.cs rename to src/Umbraco.Core/HealthCheck/StatusResultType.cs diff --git a/src/Umbraco.Abstractions/HexEncoder.cs b/src/Umbraco.Core/HexEncoder.cs similarity index 100% rename from src/Umbraco.Abstractions/HexEncoder.cs rename to src/Umbraco.Core/HexEncoder.cs diff --git a/src/Umbraco.Abstractions/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs similarity index 70% rename from src/Umbraco.Abstractions/Hosting/IHostingEnvironment.cs rename to src/Umbraco.Core/Hosting/IHostingEnvironment.cs index a71c449bb8..5b97d8e4f3 100644 --- a/src/Umbraco.Abstractions/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -1,3 +1,5 @@ +using System; + namespace Umbraco.Core.Hosting { public interface IHostingEnvironment @@ -9,19 +11,16 @@ namespace Umbraco.Core.Hosting string LocalTempPath { get; } string ApplicationVirtualPath { get; } - int CurrentDomainId { get; } - bool IsDebugMode { get; } /// /// Gets a value indicating whether Umbraco is hosted. /// bool IsHosted { get; } + Version IISVersion { get; } 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.Web/HybridAccessorBase.cs b/src/Umbraco.Core/HybridAccessorBase.cs similarity index 71% rename from src/Umbraco.Web/HybridAccessorBase.cs rename to src/Umbraco.Core/HybridAccessorBase.cs index ec18b6f3d4..ad33fbf067 100644 --- a/src/Umbraco.Web/HybridAccessorBase.cs +++ b/src/Umbraco.Core/HybridAccessorBase.cs @@ -1,6 +1,7 @@ using System; -using System.Runtime.Remoting.Messaging; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Scoping; namespace Umbraco.Web { @@ -16,13 +17,13 @@ namespace Umbraco.Web public abstract class HybridAccessorBase where T : class { + private readonly IRequestCache _requestCache; + // ReSharper disable StaticMemberInGenericType private static readonly object Locker = new object(); private static bool _registered; // ReSharper restore StaticMemberInGenericType - private readonly IHttpContextAccessor _httpContextAccessor; - protected abstract string ItemKey { get; } // read @@ -42,17 +43,16 @@ namespace Umbraco.Web // yes! flows with async! private T NonContextValue { - get => (T) CallContext.LogicalGetData(ItemKey); + get => CallContext.GetData(ItemKey); set { - if (value == null) CallContext.FreeNamedDataSlot(ItemKey); - else CallContext.LogicalSetData(ItemKey, value); + CallContext.SetData(ItemKey, value); } } - protected HybridAccessorBase(IHttpContextAccessor httpContextAccessor) + protected HybridAccessorBase(IRequestCache requestCache) { - _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); + _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache)); lock (Locker) { @@ -65,15 +65,14 @@ namespace Umbraco.Web var itemKey = ItemKey; // virtual SafeCallContext.Register(() => { - var value = CallContext.LogicalGetData(itemKey); - CallContext.FreeNamedDataSlot(itemKey); + var value = CallContext.GetData(itemKey); return value; }, o => { if (o == null) return; var value = o as T; if (value == null) throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o)); - CallContext.LogicalSetData(itemKey, value); + CallContext.SetData(itemKey, value); }); } @@ -81,20 +80,23 @@ namespace Umbraco.Web { get { - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) return NonContextValue; - return (T) httpContext.Items[ItemKey]; + if (!_requestCache.IsAvailable) + { + return NonContextValue; + } + return (T) _requestCache.Get(ItemKey); } set { - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) + if (!_requestCache.IsAvailable) + { NonContextValue = value; + } else if (value == null) - httpContext.Items.Remove(ItemKey); + _requestCache.Remove(ItemKey); else - httpContext.Items[ItemKey] = value; + _requestCache.Set(ItemKey, value); } } } diff --git a/src/Umbraco.Web/HybridEventMessagesAccessor.cs b/src/Umbraco.Core/HybridEventMessagesAccessor.cs similarity index 70% rename from src/Umbraco.Web/HybridEventMessagesAccessor.cs rename to src/Umbraco.Core/HybridEventMessagesAccessor.cs index fddde403d8..b2700eb137 100644 --- a/src/Umbraco.Web/HybridEventMessagesAccessor.cs +++ b/src/Umbraco.Core/HybridEventMessagesAccessor.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Events; +using Umbraco.Core.Cache; +using Umbraco.Core.Events; namespace Umbraco.Web { @@ -6,8 +7,8 @@ namespace Umbraco.Web { protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor"; - public HybridEventMessagesAccessor(IHttpContextAccessor httpContextAccessor) - : base(httpContextAccessor) + public HybridEventMessagesAccessor(IRequestCache requestCache) + : base(requestCache) { } public EventMessages EventMessages diff --git a/src/Umbraco.Abstractions/IBackOfficeInfo.cs b/src/Umbraco.Core/IBackOfficeInfo.cs similarity index 100% rename from src/Umbraco.Abstractions/IBackOfficeInfo.cs rename to src/Umbraco.Core/IBackOfficeInfo.cs diff --git a/src/Umbraco.Abstractions/ICompletable.cs b/src/Umbraco.Core/ICompletable.cs similarity index 100% rename from src/Umbraco.Abstractions/ICompletable.cs rename to src/Umbraco.Core/ICompletable.cs diff --git a/src/Umbraco.Abstractions/IDisposeOnRequestEnd.cs b/src/Umbraco.Core/IDisposeOnRequestEnd.cs similarity index 100% rename from src/Umbraco.Abstractions/IDisposeOnRequestEnd.cs rename to src/Umbraco.Core/IDisposeOnRequestEnd.cs diff --git a/src/Umbraco.Abstractions/IEmailSender.cs b/src/Umbraco.Core/IEmailSender.cs similarity index 100% rename from src/Umbraco.Abstractions/IEmailSender.cs rename to src/Umbraco.Core/IEmailSender.cs diff --git a/src/Umbraco.Abstractions/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/FileSystemExtensions.cs rename to src/Umbraco.Core/IO/FileSystemExtensions.cs diff --git a/src/Umbraco.Abstractions/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/FileSystemWrapper.cs rename to src/Umbraco.Core/IO/FileSystemWrapper.cs diff --git a/src/Umbraco.Abstractions/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/FileSystems.cs rename to src/Umbraco.Core/IO/FileSystems.cs diff --git a/src/Umbraco.Abstractions/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/IFileSystem.cs rename to src/Umbraco.Core/IO/IFileSystem.cs diff --git a/src/Umbraco.Abstractions/IO/IFileSystems.cs b/src/Umbraco.Core/IO/IFileSystems.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/IFileSystems.cs rename to src/Umbraco.Core/IO/IFileSystems.cs diff --git a/src/Umbraco.Abstractions/IO/IIOHelper.cs b/src/Umbraco.Core/IO/IIOHelper.cs similarity index 98% rename from src/Umbraco.Abstractions/IO/IIOHelper.cs rename to src/Umbraco.Core/IO/IIOHelper.cs index e8ef6d2973..f478b49de6 100644 --- a/src/Umbraco.Abstractions/IO/IIOHelper.cs +++ b/src/Umbraco.Core/IO/IIOHelper.cs @@ -4,6 +4,9 @@ namespace Umbraco.Core.IO { public interface IIOHelper { + + string BackOfficePath { get; } + bool ForceNotHosted { get; set; } char DirSepChar { get; } @@ -75,5 +78,6 @@ namespace Umbraco.Core.IO get; set; //Only required for unit tests } + } } diff --git a/src/Umbraco.Abstractions/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/IMediaFileSystem.cs rename to src/Umbraco.Core/IO/IMediaFileSystem.cs diff --git a/src/Umbraco.Abstractions/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/IMediaPathScheme.cs rename to src/Umbraco.Core/IO/IMediaPathScheme.cs diff --git a/src/Umbraco.Abstractions/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs similarity index 96% rename from src/Umbraco.Abstractions/IO/IOHelper.cs rename to src/Umbraco.Core/IO/IOHelper.cs index d5e1335f35..3abd413fd4 100644 --- a/src/Umbraco.Abstractions/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Reflection; using System.IO; using System.Linq; +using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.Strings; @@ -12,10 +13,22 @@ namespace Umbraco.Core.IO public class IOHelper : IIOHelper { private readonly IHostingEnvironment _hostingEnvironment; + private readonly IGlobalSettings _globalSettings; - public IOHelper(IHostingEnvironment hostingEnvironment) + public IOHelper(IHostingEnvironment hostingEnvironment, IGlobalSettings globalSettings) { _hostingEnvironment = hostingEnvironment; + _globalSettings = globalSettings; + } + + public string BackOfficePath + { + get + { + var path = _globalSettings.Path; + + return string.IsNullOrEmpty(path) ? string.Empty : ResolveUrl(path); + } } /// diff --git a/src/Umbraco.Core/IO/IOHelperExtensions.cs b/src/Umbraco.Core/IO/IOHelperExtensions.cs new file mode 100644 index 0000000000..39bd5e6cc9 --- /dev/null +++ b/src/Umbraco.Core/IO/IOHelperExtensions.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; + +namespace Umbraco.Core.IO +{ + public static class IOHelperExtensions + { + private static string _mvcArea; + + /// + /// Tries to create a directory. + /// + /// The IOHelper. + /// the directory path. + /// true if the directory was created, false otherwise. + public static bool TryCreateDirectory(this IIOHelper ioHelper, string dir) + { + try + { + var dirPath = ioHelper.MapPath(dir); + + if (Directory.Exists(dirPath) == false) + Directory.CreateDirectory(dirPath); + + var filePath = dirPath + "/" + CreateRandomFileName(ioHelper) + ".tmp"; + File.WriteAllText(filePath, "This is an Umbraco internal test file. It is safe to delete it."); + File.Delete(filePath); + return true; + } + catch + { + return false; + } + } + + public static string CreateRandomFileName(this IIOHelper ioHelper) + { + return "umbraco-test." + Guid.NewGuid().ToString("N").Substring(0, 8); + } + + /// + /// This returns the string of the MVC Area route. + /// + /// + /// This will return the MVC area that we will route all custom routes through like surface controllers, etc... + /// We will use the 'Path' (default ~/umbraco) to create it but since it cannot contain '/' and people may specify a path of ~/asdf/asdf/admin + /// we will convert the '/' to '-' and use that as the path. its a bit lame but will work. + /// + /// We also make sure that the virtual directory (SystemDirectories.Root) is stripped off first, otherwise we'd end up with something + /// like "MyVirtualDirectory-Umbraco" instead of just "Umbraco". + /// + public static string GetUmbracoMvcArea(this IIOHelper ioHelper) + { + if (_mvcArea != null) return _mvcArea; + + _mvcArea = GetUmbracoMvcAreaNoCache(ioHelper); + + return _mvcArea; + } + + internal static string GetUmbracoMvcAreaNoCache(this IIOHelper ioHelper) + { + if (ioHelper.BackOfficePath.IsNullOrWhiteSpace()) + { + throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified"); + } + + var path = ioHelper.BackOfficePath; + if (path.StartsWith(ioHelper.Root)) // beware of TrimStart, see U4-2518 + path = path.Substring(ioHelper.Root.Length); + return path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower(); + } + } +} diff --git a/src/Umbraco.Abstractions/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/MediaFileSystem.cs rename to src/Umbraco.Core/IO/MediaFileSystem.cs diff --git a/src/Umbraco.Abstractions/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs rename to src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs diff --git a/src/Umbraco.Abstractions/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/MediaPathSchemes/OriginalMediaPathScheme.cs rename to src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs diff --git a/src/Umbraco.Abstractions/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs rename to src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs diff --git a/src/Umbraco.Abstractions/IO/MediaPathSchemes/UniqueMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/MediaPathSchemes/UniqueMediaPathScheme.cs rename to src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs diff --git a/src/Umbraco.Abstractions/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/PhysicalFileSystem.cs rename to src/Umbraco.Core/IO/PhysicalFileSystem.cs diff --git a/src/Umbraco.Abstractions/IO/ShadowFileSystem.cs b/src/Umbraco.Core/IO/ShadowFileSystem.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/ShadowFileSystem.cs rename to src/Umbraco.Core/IO/ShadowFileSystem.cs diff --git a/src/Umbraco.Abstractions/IO/ShadowFileSystems.cs b/src/Umbraco.Core/IO/ShadowFileSystems.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/ShadowFileSystems.cs rename to src/Umbraco.Core/IO/ShadowFileSystems.cs diff --git a/src/Umbraco.Abstractions/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/ShadowWrapper.cs rename to src/Umbraco.Core/IO/ShadowWrapper.cs diff --git a/src/Umbraco.Abstractions/IO/SupportingFileSystems.cs b/src/Umbraco.Core/IO/SupportingFileSystems.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/SupportingFileSystems.cs rename to src/Umbraco.Core/IO/SupportingFileSystems.cs diff --git a/src/Umbraco.Abstractions/IO/SystemFiles.cs b/src/Umbraco.Core/IO/SystemFiles.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/SystemFiles.cs rename to src/Umbraco.Core/IO/SystemFiles.cs diff --git a/src/Umbraco.Abstractions/IO/ViewHelper.cs b/src/Umbraco.Core/IO/ViewHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/IO/ViewHelper.cs rename to src/Umbraco.Core/IO/ViewHelper.cs diff --git a/src/Umbraco.Core/IRegisteredObject.cs b/src/Umbraco.Core/IRegisteredObject.cs new file mode 100644 index 0000000000..abe52e2350 --- /dev/null +++ b/src/Umbraco.Core/IRegisteredObject.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core +{ + public interface IRegisteredObject + { + void Stop(bool immediate); + } +} diff --git a/src/Umbraco.Abstractions/ITagQuery.cs b/src/Umbraco.Core/ITagQuery.cs similarity index 100% rename from src/Umbraco.Abstractions/ITagQuery.cs rename to src/Umbraco.Core/ITagQuery.cs diff --git a/src/Umbraco.Core/IUmbracoContext.cs b/src/Umbraco.Core/IUmbracoContext.cs new file mode 100644 index 0000000000..66b3dc3965 --- /dev/null +++ b/src/Umbraco.Core/IUmbracoContext.cs @@ -0,0 +1,83 @@ +using System; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; + +namespace Umbraco.Web +{ + public interface IUmbracoContext + { + /// + /// This is used internally for performance calculations, the ObjectCreated DateTime is set as soon as this + /// object is instantiated which in the web site is created during the BeginRequest phase. + /// We can then determine complete rendering time from that. + /// + DateTime ObjectCreated { get; } + + /// + /// Gets the WebSecurity class + /// + IWebSecurity Security { get; } + + /// + /// Gets the uri that is handled by ASP.NET after server-side rewriting took place. + /// + Uri OriginalRequestUrl { get; } + + /// + /// Gets the cleaned up url that is handled by Umbraco. + /// + /// That is, lowercase, no trailing slash after path, no .aspx... + Uri CleanedUmbracoUrl { get; } + + /// + /// Gets the published snapshot. + /// + IPublishedSnapshot PublishedSnapshot { get; } + + /// + /// Gets the published content cache. + /// + IPublishedContentCache Content { get; } + + /// + /// Gets the published media cache. + /// + IPublishedMediaCache Media { get; } + + /// + /// Gets the domains cache. + /// + IDomainCache Domains { get; } + + /// + /// Boolean value indicating whether the current request is a front-end umbraco request + /// + bool IsFrontEndUmbracoRequest { get; } + + /// + /// Gets/sets the PublishedRequest object + /// + IPublishedRequest PublishedRequest { get; set; } + + /// + /// Gets the variation context accessor. + /// + IVariationContextAccessor VariationContextAccessor { get; } + + /// + /// Gets a value indicating whether the request has debugging enabled + /// + /// true if this instance is debug; otherwise, false. + bool IsDebug { get; } + + /// + /// Determines whether the current user is in a preview mode and browsing the site (ie. not in the admin UI) + /// + bool InPreviewMode { get; } + + IDisposable ForcedPreview(bool preview); + void Dispose(); + } +} diff --git a/src/Umbraco.Web/IUmbracoContextAccessor.cs b/src/Umbraco.Core/IUmbracoContextAccessor.cs similarity index 75% rename from src/Umbraco.Web/IUmbracoContextAccessor.cs rename to src/Umbraco.Core/IUmbracoContextAccessor.cs index 74df940865..5c7549bff6 100644 --- a/src/Umbraco.Web/IUmbracoContextAccessor.cs +++ b/src/Umbraco.Core/IUmbracoContextAccessor.cs @@ -5,6 +5,6 @@ /// public interface IUmbracoContextAccessor { - UmbracoContext UmbracoContext { get; set; } + IUmbracoContext UmbracoContext { get; set; } } } diff --git a/src/Umbraco.Web/IUmbracoContextFactory.cs b/src/Umbraco.Core/IUmbracoContextFactory.cs similarity index 70% rename from src/Umbraco.Web/IUmbracoContextFactory.cs rename to src/Umbraco.Core/IUmbracoContextFactory.cs index 6d89578da7..2a6dd50618 100644 --- a/src/Umbraco.Web/IUmbracoContextFactory.cs +++ b/src/Umbraco.Core/IUmbracoContextFactory.cs @@ -3,15 +3,15 @@ namespace Umbraco.Web { /// - /// Creates and manages instances. + /// Creates and manages instances. /// public interface IUmbracoContextFactory { /// - /// Ensures that a current exists. + /// Ensures that a current exists. /// /// - /// If an is already registered in the + /// If an is already registered in the /// , returns a non-root reference to it. /// Otherwise, create a new instance, registers it, and return a root reference /// to it. @@ -26,7 +26,6 @@ namespace Umbraco.Web /// // use umbracoContext... /// } /// - /// An optional http context. - UmbracoContextReference EnsureUmbracoContext(HttpContextBase httpContext = null); + UmbracoContextReference EnsureUmbracoContext(); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Abstractions/IfExtensions.cs b/src/Umbraco.Core/IfExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/IfExtensions.cs rename to src/Umbraco.Core/IfExtensions.cs diff --git a/src/Umbraco.Core/Install/IFilePermissionHelper.cs b/src/Umbraco.Core/Install/IFilePermissionHelper.cs new file mode 100644 index 0000000000..b60839cb00 --- /dev/null +++ b/src/Umbraco.Core/Install/IFilePermissionHelper.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Install +{ + public interface IFilePermissionHelper + { + bool RunFilePermissionTestSuite(out Dictionary> report); + bool EnsureDirectories(string[] dirs, out IEnumerable errors, bool writeCausesRestart = false); + bool EnsureFiles(string[] files, out IEnumerable errors); + } +} diff --git a/src/Umbraco.Web/Install/InstallException.cs b/src/Umbraco.Core/Install/InstallException.cs similarity index 100% rename from src/Umbraco.Web/Install/InstallException.cs rename to src/Umbraco.Core/Install/InstallException.cs diff --git a/src/Umbraco.Web/Install/InstallStatusTracker.cs b/src/Umbraco.Core/Install/InstallStatusTracker.cs similarity index 70% rename from src/Umbraco.Web/Install/InstallStatusTracker.cs rename to src/Umbraco.Core/Install/InstallStatusTracker.cs index 6a9482ad6d..c0c9a696fd 100644 --- a/src/Umbraco.Web/Install/InstallStatusTracker.cs +++ b/src/Umbraco.Core/Install/InstallStatusTracker.cs @@ -1,16 +1,11 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Collections; -using Umbraco.Core.Composing; using Umbraco.Core.IO; +using Umbraco.Core.Serialization; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install @@ -18,29 +13,37 @@ namespace Umbraco.Web.Install /// /// An internal in-memory status tracker for the current installation /// - internal static class InstallStatusTracker + public class InstallStatusTracker { + private readonly IIOHelper _ioHelper; + private readonly IJsonSerializer _jsonSerializer; + + public InstallStatusTracker(IIOHelper ioHelper, IJsonSerializer jsonSerializer) + { + _ioHelper = ioHelper; + _jsonSerializer = jsonSerializer; + } private static ConcurrentHashSet _steps = new ConcurrentHashSet(); - private static string GetFile(Guid installId) + private string GetFile(Guid installId) { - var file = Current.IOHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" - + "install_" - + installId.ToString("N") - + ".txt"); + var file = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" + + "install_" + + installId.ToString("N") + + ".txt"); return file; } - public static void Reset() + public void Reset() { _steps = new ConcurrentHashSet(); ClearFiles(); } - public static void ClearFiles() + public void ClearFiles() { - var dir = Current.IOHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); + var dir = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); if (Directory.Exists(dir)) { var files = Directory.GetFiles(dir); @@ -55,13 +58,13 @@ namespace Umbraco.Web.Install } } - public static IEnumerable InitializeFromFile(Guid installId) + public IEnumerable InitializeFromFile(Guid installId) { //check if we have our persisted file and read it var file = GetFile(installId); if (File.Exists(file)) { - var deserialized = JsonConvert.DeserializeObject>( + var deserialized = _jsonSerializer.Deserialize>( File.ReadAllText(file)); foreach (var item in deserialized) { @@ -75,7 +78,7 @@ namespace Umbraco.Web.Install return new List(_steps); } - public static IEnumerable Initialize(Guid installId, IEnumerable steps) + public IEnumerable Initialize(Guid installId, IEnumerable steps) { //if there are no steps in memory if (_steps.Count == 0) @@ -84,7 +87,7 @@ namespace Umbraco.Web.Install var file = GetFile(installId); if (File.Exists(file)) { - var deserialized = JsonConvert.DeserializeObject>( + var deserialized = _jsonSerializer.Deserialize>( File.ReadAllText(file)); foreach (var item in deserialized) { @@ -101,7 +104,7 @@ namespace Umbraco.Web.Install _steps.Add(new InstallTrackingItem(step.Name, step.ServerOrder)); } //save the file - var serialized = JsonConvert.SerializeObject(new List(_steps)); + var serialized = _jsonSerializer.Serialize(new List(_steps)); Directory.CreateDirectory(Path.GetDirectoryName(file)); File.WriteAllText(file, serialized); } @@ -115,7 +118,7 @@ namespace Umbraco.Web.Install ClearFiles(); //save the correct file - var serialized = JsonConvert.SerializeObject(new List(_steps)); + var serialized = _jsonSerializer.Serialize(new List(_steps)); Directory.CreateDirectory(Path.GetDirectoryName(file)); File.WriteAllText(file, serialized); } @@ -124,7 +127,7 @@ namespace Umbraco.Web.Install return new List(_steps); } - public static void SetComplete(Guid installId, string name, IDictionary additionalData = null) + public void SetComplete(Guid installId, string name, IDictionary additionalData = null) { var trackingItem = _steps.Single(x => x.Name == name); if (additionalData != null) @@ -135,7 +138,7 @@ namespace Umbraco.Web.Install //save the file var file = GetFile(installId); - var serialized = JsonConvert.SerializeObject(new List(_steps)); + var serialized = _jsonSerializer.Serialize(new List(_steps)); File.WriteAllText(file, serialized); } diff --git a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs b/src/Umbraco.Core/Install/InstallSteps/FilePermissionsStep.cs similarity index 73% rename from src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs rename to src/Umbraco.Core/Install/InstallSteps/FilePermissionsStep.cs index 2c236e6592..c3d7493084 100644 --- a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs +++ b/src/Umbraco.Core/Install/InstallSteps/FilePermissionsStep.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Umbraco.Core; -using Umbraco.Core.IO; +using Umbraco.Core.Install; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -13,11 +13,16 @@ namespace Umbraco.Web.Install.InstallSteps PerformsAppRestart = true)] internal class FilePermissionsStep : InstallSetupStep { + private readonly IFilePermissionHelper _filePermissionHelper; + public FilePermissionsStep(IFilePermissionHelper filePermissionHelper) + { + _filePermissionHelper = filePermissionHelper; + } public override Task ExecuteAsync(object model) { // validate file permissions Dictionary> report; - var permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out report); + var permissionsOk = _filePermissionHelper.RunFilePermissionTestSuite(out report); if (permissionsOk == false) throw new InstallException("Permission check failed", "permissionsreport", new { errors = report }); diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs similarity index 83% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs rename to src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs index f56db4c91c..e6cceb2c5b 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Web; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Web.Install.Models; @@ -13,6 +12,13 @@ namespace Umbraco.Web.Install.InstallSteps "StarterKitCleanup", 32, "Almost done")] internal class StarterKitCleanupStep : InstallSetupStep { + private readonly IIOHelper _ioHelper; + + public StarterKitCleanupStep(IIOHelper ioHelper) + { + _ioHelper = ioHelper; + } + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); @@ -27,7 +33,7 @@ namespace Umbraco.Web.Install.InstallSteps private void CleanupInstallation(int packageId, string packageFile) { - var zipFile = new FileInfo(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), HttpUtility.UrlDecode(packageFile))); + var zipFile = new FileInfo(Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Packages), HttpUtility.UrlDecode(packageFile))); if (zipFile.Exists) zipFile.Delete(); diff --git a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs b/src/Umbraco.Core/Install/InstallSteps/UpgradeStep.cs similarity index 84% rename from src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs rename to src/Umbraco.Core/Install/InstallSteps/UpgradeStep.cs index a844da87b4..0979f31dc5 100644 --- a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs +++ b/src/Umbraco.Core/Install/InstallSteps/UpgradeStep.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Umbraco.Core.Composing; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Web.Install.Models; @@ -14,10 +14,12 @@ namespace Umbraco.Web.Install.InstallSteps { public override bool RequiresExecution(object model) => true; private readonly IUmbracoVersion _umbracoVersion; + private readonly IRuntimeState _runtimeState; - public UpgradeStep(IUmbracoVersion umbracoVersion) + public UpgradeStep(IUmbracoVersion umbracoVersion, IRuntimeState runtimeState) { _umbracoVersion = umbracoVersion; + _runtimeState = runtimeState; } public override Task ExecuteAsync(object model) => Task.FromResult(null); @@ -43,9 +45,9 @@ namespace Umbraco.Web.Install.InstallSteps return value; } - var state = Current.RuntimeState; // TODO: inject - var currentState = FormatGuidState(state.CurrentMigrationState); - var newState = FormatGuidState(state.FinalMigrationState); + + var currentState = FormatGuidState(_runtimeState.CurrentMigrationState); + var newState = FormatGuidState(_runtimeState.FinalMigrationState); var reportUrl = $"https://our.umbraco.com/contribute/releases/compare?from={currentVersion}&to={newVersion}¬es=1"; diff --git a/src/Umbraco.Web/Install/Models/DatabaseModel.cs b/src/Umbraco.Core/Install/Models/DatabaseModel.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/DatabaseModel.cs rename to src/Umbraco.Core/Install/Models/DatabaseModel.cs diff --git a/src/Umbraco.Web/Install/Models/DatabaseType.cs b/src/Umbraco.Core/Install/Models/DatabaseType.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/DatabaseType.cs rename to src/Umbraco.Core/Install/Models/DatabaseType.cs diff --git a/src/Umbraco.Web/Install/Models/InstallInstructions.cs b/src/Umbraco.Core/Install/Models/InstallInstructions.cs similarity index 80% rename from src/Umbraco.Web/Install/Models/InstallInstructions.cs rename to src/Umbraco.Core/Install/Models/InstallInstructions.cs index da4dfb1671..159edda9e6 100644 --- a/src/Umbraco.Web/Install/Models/InstallInstructions.cs +++ b/src/Umbraco.Core/Install/Models/InstallInstructions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Newtonsoft.Json.Linq; namespace Umbraco.Web.Install.Models { @@ -9,7 +8,7 @@ namespace Umbraco.Web.Install.Models public class InstallInstructions { [DataMember(Name = "instructions")] - public IDictionary Instructions { get; set; } + public IDictionary Instructions { get; set; } [DataMember(Name = "installId")] public Guid InstallId { get; set; } diff --git a/src/Umbraco.Web/Install/Models/InstallProgressResultModel.cs b/src/Umbraco.Core/Install/Models/InstallProgressResultModel.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallProgressResultModel.cs rename to src/Umbraco.Core/Install/Models/InstallProgressResultModel.cs diff --git a/src/Umbraco.Web/Install/Models/InstallSetup.cs b/src/Umbraco.Core/Install/Models/InstallSetup.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallSetup.cs rename to src/Umbraco.Core/Install/Models/InstallSetup.cs diff --git a/src/Umbraco.Web/Install/Models/InstallSetupResult.cs b/src/Umbraco.Core/Install/Models/InstallSetupResult.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallSetupResult.cs rename to src/Umbraco.Core/Install/Models/InstallSetupResult.cs diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs b/src/Umbraco.Core/Install/Models/InstallSetupStep.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallSetupStep.cs rename to src/Umbraco.Core/Install/Models/InstallSetupStep.cs diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStepAttribute.cs b/src/Umbraco.Core/Install/Models/InstallSetupStepAttribute.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallSetupStepAttribute.cs rename to src/Umbraco.Core/Install/Models/InstallSetupStepAttribute.cs diff --git a/src/Umbraco.Web/Install/Models/InstallTrackingItem.cs b/src/Umbraco.Core/Install/Models/InstallTrackingItem.cs similarity index 96% rename from src/Umbraco.Web/Install/Models/InstallTrackingItem.cs rename to src/Umbraco.Core/Install/Models/InstallTrackingItem.cs index 1b1985dd1e..9bc8201ba9 100644 --- a/src/Umbraco.Web/Install/Models/InstallTrackingItem.cs +++ b/src/Umbraco.Core/Install/Models/InstallTrackingItem.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; namespace Umbraco.Web.Install.Models { - internal class InstallTrackingItem + public class InstallTrackingItem { public InstallTrackingItem(string name, int serverOrder) { diff --git a/src/Umbraco.Web/Install/Models/InstallationType.cs b/src/Umbraco.Core/Install/Models/InstallationType.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/InstallationType.cs rename to src/Umbraco.Core/Install/Models/InstallationType.cs diff --git a/src/Umbraco.Web/Install/Models/Package.cs b/src/Umbraco.Core/Install/Models/Package.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/Package.cs rename to src/Umbraco.Core/Install/Models/Package.cs diff --git a/src/Umbraco.Web/Install/Models/UserModel.cs b/src/Umbraco.Core/Install/Models/UserModel.cs similarity index 100% rename from src/Umbraco.Web/Install/Models/UserModel.cs rename to src/Umbraco.Core/Install/Models/UserModel.cs diff --git a/src/Umbraco.Core/InstallLog.cs b/src/Umbraco.Core/InstallLog.cs new file mode 100644 index 0000000000..cb14ebd650 --- /dev/null +++ b/src/Umbraco.Core/InstallLog.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Models +{ + public class InstallLog + { + public Guid InstallId { get; } + public bool IsUpgrade { get; set; } + public bool InstallCompleted { get; set; } + public DateTime Timestamp { get; set; } + public int VersionMajor { get; } + public int VersionMinor { get; } + public int VersionPatch { get; } + public string VersionComment { get; } + public string Error { get; } + public string UserAgent { get; } + public string DbProvider { get; set; } + + public InstallLog(Guid installId, bool isUpgrade, bool installCompleted, DateTime timestamp, int versionMajor, int versionMinor, int versionPatch, string versionComment, string error, string userAgent, string dbProvider) + { + InstallId = installId; + IsUpgrade = isUpgrade; + InstallCompleted = installCompleted; + Timestamp = timestamp; + VersionMajor = versionMajor; + VersionMinor = versionMinor; + VersionPatch = versionPatch; + VersionComment = versionComment; + Error = error; + UserAgent = userAgent; + DbProvider = dbProvider; + } + } +} diff --git a/src/Umbraco.Abstractions/IntExtensions.cs b/src/Umbraco.Core/IntExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/IntExtensions.cs rename to src/Umbraco.Core/IntExtensions.cs diff --git a/src/Umbraco.Abstractions/KeyValuePairExtensions.cs b/src/Umbraco.Core/KeyValuePairExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/KeyValuePairExtensions.cs rename to src/Umbraco.Core/KeyValuePairExtensions.cs diff --git a/src/Umbraco.Abstractions/LambdaExpressionCacheKey.cs b/src/Umbraco.Core/LambdaExpressionCacheKey.cs similarity index 100% rename from src/Umbraco.Abstractions/LambdaExpressionCacheKey.cs rename to src/Umbraco.Core/LambdaExpressionCacheKey.cs diff --git a/src/Umbraco.Abstractions/Logging/DebugDiagnosticsLogger.cs b/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/DebugDiagnosticsLogger.cs rename to src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs diff --git a/src/Umbraco.Abstractions/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/DisposableTimer.cs rename to src/Umbraco.Core/Logging/DisposableTimer.cs diff --git a/src/Umbraco.Abstractions/Logging/ILogger.cs b/src/Umbraco.Core/Logging/ILogger.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/ILogger.cs rename to src/Umbraco.Core/Logging/ILogger.cs diff --git a/src/Umbraco.Abstractions/Logging/IMessageTemplates.cs b/src/Umbraco.Core/Logging/IMessageTemplates.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/IMessageTemplates.cs rename to src/Umbraco.Core/Logging/IMessageTemplates.cs diff --git a/src/Umbraco.Abstractions/Logging/IProfiler.cs b/src/Umbraco.Core/Logging/IProfiler.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/IProfiler.cs rename to src/Umbraco.Core/Logging/IProfiler.cs diff --git a/src/Umbraco.Abstractions/Logging/IProfilingLogger.cs b/src/Umbraco.Core/Logging/IProfilingLogger.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/IProfilingLogger.cs rename to src/Umbraco.Core/Logging/IProfilingLogger.cs diff --git a/src/Umbraco.Abstractions/Logging/LogLevel.cs b/src/Umbraco.Core/Logging/LogLevel.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/LogLevel.cs rename to src/Umbraco.Core/Logging/LogLevel.cs diff --git a/src/Umbraco.Abstractions/Logging/LogProfiler.cs b/src/Umbraco.Core/Logging/LogProfiler.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/LogProfiler.cs rename to src/Umbraco.Core/Logging/LogProfiler.cs diff --git a/src/Umbraco.Abstractions/Logging/LoggerExtensions.cs b/src/Umbraco.Core/Logging/LoggerExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/LoggerExtensions.cs rename to src/Umbraco.Core/Logging/LoggerExtensions.cs diff --git a/src/Umbraco.Abstractions/Logging/LoggingTaskExtension.cs b/src/Umbraco.Core/Logging/LoggingTaskExtension.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/LoggingTaskExtension.cs rename to src/Umbraco.Core/Logging/LoggingTaskExtension.cs diff --git a/src/Umbraco.Abstractions/Logging/NullLogger.cs b/src/Umbraco.Core/Logging/NullLogger.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/NullLogger.cs rename to src/Umbraco.Core/Logging/NullLogger.cs diff --git a/src/Umbraco.Abstractions/Logging/ProfilerExtensions.cs b/src/Umbraco.Core/Logging/ProfilerExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/ProfilerExtensions.cs rename to src/Umbraco.Core/Logging/ProfilerExtensions.cs diff --git a/src/Umbraco.Abstractions/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/ProfilingLogger.cs rename to src/Umbraco.Core/Logging/ProfilingLogger.cs diff --git a/src/Umbraco.Abstractions/Logging/VoidProfiler.cs b/src/Umbraco.Core/Logging/VoidProfiler.cs similarity index 100% rename from src/Umbraco.Abstractions/Logging/VoidProfiler.cs rename to src/Umbraco.Core/Logging/VoidProfiler.cs diff --git a/src/Umbraco.Web/Macros/IMacroRenderer.cs b/src/Umbraco.Core/Macros/IMacroRenderer.cs similarity index 100% rename from src/Umbraco.Web/Macros/IMacroRenderer.cs rename to src/Umbraco.Core/Macros/IMacroRenderer.cs diff --git a/src/Umbraco.Core/Macros/MacroContent.cs b/src/Umbraco.Core/Macros/MacroContent.cs new file mode 100644 index 0000000000..60dfb9210d --- /dev/null +++ b/src/Umbraco.Core/Macros/MacroContent.cs @@ -0,0 +1,20 @@ +using System; + +namespace Umbraco.Web.Macros +{ + // represents the content of a macro + public class MacroContent + { + // gets or sets the text content + public string Text { get; set; } + + // gets or sets the date the content was generated + public DateTime Date { get; set; } = DateTime.Now; + + // a value indicating whether the content is empty + public bool IsEmpty => Text is null; + + // gets an empty macro content + public static MacroContent Empty { get; } = new MacroContent(); + } +} diff --git a/src/Umbraco.Abstractions/Macros/MacroErrorBehaviour.cs b/src/Umbraco.Core/Macros/MacroErrorBehaviour.cs similarity index 100% rename from src/Umbraco.Abstractions/Macros/MacroErrorBehaviour.cs rename to src/Umbraco.Core/Macros/MacroErrorBehaviour.cs diff --git a/src/Umbraco.Web/Macros/MacroModel.cs b/src/Umbraco.Core/Macros/MacroModel.cs similarity index 91% rename from src/Umbraco.Web/Macros/MacroModel.cs rename to src/Umbraco.Core/Macros/MacroModel.cs index dd9a73b147..6b5013115a 100644 --- a/src/Umbraco.Web/Macros/MacroModel.cs +++ b/src/Umbraco.Core/Macros/MacroModel.cs @@ -20,8 +20,6 @@ namespace Umbraco.Web.Macros /// public string Alias { get; set; } - public MacroTypes MacroType { get; set; } - public string MacroSource { get; set; } public int CacheDuration { get; set; } @@ -46,7 +44,6 @@ namespace Umbraco.Web.Macros Id = macro.Id; Name = macro.Name; Alias = macro.Alias; - MacroType = macro.MacroType; MacroSource = macro.MacroSource; CacheDuration = macro.CacheDuration; CacheByPage = macro.CacheByPage; @@ -55,8 +52,6 @@ namespace Umbraco.Web.Macros foreach (var prop in macro.Properties) Properties.Add(new MacroPropertyModel(prop.Alias, string.Empty, prop.EditorAlias)); - - MacroType = macro.MacroType; } } } diff --git a/src/Umbraco.Web/Macros/MacroPropertyModel.cs b/src/Umbraco.Core/Macros/MacroPropertyModel.cs similarity index 100% rename from src/Umbraco.Web/Macros/MacroPropertyModel.cs rename to src/Umbraco.Core/Macros/MacroPropertyModel.cs diff --git a/src/Umbraco.Abstractions/Manifest/IManifestFilter.cs b/src/Umbraco.Core/Manifest/IManifestFilter.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/IManifestFilter.cs rename to src/Umbraco.Core/Manifest/IManifestFilter.cs diff --git a/src/Umbraco.Abstractions/Manifest/IManifestParser.cs b/src/Umbraco.Core/Manifest/IManifestParser.cs similarity index 88% rename from src/Umbraco.Abstractions/Manifest/IManifestParser.cs rename to src/Umbraco.Core/Manifest/IManifestParser.cs index eeb0c756f6..3eec7007b3 100644 --- a/src/Umbraco.Abstractions/Manifest/IManifestParser.cs +++ b/src/Umbraco.Core/Manifest/IManifestParser.cs @@ -17,7 +17,5 @@ namespace Umbraco.Core.Manifest /// Parses a manifest. /// PackageManifest ParseManifest(string text); - - IEnumerable ParseGridEditors(string text); } } diff --git a/src/Umbraco.Abstractions/Manifest/IPackageManifest.cs b/src/Umbraco.Core/Manifest/IPackageManifest.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/IPackageManifest.cs rename to src/Umbraco.Core/Manifest/IPackageManifest.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestContentAppDefinition.cs b/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestContentAppDefinition.cs rename to src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs b/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs rename to src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestDashboard.cs b/src/Umbraco.Core/Manifest/ManifestDashboard.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestDashboard.cs rename to src/Umbraco.Core/Manifest/ManifestDashboard.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestFilterCollection.cs b/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestFilterCollection.cs rename to src/Umbraco.Core/Manifest/ManifestFilterCollection.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestFilterCollectionBuilder.cs b/src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestFilterCollectionBuilder.cs rename to src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestSection.cs b/src/Umbraco.Core/Manifest/ManifestSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/ManifestSection.cs rename to src/Umbraco.Core/Manifest/ManifestSection.cs diff --git a/src/Umbraco.Abstractions/Manifest/ManifestWatcher.cs b/src/Umbraco.Core/Manifest/ManifestWatcher.cs similarity index 86% rename from src/Umbraco.Abstractions/Manifest/ManifestWatcher.cs rename to src/Umbraco.Core/Manifest/ManifestWatcher.cs index 8d81ac3de5..23caac3a1b 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Manifest/PackageManifest.cs b/src/Umbraco.Core/Manifest/PackageManifest.cs similarity index 100% rename from src/Umbraco.Abstractions/Manifest/PackageManifest.cs rename to src/Umbraco.Core/Manifest/PackageManifest.cs diff --git a/src/Umbraco.Abstractions/Mapping/IMapDefinition.cs b/src/Umbraco.Core/Mapping/IMapDefinition.cs similarity index 100% rename from src/Umbraco.Abstractions/Mapping/IMapDefinition.cs rename to src/Umbraco.Core/Mapping/IMapDefinition.cs diff --git a/src/Umbraco.Abstractions/Mapping/MapDefinitionCollection.cs b/src/Umbraco.Core/Mapping/MapDefinitionCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Mapping/MapDefinitionCollection.cs rename to src/Umbraco.Core/Mapping/MapDefinitionCollection.cs diff --git a/src/Umbraco.Abstractions/Mapping/MapDefinitionCollectionBuilder.cs b/src/Umbraco.Core/Mapping/MapDefinitionCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Mapping/MapDefinitionCollectionBuilder.cs rename to src/Umbraco.Core/Mapping/MapDefinitionCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Mapping/MapperContext.cs b/src/Umbraco.Core/Mapping/MapperContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Mapping/MapperContext.cs rename to src/Umbraco.Core/Mapping/MapperContext.cs diff --git a/src/Umbraco.Abstractions/Mapping/UmbracoMapper.cs b/src/Umbraco.Core/Mapping/UmbracoMapper.cs similarity index 100% rename from src/Umbraco.Abstractions/Mapping/UmbracoMapper.cs rename to src/Umbraco.Core/Mapping/UmbracoMapper.cs diff --git a/src/Umbraco.Abstractions/Media/IEmbedProvider.cs b/src/Umbraco.Core/Media/IEmbedProvider.cs similarity index 100% rename from src/Umbraco.Abstractions/Media/IEmbedProvider.cs rename to src/Umbraco.Core/Media/IEmbedProvider.cs diff --git a/src/Umbraco.Abstractions/Media/OEmbedResult.cs b/src/Umbraco.Core/Media/OEmbedResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Media/OEmbedResult.cs rename to src/Umbraco.Core/Media/OEmbedResult.cs diff --git a/src/Umbraco.Abstractions/Media/OEmbedStatus.cs b/src/Umbraco.Core/Media/OEmbedStatus.cs similarity index 100% rename from src/Umbraco.Abstractions/Media/OEmbedStatus.cs rename to src/Umbraco.Core/Media/OEmbedStatus.cs diff --git a/src/Umbraco.Core/MediaTypeExtensions.cs b/src/Umbraco.Core/MediaTypeExtensions.cs new file mode 100644 index 0000000000..3a2a3ba6e2 --- /dev/null +++ b/src/Umbraco.Core/MediaTypeExtensions.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Models +{ + public static class MediaTypeExtensions + { + public static bool IsSystemMediaType(this IMediaType mediaType) => + mediaType.Alias == Constants.Conventions.MediaTypes.File + || mediaType.Alias == Constants.Conventions.MediaTypes.Folder + || mediaType.Alias == Constants.Conventions.MediaTypes.Image; + } +} diff --git a/src/Umbraco.Abstractions/Migrations/IMigration.cs b/src/Umbraco.Core/Migrations/IMigration.cs similarity index 100% rename from src/Umbraco.Abstractions/Migrations/IMigration.cs rename to src/Umbraco.Core/Migrations/IMigration.cs diff --git a/src/Umbraco.Abstractions/Migrations/IncompleteMigrationExpressionException.cs b/src/Umbraco.Core/Migrations/IncompleteMigrationExpressionException.cs similarity index 100% rename from src/Umbraco.Abstractions/Migrations/IncompleteMigrationExpressionException.cs rename to src/Umbraco.Core/Migrations/IncompleteMigrationExpressionException.cs diff --git a/src/Umbraco.Abstractions/Migrations/NoopMigration.cs b/src/Umbraco.Core/Migrations/NoopMigration.cs similarity index 100% rename from src/Umbraco.Abstractions/Migrations/NoopMigration.cs rename to src/Umbraco.Core/Migrations/NoopMigration.cs diff --git a/src/Umbraco.Web/Models/AnchorsModel.cs b/src/Umbraco.Core/Models/AnchorsModel.cs similarity index 100% rename from src/Umbraco.Web/Models/AnchorsModel.cs rename to src/Umbraco.Core/Models/AnchorsModel.cs diff --git a/src/Umbraco.Abstractions/Models/AuditEntry.cs b/src/Umbraco.Core/Models/AuditEntry.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/AuditEntry.cs rename to src/Umbraco.Core/Models/AuditEntry.cs diff --git a/src/Umbraco.Abstractions/Models/AuditItem.cs b/src/Umbraco.Core/Models/AuditItem.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/AuditItem.cs rename to src/Umbraco.Core/Models/AuditItem.cs diff --git a/src/Umbraco.Abstractions/Models/AuditType.cs b/src/Umbraco.Core/Models/AuditType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/AuditType.cs rename to src/Umbraco.Core/Models/AuditType.cs diff --git a/src/Umbraco.Web/Models/ChangingPasswordModel.cs b/src/Umbraco.Core/Models/ChangingPasswordModel.cs similarity index 75% rename from src/Umbraco.Web/Models/ChangingPasswordModel.cs rename to src/Umbraco.Core/Models/ChangingPasswordModel.cs index 50f2a1bed7..3816044c0a 100644 --- a/src/Umbraco.Web/Models/ChangingPasswordModel.cs +++ b/src/Umbraco.Core/Models/ChangingPasswordModel.cs @@ -19,5 +19,10 @@ namespace Umbraco.Web.Models [DataMember(Name = "oldPassword")] public string OldPassword { get; set; } + /// + /// The id of the user - required to allow changing password without the entire UserSave model + /// + [DataMember(Name = "id")] + public int Id { get; set; } } } diff --git a/src/Umbraco.Abstractions/Models/Consent.cs b/src/Umbraco.Core/Models/Consent.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Consent.cs rename to src/Umbraco.Core/Models/Consent.cs diff --git a/src/Umbraco.Abstractions/Models/ConsentExtensions.cs b/src/Umbraco.Core/Models/ConsentExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ConsentExtensions.cs rename to src/Umbraco.Core/Models/ConsentExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/ConsentState.cs b/src/Umbraco.Core/Models/ConsentState.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ConsentState.cs rename to src/Umbraco.Core/Models/ConsentState.cs diff --git a/src/Umbraco.Abstractions/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentBase.cs rename to src/Umbraco.Core/Models/ContentBase.cs diff --git a/src/Umbraco.Abstractions/Models/ContentCultureInfos.cs b/src/Umbraco.Core/Models/ContentCultureInfos.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentCultureInfos.cs rename to src/Umbraco.Core/Models/ContentCultureInfos.cs diff --git a/src/Umbraco.Abstractions/Models/ContentCultureInfosCollection.cs b/src/Umbraco.Core/Models/ContentCultureInfosCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentCultureInfosCollection.cs rename to src/Umbraco.Core/Models/ContentCultureInfosCollection.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/AuditLog.cs b/src/Umbraco.Core/Models/ContentEditing/AuditLog.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/AuditLog.cs rename to src/Umbraco.Core/Models/ContentEditing/AuditLog.cs diff --git a/src/Umbraco.Abstractions/Models/ContentEditing/ContentApp.cs b/src/Umbraco.Core/Models/ContentEditing/ContentApp.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentEditing/ContentApp.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentApp.cs diff --git a/src/Umbraco.Abstractions/Models/ContentEditing/ContentAppBadge.cs b/src/Umbraco.Core/Models/ContentEditing/ContentAppBadge.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentEditing/ContentAppBadge.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentAppBadge.cs diff --git a/src/Umbraco.Abstractions/Models/ContentEditing/ContentAppBadgeType.cs b/src/Umbraco.Core/Models/ContentEditing/ContentAppBadgeType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentEditing/ContentAppBadgeType.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentAppBadgeType.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentDomainsAndCulture.cs b/src/Umbraco.Core/Models/ContentEditing/ContentDomainsAndCulture.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentDomainsAndCulture.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentDomainsAndCulture.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs b/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs similarity index 95% rename from src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs index 4dbbb1385a..195c1800b7 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; -using Newtonsoft.Json; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; @@ -30,7 +28,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "variants", IsRequired = true)] public IEnumerable Variants { get; set; } - + [DataMember(Name = "contentTypeAlias", IsRequired = true)] [Required(AllowEmptyStrings = false)] public string ContentTypeAlias { get; set; } @@ -40,7 +38,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "templateAlias")] public string TemplateAlias { get; set; } - + #region IContentSave [DataMember(Name = "action", IsRequired = true)] @@ -54,7 +52,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [IgnoreDataMember] IContent IContentSave.PersistedContent { get; set; } - + //Non explicit internal getter so we don't need to explicitly cast in our own code [IgnoreDataMember] internal IContent PersistedContent diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyBasic.cs similarity index 83% rename from src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentPropertyBasic.cs index c5c22484ad..2b70a63035 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyBasic.cs @@ -53,11 +53,21 @@ namespace Umbraco.Web.Models.ContentEditing [ReadOnly(true)] public string Culture { get; set; } + /// + /// The segment of the property + /// + /// + /// The segment value of a property can always be null but can only have a non-null value + /// when the property can be varied by segment. + /// + [DataMember(Name = "segment")] + [ReadOnly(true)] + public string Segment { get; set; } + /// /// Used internally during model mapping /// [IgnoreDataMember] internal IDataEditor PropertyEditor { get; set; } - } } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyCollectionDto.cs similarity index 79% rename from src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentPropertyCollectionDto.cs index dc6a92cc93..d4c94410fe 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyCollectionDto.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; -using Umbraco.Core.Models; namespace Umbraco.Web.Models.ContentEditing { @@ -11,7 +9,7 @@ namespace Umbraco.Web.Models.ContentEditing /// /// This is only used during mapping operations, it is not used for angular purposes /// - internal class ContentPropertyCollectionDto : IContentProperties + public class ContentPropertyCollectionDto : IContentProperties { public ContentPropertyCollectionDto() { diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentPropertyDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyDto.cs similarity index 73% rename from src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentPropertyDto.cs index 83e5e2a9b2..865a10d5d5 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentPropertyDto.cs @@ -1,14 +1,11 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Models; namespace Umbraco.Web.Models.ContentEditing { /// /// Represents a content property from the database /// - internal class ContentPropertyDto : ContentPropertyBasic + public class ContentPropertyDto : ContentPropertyBasic { public IDataType DataType { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs b/src/Umbraco.Core/Models/ContentEditing/ContentRedirectUrl.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentRedirectUrl.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs b/src/Umbraco.Core/Models/ContentEditing/ContentSaveAction.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentSaveAction.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs b/src/Umbraco.Core/Models/ContentEditing/ContentSavedState.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentSavedState.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSortOrder.cs b/src/Umbraco.Core/Models/ContentEditing/ContentSortOrder.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentSortOrder.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentSortOrder.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs b/src/Umbraco.Core/Models/ContentEditing/ContentVariantSave.cs similarity index 92% rename from src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs rename to src/Umbraco.Core/Models/ContentEditing/ContentVariantSave.cs index deadac949a..9a7555ad92 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentVariantSave.cs @@ -29,6 +29,12 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "culture")] public string Culture { get; set; } + /// + /// The segment of this variant, if this is invariant than this is null or empty + /// + [DataMember(Name = "segment")] + public string Segment { get; set; } + /// /// Indicates if the variant should be updated /// diff --git a/src/Umbraco.Web/Models/ContentEditing/CreatedDocumentTypeCollectionResult.cs b/src/Umbraco.Core/Models/ContentEditing/CreatedDocumentTypeCollectionResult.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/CreatedDocumentTypeCollectionResult.cs rename to src/Umbraco.Core/Models/ContentEditing/CreatedDocumentTypeCollectionResult.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionaryOverviewDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/DictionaryOverviewDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DictionaryOverviewDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/DictionaryOverviewDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionaryOverviewTranslationDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/DictionaryOverviewTranslationDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DictionaryOverviewTranslationDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/DictionaryOverviewTranslationDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionaryTranslationDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/DictionaryTranslationDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DictionaryTranslationDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/DictionaryTranslationDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionaryTranslationSave.cs b/src/Umbraco.Core/Models/ContentEditing/DictionaryTranslationSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DictionaryTranslationSave.cs rename to src/Umbraco.Core/Models/ContentEditing/DictionaryTranslationSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DomainDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/DomainDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DomainDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/DomainDisplay.cs diff --git a/src/Umbraco.Abstractions/Models/ContentEditing/IContentAppFactory.cs b/src/Umbraco.Core/Models/ContentEditing/IContentAppFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentEditing/IContentAppFactory.cs rename to src/Umbraco.Core/Models/ContentEditing/IContentAppFactory.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/IContentProperties.cs b/src/Umbraco.Core/Models/ContentEditing/IContentProperties.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/IContentProperties.cs rename to src/Umbraco.Core/Models/ContentEditing/IContentProperties.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/IContentSave.cs b/src/Umbraco.Core/Models/ContentEditing/IContentSave.cs similarity index 91% rename from src/Umbraco.Web/Models/ContentEditing/IContentSave.cs rename to src/Umbraco.Core/Models/ContentEditing/IContentSave.cs index bcac5d0aea..789b44dadf 100644 --- a/src/Umbraco.Web/Models/ContentEditing/IContentSave.cs +++ b/src/Umbraco.Core/Models/ContentEditing/IContentSave.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.Models.ContentEditing /// An interface exposes the shared parts of content, media, members that we use during model binding in order to share logic /// /// - internal interface IContentSave : IHaveUploadedFiles + public interface IContentSave : IHaveUploadedFiles where TPersisted : IContentBase { /// diff --git a/src/Umbraco.Web/Models/ContentEditing/IHaveUploadedFiles.cs b/src/Umbraco.Core/Models/ContentEditing/IHaveUploadedFiles.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/IHaveUploadedFiles.cs rename to src/Umbraco.Core/Models/ContentEditing/IHaveUploadedFiles.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ITabbedContent.cs b/src/Umbraco.Core/Models/ContentEditing/ITabbedContent.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ITabbedContent.cs rename to src/Umbraco.Core/Models/ContentEditing/ITabbedContent.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/Language.cs b/src/Umbraco.Core/Models/ContentEditing/Language.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/Language.cs rename to src/Umbraco.Core/Models/ContentEditing/Language.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/LinkDisplay.cs similarity index 96% rename from src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/LinkDisplay.cs index 650b7b8db5..5b6379288f 100644 --- a/src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/LinkDisplay.cs @@ -4,7 +4,7 @@ using Umbraco.Core; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "link", Namespace = "")] - internal class LinkDisplay + public class LinkDisplay { [DataMember(Name = "icon")] public string Icon { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberPropertyTypeBasic.cs b/src/Umbraco.Core/Models/ContentEditing/MemberPropertyTypeBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberPropertyTypeBasic.cs rename to src/Umbraco.Core/Models/ContentEditing/MemberPropertyTypeBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberPropertyTypeDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/MemberPropertyTypeDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberPropertyTypeDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/MemberPropertyTypeDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MoveOrCopy.cs b/src/Umbraco.Core/Models/ContentEditing/MoveOrCopy.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MoveOrCopy.cs rename to src/Umbraco.Core/Models/ContentEditing/MoveOrCopy.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/NotificationStyle.cs b/src/Umbraco.Core/Models/ContentEditing/NotificationStyle.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/NotificationStyle.cs rename to src/Umbraco.Core/Models/ContentEditing/NotificationStyle.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/NotifySetting.cs b/src/Umbraco.Core/Models/ContentEditing/NotifySetting.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/NotifySetting.cs rename to src/Umbraco.Core/Models/ContentEditing/NotifySetting.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ObjectType.cs b/src/Umbraco.Core/Models/ContentEditing/ObjectType.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ObjectType.cs rename to src/Umbraco.Core/Models/ContentEditing/ObjectType.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/Permission.cs b/src/Umbraco.Core/Models/ContentEditing/Permission.cs similarity index 95% rename from src/Umbraco.Web/Models/ContentEditing/Permission.cs rename to src/Umbraco.Core/Models/ContentEditing/Permission.cs index 7b444e5959..2bb905200a 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Permission.cs +++ b/src/Umbraco.Core/Models/ContentEditing/Permission.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Models.ContentEditing /// We'll use this to map the categories but it wont' be returned in the json /// [IgnoreDataMember] - internal string Category { get; set; } + public string Category { get; set; } /// /// The letter from the IAction diff --git a/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs b/src/Umbraco.Core/Models/ContentEditing/PostedFolder.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs rename to src/Umbraco.Core/Models/ContentEditing/PostedFolder.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyEditorBasic.cs b/src/Umbraco.Core/Models/ContentEditing/PropertyEditorBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyEditorBasic.cs rename to src/Umbraco.Core/Models/ContentEditing/PropertyEditorBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs b/src/Umbraco.Core/Models/ContentEditing/PropertyGroupBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs rename to src/Umbraco.Core/Models/ContentEditing/PropertyGroupBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs b/src/Umbraco.Core/Models/ContentEditing/PropertyTypeBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs rename to src/Umbraco.Core/Models/ContentEditing/PropertyTypeBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/PropertyTypeDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/PropertyTypeDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs b/src/Umbraco.Core/Models/ContentEditing/PropertyTypeValidation.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs rename to src/Umbraco.Core/Models/ContentEditing/PropertyTypeValidation.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RedirectUrlSearchResults.cs b/src/Umbraco.Core/Models/ContentEditing/RedirectUrlSearchResults.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RedirectUrlSearchResults.cs rename to src/Umbraco.Core/Models/ContentEditing/RedirectUrlSearchResults.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/RelationDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/RelationDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs b/src/Umbraco.Core/Models/ContentEditing/RichTextEditorPlugin.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs rename to src/Umbraco.Core/Models/ContentEditing/RichTextEditorPlugin.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RollbackVersion.cs b/src/Umbraco.Core/Models/ContentEditing/RollbackVersion.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RollbackVersion.cs rename to src/Umbraco.Core/Models/ContentEditing/RollbackVersion.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs b/src/Umbraco.Core/Models/ContentEditing/SearchResult.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/SearchResult.cs rename to src/Umbraco.Core/Models/ContentEditing/SearchResult.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/Section.cs b/src/Umbraco.Core/Models/ContentEditing/Section.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/Section.cs rename to src/Umbraco.Core/Models/ContentEditing/Section.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/SnippetDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/SnippetDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/SnippetDisplay.cs rename to src/Umbraco.Core/Models/ContentEditing/SnippetDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs b/src/Umbraco.Core/Models/ContentEditing/StyleSheet.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs rename to src/Umbraco.Core/Models/ContentEditing/StyleSheet.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs b/src/Umbraco.Core/Models/ContentEditing/StylesheetRule.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs rename to src/Umbraco.Core/Models/ContentEditing/StylesheetRule.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/Tab.cs b/src/Umbraco.Core/Models/ContentEditing/Tab.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/Tab.cs rename to src/Umbraco.Core/Models/ContentEditing/Tab.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs b/src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs rename to src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UnpublishContent.cs b/src/Umbraco.Core/Models/ContentEditing/UnpublishContent.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UnpublishContent.cs rename to src/Umbraco.Core/Models/ContentEditing/UnpublishContent.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UrlAndAnchors.cs b/src/Umbraco.Core/Models/ContentEditing/UrlAndAnchors.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UrlAndAnchors.cs rename to src/Umbraco.Core/Models/ContentEditing/UrlAndAnchors.cs diff --git a/src/Umbraco.Abstractions/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentExtensions.cs rename to src/Umbraco.Core/Models/ContentExtensions.cs diff --git a/src/Umbraco.Web/Models/ContentModel.cs b/src/Umbraco.Core/Models/ContentModel.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentModel.cs rename to src/Umbraco.Core/Models/ContentModel.cs diff --git a/src/Umbraco.Web/Models/ContentModelOfTContent.cs b/src/Umbraco.Core/Models/ContentModelOfTContent.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentModelOfTContent.cs rename to src/Umbraco.Core/Models/ContentModelOfTContent.cs diff --git a/src/Umbraco.Abstractions/Models/ContentRepositoryExtensions.cs b/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentRepositoryExtensions.cs rename to src/Umbraco.Core/Models/ContentRepositoryExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/ContentSchedule.cs b/src/Umbraco.Core/Models/ContentSchedule.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentSchedule.cs rename to src/Umbraco.Core/Models/ContentSchedule.cs diff --git a/src/Umbraco.Abstractions/Models/ContentScheduleAction.cs b/src/Umbraco.Core/Models/ContentScheduleAction.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentScheduleAction.cs rename to src/Umbraco.Core/Models/ContentScheduleAction.cs diff --git a/src/Umbraco.Abstractions/Models/ContentScheduleCollection.cs b/src/Umbraco.Core/Models/ContentScheduleCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentScheduleCollection.cs rename to src/Umbraco.Core/Models/ContentScheduleCollection.cs diff --git a/src/Umbraco.Abstractions/Models/ContentStatus.cs b/src/Umbraco.Core/Models/ContentStatus.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentStatus.cs rename to src/Umbraco.Core/Models/ContentStatus.cs diff --git a/src/Umbraco.Abstractions/Models/ContentTypeAvailableCompositionsResult.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentTypeAvailableCompositionsResult.cs rename to src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs diff --git a/src/Umbraco.Abstractions/Models/ContentTypeAvailableCompositionsResults.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentTypeAvailableCompositionsResults.cs rename to src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs diff --git a/src/Umbraco.Abstractions/Models/ContentTypeBaseExtensions.cs b/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentTypeBaseExtensions.cs rename to src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/ContentTypeSort.cs b/src/Umbraco.Core/Models/ContentTypeSort.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentTypeSort.cs rename to src/Umbraco.Core/Models/ContentTypeSort.cs diff --git a/src/Umbraco.Abstractions/Models/ContentVariation.cs b/src/Umbraco.Core/Models/ContentVariation.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ContentVariation.cs rename to src/Umbraco.Core/Models/ContentVariation.cs diff --git a/src/Umbraco.Abstractions/Models/CultureImpact.cs b/src/Umbraco.Core/Models/CultureImpact.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/CultureImpact.cs rename to src/Umbraco.Core/Models/CultureImpact.cs diff --git a/src/Umbraco.Abstractions/Models/DataTypeExtensions.cs b/src/Umbraco.Core/Models/DataTypeExtensions.cs similarity index 96% rename from src/Umbraco.Abstractions/Models/DataTypeExtensions.cs rename to src/Umbraco.Core/Models/DataTypeExtensions.cs index c2116c6c45..4f4bd4d6c3 100644 --- a/src/Umbraco.Abstractions/Models/DataTypeExtensions.cs +++ b/src/Umbraco.Core/Models/DataTypeExtensions.cs @@ -75,7 +75,7 @@ namespace Umbraco.Core.Models /// /// The data type definition. /// - internal static bool IsBuildInDataType(this IDataType dataType) + public static bool IsBuildInDataType(this IDataType dataType) { return IsBuildInDataType(dataType.Key); } @@ -83,7 +83,7 @@ namespace Umbraco.Core.Models /// /// Returns true if this date type is build-in/default. /// - internal static bool IsBuildInDataType(Guid key) + public static bool IsBuildInDataType(Guid key) { return IdsOfBuildInDataTypes.Contains(key); } diff --git a/src/Umbraco.Abstractions/Models/DeepCloneHelper.cs b/src/Umbraco.Core/Models/DeepCloneHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/DeepCloneHelper.cs rename to src/Umbraco.Core/Models/DeepCloneHelper.cs diff --git a/src/Umbraco.Abstractions/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/DictionaryItem.cs rename to src/Umbraco.Core/Models/DictionaryItem.cs diff --git a/src/Umbraco.Abstractions/Models/DictionaryItemExtensions.cs b/src/Umbraco.Core/Models/DictionaryItemExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/DictionaryItemExtensions.cs rename to src/Umbraco.Core/Models/DictionaryItemExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/DictionaryTranslation.cs b/src/Umbraco.Core/Models/DictionaryTranslation.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/DictionaryTranslation.cs rename to src/Umbraco.Core/Models/DictionaryTranslation.cs diff --git a/src/Umbraco.Abstractions/Models/DoNotCloneAttribute.cs b/src/Umbraco.Core/Models/DoNotCloneAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/DoNotCloneAttribute.cs rename to src/Umbraco.Core/Models/DoNotCloneAttribute.cs diff --git a/src/Umbraco.Abstractions/Models/Editors/ContentPropertyData.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Editors/ContentPropertyData.cs rename to src/Umbraco.Core/Models/Editors/ContentPropertyData.cs diff --git a/src/Umbraco.Abstractions/Models/Editors/ContentPropertyFile.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Editors/ContentPropertyFile.cs rename to src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/BeingDirty.cs b/src/Umbraco.Core/Models/Entities/BeingDirty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/BeingDirty.cs rename to src/Umbraco.Core/Models/Entities/BeingDirty.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/BeingDirtyBase.cs b/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/BeingDirtyBase.cs rename to src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/ContentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/ContentEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/ContentEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/ContentEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/DocumentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/DocumentEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/EntityBase.cs b/src/Umbraco.Core/Models/Entities/EntityBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/EntityBase.cs rename to src/Umbraco.Core/Models/Entities/EntityBase.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/EntityExtensions.cs b/src/Umbraco.Core/Models/Entities/EntityExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/EntityExtensions.cs rename to src/Umbraco.Core/Models/Entities/EntityExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/EntitySlim.cs b/src/Umbraco.Core/Models/Entities/EntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/EntitySlim.cs rename to src/Umbraco.Core/Models/Entities/EntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/ICanBeDirty.cs b/src/Umbraco.Core/Models/Entities/ICanBeDirty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/ICanBeDirty.cs rename to src/Umbraco.Core/Models/Entities/ICanBeDirty.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IContentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IContentEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IDocumentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IDocumentEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IEntity.cs b/src/Umbraco.Core/Models/Entities/IEntity.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IEntity.cs rename to src/Umbraco.Core/Models/Entities/IEntity.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/IEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IHaveAdditionalData.cs b/src/Umbraco.Core/Models/Entities/IHaveAdditionalData.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IHaveAdditionalData.cs rename to src/Umbraco.Core/Models/Entities/IHaveAdditionalData.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IMediaEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IMediaEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IMemberEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IMemberEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IMemberEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/IMemberEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IRememberBeingDirty.cs b/src/Umbraco.Core/Models/Entities/IRememberBeingDirty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IRememberBeingDirty.cs rename to src/Umbraco.Core/Models/Entities/IRememberBeingDirty.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/ITreeEntity.cs b/src/Umbraco.Core/Models/Entities/ITreeEntity.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/ITreeEntity.cs rename to src/Umbraco.Core/Models/Entities/ITreeEntity.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IUmbracoEntity.cs b/src/Umbraco.Core/Models/Entities/IUmbracoEntity.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IUmbracoEntity.cs rename to src/Umbraco.Core/Models/Entities/IUmbracoEntity.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/IValueObject.cs b/src/Umbraco.Core/Models/Entities/IValueObject.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/IValueObject.cs rename to src/Umbraco.Core/Models/Entities/IValueObject.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/MediaEntitySlim.cs b/src/Umbraco.Core/Models/Entities/MediaEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/MediaEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/MediaEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/MemberEntitySlim.cs b/src/Umbraco.Core/Models/Entities/MemberEntitySlim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/MemberEntitySlim.cs rename to src/Umbraco.Core/Models/Entities/MemberEntitySlim.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/TreeEntityBase.cs b/src/Umbraco.Core/Models/Entities/TreeEntityBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/TreeEntityBase.cs rename to src/Umbraco.Core/Models/Entities/TreeEntityBase.cs diff --git a/src/Umbraco.Abstractions/Models/Entities/TreeEntityPath.cs b/src/Umbraco.Core/Models/Entities/TreeEntityPath.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Entities/TreeEntityPath.cs rename to src/Umbraco.Core/Models/Entities/TreeEntityPath.cs diff --git a/src/Umbraco.Abstractions/Models/EntityContainer.cs b/src/Umbraco.Core/Models/EntityContainer.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/EntityContainer.cs rename to src/Umbraco.Core/Models/EntityContainer.cs diff --git a/src/Umbraco.Abstractions/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/EntityExtensions.cs rename to src/Umbraco.Core/Models/EntityExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/File.cs b/src/Umbraco.Core/Models/File.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/File.cs rename to src/Umbraco.Core/Models/File.cs diff --git a/src/Umbraco.Abstractions/Models/Folder.cs b/src/Umbraco.Core/Models/Folder.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Folder.cs rename to src/Umbraco.Core/Models/Folder.cs diff --git a/src/Umbraco.Abstractions/Models/IAuditEntry.cs b/src/Umbraco.Core/Models/IAuditEntry.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IAuditEntry.cs rename to src/Umbraco.Core/Models/IAuditEntry.cs diff --git a/src/Umbraco.Abstractions/Models/IAuditItem.cs b/src/Umbraco.Core/Models/IAuditItem.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IAuditItem.cs rename to src/Umbraco.Core/Models/IAuditItem.cs diff --git a/src/Umbraco.Abstractions/Models/IConsent.cs b/src/Umbraco.Core/Models/IConsent.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IConsent.cs rename to src/Umbraco.Core/Models/IConsent.cs diff --git a/src/Umbraco.Abstractions/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IContent.cs rename to src/Umbraco.Core/Models/IContent.cs diff --git a/src/Umbraco.Abstractions/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IContentBase.cs rename to src/Umbraco.Core/Models/IContentBase.cs diff --git a/src/Umbraco.Web/Models/IContentModel.cs b/src/Umbraco.Core/Models/IContentModel.cs similarity index 100% rename from src/Umbraco.Web/Models/IContentModel.cs rename to src/Umbraco.Core/Models/IContentModel.cs diff --git a/src/Umbraco.Abstractions/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IContentType.cs rename to src/Umbraco.Core/Models/IContentType.cs diff --git a/src/Umbraco.Abstractions/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IContentTypeBase.cs rename to src/Umbraco.Core/Models/IContentTypeBase.cs diff --git a/src/Umbraco.Abstractions/Models/IContentTypeComposition.cs b/src/Umbraco.Core/Models/IContentTypeComposition.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IContentTypeComposition.cs rename to src/Umbraco.Core/Models/IContentTypeComposition.cs diff --git a/src/Umbraco.Abstractions/Models/IDataType.cs b/src/Umbraco.Core/Models/IDataType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDataType.cs rename to src/Umbraco.Core/Models/IDataType.cs diff --git a/src/Umbraco.Abstractions/Models/IDataValueEditor.cs b/src/Umbraco.Core/Models/IDataValueEditor.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDataValueEditor.cs rename to src/Umbraco.Core/Models/IDataValueEditor.cs diff --git a/src/Umbraco.Abstractions/Models/IDeepCloneable.cs b/src/Umbraco.Core/Models/IDeepCloneable.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDeepCloneable.cs rename to src/Umbraco.Core/Models/IDeepCloneable.cs diff --git a/src/Umbraco.Abstractions/Models/IDictionaryItem.cs b/src/Umbraco.Core/Models/IDictionaryItem.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDictionaryItem.cs rename to src/Umbraco.Core/Models/IDictionaryItem.cs diff --git a/src/Umbraco.Abstractions/Models/IDictionaryTranslation.cs b/src/Umbraco.Core/Models/IDictionaryTranslation.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDictionaryTranslation.cs rename to src/Umbraco.Core/Models/IDictionaryTranslation.cs diff --git a/src/Umbraco.Abstractions/Models/IDomain.cs b/src/Umbraco.Core/Models/IDomain.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IDomain.cs rename to src/Umbraco.Core/Models/IDomain.cs diff --git a/src/Umbraco.Abstractions/Models/IFile.cs b/src/Umbraco.Core/Models/IFile.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IFile.cs rename to src/Umbraco.Core/Models/IFile.cs diff --git a/src/Umbraco.Core/Models/IImageUrlGenerator.cs b/src/Umbraco.Core/Models/IImageUrlGenerator.cs new file mode 100644 index 0000000000..0dc2933fd9 --- /dev/null +++ b/src/Umbraco.Core/Models/IImageUrlGenerator.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Models +{ + public interface IImageUrlGenerator + { + string GetImageUrl(ImageUrlGenerationOptions options); + } +} diff --git a/src/Umbraco.Abstractions/Models/ILanguage.cs b/src/Umbraco.Core/Models/ILanguage.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ILanguage.cs rename to src/Umbraco.Core/Models/ILanguage.cs diff --git a/src/Umbraco.Abstractions/Models/IMacro.cs b/src/Umbraco.Core/Models/IMacro.cs similarity index 83% rename from src/Umbraco.Abstractions/Models/IMacro.cs rename to src/Umbraco.Core/Models/IMacro.cs index c3d37b0700..9d1c47154c 100644 --- a/src/Umbraco.Abstractions/Models/IMacro.cs +++ b/src/Umbraco.Core/Models/IMacro.cs @@ -58,22 +58,10 @@ namespace Umbraco.Core.Models [DataMember] string MacroSource { get; set; } - /// - /// Gets or set the macro type - /// - [DataMember] - MacroTypes MacroType { get; set; } - /// /// Gets or sets a list of Macro Properties /// [DataMember] MacroPropertyCollection Properties { get; } - - ///// - ///// Returns an enum based on the properties on the Macro - ///// - ///// - //MacroTypes MacroType(); } } diff --git a/src/Umbraco.Abstractions/Models/IMacroProperty.cs b/src/Umbraco.Core/Models/IMacroProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMacroProperty.cs rename to src/Umbraco.Core/Models/IMacroProperty.cs diff --git a/src/Umbraco.Abstractions/Models/IMedia.cs b/src/Umbraco.Core/Models/IMedia.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMedia.cs rename to src/Umbraco.Core/Models/IMedia.cs diff --git a/src/Umbraco.Abstractions/Models/IMediaType.cs b/src/Umbraco.Core/Models/IMediaType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMediaType.cs rename to src/Umbraco.Core/Models/IMediaType.cs diff --git a/src/Umbraco.Core/Models/IMediaUrlGenerator.cs b/src/Umbraco.Core/Models/IMediaUrlGenerator.cs new file mode 100644 index 0000000000..41e1be8d6c --- /dev/null +++ b/src/Umbraco.Core/Models/IMediaUrlGenerator.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Used to generate paths to media items for a specified property editor alias + /// + public interface IMediaUrlGenerator + { + /// + /// Tries to get a media path for a given property editor alias + /// + /// The property editor alias + /// The value of the property + /// + /// True if a media path was returned + /// + bool TryGetMediaPath(string alias, object value, out string mediaPath); + } +} diff --git a/src/Umbraco.Abstractions/Models/IMember.cs b/src/Umbraco.Core/Models/IMember.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMember.cs rename to src/Umbraco.Core/Models/IMember.cs diff --git a/src/Umbraco.Abstractions/Models/IMemberGroup.cs b/src/Umbraco.Core/Models/IMemberGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMemberGroup.cs rename to src/Umbraco.Core/Models/IMemberGroup.cs diff --git a/src/Umbraco.Abstractions/Models/IMemberType.cs b/src/Umbraco.Core/Models/IMemberType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMemberType.cs rename to src/Umbraco.Core/Models/IMemberType.cs diff --git a/src/Umbraco.Abstractions/Models/IMigrationEntry.cs b/src/Umbraco.Core/Models/IMigrationEntry.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IMigrationEntry.cs rename to src/Umbraco.Core/Models/IMigrationEntry.cs diff --git a/src/Umbraco.Abstractions/Models/IPartialView.cs b/src/Umbraco.Core/Models/IPartialView.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IPartialView.cs rename to src/Umbraco.Core/Models/IPartialView.cs diff --git a/src/Umbraco.Abstractions/Models/IProperty.cs b/src/Umbraco.Core/Models/IProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IProperty.cs rename to src/Umbraco.Core/Models/IProperty.cs diff --git a/src/Umbraco.Abstractions/Models/IPropertyCollection.cs b/src/Umbraco.Core/Models/IPropertyCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IPropertyCollection.cs rename to src/Umbraco.Core/Models/IPropertyCollection.cs diff --git a/src/Umbraco.Abstractions/Models/IPropertyType.cs b/src/Umbraco.Core/Models/IPropertyType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IPropertyType.cs rename to src/Umbraco.Core/Models/IPropertyType.cs diff --git a/src/Umbraco.Abstractions/Models/IPropertyValue.cs b/src/Umbraco.Core/Models/IPropertyValue.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IPropertyValue.cs rename to src/Umbraco.Core/Models/IPropertyValue.cs diff --git a/src/Umbraco.Abstractions/Models/IRedirectUrl.cs b/src/Umbraco.Core/Models/IRedirectUrl.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IRedirectUrl.cs rename to src/Umbraco.Core/Models/IRedirectUrl.cs diff --git a/src/Umbraco.Abstractions/Models/IRelation.cs b/src/Umbraco.Core/Models/IRelation.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IRelation.cs rename to src/Umbraco.Core/Models/IRelation.cs diff --git a/src/Umbraco.Abstractions/Models/IRelationType.cs b/src/Umbraco.Core/Models/IRelationType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IRelationType.cs rename to src/Umbraco.Core/Models/IRelationType.cs diff --git a/src/Umbraco.Abstractions/Models/IScript.cs b/src/Umbraco.Core/Models/IScript.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IScript.cs rename to src/Umbraco.Core/Models/IScript.cs diff --git a/src/Umbraco.Abstractions/Models/IServerRegistration.cs b/src/Umbraco.Core/Models/IServerRegistration.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IServerRegistration.cs rename to src/Umbraco.Core/Models/IServerRegistration.cs diff --git a/src/Umbraco.Abstractions/Models/ISimpleContentType.cs b/src/Umbraco.Core/Models/ISimpleContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ISimpleContentType.cs rename to src/Umbraco.Core/Models/ISimpleContentType.cs diff --git a/src/Umbraco.Abstractions/Models/IStylesheet.cs b/src/Umbraco.Core/Models/IStylesheet.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IStylesheet.cs rename to src/Umbraco.Core/Models/IStylesheet.cs diff --git a/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs b/src/Umbraco.Core/Models/IStylesheetProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/IStylesheetProperty.cs rename to src/Umbraco.Core/Models/IStylesheetProperty.cs diff --git a/src/Umbraco.Abstractions/Models/ITag.cs b/src/Umbraco.Core/Models/ITag.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ITag.cs rename to src/Umbraco.Core/Models/ITag.cs diff --git a/src/Umbraco.Abstractions/Models/ITemplate.cs b/src/Umbraco.Core/Models/ITemplate.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ITemplate.cs rename to src/Umbraco.Core/Models/ITemplate.cs diff --git a/src/Umbraco.Abstractions/Models/Identity/IIdentityUserLogin.cs b/src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Identity/IIdentityUserLogin.cs rename to src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs diff --git a/src/Umbraco.Abstractions/Models/Identity/IUserLoginInfo.cs b/src/Umbraco.Core/Models/Identity/IUserLoginInfo.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Identity/IUserLoginInfo.cs rename to src/Umbraco.Core/Models/Identity/IUserLoginInfo.cs diff --git a/src/Umbraco.Abstractions/Models/Identity/IdentityUserClaim.cs b/src/Umbraco.Core/Models/Identity/IdentityUserClaim.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Identity/IdentityUserClaim.cs rename to src/Umbraco.Core/Models/Identity/IdentityUserClaim.cs diff --git a/src/Umbraco.Abstractions/Models/Identity/IdentityUserLogin.cs b/src/Umbraco.Core/Models/Identity/IdentityUserLogin.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Identity/IdentityUserLogin.cs rename to src/Umbraco.Core/Models/Identity/IdentityUserLogin.cs diff --git a/src/Umbraco.Abstractions/Models/Identity/IdentityUserRole.cs b/src/Umbraco.Core/Models/Identity/IdentityUserRole.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Identity/IdentityUserRole.cs rename to src/Umbraco.Core/Models/Identity/IdentityUserRole.cs diff --git a/src/Umbraco.Web/Models/ImageCropAnchor.cs b/src/Umbraco.Core/Models/ImageCropAnchor.cs similarity index 100% rename from src/Umbraco.Web/Models/ImageCropAnchor.cs rename to src/Umbraco.Core/Models/ImageCropAnchor.cs diff --git a/src/Umbraco.Web/Models/ImageCropMode.cs b/src/Umbraco.Core/Models/ImageCropMode.cs similarity index 100% rename from src/Umbraco.Web/Models/ImageCropMode.cs rename to src/Umbraco.Core/Models/ImageCropMode.cs diff --git a/src/Umbraco.Web/Models/ImageCropRatioMode.cs b/src/Umbraco.Core/Models/ImageCropRatioMode.cs similarity index 100% rename from src/Umbraco.Web/Models/ImageCropRatioMode.cs rename to src/Umbraco.Core/Models/ImageCropRatioMode.cs diff --git a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs new file mode 100644 index 0000000000..f87657c33d --- /dev/null +++ b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs @@ -0,0 +1,66 @@ +namespace Umbraco.Core.Models +{ + /// + /// These are options that are passed to the IImageUrlGenerator implementation to determine + /// the propery URL that is needed + /// + public class ImageUrlGenerationOptions + { + public ImageUrlGenerationOptions (string imageUrl) + { + ImageUrl = imageUrl; + } + + public string ImageUrl { get; } + public int? Width { get; set; } + public int? Height { get; set; } + public decimal? WidthRatio { get; set; } + public decimal? HeightRatio { get; set; } + public int? Quality { get; set; } + public string ImageCropMode { get; set; } + public string ImageCropAnchor { get; set; } + public bool DefaultCrop { get; set; } + public FocalPointPosition FocalPoint { get; set; } + public CropCoordinates Crop { get; set; } + public string CacheBusterValue { get; set; } + public string FurtherOptions { get; set; } + public bool UpScale { get; set; } = true; + public string AnimationProcessMode { get; set; } + + /// + /// The focal point position, in whatever units the registered IImageUrlGenerator uses, + /// typically a percentage of the total image from 0.0 to 1.0. + /// + public class FocalPointPosition + { + public FocalPointPosition (decimal top, decimal left) + { + Left = left; + Top = top; + } + + public decimal Left { get; } + public decimal Top { get; } + } + + /// + /// The bounds of the crop within the original image, in whatever units the registered + /// IImageUrlGenerator uses, typically a percentage between 0 and 100. + /// + public class CropCoordinates + { + public CropCoordinates (decimal x1, decimal y1, decimal x2, decimal y2) + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } + + public decimal X1 { get; } + public decimal Y1 { get; } + public decimal X2 { get; } + public decimal Y2 { get; } + } + } +} diff --git a/src/Umbraco.Web/Models/Link.cs b/src/Umbraco.Core/Models/Link.cs similarity index 100% rename from src/Umbraco.Web/Models/Link.cs rename to src/Umbraco.Core/Models/Link.cs diff --git a/src/Umbraco.Web/Models/LinkType.cs b/src/Umbraco.Core/Models/LinkType.cs similarity index 100% rename from src/Umbraco.Web/Models/LinkType.cs rename to src/Umbraco.Core/Models/LinkType.cs diff --git a/src/Umbraco.Web/Models/LoginModel.cs b/src/Umbraco.Core/Models/LoginModel.cs similarity index 100% rename from src/Umbraco.Web/Models/LoginModel.cs rename to src/Umbraco.Core/Models/LoginModel.cs diff --git a/src/Umbraco.Abstractions/Models/MacroProperty.cs b/src/Umbraco.Core/Models/MacroProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/MacroProperty.cs rename to src/Umbraco.Core/Models/MacroProperty.cs diff --git a/src/Umbraco.Abstractions/Models/MacroPropertyCollection.cs b/src/Umbraco.Core/Models/MacroPropertyCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/MacroPropertyCollection.cs rename to src/Umbraco.Core/Models/MacroPropertyCollection.cs diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyBasicMapper.cs similarity index 90% rename from src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs rename to src/Umbraco.Core/Models/Mapping/ContentPropertyBasicMapper.cs index cddcfc6d1c..eaaae3616b 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs +++ b/src/Umbraco.Core/Models/Mapping/ContentPropertyBasicMapper.cs @@ -70,8 +70,14 @@ namespace Umbraco.Web.Models.Mapping dest.Culture = culture; + // Get the segment, which is always allowed to be null even if the propertyType *can* be varied by segment. + // There is therefore no need to perform the null check like with culture above. + var segment = !property.PropertyType.VariesBySegment() ? null : context.GetSegment(); + dest.Segment = segment; + // if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return. - dest.Value = editor.GetValueEditor().ToEditor(property, culture); + dest.Value = editor.GetValueEditor().ToEditor(property, culture, segment); + } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs similarity index 100% rename from src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs rename to src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyDtoMapper.cs similarity index 100% rename from src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs rename to src/Umbraco.Core/Models/Mapping/ContentPropertyDtoMapper.cs diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs similarity index 100% rename from src/Umbraco.Web/Models/Mapping/ContentPropertyMapDefinition.cs rename to src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs diff --git a/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs b/src/Umbraco.Core/Models/Mapping/MapperContextExtensions.cs similarity index 69% rename from src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs rename to src/Umbraco.Core/Models/Mapping/MapperContextExtensions.cs index 1538f1a987..3133441846 100644 --- a/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs +++ b/src/Umbraco.Core/Models/Mapping/MapperContextExtensions.cs @@ -5,9 +5,10 @@ namespace Umbraco.Web.Models.Mapping /// /// Provides extension methods for the class. /// - internal static class MapperContextExtensions + public static class MapperContextExtensions { private const string CultureKey = "Map.Culture"; + private const string SegmentKey = "Map.Segment"; private const string IncludedPropertiesKey = "Map.IncludedProperties"; /// @@ -18,6 +19,14 @@ namespace Umbraco.Web.Models.Mapping return context.HasItems && context.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null; } + /// + /// Gets the context segment. + /// + public static string GetSegment(this MapperContext context) + { + return context.HasItems && context.Items.TryGetValue(SegmentKey, out var obj) && obj is string s ? s : null; + } + /// /// Sets a context culture. /// @@ -26,6 +35,14 @@ namespace Umbraco.Web.Models.Mapping context.Items[CultureKey] = culture; } + /// + /// Sets a context segment. + /// + public static void SetSegment(this MapperContext context, string segment) + { + context.Items[SegmentKey] = segment; + } + /// /// Get included properties. /// @@ -42,4 +59,4 @@ namespace Umbraco.Web.Models.Mapping context.Items[IncludedPropertiesKey] = properties; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Abstractions/Models/MemberGroup.cs b/src/Umbraco.Core/Models/MemberGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/MemberGroup.cs rename to src/Umbraco.Core/Models/MemberGroup.cs diff --git a/src/Umbraco.Abstractions/Models/MemberTypePropertyProfileAccess.cs b/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/MemberTypePropertyProfileAccess.cs rename to src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/ContentPermissionSet.cs b/src/Umbraco.Core/Models/Membership/ContentPermissionSet.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/ContentPermissionSet.cs rename to src/Umbraco.Core/Models/Membership/ContentPermissionSet.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/EntityPermission.cs b/src/Umbraco.Core/Models/Membership/EntityPermission.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/EntityPermission.cs rename to src/Umbraco.Core/Models/Membership/EntityPermission.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/EntityPermissionCollection.cs b/src/Umbraco.Core/Models/Membership/EntityPermissionCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/EntityPermissionCollection.cs rename to src/Umbraco.Core/Models/Membership/EntityPermissionCollection.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/EntityPermissionSet.cs b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/EntityPermissionSet.cs rename to src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/IMembershipUser.cs b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/IMembershipUser.cs rename to src/Umbraco.Core/Models/Membership/IMembershipUser.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/IProfile.cs b/src/Umbraco.Core/Models/Membership/IProfile.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/IProfile.cs rename to src/Umbraco.Core/Models/Membership/IProfile.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/IReadOnlyUserGroup.cs b/src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/IReadOnlyUserGroup.cs rename to src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/IUser.cs rename to src/Umbraco.Core/Models/Membership/IUser.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/IUserGroup.cs b/src/Umbraco.Core/Models/Membership/IUserGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/IUserGroup.cs rename to src/Umbraco.Core/Models/Membership/IUserGroup.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/MemberCountType.cs b/src/Umbraco.Core/Models/Membership/MemberCountType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/MemberCountType.cs rename to src/Umbraco.Core/Models/Membership/MemberCountType.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/MemberExportModel.cs b/src/Umbraco.Core/Models/Membership/MemberExportModel.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/MemberExportModel.cs rename to src/Umbraco.Core/Models/Membership/MemberExportModel.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/MemberExportProperty.cs b/src/Umbraco.Core/Models/Membership/MemberExportProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/MemberExportProperty.cs rename to src/Umbraco.Core/Models/Membership/MemberExportProperty.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/ReadOnlyUserGroup.cs b/src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/ReadOnlyUserGroup.cs rename to src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/UserProfile.cs rename to src/Umbraco.Core/Models/Membership/UserProfile.cs diff --git a/src/Umbraco.Abstractions/Models/Membership/UserState.cs b/src/Umbraco.Core/Models/Membership/UserState.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Membership/UserState.cs rename to src/Umbraco.Core/Models/Membership/UserState.cs diff --git a/src/Umbraco.Abstractions/Models/MigrationEntry.cs b/src/Umbraco.Core/Models/MigrationEntry.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/MigrationEntry.cs rename to src/Umbraco.Core/Models/MigrationEntry.cs diff --git a/src/Umbraco.Abstractions/Models/Notification.cs b/src/Umbraco.Core/Models/Notification.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Notification.cs rename to src/Umbraco.Core/Models/Notification.cs diff --git a/src/Umbraco.Abstractions/Models/NotificationEmailBodyParams.cs b/src/Umbraco.Core/Models/NotificationEmailBodyParams.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/NotificationEmailBodyParams.cs rename to src/Umbraco.Core/Models/NotificationEmailBodyParams.cs diff --git a/src/Umbraco.Abstractions/Models/NotificationEmailSubjectParams.cs b/src/Umbraco.Core/Models/NotificationEmailSubjectParams.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/NotificationEmailSubjectParams.cs rename to src/Umbraco.Core/Models/NotificationEmailSubjectParams.cs diff --git a/src/Umbraco.Web/Models/PackageInstallModel.cs b/src/Umbraco.Core/Models/PackageInstallModel.cs similarity index 100% rename from src/Umbraco.Web/Models/PackageInstallModel.cs rename to src/Umbraco.Core/Models/PackageInstallModel.cs diff --git a/src/Umbraco.Web/Models/PackageInstallResult.cs b/src/Umbraco.Core/Models/PackageInstallResult.cs similarity index 100% rename from src/Umbraco.Web/Models/PackageInstallResult.cs rename to src/Umbraco.Core/Models/PackageInstallResult.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/ActionRunAt.cs b/src/Umbraco.Core/Models/Packaging/ActionRunAt.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/ActionRunAt.cs rename to src/Umbraco.Core/Models/Packaging/ActionRunAt.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/CompiledPackage.cs rename to src/Umbraco.Core/Models/Packaging/CompiledPackage.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/CompiledPackageDocument.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/CompiledPackageDocument.cs rename to src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/CompiledPackageFile.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/CompiledPackageFile.cs rename to src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/IPackageInfo.cs b/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/IPackageInfo.cs rename to src/Umbraco.Core/Models/Packaging/IPackageInfo.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/PackageAction.cs b/src/Umbraco.Core/Models/Packaging/PackageAction.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/PackageAction.cs rename to src/Umbraco.Core/Models/Packaging/PackageAction.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/PreInstallWarnings.cs b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/PreInstallWarnings.cs rename to src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs diff --git a/src/Umbraco.Abstractions/Models/Packaging/RequirementsType.cs b/src/Umbraco.Core/Models/Packaging/RequirementsType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Packaging/RequirementsType.cs rename to src/Umbraco.Core/Models/Packaging/RequirementsType.cs diff --git a/src/Umbraco.Abstractions/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PagedResult.cs rename to src/Umbraco.Core/Models/PagedResult.cs diff --git a/src/Umbraco.Abstractions/Models/PagedResultOfT.cs b/src/Umbraco.Core/Models/PagedResultOfT.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PagedResultOfT.cs rename to src/Umbraco.Core/Models/PagedResultOfT.cs diff --git a/src/Umbraco.Abstractions/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PartialView.cs rename to src/Umbraco.Core/Models/PartialView.cs diff --git a/src/Umbraco.Web/Models/PartialViewMacroModel.cs b/src/Umbraco.Core/Models/PartialViewMacroModel.cs similarity index 94% rename from src/Umbraco.Web/Models/PartialViewMacroModel.cs rename to src/Umbraco.Core/Models/PartialViewMacroModel.cs index 9964877cba..f3272644f4 100644 --- a/src/Umbraco.Web/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.Web/Models/PartialViewMacroModelExtensions.cs b/src/Umbraco.Core/Models/PartialViewMacroModelExtensions.cs similarity index 100% rename from src/Umbraco.Web/Models/PartialViewMacroModelExtensions.cs rename to src/Umbraco.Core/Models/PartialViewMacroModelExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/PartialViewType.cs b/src/Umbraco.Core/Models/PartialViewType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PartialViewType.cs rename to src/Umbraco.Core/Models/PartialViewType.cs diff --git a/src/Umbraco.Web/Models/PasswordChangedModel.cs b/src/Umbraco.Core/Models/PasswordChangedModel.cs similarity index 100% rename from src/Umbraco.Web/Models/PasswordChangedModel.cs rename to src/Umbraco.Core/Models/PasswordChangedModel.cs diff --git a/src/Umbraco.Web/Models/PostRedirectModel.cs b/src/Umbraco.Core/Models/PostRedirectModel.cs similarity index 100% rename from src/Umbraco.Web/Models/PostRedirectModel.cs rename to src/Umbraco.Core/Models/PostRedirectModel.cs diff --git a/src/Umbraco.Abstractions/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PropertyGroup.cs rename to src/Umbraco.Core/Models/PropertyGroup.cs diff --git a/src/Umbraco.Abstractions/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PropertyGroupCollection.cs rename to src/Umbraco.Core/Models/PropertyGroupCollection.cs diff --git a/src/Umbraco.Abstractions/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PropertyTypeCollection.cs rename to src/Umbraco.Core/Models/PropertyTypeCollection.cs diff --git a/src/Umbraco.Abstractions/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublicAccessEntry.cs rename to src/Umbraco.Core/Models/PublicAccessEntry.cs diff --git a/src/Umbraco.Abstractions/Models/PublicAccessRule.cs b/src/Umbraco.Core/Models/PublicAccessRule.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublicAccessRule.cs rename to src/Umbraco.Core/Models/PublicAccessRule.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/Fallback.cs b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/Fallback.cs rename to src/Umbraco.Core/Models/PublishedContent/Fallback.cs diff --git a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/HttpContextVariationContextAccessor.cs similarity index 50% rename from src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/HttpContextVariationContextAccessor.cs index 0007b346c5..09604281dc 100644 --- a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/HttpContextVariationContextAccessor.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Cache; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent { @@ -7,22 +8,22 @@ namespace Umbraco.Web.Models.PublishedContent /// public class HttpContextVariationContextAccessor : IVariationContextAccessor { - public const string ContextKey = "Umbraco.Web.Models.PublishedContent.DefaultVariationContextAccessor"; - public readonly IHttpContextAccessor HttpContextAccessor; + private readonly IRequestCache _requestCache; + private const string ContextKey = "Umbraco.Web.Models.PublishedContent.DefaultVariationContextAccessor"; /// /// Initializes a new instance of the class. /// - public HttpContextVariationContextAccessor(IHttpContextAccessor httpContextAccessor) + public HttpContextVariationContextAccessor(IRequestCache requestCache) { - HttpContextAccessor = httpContextAccessor; + _requestCache = requestCache; } /// public VariationContext VariationContext { - get => (VariationContext) HttpContextAccessor.HttpContext?.Items[ContextKey]; - set => HttpContextAccessor.HttpContext.Items[ContextKey] = value; + get => (VariationContext) _requestCache.Get(ContextKey); + set => _requestCache.Set(ContextKey, value); } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/HybridVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs similarity index 64% rename from src/Umbraco.Web/Models/PublishedContent/HybridVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs index cb002e11b0..6f97c1dc5c 100644 --- a/src/Umbraco.Web/Models/PublishedContent/HybridVariationContextAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs @@ -1,14 +1,15 @@ -using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Cache; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent { /// /// Implements a hybrid . /// - internal class HybridVariationContextAccessor : HybridAccessorBase, IVariationContextAccessor + public class HybridVariationContextAccessor : HybridAccessorBase, IVariationContextAccessor { - public HybridVariationContextAccessor(IHttpContextAccessor httpContextAccessor) - : base(httpContextAccessor) + public HybridVariationContextAccessor(IRequestCache requestCache) + : base(requestCache) { } /// @@ -23,4 +24,4 @@ namespace Umbraco.Web.Models.PublishedContent set => Value = value; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/ILivePublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/ILivePublishedModelFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs similarity index 90% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContent.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 1c0d39a8b8..4872d04321 100644 --- a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -60,11 +60,6 @@ namespace Umbraco.Core.Models.PublishedContent /// int CreatorId { get; } - /// - /// Gets the name of the user who created the content item. - /// - string CreatorName { get; } - /// /// Gets the date the content item was created. /// @@ -75,11 +70,6 @@ namespace Umbraco.Core.Models.PublishedContent /// int WriterId { get; } - /// - /// Gets the name of the user who last updated the content item. - /// - string WriterName { get; } - /// /// Gets the date the content item was last updated. /// @@ -90,15 +80,6 @@ namespace Umbraco.Core.Models.PublishedContent /// DateTime UpdateDate { get; } - /// - /// Gets the url of the content item for the current culture. - /// - /// - /// The value of this property is contextual. It depends on the 'current' request uri, - /// if any. - /// - string Url { get; } - /// /// Gets available culture infos. /// diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContentType.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedContentTypeFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedElement.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedElement.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedModelFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedProperty.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedPropertyType.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IPublishedValueFallback.cs rename to src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/IVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/IVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs diff --git a/src/Umbraco.Infrastructure/Models/PublishedContent/IndexedArrayItem.cs b/src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs similarity index 100% rename from src/Umbraco.Infrastructure/Models/PublishedContent/IndexedArrayItem.cs rename to src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/ModelType.cs b/src/Umbraco.Core/Models/PublishedContent/ModelType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/ModelType.cs rename to src/Umbraco.Core/Models/PublishedContent/ModelType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/NoopPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/NoopPublishedModelFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/NoopPublishedValueFallback.cs rename to src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs similarity index 85% rename from src/Umbraco.Web/Models/PublishedContentBase.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs index 148bab11c0..57c116dc54 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs @@ -14,6 +14,13 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}")] public abstract class PublishedContentBase : IPublishedContent { + private readonly IVariationContextAccessor _variationContextAccessor; + + protected PublishedContentBase(IVariationContextAccessor variationContextAccessor) + { + _variationContextAccessor = variationContextAccessor; + } + #region ContentType public abstract IPublishedContentType ContentType { get; } @@ -33,10 +40,10 @@ namespace Umbraco.Web.Models public abstract int Id { get; } /// - public virtual string Name => this.Name(); + public virtual string Name => this.Name(_variationContextAccessor); /// - public virtual string UrlSegment => this.UrlSegment(); + public virtual string UrlSegment => this.UrlSegment(_variationContextAccessor); /// public abstract int SortOrder { get; } @@ -53,24 +60,15 @@ namespace Umbraco.Web.Models /// public abstract int CreatorId { get; } - /// - public abstract string CreatorName { get; } - /// public abstract DateTime CreateDate { get; } /// public abstract int WriterId { get; } - /// - public abstract string WriterName { get; } - /// public abstract DateTime UpdateDate { get; } - /// - public virtual string Url => this.Url(); - /// public abstract IReadOnlyDictionary Cultures { get; } @@ -91,7 +89,7 @@ namespace Umbraco.Web.Models public abstract IPublishedContent Parent { get; } /// - public virtual IEnumerable Children => this.Children(); + public virtual IEnumerable Children => this.Children(_variationContextAccessor); /// public abstract IEnumerable ChildrenForAllCultures { get; } diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentType.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentTypeConverter.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentTypeConverter.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs similarity index 93% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentWrapped.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index fb41c95419..88a904bac1 100644 --- a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -78,24 +78,15 @@ namespace Umbraco.Core.Models.PublishedContent /// public virtual int CreatorId => _content.CreatorId; - /// - public virtual string CreatorName => _content.CreatorName; - /// public virtual DateTime CreateDate => _content.CreateDate; /// public virtual int WriterId => _content.WriterId; - /// - public virtual string WriterName => _content.WriterName; - /// public virtual DateTime UpdateDate => _content.UpdateDate; - /// - public virtual string Url => _content.Url; - /// public IReadOnlyDictionary Cultures => _content.Cultures; diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedCultureInfos.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedDataType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedDataType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedDataType.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedDataType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedElementModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedElementModel.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedElementWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedElementWrapped.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedItemType.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedModelAttribute.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedModelAttribute.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedPropertyBase.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedPropertyType.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/PublishedSearchResult.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedSearchResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/PublishedSearchResult.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedSearchResult.cs diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs similarity index 97% rename from src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs index 7b467b6d15..7c207c23c0 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs @@ -182,7 +182,7 @@ namespace Umbraco.Web.Models.PublishedContent // if we found a content with the property having a value, return that property value if (property != null && property.HasValue(culture, segment)) { - value = property.Value(culture, segment); + value = property.Value(this, culture, segment); return true; } @@ -216,7 +216,7 @@ namespace Umbraco.Web.Models.PublishedContent if (property.HasValue(culture2, segment)) { - value = property.Value(culture2, segment); + value = property.Value(this, culture2, segment); return true; } @@ -250,7 +250,7 @@ namespace Umbraco.Web.Models.PublishedContent if (content.HasValue(alias, culture2, segment)) { - value = content.Value(alias, culture2, segment); + value = content.Value(this, alias, culture2, segment); return true; } @@ -287,7 +287,7 @@ namespace Umbraco.Web.Models.PublishedContent if (content.HasValue(alias, culture2, segment)) { - value = content.Value(alias, culture2, segment); + value = content.Value(this, alias, culture2, segment); return true; } diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/RawValueProperty.cs rename to src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/UrlMode.cs b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/UrlMode.cs rename to src/Umbraco.Core/Models/PublishedContent/UrlMode.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedContent/VariationContext.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedContent/VariationContext.cs rename to src/Umbraco.Core/Models/PublishedContent/VariationContext.cs diff --git a/src/Umbraco.Infrastructure/Models/PublishedContent/VariationContextAccessorExtensions.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs similarity index 100% rename from src/Umbraco.Infrastructure/Models/PublishedContent/VariationContextAccessorExtensions.cs rename to src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/PublishedState.cs b/src/Umbraco.Core/Models/PublishedState.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/PublishedState.cs rename to src/Umbraco.Core/Models/PublishedState.cs diff --git a/src/Umbraco.Abstractions/Models/Range.cs b/src/Umbraco.Core/Models/Range.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Range.cs rename to src/Umbraco.Core/Models/Range.cs diff --git a/src/Umbraco.Abstractions/Models/RedirectUrl.cs b/src/Umbraco.Core/Models/RedirectUrl.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/RedirectUrl.cs rename to src/Umbraco.Core/Models/RedirectUrl.cs diff --git a/src/Umbraco.Abstractions/Models/Relation.cs b/src/Umbraco.Core/Models/Relation.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Relation.cs rename to src/Umbraco.Core/Models/Relation.cs diff --git a/src/Umbraco.Abstractions/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/RelationType.cs rename to src/Umbraco.Core/Models/RelationType.cs diff --git a/src/Umbraco.Web/Models/RequestPasswordResetModel.cs b/src/Umbraco.Core/Models/RequestPasswordResetModel.cs similarity index 100% rename from src/Umbraco.Web/Models/RequestPasswordResetModel.cs rename to src/Umbraco.Core/Models/RequestPasswordResetModel.cs diff --git a/src/Umbraco.Abstractions/Models/Script.cs b/src/Umbraco.Core/Models/Script.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Script.cs rename to src/Umbraco.Core/Models/Script.cs diff --git a/src/Umbraco.Web/Models/SendCodeViewModel.cs b/src/Umbraco.Core/Models/SendCodeViewModel.cs similarity index 100% rename from src/Umbraco.Web/Models/SendCodeViewModel.cs rename to src/Umbraco.Core/Models/SendCodeViewModel.cs diff --git a/src/Umbraco.Abstractions/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ServerRegistration.cs rename to src/Umbraco.Core/Models/ServerRegistration.cs diff --git a/src/Umbraco.Web/Models/SetPasswordModel.cs b/src/Umbraco.Core/Models/SetPasswordModel.cs similarity index 100% rename from src/Umbraco.Web/Models/SetPasswordModel.cs rename to src/Umbraco.Core/Models/SetPasswordModel.cs diff --git a/src/Umbraco.Abstractions/Models/SimpleContentType.cs b/src/Umbraco.Core/Models/SimpleContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/SimpleContentType.cs rename to src/Umbraco.Core/Models/SimpleContentType.cs diff --git a/src/Umbraco.Abstractions/Models/Stylesheet.cs b/src/Umbraco.Core/Models/Stylesheet.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Stylesheet.cs rename to src/Umbraco.Core/Models/Stylesheet.cs diff --git a/src/Umbraco.Abstractions/Models/StylesheetProperty.cs b/src/Umbraco.Core/Models/StylesheetProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/StylesheetProperty.cs rename to src/Umbraco.Core/Models/StylesheetProperty.cs diff --git a/src/Umbraco.Abstractions/Models/Tag.cs b/src/Umbraco.Core/Models/Tag.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Tag.cs rename to src/Umbraco.Core/Models/Tag.cs diff --git a/src/Umbraco.Abstractions/Models/TagModel.cs b/src/Umbraco.Core/Models/TagModel.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TagModel.cs rename to src/Umbraco.Core/Models/TagModel.cs diff --git a/src/Umbraco.Abstractions/Models/TaggableObjectTypes.cs b/src/Umbraco.Core/Models/TaggableObjectTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TaggableObjectTypes.cs rename to src/Umbraco.Core/Models/TaggableObjectTypes.cs diff --git a/src/Umbraco.Abstractions/Models/TaggedEntity.cs b/src/Umbraco.Core/Models/TaggedEntity.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TaggedEntity.cs rename to src/Umbraco.Core/Models/TaggedEntity.cs diff --git a/src/Umbraco.Abstractions/Models/TaggedProperty.cs b/src/Umbraco.Core/Models/TaggedProperty.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TaggedProperty.cs rename to src/Umbraco.Core/Models/TaggedProperty.cs diff --git a/src/Umbraco.Abstractions/Models/TagsStorageType.cs b/src/Umbraco.Core/Models/TagsStorageType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TagsStorageType.cs rename to src/Umbraco.Core/Models/TagsStorageType.cs diff --git a/src/Umbraco.Abstractions/Models/TemplateNode.cs b/src/Umbraco.Core/Models/TemplateNode.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/TemplateNode.cs rename to src/Umbraco.Core/Models/TemplateNode.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/ContentTypeModel.cs b/src/Umbraco.Core/Models/TemplateQuery/ContentTypeModel.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/ContentTypeModel.cs rename to src/Umbraco.Core/Models/TemplateQuery/ContentTypeModel.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/Operator.cs b/src/Umbraco.Core/Models/TemplateQuery/Operator.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/Operator.cs rename to src/Umbraco.Core/Models/TemplateQuery/Operator.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs b/src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs rename to src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperatorTerm.cs b/src/Umbraco.Core/Models/TemplateQuery/OperatorTerm.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/OperatorTerm.cs rename to src/Umbraco.Core/Models/TemplateQuery/OperatorTerm.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/PropertyModel.cs b/src/Umbraco.Core/Models/TemplateQuery/PropertyModel.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/PropertyModel.cs rename to src/Umbraco.Core/Models/TemplateQuery/PropertyModel.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Core/Models/TemplateQuery/QueryCondition.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs rename to src/Umbraco.Core/Models/TemplateQuery/QueryCondition.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs b/src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs rename to src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryModel.cs b/src/Umbraco.Core/Models/TemplateQuery/QueryModel.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/QueryModel.cs rename to src/Umbraco.Core/Models/TemplateQuery/QueryModel.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryResultModel.cs b/src/Umbraco.Core/Models/TemplateQuery/QueryResultModel.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/QueryResultModel.cs rename to src/Umbraco.Core/Models/TemplateQuery/QueryResultModel.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/SortExpression.cs b/src/Umbraco.Core/Models/TemplateQuery/SortExpression.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/SortExpression.cs rename to src/Umbraco.Core/Models/TemplateQuery/SortExpression.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/SourceModel.cs b/src/Umbraco.Core/Models/TemplateQuery/SourceModel.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/SourceModel.cs rename to src/Umbraco.Core/Models/TemplateQuery/SourceModel.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/TemplateQueryResult.cs b/src/Umbraco.Core/Models/TemplateQuery/TemplateQueryResult.cs similarity index 100% rename from src/Umbraco.Web/Models/TemplateQuery/TemplateQueryResult.cs rename to src/Umbraco.Core/Models/TemplateQuery/TemplateQueryResult.cs diff --git a/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs b/src/Umbraco.Core/Models/Trees/ActionMenuItem.cs similarity index 96% rename from src/Umbraco.Web/Models/Trees/ActionMenuItem.cs rename to src/Umbraco.Core/Models/Trees/ActionMenuItem.cs index 9354417155..fe760fb94d 100644 --- a/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs +++ b/src/Umbraco.Core/Models/Trees/ActionMenuItem.cs @@ -1,6 +1,4 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Services; namespace Umbraco.Web.Models.Trees diff --git a/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs b/src/Umbraco.Core/Models/Trees/CreateChildEntity.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/CreateChildEntity.cs rename to src/Umbraco.Core/Models/Trees/CreateChildEntity.cs diff --git a/src/Umbraco.Web/Models/Trees/ExportMember.cs b/src/Umbraco.Core/Models/Trees/ExportMember.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/ExportMember.cs rename to src/Umbraco.Core/Models/Trees/ExportMember.cs diff --git a/src/Umbraco.Web/Models/Trees/MenuItem.cs b/src/Umbraco.Core/Models/Trees/MenuItem.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/MenuItem.cs rename to src/Umbraco.Core/Models/Trees/MenuItem.cs diff --git a/src/Umbraco.Web/Models/Trees/RefreshNode.cs b/src/Umbraco.Core/Models/Trees/RefreshNode.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/RefreshNode.cs rename to src/Umbraco.Core/Models/Trees/RefreshNode.cs diff --git a/src/Umbraco.Abstractions/Models/UmbracoDomain.cs b/src/Umbraco.Core/Models/UmbracoDomain.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/UmbracoDomain.cs rename to src/Umbraco.Core/Models/UmbracoDomain.cs diff --git a/src/Umbraco.Abstractions/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs similarity index 95% rename from src/Umbraco.Abstractions/Models/UmbracoObjectTypes.cs rename to src/Umbraco.Core/Models/UmbracoObjectTypes.cs index a9bcbc2b65..f403ea6e67 100644 --- a/src/Umbraco.Abstractions/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -83,14 +83,6 @@ namespace Umbraco.Core.Models [FriendlyName("Recycle Bin")] RecycleBin, - /// - /// Stylesheet - /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.Stylesheet)] - [FriendlyName("Stylesheet")] - [UmbracoUdiType(Constants.UdiEntityType.Stylesheet)] - Stylesheet, - /// /// Member /// diff --git a/src/Umbraco.Web/Models/UmbracoProperty.cs b/src/Umbraco.Core/Models/UmbracoProperty.cs similarity index 100% rename from src/Umbraco.Web/Models/UmbracoProperty.cs rename to src/Umbraco.Core/Models/UmbracoProperty.cs diff --git a/src/Umbraco.Abstractions/Models/UmbracoUserExtensions.cs b/src/Umbraco.Core/Models/UmbracoUserExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/UmbracoUserExtensions.cs rename to src/Umbraco.Core/Models/UmbracoUserExtensions.cs diff --git a/src/Umbraco.Web/Models/UnLinkLoginModel.cs b/src/Umbraco.Core/Models/UnLinkLoginModel.cs similarity index 100% rename from src/Umbraco.Web/Models/UnLinkLoginModel.cs rename to src/Umbraco.Core/Models/UnLinkLoginModel.cs diff --git a/src/Umbraco.Web/Models/UpgradeCheckResponse.cs b/src/Umbraco.Core/Models/UpgradeCheckResponse.cs similarity index 82% rename from src/Umbraco.Web/Models/UpgradeCheckResponse.cs rename to src/Umbraco.Core/Models/UpgradeCheckResponse.cs index 6b15e33625..854f6cc4de 100644 --- a/src/Umbraco.Web/Models/UpgradeCheckResponse.cs +++ b/src/Umbraco.Core/Models/UpgradeCheckResponse.cs @@ -1,6 +1,5 @@ using System.Runtime.Serialization; -using System.Web; -using Umbraco.Core.Composing; +using System.Net; using Umbraco.Core.Configuration; namespace Umbraco.Web.Models @@ -22,7 +21,7 @@ namespace Umbraco.Web.Models { Type = upgradeType; Comment = upgradeComment; - Url = upgradeUrl + "?version=" + HttpUtility.UrlEncode(umbracoVersion.Current.ToString(3)); + Url = upgradeUrl + "?version=" + WebUtility.UrlEncode(umbracoVersion.Current.ToString(3)); } } } diff --git a/src/Umbraco.Web/Models/UserTourStatus.cs b/src/Umbraco.Core/Models/UserTourStatus.cs similarity index 100% rename from src/Umbraco.Web/Models/UserTourStatus.cs rename to src/Umbraco.Core/Models/UserTourStatus.cs diff --git a/src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs b/src/Umbraco.Core/Models/ValidatePasswordResetCodeModel.cs similarity index 100% rename from src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs rename to src/Umbraco.Core/Models/ValidatePasswordResetCodeModel.cs diff --git a/src/Umbraco.Abstractions/Models/Validation/RequiredForPersistenceAttribute.cs b/src/Umbraco.Core/Models/Validation/RequiredForPersistenceAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/Validation/RequiredForPersistenceAttribute.cs rename to src/Umbraco.Core/Models/Validation/RequiredForPersistenceAttribute.cs diff --git a/src/Umbraco.Abstractions/Models/ValueStorageType.cs b/src/Umbraco.Core/Models/ValueStorageType.cs similarity index 100% rename from src/Umbraco.Abstractions/Models/ValueStorageType.cs rename to src/Umbraco.Core/Models/ValueStorageType.cs diff --git a/src/Umbraco.Abstractions/MonitorLock.cs b/src/Umbraco.Core/MonitorLock.cs similarity index 100% rename from src/Umbraco.Abstractions/MonitorLock.cs rename to src/Umbraco.Core/MonitorLock.cs diff --git a/src/Umbraco.Abstractions/NameValueCollectionExtensions.cs b/src/Umbraco.Core/NameValueCollectionExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/NameValueCollectionExtensions.cs rename to src/Umbraco.Core/NameValueCollectionExtensions.cs diff --git a/src/Umbraco.Abstractions/NamedUdiRange.cs b/src/Umbraco.Core/NamedUdiRange.cs similarity index 100% rename from src/Umbraco.Abstractions/NamedUdiRange.cs rename to src/Umbraco.Core/NamedUdiRange.cs diff --git a/src/Umbraco.Abstractions/Net/IIpResolver.cs b/src/Umbraco.Core/Net/IIpResolver.cs similarity index 100% rename from src/Umbraco.Abstractions/Net/IIpResolver.cs rename to src/Umbraco.Core/Net/IIpResolver.cs diff --git a/src/Umbraco.Abstractions/Net/ISessionIdResolver.cs b/src/Umbraco.Core/Net/ISessionIdResolver.cs similarity index 100% rename from src/Umbraco.Abstractions/Net/ISessionIdResolver.cs rename to src/Umbraco.Core/Net/ISessionIdResolver.cs 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.Abstractions/NetworkHelper.cs b/src/Umbraco.Core/NetworkHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/NetworkHelper.cs rename to src/Umbraco.Core/NetworkHelper.cs diff --git a/src/Umbraco.Abstractions/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/ObjectExtensions.cs rename to src/Umbraco.Core/ObjectExtensions.cs diff --git a/src/Umbraco.Abstractions/PackageActions/AllowDoctype.cs b/src/Umbraco.Core/PackageActions/AllowDoctype.cs similarity index 100% rename from src/Umbraco.Abstractions/PackageActions/AllowDoctype.cs rename to src/Umbraco.Core/PackageActions/AllowDoctype.cs diff --git a/src/Umbraco.Abstractions/PackageActions/IPackageAction.cs b/src/Umbraco.Core/PackageActions/IPackageAction.cs similarity index 100% rename from src/Umbraco.Abstractions/PackageActions/IPackageAction.cs rename to src/Umbraco.Core/PackageActions/IPackageAction.cs diff --git a/src/Umbraco.Abstractions/PackageActions/PackageActionCollection.cs b/src/Umbraco.Core/PackageActions/PackageActionCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PackageActions/PackageActionCollection.cs rename to src/Umbraco.Core/PackageActions/PackageActionCollection.cs diff --git a/src/Umbraco.Abstractions/PackageActions/PackageActionCollectionBuilder.cs b/src/Umbraco.Core/PackageActions/PackageActionCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/PackageActions/PackageActionCollectionBuilder.cs rename to src/Umbraco.Core/PackageActions/PackageActionCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/PackageActions/PublishRootDocument.cs b/src/Umbraco.Core/PackageActions/PublishRootDocument.cs similarity index 100% rename from src/Umbraco.Abstractions/PackageActions/PublishRootDocument.cs rename to src/Umbraco.Core/PackageActions/PublishRootDocument.cs diff --git a/src/Umbraco.Abstractions/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/CompiledPackageXmlParser.cs rename to src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs diff --git a/src/Umbraco.Abstractions/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs similarity index 97% rename from src/Umbraco.Abstractions/Packaging/ConflictingPackageData.cs rename to src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 7425f64727..9a976bea41 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Packaging/ICreatedPackagesRepository.cs b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/ICreatedPackagesRepository.cs rename to src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs diff --git a/src/Umbraco.Abstractions/Packaging/IInstalledPackagesRepository.cs b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/IInstalledPackagesRepository.cs rename to src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs diff --git a/src/Umbraco.Abstractions/Packaging/IPackageActionRunner.cs b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/IPackageActionRunner.cs rename to src/Umbraco.Core/Packaging/IPackageActionRunner.cs diff --git a/src/Umbraco.Abstractions/Packaging/IPackageDefinitionRepository.cs b/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/IPackageDefinitionRepository.cs rename to src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs diff --git a/src/Umbraco.Abstractions/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/IPackageInstallation.cs rename to src/Umbraco.Core/Packaging/IPackageInstallation.cs diff --git a/src/Umbraco.Abstractions/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/InstallationSummary.cs rename to src/Umbraco.Core/Packaging/InstallationSummary.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/PackageActionRunner.cs rename to src/Umbraco.Core/Packaging/PackageActionRunner.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Packaging/PackageDefinition.cs similarity index 98% rename from src/Umbraco.Abstractions/Packaging/PackageDefinition.cs rename to src/Umbraco.Core/Packaging/PackageDefinition.cs index 11bf26c579..29a1919a2b 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/PackageDefinitionXmlParser.cs rename to src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackageExtraction.cs b/src/Umbraco.Core/Packaging/PackageExtraction.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/PackageExtraction.cs rename to src/Umbraco.Core/Packaging/PackageExtraction.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackageFileInstallation.cs b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/PackageFileInstallation.cs rename to src/Umbraco.Core/Packaging/PackageFileInstallation.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackageInstallType.cs b/src/Umbraco.Core/Packaging/PackageInstallType.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/PackageInstallType.cs rename to src/Umbraco.Core/Packaging/PackageInstallType.cs diff --git a/src/Umbraco.Abstractions/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs similarity index 99% rename from src/Umbraco.Abstractions/Packaging/PackagesRepository.cs rename to src/Umbraco.Core/Packaging/PackagesRepository.cs index a2a30f3a06..2c5b964518 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Packaging/UninstallationSummary.cs b/src/Umbraco.Core/Packaging/UninstallationSummary.cs similarity index 100% rename from src/Umbraco.Abstractions/Packaging/UninstallationSummary.cs rename to src/Umbraco.Core/Packaging/UninstallationSummary.cs diff --git a/src/Umbraco.Web/PasswordConfigurationExtensions.cs b/src/Umbraco.Core/PasswordConfigurationExtensions.cs similarity index 96% rename from src/Umbraco.Web/PasswordConfigurationExtensions.cs rename to src/Umbraco.Core/PasswordConfigurationExtensions.cs index 1105fc31a7..be13b574ed 100644 --- a/src/Umbraco.Web/PasswordConfigurationExtensions.cs +++ b/src/Umbraco.Core/PasswordConfigurationExtensions.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web { - internal static class PasswordConfigurationExtensions + public static class PasswordConfigurationExtensions { /// /// Returns the configuration of the membership provider used to configure change password editors diff --git a/src/Umbraco.Abstractions/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Constants-DatabaseSchema.cs rename to src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs diff --git a/src/Umbraco.Abstractions/Persistence/Constants-DbProviderNames.cs b/src/Umbraco.Core/Persistence/Constants-DbProviderNames.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Constants-DbProviderNames.cs rename to src/Umbraco.Core/Persistence/Constants-DbProviderNames.cs diff --git a/src/Umbraco.Abstractions/Persistence/Constants-Locks.cs b/src/Umbraco.Core/Persistence/Constants-Locks.cs similarity index 91% rename from src/Umbraco.Abstractions/Persistence/Constants-Locks.cs rename to src/Umbraco.Core/Persistence/Constants-Locks.cs index 1dcd2408e7..e64f40ced7 100644 --- a/src/Umbraco.Abstractions/Persistence/Constants-Locks.cs +++ b/src/Umbraco.Core/Persistence/Constants-Locks.cs @@ -8,6 +8,11 @@ namespace Umbraco.Core /// public static class Locks { + /// + /// The lock + /// + public const int MainDom = -1000; + /// /// All servers. /// diff --git a/src/Umbraco.Abstractions/Persistence/IQueryRepository.cs b/src/Umbraco.Core/Persistence/IQueryRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/IQueryRepository.cs rename to src/Umbraco.Core/Persistence/IQueryRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/IReadRepository.cs b/src/Umbraco.Core/Persistence/IReadRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/IReadRepository.cs rename to src/Umbraco.Core/Persistence/IReadRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/IReadWriteQueryRepository.cs b/src/Umbraco.Core/Persistence/IReadWriteQueryRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/IReadWriteQueryRepository.cs rename to src/Umbraco.Core/Persistence/IReadWriteQueryRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/IRepository.cs b/src/Umbraco.Core/Persistence/IRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/IRepository.cs rename to src/Umbraco.Core/Persistence/IRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/IWriteRepository.cs b/src/Umbraco.Core/Persistence/IWriteRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/IWriteRepository.cs rename to src/Umbraco.Core/Persistence/IWriteRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Querying/IQuery.cs b/src/Umbraco.Core/Persistence/Querying/IQuery.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Querying/IQuery.cs rename to src/Umbraco.Core/Persistence/Querying/IQuery.cs diff --git a/src/Umbraco.Abstractions/Persistence/Querying/StringPropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Querying/StringPropertyMatchType.cs rename to src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs diff --git a/src/Umbraco.Abstractions/Persistence/Querying/ValuePropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Querying/ValuePropertyMatchType.cs rename to src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IAuditEntryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IAuditEntryRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IAuditEntryRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IAuditEntryRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IAuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IAuditRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IAuditRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IAuditRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IConsentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IConsentRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IConsentRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IConsentRepository.cs diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IContentTypeCommonRepository.cs similarity index 100% rename from src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeCommonRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IContentTypeCommonRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IDataTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IDataTypeContainerRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IDictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IDictionaryRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IDocumentTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IDocumentTypeContainerRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IDomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDomainRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IDomainRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IDomainRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IEntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IEntityContainerRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IExternalLoginRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IExternalLoginRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IExternalLoginRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IExternalLoginRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IInstallationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IInstallationRepository.cs new file mode 100644 index 0000000000..fbc7d2cfbc --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IInstallationRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IInstallationRepository + { + Task SaveInstallLogAsync(InstallLog installLog); + } +} diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/ILanguageRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IMacroRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IMediaTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IMediaTypeContainerRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IMemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IMemberGroupRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/INotificationsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/INotificationsRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/INotificationsRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/INotificationsRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IPartialViewMacroRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewMacroRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IPartialViewMacroRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IRedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IRedirectUrlRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IRelationRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IRelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRelationTypeRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IRelationTypeRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IRelationTypeRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IScriptRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IServerRegistrationRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IServerRegistrationRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IServerRegistrationRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IStylesheetRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/ITagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITagRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/ITagRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/ITagRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/ITemplateRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IUpgradeCheckRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IUpgradeCheckRepository.cs new file mode 100644 index 0000000000..6d56994781 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IUpgradeCheckRepository.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Semver; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IUpgradeCheckRepository + { + Task CheckUpgradeAsync(SemVersion version); + } +} diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IUserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IUserGroupRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IUserGroupRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IUserGroupRepository.cs diff --git a/src/Umbraco.Abstractions/Persistence/Repositories/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/Repositories/IUserRepository.cs rename to src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/InstallationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/InstallationRepository.cs new file mode 100644 index 0000000000..01c91fe051 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/InstallationRepository.cs @@ -0,0 +1,36 @@ +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.Serialization; + +namespace Umbraco.Core.Persistence.Repositories.Implement +{ + public class InstallationRepository : IInstallationRepository + { + private readonly IJsonSerializer _jsonSerializer; + private static HttpClient _httpClient; + private const string RestApiInstallUrl = "https://our.umbraco.com/umbraco/api/Installation/Install"; + + public InstallationRepository(IJsonSerializer jsonSerializer) + { + _jsonSerializer = jsonSerializer; + } + + public async Task SaveInstallLogAsync(InstallLog installLog) + { + try + { + if (_httpClient == null) + _httpClient = new HttpClient(); + + var content = new StringContent(_jsonSerializer.Serialize(installLog), Encoding.UTF8, "application/json"); + + await _httpClient.PostAsync(RestApiInstallUrl, content); + } + // this occurs if the server for Our is down or cannot be reached + catch (HttpRequestException) + { } + } + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryCacheKeys.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs similarity index 100% rename from src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryCacheKeys.cs rename to src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs new file mode 100644 index 0000000000..b20c6d26b9 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs @@ -0,0 +1,58 @@ +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Semver; +using Umbraco.Core.Models; +using Umbraco.Core.Serialization; + +namespace Umbraco.Core.Persistence.Repositories.Implement +{ + public class UpgradeCheckRepository : IUpgradeCheckRepository + { + private readonly IJsonSerializer _jsonSerializer; + private static HttpClient _httpClient; + private const string RestApiUpgradeChecklUrl = "https://our.umbraco.com/umbraco/api/UpgradeCheck/CheckUpgrade"; + + public UpgradeCheckRepository(IJsonSerializer jsonSerializer) + { + _jsonSerializer = jsonSerializer; + } + + public async Task CheckUpgradeAsync(SemVersion version) + { + try + { + if (_httpClient == null) + _httpClient = new HttpClient(); + + var content = new StringContent(_jsonSerializer.Serialize(new CheckUpgradeDto(version)), Encoding.UTF8, "application/json"); + + var task = await _httpClient.PostAsync(RestApiUpgradeChecklUrl,content); + var json = await task.Content.ReadAsStringAsync(); + var result = _jsonSerializer.Deserialize(json); + + return result ?? new UpgradeResult("None", "", ""); + } + catch (HttpRequestException) + { + // this occurs if the server for Our is down or cannot be reached + return new UpgradeResult("None", "", ""); + } + } + private class CheckUpgradeDto + { + public CheckUpgradeDto(SemVersion version) + { + VersionMajor = version.Major; + VersionMinor = version.Minor; + VersionPatch = version.Patch; + VersionComment = version.Prerelease; + } + + public int VersionMajor { get; } + public int VersionMinor { get; } + public int VersionPatch { get; } + public string VersionComment { get; } + } + } +} diff --git a/src/Umbraco.Abstractions/Persistence/SqlExtensionsStatics.cs b/src/Umbraco.Core/Persistence/SqlExtensionsStatics.cs similarity index 100% rename from src/Umbraco.Abstractions/Persistence/SqlExtensionsStatics.cs rename to src/Umbraco.Core/Persistence/SqlExtensionsStatics.cs diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 87e0732d47..f129ca7731 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -2,18 +2,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("Umbraco.Core")] -[assembly: AssemblyDescription("Umbraco Core")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Umbraco CMS")] - [assembly: ComVisible(false)] -[assembly: Guid("130a6b5c-50e7-4df3-a0dd-e9e7eb0b7c5c")] +[assembly: Guid("DA322714-FB89-4943-92BD-BB122B82F66B")] // Umbraco Cms [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("Umbraco.Examine")] [assembly: InternalsVisibleTo("Umbraco.ModelsBuilder.Embedded")] [assembly: InternalsVisibleTo("Umbraco.Tests")] diff --git a/src/Umbraco.Abstractions/PropertyEditors/ColorPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ColorPickerConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ColorPickerConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/ColorPickerConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ConfigurationField.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationField.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ConfigurationField.cs rename to src/Umbraco.Core/PropertyEditors/ConfigurationField.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ConfigurationFieldAttribute.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ConfigurationFieldAttribute.cs rename to src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ContentPickerConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/ContentPickerConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DataEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DataEditorAttribute.cs rename to src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DataEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DataEditorCollection.cs rename to src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DataEditorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DataEditorCollectionBuilder.cs rename to src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DateTimeConfiguration.cs b/src/Umbraco.Core/PropertyEditors/DateTimeConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DateTimeConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/DateTimeConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DefaultPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DefaultPropertyIndexValueFactory.cs rename to src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DefaultPropertyValueConverterAttribute.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DefaultPropertyValueConverterAttribute.cs rename to src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/DropDownFlexibleConfiguration.cs b/src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/DropDownFlexibleConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/EditorType.cs b/src/Umbraco.Core/PropertyEditors/EditorType.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/EditorType.cs rename to src/Umbraco.Core/PropertyEditors/EditorType.cs diff --git a/src/Umbraco.Web/PropertyEditors/EmailAddressConfiguration.cs b/src/Umbraco.Core/PropertyEditors/EmailAddressConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/EmailAddressConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/EmailAddressConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/GridEditor.cs b/src/Umbraco.Core/PropertyEditors/GridEditor.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/GridEditor.cs rename to src/Umbraco.Core/PropertyEditors/GridEditor.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IConfigurationEditor.cs rename to src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IConfigureValueType.cs b/src/Umbraco.Core/PropertyEditors/IConfigureValueType.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IConfigureValueType.cs rename to src/Umbraco.Core/PropertyEditors/IConfigureValueType.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IDataEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IDataEditor.cs rename to src/Umbraco.Core/PropertyEditors/IDataEditor.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IIgnoreUserStartNodesConfig.cs b/src/Umbraco.Core/PropertyEditors/IIgnoreUserStartNodesConfig.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IIgnoreUserStartNodesConfig.cs rename to src/Umbraco.Core/PropertyEditors/IIgnoreUserStartNodesConfig.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IManifestValueValidator.cs b/src/Umbraco.Core/PropertyEditors/IManifestValueValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IManifestValueValidator.cs rename to src/Umbraco.Core/PropertyEditors/IManifestValueValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IPropertyIndexValueFactory.cs rename to src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IPropertyValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IPropertyValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IValueFormatValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IValueFormatValidator.cs rename to src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IValueRequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IValueRequiredValidator.cs rename to src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/IValueValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/IValueValidator.cs rename to src/Umbraco.Core/PropertyEditors/IValueValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ImageCropperConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ImageCropperConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ImageCropperConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/ImageCropperConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/LabelConfiguration.cs b/src/Umbraco.Core/PropertyEditors/LabelConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/LabelConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/LabelConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ManifestValueValidatorCollection.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ManifestValueValidatorCollection.cs rename to src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs rename to src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MarkdownConfiguration.cs similarity index 99% rename from src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/MarkdownConfiguration.cs index 4c2374ccd5..f32b3ffb44 100644 --- a/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/MarkdownConfiguration.cs @@ -13,4 +13,4 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("defaultValue", "Default value", "textarea", Description = "If value is blank, the editor will show this")] public string DefaultValue { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs new file mode 100644 index 0000000000..0374ca5cd2 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Umbraco.Core.Composing; + +namespace Umbraco.Core.PropertyEditors +{ + public class MediaUrlGeneratorCollection : BuilderCollectionBase + { + public MediaUrlGeneratorCollection(IEnumerable items) : base(items) + { + } + + public bool TryGetMediaPath(string alias, object value, out string mediaPath) + { + foreach(var generator in this) + { + if (generator.TryGetMediaPath(alias, value, out var mp)) + { + mediaPath = mp; + return true; + } + } + mediaPath = null; + return false; + } + + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs new file mode 100644 index 0000000000..40a68fd4e3 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Core.PropertyEditors +{ + public class MediaUrlGeneratorCollectionBuilder : LazyCollectionBuilderBase + { + protected override MediaUrlGeneratorCollectionBuilder This => this; + } +} diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MultiUrlPickerConfiguration.cs similarity index 74% rename from src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/MultiUrlPickerConfiguration.cs index 73a098cc9d..239569478f 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/MultiUrlPickerConfiguration.cs @@ -16,6 +16,9 @@ namespace Umbraco.Web.PropertyEditors Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } - + [ConfigurationField("hideAnchor", + "Hide anchor/query string input", "boolean", + Description = "Selecting this hides the anchor/query string input field in the linkpicker overlay.")] + public bool HideAnchor { get; set; } } } diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MultipleTextStringConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MultipleTextStringConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/MultipleTextStringConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ParameterEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ParameterEditorCollection.cs rename to src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyCacheLevel.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyCacheLevel.cs rename to src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyEditorCollection.cs rename to src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyEditorTagsExtensions.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorTagsExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyEditorTagsExtensions.cs rename to src/Umbraco.Core/PropertyEditors/PropertyEditorTagsExtensions.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterBase.cs rename to src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterCollection.cs rename to src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyValueConverterCollectionBuilder.cs rename to src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/PropertyValueLevel.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/PropertyValueLevel.cs rename to src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/SliderConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/SliderConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/TagConfiguration.cs b/src/Umbraco.Core/PropertyEditors/TagConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/TagConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/TagConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/TagsPropertyEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/TagsPropertyEditorAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/TagsPropertyEditorAttribute.cs rename to src/Umbraco.Core/PropertyEditors/TagsPropertyEditorAttribute.cs diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs b/src/Umbraco.Core/PropertyEditors/TextAreaConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/TextAreaConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/TextboxConfiguration.cs b/src/Umbraco.Core/PropertyEditors/TextboxConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TextboxConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/TextboxConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalseConfiguration.cs b/src/Umbraco.Core/PropertyEditors/TrueFalseConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TrueFalseConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/TrueFalseConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/DateTimeValidator.cs similarity index 94% rename from src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/DateTimeValidator.cs index 1000922b36..e964eae448 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/DateTimeValidator.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Core; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { /// /// Used to validate if the value is a valid date/time /// - internal class DateTimeValidator : IValueValidator + public class DateTimeValidator : IValueValidator { public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/DecimalValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/DecimalValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/DelimitedValueValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/DelimitedValueValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/DelimitedValueValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/DelimitedValueValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/EmailValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/EmailValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/IntegerValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/IntegerValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/RegexValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/Validators/RequiredValidator.cs rename to src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/DatePickerValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/DecimalValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/DecimalValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/IntegerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/IntegerValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/SliderValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs similarity index 100% rename from src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/SliderValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TagsValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs similarity index 88% rename from src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TagsValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs index 0b347b8500..edacd500df 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TagsValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Serialization; using Umbraco.Core.Services; namespace Umbraco.Core.PropertyEditors.ValueConverters @@ -13,10 +12,12 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters public class TagsValueConverter : PropertyValueConverterBase { private readonly IDataTypeService _dataTypeService; + private readonly IJsonSerializer _jsonSerializer; - public TagsValueConverter(IDataTypeService dataTypeService) + public TagsValueConverter(IDataTypeService dataTypeService, IJsonSerializer jsonSerializer) { _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); + _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer)); } public override bool IsConverter(IPublishedPropertyType propertyType) @@ -35,8 +36,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // if Json storage type deserialize and return as string array if (JsonStorageType(propertyType.DataType.Id)) { - var jArray = JsonConvert.DeserializeObject(source.ToString()); - return jArray.ToObject() ?? Array.Empty(); + var array = _jsonSerializer.Deserialize(source.ToString()); + return array ?? Array.Empty(); } // Otherwise assume CSV storage type and return as string array diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/TextStringValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/UploadPropertyConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/UploadPropertyConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueConverters/YesNoValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueConverters/YesNoValueConverter.cs rename to src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueListConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ValueListConfiguration.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueListConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/ValueListConfiguration.cs diff --git a/src/Umbraco.Abstractions/PropertyEditors/ValueTypes.cs b/src/Umbraco.Core/PropertyEditors/ValueTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/PropertyEditors/ValueTypes.cs rename to src/Umbraco.Core/PropertyEditors/ValueTypes.cs diff --git a/src/Umbraco.Web/PublishedCache/DefaultCultureAccessor.cs b/src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/DefaultCultureAccessor.cs rename to src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs diff --git a/src/Umbraco.Web/PublishedCache/IDefaultCultureAccessor.cs b/src/Umbraco.Core/PublishedCache/IDefaultCultureAccessor.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IDefaultCultureAccessor.cs rename to src/Umbraco.Core/PublishedCache/IDefaultCultureAccessor.cs diff --git a/src/Umbraco.Web/PublishedCache/IDomainCache.cs b/src/Umbraco.Core/PublishedCache/IDomainCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IDomainCache.cs rename to src/Umbraco.Core/PublishedCache/IDomainCache.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedCache.cs rename to src/Umbraco.Core/PublishedCache/IPublishedCache.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs rename to src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedMediaCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs rename to src/Umbraco.Core/PublishedCache/IPublishedMediaCache.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedMemberCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs rename to src/Umbraco.Core/PublishedCache/IPublishedMemberCache.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs rename to src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotAccessor.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs rename to src/Umbraco.Core/PublishedCache/IPublishedSnapshotAccessor.cs diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs similarity index 98% rename from src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs rename to src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs index 9949b4b3f8..b23c8ae10f 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs @@ -160,5 +160,15 @@ namespace Umbraco.Web.PublishedCache void Notify(DomainCacheRefresher.JsonPayload[] payloads); #endregion + + #region Status + + string GetStatus(); + + string StatusUrl { get; } + + #endregion + + void Collect(); } } diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs similarity index 88% rename from src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs rename to src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs index 1f637663e5..56fbf6e553 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs @@ -8,8 +8,15 @@ using Umbraco.Core.Xml; namespace Umbraco.Web.PublishedCache { - abstract class PublishedCacheBase : IPublishedCache + public abstract class PublishedCacheBase : IPublishedCache { + private readonly IVariationContextAccessor _variationContextAccessor; + + public PublishedCacheBase(IVariationContextAccessor variationContextAccessor) + { + _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); + + } public bool PreviewDefault { get; } protected PublishedCacheBase(bool previewDefault) @@ -97,7 +104,7 @@ namespace Umbraco.Web.PublishedCache // this is probably not super-efficient, but works // some cache implementation may want to override it, though return GetAtRoot() - .SelectMany(x => x.DescendantsOrSelf()) + .SelectMany(x => x.DescendantsOrSelf(_variationContextAccessor)) .Where(x => x.ContentType.Id == contentType.Id); } } diff --git a/src/Umbraco.Web/PublishedCache/PublishedElement.cs b/src/Umbraco.Core/PublishedCache/PublishedElement.cs similarity index 98% rename from src/Umbraco.Web/PublishedCache/PublishedElement.cs rename to src/Umbraco.Core/PublishedCache/PublishedElement.cs index 618c075b9b..b7c8f77bb7 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElement.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedElement.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.PublishedCache // an entirely new models factory + not even sure it makes sense at all since // sets are created manually todo yes it does! - what does this all mean? // - internal class PublishedElement : IPublishedElement + public class PublishedElement : IPublishedElement { // initializes a new instance of the PublishedElement class // within the context of a published snapshot service (eg a published content property value) diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs rename to src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Core/PublishedCache/PublishedMember.cs similarity index 96% rename from src/Umbraco.Web/PublishedCache/PublishedMember.cs rename to src/Umbraco.Core/PublishedCache/PublishedMember.cs index ed8e3bd7cf..bd84e4a9d2 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedMember.cs @@ -24,7 +24,8 @@ namespace Umbraco.Web.PublishedCache public PublishedMember( IMember member, IPublishedContentType publishedMemberType, - IUserService userService) + IUserService userService, + IVariationContextAccessor variationContextAccessor) : base(variationContextAccessor) { _member = member ?? throw new ArgumentNullException(nameof(member)); _membershipUser = member; @@ -140,10 +141,6 @@ namespace Umbraco.Web.PublishedCache public override string UrlSegment => throw new NotSupportedException(); - public override string WriterName => _member.GetCreatorProfile(_userService).Name; - - public override string CreatorName => _member.GetCreatorProfile(_userService).Name; - public override int WriterId => _member.CreatorId; public override int CreatorId => _member.CreatorId; diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Core/PublishedCache/PublishedSnapshotServiceBase.cs similarity index 87% rename from src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs rename to src/Umbraco.Core/PublishedCache/PublishedSnapshotServiceBase.cs index 20d3e6d8e3..cb3ca269f4 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedSnapshotServiceBase.cs @@ -5,7 +5,7 @@ using Umbraco.Web.Cache; namespace Umbraco.Web.PublishedCache { - abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService + public abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService { protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { @@ -38,5 +38,13 @@ namespace Umbraco.Web.PublishedCache public virtual void Dispose() { } + + public abstract string GetStatus(); + + public virtual string StatusUrl => "views/dashboard/settings/publishedsnapshotcache.html"; + + public virtual void Collect() + { + } } } diff --git a/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs b/src/Umbraco.Core/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs similarity index 99% rename from src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs rename to src/Umbraco.Core/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs index 76936dd2cb..7e0ebc6f13 100644 --- a/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Core/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs @@ -1,5 +1,4 @@ using System; - namespace Umbraco.Web.PublishedCache { public class UmbracoContextPublishedSnapshotAccessor : IPublishedSnapshotAccessor diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index 921883b822..03ce0c066a 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -1,14 +1,67 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core.Composing; +using Umbraco.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Core { public static class PublishedContentExtensions { - private static IVariationContextAccessor VariationContextAccessor => Current.VariationContextAccessor; + #region Name + + /// + /// Gets the name of the content item. + /// + /// The content item. + /// + /// The specific culture to get the name for. If null is used the current culture is used (Default is null). + public static string Name(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture()) + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.Name : null; + + // handle context culture for variant + if (culture == null) + culture = variationContextAccessor?.VariationContext?.Culture ?? ""; + + // get + return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.Name : null; + } + + #endregion + + #region Url segment + + /// + /// Gets the url segment of the content item. + /// + /// The content item. + /// + /// The specific culture to get the url segment for. If null is used the current culture is used (Default is null). + public static string UrlSegment(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture()) + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.UrlSegment : null; + + // handle context culture for variant + if (culture == null) + culture = variationContextAccessor?.VariationContext?.Culture ?? ""; + + // get + return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; + } + + #endregion + + #region Culture /// /// Determines whether the content has a culture. @@ -28,63 +81,26 @@ namespace Umbraco.Core /// Filters a sequence of to return invariant items, and items that are published for the specified culture. /// /// The content items. + /// /// The specific culture to filter for. If null is used the current culture is used. (Default is null). - internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, string culture = null) + internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, IVariationContextAccessor variationContextAccessor, string culture = null) where T : class, IPublishedContent { if (contents == null) throw new ArgumentNullException(nameof(contents)); - culture = culture ?? Current.VariationContextAccessor.VariationContext?.Culture ?? ""; + culture = culture ?? variationContextAccessor.VariationContext?.Culture ?? ""; // either does not vary by culture, or has the specified culture return contents.Where(x => !x.ContentType.VariesByCulture() || HasCulture(x, culture)); } - /// - /// Gets the name of the content item. - /// - /// The content item. - /// The specific culture to get the name for. If null is used the current culture is used (Default is null). - public static string Name(this IPublishedContent content, string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!content.ContentType.VariesByCulture()) - return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.Name : null; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - // get - return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.Name : null; - } - - - /// - /// Gets the url segment of the content item. - /// - /// The content item. - /// The specific culture to get the url segment for. If null is used the current culture is used (Default is null). - public static string UrlSegment(this IPublishedContent content, string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!content.ContentType.VariesByCulture()) - return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.UrlSegment : null; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - // get - return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; - } - /// /// Gets the culture date of the content item. /// /// The content item. + /// /// The specific culture to get the name for. If null is used the current culture is used (Default is null). - public static DateTime CultureDate(this IPublishedContent content, string culture = null) + public static DateTime CultureDate(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) { // invariant has invariant value (whatever the requested culture) if (!content.ContentType.VariesByCulture()) @@ -92,25 +108,729 @@ namespace Umbraco.Core // handle context culture for variant if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + culture = variationContextAccessor?.VariationContext?.Culture ?? ""; // get return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; } + #endregion + + #region IsComposedOf + + /// + /// Gets a value indicating whether the content is of a content type composed of the given alias + /// + /// The content. + /// The content type alias. + /// A value indicating whether the content is of a content type composed of a content type identified by the alias. + public static bool IsComposedOf(this IPublishedContent content, string alias) + { + return content.ContentType.CompositionAliases.InvariantContains(alias); + } + + #endregion + + #region Template + + /// + /// Returns the current template Alias + /// + /// Empty string if none is set. + public static string GetTemplateAlias(this IPublishedContent content, IFileService fileService) + { + if (content.TemplateId.HasValue == false) + { + return string.Empty; + } + + var template = fileService.GetTemplate(content.TemplateId.Value); + return template == null ? string.Empty : template.Alias; + } + + public static bool IsAllowedTemplate(this IPublishedContent content, IContentTypeService contentTypeService, + IWebRoutingSettings webRoutingSettings, int templateId) + { + return content.IsAllowedTemplate(contentTypeService, + webRoutingSettings.DisableAlternativeTemplates, + webRoutingSettings.ValidateAlternativeTemplates, templateId); + } + + public static bool IsAllowedTemplate(this IPublishedContent content, IContentTypeService contentTypeService, bool disableAlternativeTemplates, bool validateAlternativeTemplates, int templateId) + { + if (disableAlternativeTemplates) + return content.TemplateId == templateId; + + if (content.TemplateId == templateId || !validateAlternativeTemplates) + return true; + + var publishedContentContentType = contentTypeService.Get(content.ContentType.Id); + if (publishedContentContentType == null) + throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); + + return publishedContentContentType.IsAllowedTemplate(templateId); + + } + public static bool IsAllowedTemplate(this IPublishedContent content, IFileService fileService, IContentTypeService contentTypeService, bool disableAlternativeTemplates, bool validateAlternativeTemplates, string templateAlias) + { + var template = fileService.GetTemplate(templateAlias); + return template != null && content.IsAllowedTemplate(contentTypeService, disableAlternativeTemplates, validateAlternativeTemplates, template.Id); + } + + #endregion + + #region HasValue, Value, Value + + /// + /// Gets a value indicating whether the content has a value for a property identified by its alias. + /// + /// The content. + /// The published value fallback implementation. + /// The property alias. + /// The variation language. + /// The variation segment. + /// Optional fallback strategy. + /// A value indicating whether the content has a value for the property identified by the alias. + /// Returns true if HasValue is true, or a fallback strategy can provide a value. + public static bool HasValue(this IPublishedContent content, IPublishedValueFallback publishedValueFallback, string alias, string culture = null, string segment = null, Fallback fallback = default) + { + var property = content.GetProperty(alias); + + // if we have a property, and it has a value, return that value + if (property != null && property.HasValue(culture, segment)) + return true; + + // else let fallback try to get a value + return publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, null, out _, out _); + } + + /// + /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. + /// + /// The content. + /// The published value fallback implementation. + /// The property alias. + /// The variation language. + /// The variation segment. + /// Optional fallback strategy. + /// The default value. + /// The value of the content's property identified by the alias, if it exists, otherwise a default value. + public static object Value(this IPublishedContent content, IPublishedValueFallback publishedValueFallback, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) + { + var property = content.GetProperty(alias); + + // if we have a property, and it has a value, return that value + if (property != null && property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + // else let fallback try to get a value + if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value, out property)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) + return property?.GetValue(culture, segment); + } + + /// + /// Gets the value of a content's property identified by its alias, converted to a specified type. + /// + /// The target property type. + /// The content. + /// The published value fallback implementation. + /// The property alias. + /// The variation language. + /// The variation segment. + /// Optional fallback strategy. + /// The default value. + /// The value of the content's property identified by the alias, converted to the specified type. + public static T Value(this IPublishedContent content, IPublishedValueFallback publishedValueFallback, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) + { + var property = content.GetProperty(alias); + + // if we have a property, and it has a value, return that value + if (property != null && property.HasValue(culture, segment)) + return property.Value(publishedValueFallback, culture, segment); + + // else let fallback try to get a value + if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value, out property)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(publishedValueFallback, culture, segment); + } + + #endregion + + #region IsSomething: misc. + + /// + /// Determines whether the specified content is a specified content type. + /// + /// The content to determine content type of. + /// The alias of the content type to test against. + /// True if the content is of the specified content type; otherwise false. + public static bool IsDocumentType(this IPublishedContent content, string docTypeAlias) + { + return content.ContentType.Alias.InvariantEquals(docTypeAlias); + } + + /// + /// Determines whether the specified content is a specified content type or it's derived types. + /// + /// The content to determine content type of. + /// The alias of the content type to test against. + /// When true, recurses up the content type tree to check inheritance; when false just calls IsDocumentType(this IPublishedContent content, string docTypeAlias). + /// True if the content is of the specified content type or a derived content type; otherwise false. + public static bool IsDocumentType(this IPublishedContent content, string docTypeAlias, bool recursive) + { + if (content.IsDocumentType(docTypeAlias)) + return true; + + return recursive && content.IsComposedOf(docTypeAlias); + } + + #endregion + + #region IsSomething: equality + + public static bool IsEqual(this IPublishedContent content, IPublishedContent other) + { + return content.Id == other.Id; + } + + public static bool IsNotEqual(this IPublishedContent content, IPublishedContent other) + { + return content.IsEqual(other) == false; + } + + #endregion + + #region IsSomething: ancestors and descendants + + public static bool IsDescendant(this IPublishedContent content, IPublishedContent other) + { + return other.Level < content.Level && content.Path.InvariantStartsWith(other.Path.EnsureEndsWith(',')); + } + + public static bool IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other) + { + return content.Path.InvariantEquals(other.Path) || content.IsDescendant(other); + } + + public static bool IsAncestor(this IPublishedContent content, IPublishedContent other) + { + return content.Level < other.Level && other.Path.InvariantStartsWith(content.Path.EnsureEndsWith(',')); + } + + public static bool IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other) + { + return other.Path.InvariantEquals(content.Path) || content.IsAncestor(other); + } + + #endregion + + #region Axes: ancestors, ancestors-or-self + + // as per XPath 1.0 specs �2.2, + // - the ancestor axis contains the ancestors of the context node; the ancestors of the context node consist + // of the parent of context node and the parent's parent and so on; thus, the ancestor axis will always + // include the root node, unless the context node is the root node. + // - the ancestor-or-self axis contains the context node and the ancestors of the context node; thus, + // the ancestor axis will always include the root node. + // + // as per XPath 2.0 specs �3.2.1.1, + // - the ancestor axis is defined as the transitive closure of the parent axis; it contains the ancestors + // of the context node (the parent, the parent of the parent, and so on) - The ancestor axis includes the + // root node of the tree in which the context node is found, unless the context node is the root node. + // - the ancestor-or-self axis contains the context node and the ancestors of the context node; thus, + // the ancestor-or-self axis will always include the root node. + // + // the ancestor and ancestor-or-self axis are reverse axes ie they contain the context node or nodes that + // are before the context node in document order. + // + // document order is defined by �2.4.1 as: + // - the root node is the first node. + // - every node occurs before all of its children and descendants. + // - the relative order of siblings is the order in which they occur in the children property of their parent node. + // - children and descendants occur before following siblings. + + /// + /// Gets the ancestors of the content. + /// + /// The content. + /// The ancestors of the content, in down-top order. + /// Does not consider the content itself. + public static IEnumerable Ancestors(this IPublishedContent content) + { + return content.AncestorsOrSelf(false, null); + } + + /// + /// Gets the ancestors of the content, at a level lesser or equal to a specified level. + /// + /// The content. + /// The level. + /// The ancestors of the content, at a level lesser or equal to the specified level, in down-top order. + /// Does not consider the content itself. Only content that are "high enough" in the tree are returned. + public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) + { + return content.AncestorsOrSelf(false, n => n.Level <= maxLevel); + } + + /// + /// Gets the ancestors of the content, of a specified content type. + /// + /// The content. + /// The content type. + /// The ancestors of the content, of the specified content type, in down-top order. + /// Does not consider the content itself. Returns all ancestors, of the specified content type. + public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias) + { + return content.AncestorsOrSelf(false, n => n.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + /// + /// Gets the ancestors of the content, of a specified content type. + /// + /// The content type. + /// The content. + /// The ancestors of the content, of the specified content type, in down-top order. + /// Does not consider the content itself. Returns all ancestors, of the specified content type. + public static IEnumerable Ancestors(this IPublishedContent content) + where T : class, IPublishedContent + { + return content.Ancestors().OfType(); + } + + /// + /// Gets the ancestors of the content, at a level lesser or equal to a specified level, and of a specified content type. + /// + /// The content type. + /// The content. + /// The level. + /// The ancestors of the content, at a level lesser or equal to the specified level, and of the specified + /// content type, in down-top order. + /// Does not consider the content itself. Only content that are "high enough" in the trees, and of the + /// specified content type, are returned. + public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return content.Ancestors(maxLevel).OfType(); + } + + /// + /// Gets the content and its ancestors. + /// + /// The content. + /// The content and its ancestors, in down-top order. + public static IEnumerable AncestorsOrSelf(this IPublishedContent content) + { + return content.AncestorsOrSelf(true, null); + } + + /// + /// Gets the content and its ancestors, at a level lesser or equal to a specified level. + /// + /// The content. + /// The level. + /// The content and its ancestors, at a level lesser or equal to the specified level, + /// in down-top order. + /// Only content that are "high enough" in the tree are returned. So it may or may not begin + /// with the content itself, depending on its level. + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) + { + return content.AncestorsOrSelf(true, n => n.Level <= maxLevel); + } + + /// + /// Gets the content and its ancestors, of a specified content type. + /// + /// The content. + /// The content type. + /// The content and its ancestors, of the specified content type, in down-top order. + /// May or may not begin with the content itself, depending on its content type. + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias) + { + return content.AncestorsOrSelf(true, n => n.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + /// + /// Gets the content and its ancestors, of a specified content type. + /// + /// The content type. + /// The content. + /// The content and its ancestors, of the specified content type, in down-top order. + /// May or may not begin with the content itself, depending on its content type. + public static IEnumerable AncestorsOrSelf(this IPublishedContent content) + where T : class, IPublishedContent + { + return content.AncestorsOrSelf().OfType(); + } + + /// + /// Gets the content and its ancestor, at a lever lesser or equal to a specified level, and of a specified content type. + /// + /// The content type. + /// The content. + /// The level. + /// The content and its ancestors, at a level lesser or equal to the specified level, and of the specified + /// content type, in down-top order. + /// May or may not begin with the content itself, depending on its level and content type. + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return content.AncestorsOrSelf(maxLevel).OfType(); + } + + /// + /// Gets the ancestor of the content, ie its parent. + /// + /// The content. + /// The ancestor of the content. + /// This method is here for consistency purposes but does not make much sense. + public static IPublishedContent Ancestor(this IPublishedContent content) + { + return content.Parent; + } + + /// + /// Gets the nearest ancestor of the content, at a lever lesser or equal to a specified level. + /// + /// The content. + /// The level. + /// The nearest (in down-top order) ancestor of the content, at a level lesser or equal to the specified level. + /// Does not consider the content itself. May return null. + public static IPublishedContent Ancestor(this IPublishedContent content, int maxLevel) + { + return content.EnumerateAncestors(false).FirstOrDefault(x => x.Level <= maxLevel); + } + + /// + /// Gets the nearest ancestor of the content, of a specified content type. + /// + /// The content. + /// The content type alias. + /// The nearest (in down-top order) ancestor of the content, of the specified content type. + /// Does not consider the content itself. May return null. + public static IPublishedContent Ancestor(this IPublishedContent content, string contentTypeAlias) + { + return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + /// + /// Gets the nearest ancestor of the content, of a specified content type. + /// + /// The content type. + /// The content. + /// The nearest (in down-top order) ancestor of the content, of the specified content type. + /// Does not consider the content itself. May return null. + public static T Ancestor(this IPublishedContent content) + where T : class, IPublishedContent + { + return content.Ancestors().FirstOrDefault(); + } + + /// + /// Gets the nearest ancestor of the content, at the specified level and of the specified content type. + /// + /// The content type. + /// The content. + /// The level. + /// The ancestor of the content, at the specified level and of the specified content type. + /// Does not consider the content itself. If the ancestor at the specified level is + /// not of the specified type, returns null. + public static T Ancestor(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return content.Ancestors(maxLevel).FirstOrDefault(); + } + + /// + /// Gets the content or its nearest ancestor. + /// + /// The content. + /// The content. + /// This method is here for consistency purposes but does not make much sense. + public static IPublishedContent AncestorOrSelf(this IPublishedContent content) + { + return content; + } + + /// + /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level. + /// + /// The content. + /// The level. + /// The content or its nearest (in down-top order) ancestor, at a level lesser or equal to the specified level. + /// May or may not return the content itself depending on its level. May return null. + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int maxLevel) + { + return content.EnumerateAncestors(true).FirstOrDefault(x => x.Level <= maxLevel); + } + + /// + /// Gets the content or its nearest ancestor, of a specified content type. + /// + /// The content. + /// The content type. + /// The content or its nearest (in down-top order) ancestor, of the specified content type. + /// May or may not return the content itself depending on its content type. May return null. + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) + { + return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + /// + /// Gets the content or its nearest ancestor, of a specified content type. + /// + /// The content type. + /// The content. + /// The content or its nearest (in down-top order) ancestor, of the specified content type. + /// May or may not return the content itself depending on its content type. May return null. + public static T AncestorOrSelf(this IPublishedContent content) + where T : class, IPublishedContent + { + return content.AncestorsOrSelf().FirstOrDefault(); + } + + /// + /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level, and of a specified content type. + /// + /// The content type. + /// The content. + /// The level. + /// + public static T AncestorOrSelf(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return content.AncestorsOrSelf(maxLevel).FirstOrDefault(); + } + + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, bool orSelf, Func func) + { + var ancestorsOrSelf = content.EnumerateAncestors(orSelf); + return func == null ? ancestorsOrSelf : ancestorsOrSelf.Where(func); + } + + /// + /// Enumerates ancestors of the content, bottom-up. + /// + /// The content. + /// Indicates whether the content should be included. + /// Enumerates bottom-up ie walking up the tree (parent, grand-parent, etc). + internal static IEnumerable EnumerateAncestors(this IPublishedContent content, bool orSelf) + { + if (content == null) throw new ArgumentNullException(nameof(content)); + if (orSelf) yield return content; + while ((content = content.Parent) != null) + yield return content; + } + + #endregion + + #region Axes: descendants, descendants-or-self + + /// + /// Returns all DescendantsOrSelf of all content referenced + /// + /// + /// Variation context accessor. + /// + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// + /// + /// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot + /// + public static IEnumerable DescendantsOrSelfOfType(this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, string docTypeAlias, string culture = null) + { + return parentNodes.SelectMany(x => x.DescendantsOrSelfOfType(variationContextAccessor, docTypeAlias, culture)); + } + + /// + /// Returns all DescendantsOrSelf of all content referenced + /// + /// + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// + /// + /// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot + /// + public static IEnumerable DescendantsOrSelf(this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return parentNodes.SelectMany(x => x.DescendantsOrSelf(variationContextAccessor, culture)); + } + + + // as per XPath 1.0 specs �2.2, + // - the descendant axis contains the descendants of the context node; a descendant is a child or a child of a child and so on; thus + // the descendant axis never contains attribute or namespace nodes. + // - the descendant-or-self axis contains the context node and the descendants of the context node. + // + // as per XPath 2.0 specs �3.2.1.1, + // - the descendant axis is defined as the transitive closure of the child axis; it contains the descendants of the context node (the + // children, the children of the children, and so on). + // - the descendant-or-self axis contains the context node and the descendants of the context node. + // + // the descendant and descendant-or-self axis are forward axes ie they contain the context node or nodes that are after the context + // node in document order. + // + // document order is defined by �2.4.1 as: + // - the root node is the first node. + // - every node occurs before all of its children and descendants. + // - the relative order of siblings is the order in which they occur in the children property of their parent node. + // - children and descendants occur before following siblings. + + public static IEnumerable Descendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, false, null, culture); + } + + public static IEnumerable Descendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, false, p => p.Level >= level, culture); + } + + public static IEnumerable DescendantsOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, false, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); + } + + public static IEnumerable Descendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.Descendants(variationContextAccessor, culture).OfType(); + } + + public static IEnumerable Descendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + where T : class, IPublishedContent + { + return content.Descendants(variationContextAccessor, level, culture).OfType(); + } + + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, true, null, culture); + } + + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, true, p => p.Level >= level, culture); + } + + public static IEnumerable DescendantsOrSelfOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.DescendantsOrSelf(variationContextAccessor, true, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); + } + + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.DescendantsOrSelf(variationContextAccessor, culture).OfType(); + } + + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + where T : class, IPublishedContent + { + return content.DescendantsOrSelf(variationContextAccessor, level, culture).OfType(); + } + + public static IPublishedContent Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content.Children(variationContextAccessor, culture).FirstOrDefault(); + } + + public static IPublishedContent Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + { + return content.EnumerateDescendants(variationContextAccessor, false, culture).FirstOrDefault(x => x.Level == level); + } + + public static IPublishedContent DescendantOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.EnumerateDescendants(variationContextAccessor, false, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + public static T Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.EnumerateDescendants(variationContextAccessor, false, culture).FirstOrDefault(x => x is T) as T; + } + + public static T Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + where T : class, IPublishedContent + { + return content.Descendant(variationContextAccessor, level, culture) as T; + } + + public static IPublishedContent DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content; + } + + public static IPublishedContent DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + { + return content.EnumerateDescendants(variationContextAccessor, true, culture).FirstOrDefault(x => x.Level == level); + } + + public static IPublishedContent DescendantOrSelfOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.EnumerateDescendants(variationContextAccessor, true, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + } + + public static T DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.EnumerateDescendants(variationContextAccessor, true, culture).FirstOrDefault(x => x is T) as T; + } + + public static T DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string culture = null) + where T : class, IPublishedContent + { + return content.DescendantOrSelf(variationContextAccessor, level, culture) as T; + } + + internal static IEnumerable DescendantsOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, bool orSelf, Func func, string culture = null) + { + return content.EnumerateDescendants(variationContextAccessor, orSelf, culture).Where(x => func == null || func(x)); + } + + internal static IEnumerable EnumerateDescendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, bool orSelf, string culture = null) + { + if (content == null) throw new ArgumentNullException(nameof(content)); + if (orSelf) yield return content; + + foreach (var desc in content.Children(variationContextAccessor, culture).SelectMany(x => x.EnumerateDescendants(variationContextAccessor, culture))) + yield return desc; + } + + internal static IEnumerable EnumerateDescendants(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + yield return content; + + foreach (var desc in content.Children(variationContextAccessor, culture).SelectMany(x => x.EnumerateDescendants(variationContextAccessor, culture))) + yield return desc; + } + + #endregion + + #region Axes: children /// /// Gets the children of the content item. /// /// The content item. + /// /// - /// The specific culture to get the url children for. Default is null which will use the current culture in + /// The specific culture to get the url children for. Default is null which will use the current culture in /// /// /// Gets children that are available for the specified culture. /// Children are sorted by their sortOrder. /// - /// For culture, + /// For culture, /// if null is used the current culture is used. /// If an empty string is used only invariant children are returned. /// If "*" is used all children are returned. @@ -121,16 +841,285 @@ namespace Umbraco.Core /// However, if an empty string is specified only invariant children are returned. /// /// - public static IEnumerable Children(this IPublishedContent content, string culture = null) + public static IEnumerable Children(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) { // handle context culture for variant if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + culture = variationContextAccessor?.VariationContext?.Culture ?? ""; var children = content.ChildrenForAllCultures; return culture == "*" ? children : children.Where(x => x.IsInvariantOrHasCulture(culture)); } + + /// + /// Gets the children of the content, filtered by a predicate. + /// + /// The content. + /// Published snapshot instance + /// The predicate. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The children of the content, filtered by the predicate. + /// + /// Children are sorted by their sortOrder. + /// + public static IEnumerable Children(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string culture = null) + { + return content.Children(variationContextAccessor, culture).Where(predicate); + } + + /// + /// Gets the children of the content, of any of the specified types. + /// + /// The content. + /// Published snapshot instance + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The content type alias. + /// The children of the content, of any of the specified types. + public static IEnumerable ChildrenOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.Children(variationContextAccessor, x => x.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); + } + + /// + /// Gets the children of the content, of a given content type. + /// + /// The content type. + /// The content. + /// Published snapshot instance + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The children of content, of the given content type. + /// + /// Children are sorted by their sortOrder. + /// + public static IEnumerable Children(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.Children(variationContextAccessor, culture).OfType(); + } + + public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content.Children(variationContextAccessor, culture).FirstOrDefault(); + } + + /// + /// Gets the first child of the content, of a given content type. + /// + public static IPublishedContent FirstChildOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.ChildrenOfType(variationContextAccessor, contentTypeAlias, culture).FirstOrDefault(); + } + + public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string culture = null) + { + return content.Children(variationContextAccessor, predicate, culture).FirstOrDefault(); + } + + public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Guid uniqueId, string culture = null) + { + return content.Children(variationContextAccessor, x => x.Key == uniqueId, culture).FirstOrDefault(); + } + + public static T FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.Children(variationContextAccessor, culture).FirstOrDefault(); + } + + public static T FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string culture = null) + where T : class, IPublishedContent + { + return content.Children(variationContextAccessor, culture).FirstOrDefault(predicate); + } + + #endregion + + #region Axes: parent + + // Parent is native + + /// + /// Gets the parent of the content, of a given content type. + /// + /// The content type. + /// The content. + /// The parent of content, of the given content type, else null. + public static T Parent(this IPublishedContent content) + where T : class, IPublishedContent + { + if (content == null) throw new ArgumentNullException(nameof(content)); + return content.Parent as T; + } + + #endregion + + #region Axes: siblings + + /// + /// Gets the siblings of the content. + /// + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable Siblings(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return SiblingsAndSelf(content, publishedSnapshot, variationContextAccessor, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The content type alias. + /// The siblings of the content, of the given content type. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable SiblingsOfType(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return SiblingsAndSelfOfType(content, publishedSnapshot, variationContextAccessor, contentTypeAlias, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content type. + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content, of the given content type. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable Siblings(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return SiblingsAndSelf(content, publishedSnapshot, variationContextAccessor, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content including the node itself to indicate the position. + /// + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content including the node itself. + public static IEnumerable SiblingsAndSelf(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string culture = null) + { + return content.Parent != null + ? content.Parent.Children(variationContextAccessor, culture) + : publishedSnapshot.Content.GetAtRoot().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + } + + /// + /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. + /// + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The content type alias. + /// The siblings of the content including the node itself, of the given content type. + public static IEnumerable SiblingsAndSelfOfType(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string culture = null) + { + return content.Parent != null + ? content.Parent.ChildrenOfType(variationContextAccessor, contentTypeAlias, culture) + : publishedSnapshot.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + } + + /// + /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. + /// + /// The content type. + /// The content. + /// Published snapshot instance + /// Variation context accessor. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content including the node itself, of the given content type. + public static IEnumerable SiblingsAndSelf(this IPublishedContent content, IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, string culture = null) + where T : class, IPublishedContent + { + return content.Parent != null + ? content.Parent.Children(variationContextAccessor, culture) + : publishedSnapshot.Content.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + } + + #endregion + + #region Axes: custom + + /// + /// Gets the root content for this content. + /// + /// The content. + /// The 'site' content ie AncestorOrSelf(1). + public static IPublishedContent Root(this IPublishedContent content) + { + return content.AncestorOrSelf(1); + } + + #endregion + + #region Writer and creator + + public static string GetCreatorName(this IPublishedContent content, IUserService userService) + { + var user = userService.GetProfileById(content.CreatorId); + return user?.Name; + } + + public static string GetWriterName(this IPublishedContent content, IUserService userService) + { + var user = userService.GetProfileById(content.WriterId); + return user?.Name; + } + + #endregion + + #region Url + + /// + /// Gets the url of the content item. + /// + /// + /// If the content item is a document, then this method returns the url of the + /// document. If it is a media, then this methods return the media url for the + /// 'umbracoFile' property. Use the MediaUrl() method to get the media url for other + /// properties. + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. In addition, when the content type is multi-lingual, this is the url for the + /// specified culture. Otherwise, it is the invariant url. + /// + public static string Url(this IPublishedContent content, IPublishedUrlProvider publishedUrlProvider, string culture = null, UrlMode mode = UrlMode.Default) + { + if (publishedUrlProvider == null) + throw new InvalidOperationException("Cannot resolve a Url when Current.UmbracoContext.UrlProvider is null."); + + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return publishedUrlProvider.GetUrl(content, mode, culture); + + case PublishedItemType.Media: + return publishedUrlProvider.GetMediaUrl(content, mode, culture, Constants.Conventions.Media.File); + + default: + throw new NotSupportedException(); + } + } + + #endregion } } diff --git a/src/Umbraco.Core/PublishedElementExtensions.cs b/src/Umbraco.Core/PublishedElementExtensions.cs new file mode 100644 index 0000000000..4b529147e3 --- /dev/null +++ b/src/Umbraco.Core/PublishedElementExtensions.cs @@ -0,0 +1,179 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for IPublishedElement. + /// + public static class PublishedElementExtensions + { + #region OfTypes + + // the .OfType() filter is nice when there's only one type + // this is to support filtering with multiple types + public static IEnumerable OfTypes(this IEnumerable contents, params string[] types) + where T : IPublishedElement + { + if (types == null || types.Length == 0) return Enumerable.Empty(); + + return contents.Where(x => types.InvariantContains(x.ContentType.Alias)); + } + + #endregion + + #region IsComposedOf + + /// + /// Gets a value indicating whether the content is of a content type composed of the given alias + /// + /// The content. + /// The content type alias. + /// A value indicating whether the content is of a content type composed of a content type identified by the alias. + public static bool IsComposedOf(this IPublishedElement content, string alias) + { + return content.ContentType.CompositionAliases.InvariantContains(alias); + } + + #endregion + + #region HasProperty + + /// + /// Gets a value indicating whether the content has a property identified by its alias. + /// + /// The content. + /// The property alias. + /// A value indicating whether the content has the property identified by the alias. + /// The content may have a property, and that property may not have a value. + public static bool HasProperty(this IPublishedElement content, string alias) + { + return content.ContentType.GetPropertyType(alias) != null; + } + + #endregion + + #region HasValue + + /// + /// Gets a value indicating whether the content has a value for a property identified by its alias. + /// + /// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true. + public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null) + { + var prop = content.GetProperty(alias); + return prop != null && prop.HasValue(culture, segment); + } + + #endregion + + #region Value + + /// + /// Gets the value of a content's property identified by its alias. + /// + /// The content. + /// The published value fallback implementation. + /// The property alias. + /// The variation language. + /// The variation segment. + /// Optional fallback strategy. + /// The default value. + /// The value of the content's property identified by the alias, if it exists, otherwise a default value. + /// + /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. + /// If no property with the specified alias exists, or if the property has no value, returns . + /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. + /// The alias is case-insensitive. + /// + public static object Value(this IPublishedElement content, IPublishedValueFallback publishedValueFallback, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) + { + var property = content.GetProperty(alias); + + // if we have a property, and it has a value, return that value + if (property != null && property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + // else let fallback try to get a value + if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property?.GetValue(culture, segment); + } + + #endregion + + #region Value + + /// + /// Gets the value of a content's property identified by its alias, converted to a specified type. + /// + /// The target property type. + /// The content. + /// The published value fallback implementation. + /// The property alias. + /// The variation language. + /// The variation segment. + /// Optional fallback strategy. + /// The default value. + /// The value of the content's property identified by the alias, converted to the specified type. + /// + /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. + /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns default(T). + /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. + /// The alias is case-insensitive. + /// + public static T Value(this IPublishedElement content, IPublishedValueFallback publishedValueFallback, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) + { + var property = content.GetProperty(alias); + + // if we have a property, and it has a value, return that value + if (property != null && property.HasValue(culture, segment)) + return property.Value(publishedValueFallback, culture, segment); + + // else let fallback try to get a value + if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(publishedValueFallback, culture, segment); + } + + #endregion + + #region ToIndexedArray + + public static IndexedArrayItem[] ToIndexedArray(this IEnumerable source) + where TContent : class, IPublishedElement + { + var set = source.Select((content, index) => new IndexedArrayItem(content, index)).ToArray(); + foreach (var setItem in set) setItem.TotalCount = set.Length; + return set; + } + + #endregion + + #region IsSomething + + /// + /// Gets a value indicating whether the content is visible. + /// + /// The content. + /// The published value fallback implementation. + /// A value indicating whether the content is visible. + /// A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise, + /// the content is visible. + public static bool IsVisible(this IPublishedElement content, IPublishedValueFallback publishedValueFallback) + { + // rely on the property converter - will return default bool value, ie false, if property + // is not defined, or has no value, else will return its value. + return content.Value(publishedValueFallback, Constants.Conventions.Content.NaviHide) == false; + } + + #endregion + } +} diff --git a/src/Umbraco.Abstractions/PublishedModelFactoryExtensions.cs b/src/Umbraco.Core/PublishedModelFactoryExtensions.cs similarity index 78% rename from src/Umbraco.Abstractions/PublishedModelFactoryExtensions.cs rename to src/Umbraco.Core/PublishedModelFactoryExtensions.cs index ac8c9f1be7..29ffee8ed4 100644 --- a/src/Umbraco.Abstractions/PublishedModelFactoryExtensions.cs +++ b/src/Umbraco.Core/PublishedModelFactoryExtensions.cs @@ -17,25 +17,6 @@ namespace Umbraco.Core /// public static bool IsLiveFactory(this IPublishedModelFactory factory) => factory is ILivePublishedModelFactory; - [Obsolete("This method is no longer used or necessary and will be removed from future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static void WithSafeLiveFactory(this IPublishedModelFactory factory, Action action) - { - if (factory is ILivePublishedModelFactory liveFactory) - { - lock (liveFactory.SyncRoot) - { - //Call refresh on the live factory to re-compile the models - liveFactory.Refresh(); - action(); - } - } - else - { - action(); - } - } - /// /// Sets a flag to reset the ModelsBuilder models if the is /// diff --git a/src/Umbraco.Core/PublishedPropertyExtension.cs b/src/Umbraco.Core/PublishedPropertyExtension.cs new file mode 100644 index 0000000000..259a2714d3 --- /dev/null +++ b/src/Umbraco.Core/PublishedPropertyExtension.cs @@ -0,0 +1,59 @@ +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for IPublishedProperty. + /// + public static class PublishedPropertyExtension + { + #region Value + + public static object Value(this IPublishedProperty property, IPublishedValueFallback publishedValueFallback, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) + { + if (property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return publishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var value) + ? value + : property.GetValue(culture, segment); // give converter a chance to return it's own vision of "no value" + } + + #endregion + + #region Value + + public static T Value(this IPublishedProperty property, IPublishedValueFallback publishedValueFallback, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) + { + if (property.HasValue(culture, segment)) + { + // we have a value + // try to cast or convert it + var value = property.GetValue(culture, segment); + if (value is T valueAsT) return valueAsT; + var valueConverted = value.TryConvertTo(); + if (valueConverted) return valueConverted.Result; + + // cannot cast nor convert the value, nothing we can return but 'default' + // note: we don't want to fallback in that case - would make little sense + return default; + } + + // we don't have a value, try fallback + if (publishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) + return fallbackValue; + + // we don't have a value - neither direct nor fallback + // give a chance to the converter to return something (eg empty enumerable) + var noValue = property.GetValue(culture, segment); + if (noValue is T noValueAsT) return noValueAsT; + var noValueConverted = noValue.TryConvertTo(); + if (noValueConverted) return noValueConverted.Result; + + // cannot cast noValue nor convert it, nothing we can return but 'default' + return default; + } + + #endregion + } +} diff --git a/src/Umbraco.Abstractions/ReadLock.cs b/src/Umbraco.Core/ReadLock.cs similarity index 100% rename from src/Umbraco.Abstractions/ReadLock.cs rename to src/Umbraco.Core/ReadLock.cs diff --git a/src/Umbraco.Abstractions/ReflectionUtilities.cs b/src/Umbraco.Core/ReflectionUtilities.cs similarity index 100% rename from src/Umbraco.Abstractions/ReflectionUtilities.cs rename to src/Umbraco.Core/ReflectionUtilities.cs diff --git a/src/Umbraco.Abstractions/RegisterExtensions.cs b/src/Umbraco.Core/RegisterExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/RegisterExtensions.cs rename to src/Umbraco.Core/RegisterExtensions.cs diff --git a/src/Umbraco.Core/Request/IRequestAccessor.cs b/src/Umbraco.Core/Request/IRequestAccessor.cs new file mode 100644 index 0000000000..63a8de6b1e --- /dev/null +++ b/src/Umbraco.Core/Request/IRequestAccessor.cs @@ -0,0 +1,13 @@ +using System; +using Umbraco.Web.Routing; + +namespace Umbraco.Core.Request +{ + public interface IRequestAccessor + { + string GetRequestValue(string name); + string GetQueryStringValue(string culture); + event EventHandler EndRequest; + event EventHandler RouteAttempt; + } +} diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs similarity index 77% rename from src/Umbraco.Web/Routing/AliasUrlProvider.cs rename to src/Umbraco.Core/Routing/AliasUrlProvider.cs index f84ac608d2..e71de2f6c6 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -14,14 +14,20 @@ namespace Umbraco.Web.Routing public class AliasUrlProvider : IUrlProvider { private readonly IGlobalSettings _globalSettings; - private readonly IRequestHandlerSection _requestConfig; + private readonly IRequestHandlerSettings _requestConfig; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly UriUtility _uriUtility; + private readonly IPublishedValueFallback _publishedValueFallback; - public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ISiteDomainHelper siteDomainHelper) + public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSettings requestConfig, ISiteDomainHelper siteDomainHelper, UriUtility uriUtility, IPublishedValueFallback publishedValueFallback, IUmbracoContextAccessor umbracoContextAccessor) { _globalSettings = globalSettings; _requestConfig = requestConfig; _siteDomainHelper = siteDomainHelper; + _uriUtility = uriUtility; + _publishedValueFallback = publishedValueFallback; + _umbracoContextAccessor = umbracoContextAccessor; } // note - at the moment we seem to accept pretty much anything as an alias @@ -31,7 +37,7 @@ namespace Umbraco.Web.Routing #region GetUrl /// - public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current) + public UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current) { return null; // we have nothing to say } @@ -51,8 +57,9 @@ namespace Umbraco.Web.Routing /// Other urls are those that GetUrl would not return in the current context, but would be valid /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). /// - public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) + public IEnumerable GetOtherUrls(int id, Uri current) { + var umbracoContext = _umbracoContextAccessor.UmbracoContext; var node = umbracoContext.Content.GetById(id); if (node == null) yield break; @@ -83,7 +90,7 @@ namespace Umbraco.Web.Routing if (varies) yield break; - var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias); + var umbracoUrlName = node.Value(_publishedValueFallback, Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (aliases == null || aliases.Any() == false) @@ -93,7 +100,7 @@ namespace Umbraco.Web.Routing { var path = "/" + alias; var uri = new Uri(path, UriKind.Relative); - yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString()); + yield return UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString()); } } else @@ -108,8 +115,8 @@ namespace Umbraco.Web.Routing if (varies && !node.HasCulture(domainUri.Culture.Name)) continue; var umbracoUrlName = varies - ? node.Value(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name) - : node.Value(Constants.Conventions.Content.UrlAlias); + ? node.Value(_publishedValueFallback,Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name) + : node.Value(_publishedValueFallback, Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(new [] {','}, StringSplitOptions.RemoveEmptyEntries); @@ -120,7 +127,7 @@ namespace Umbraco.Web.Routing { var path = "/" + alias; var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); - yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString(), domainUri.Culture.Name); + yield return UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString(), domainUri.Culture.Name); } } } diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs similarity index 77% rename from src/Umbraco.Web/Routing/ContentFinderByIdPath.cs rename to src/Umbraco.Core/Routing/ContentFinderByIdPath.cs index b339198928..49a6ff6bfe 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; using System.Globalization; +using Umbraco.Core.Request; namespace Umbraco.Web.Routing { @@ -16,12 +17,14 @@ namespace Umbraco.Web.Routing public class ContentFinderByIdPath : IContentFinder { private readonly ILogger _logger; - private readonly IWebRoutingSection _webRoutingSection; - - public ContentFinderByIdPath(IWebRoutingSection webRoutingSection, ILogger logger) + private readonly IRequestAccessor _requestAccessor; + private readonly IWebRoutingSettings _webRoutingSettings; + + public ContentFinderByIdPath(IWebRoutingSettings webRoutingSettings, ILogger logger, IRequestAccessor requestAccessor) { - _webRoutingSection = webRoutingSection ?? throw new System.ArgumentNullException(nameof(webRoutingSection)); + _webRoutingSettings = webRoutingSettings ?? throw new System.ArgumentNullException(nameof(webRoutingSettings)); _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + _requestAccessor = requestAccessor; } /// @@ -29,11 +32,11 @@ namespace Umbraco.Web.Routing /// /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindContent(PublishedRequest frequest) + public bool TryFindContent(IPublishedRequest frequest) { if (frequest.UmbracoContext != null && frequest.UmbracoContext.InPreviewMode == false - && _webRoutingSection.DisableFindContentByIdPath) + && _webRoutingSettings.DisableFindContentByIdPath) return false; IPublishedContent node = null; @@ -54,11 +57,14 @@ namespace Umbraco.Web.Routing if (node != null) { + + var cultureFromQuerystring = _requestAccessor.GetQueryStringValue("culture"); + //if we have a node, check if we have a culture in the query string - if (frequest.UmbracoContext.HttpContext.Request.QueryString.ContainsKey("culture")) + if (!string.IsNullOrEmpty(cultureFromQuerystring)) { //we're assuming it will match a culture, if an invalid one is passed in, an exception will throw (there is no TryGetCultureInfo method), i think this is ok though - frequest.Culture = CultureInfo.GetCultureInfo(frequest.UmbracoContext.HttpContext.Request.QueryString["culture"]); + frequest.Culture = CultureInfo.GetCultureInfo(cultureFromQuerystring); } frequest.PublishedContent = node; diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs similarity index 62% rename from src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs rename to src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs index 70a920f6f0..6a9adda5f8 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Web.Routing +using Umbraco.Core.Request; + +namespace Umbraco.Web.Routing { /// /// This looks up a document by checking for the umbPageId of a request/query string @@ -9,10 +11,17 @@ /// public class ContentFinderByPageIdQuery : IContentFinder { - public bool TryFindContent(PublishedRequest frequest) + private readonly IRequestAccessor _requestAccessor; + + public ContentFinderByPageIdQuery(IRequestAccessor requestAccessor) + { + _requestAccessor = requestAccessor; + } + + public bool TryFindContent(IPublishedRequest frequest) { int pageId; - if (int.TryParse(frequest.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) + if (int.TryParse(_requestAccessor.GetRequestValue("umbPageID"), out pageId)) { var doc = frequest.UmbracoContext.Content.GetById(pageId); diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs similarity index 86% rename from src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs rename to src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs index 46e44dc5a3..5b3e0a5d99 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs @@ -17,11 +17,13 @@ namespace Umbraco.Web.Routing { private readonly IRedirectUrlService _redirectUrlService; private readonly ILogger _logger; + private readonly IPublishedUrlProvider _publishedUrlProvider; - public ContentFinderByRedirectUrl(IRedirectUrlService redirectUrlService, ILogger logger) + public ContentFinderByRedirectUrl(IRedirectUrlService redirectUrlService, ILogger logger, IPublishedUrlProvider publishedUrlProvider) { _redirectUrlService = redirectUrlService; _logger = logger; + _publishedUrlProvider = publishedUrlProvider; } /// @@ -30,7 +32,7 @@ namespace Umbraco.Web.Routing /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. /// Optionally, can also assign the template or anything else on the document request, although that is not required. - public bool TryFindContent(PublishedRequest frequest) + public bool TryFindContent(IPublishedRequest frequest) { var route = frequest.HasDomain ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()) @@ -45,7 +47,7 @@ namespace Umbraco.Web.Routing } var content = frequest.UmbracoContext.Content.GetById(redirectUrl.ContentId); - var url = content == null ? "#" : content.Url(redirectUrl.Culture); + var url = content == null ? "#" : content.Url(_publishedUrlProvider, redirectUrl.Culture); if (url.StartsWith("#")) { _logger.Debug("Route {Route} matches content {ContentId} which has no url.", route, redirectUrl.ContentId); @@ -63,7 +65,8 @@ namespace Umbraco.Web.Routing // See http://issues.umbraco.org/issue/U4-8361#comment=67-30532 // Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads // to problems if you rename a page back to it's original name or create a new page with the original name - frequest.Cacheability = HttpCacheability.NoCache; + //frequest.Cacheability = HttpCacheability.NoCache; + frequest.CacheabilityNoCache = true; frequest.CacheExtensions = new List { "no-store, must-revalidate" }; frequest.Headers = new Dictionary { { "Pragma", "no-cache" }, { "Expires", "0" } }; diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs similarity index 93% rename from src/Umbraco.Web/Routing/ContentFinderByUrl.cs rename to src/Umbraco.Core/Routing/ContentFinderByUrl.cs index 0a14dc97fe..3fcffff842 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. - public virtual bool TryFindContent(PublishedRequest frequest) + public virtual bool TryFindContent(IPublishedRequest frequest) { string route; if (frequest.HasDomain) @@ -42,7 +42,7 @@ namespace Umbraco.Web.Routing /// The document request. /// The route. /// The document node, or null. - protected IPublishedContent FindContent(PublishedRequest docreq, string route) + protected IPublishedContent FindContent(IPublishedRequest docreq, string route) { if (docreq == null) throw new System.ArgumentNullException(nameof(docreq)); diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs similarity index 79% rename from src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs rename to src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index cf71611047..6bc1b329ed 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -18,10 +18,14 @@ namespace Umbraco.Web.Routing /// public class ContentFinderByUrlAlias : IContentFinder { + private readonly IPublishedValueFallback _publishedValueFallback; + private readonly IVariationContextAccessor _variationContextAccessor; protected ILogger Logger { get; } - public ContentFinderByUrlAlias(ILogger logger) + public ContentFinderByUrlAlias(ILogger logger, IPublishedValueFallback publishedValueFallback, IVariationContextAccessor variationContextAccessor) { + _publishedValueFallback = publishedValueFallback; + _variationContextAccessor = variationContextAccessor; Logger = logger; } @@ -30,7 +34,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindContent(PublishedRequest frequest) + public bool TryFindContent(IPublishedRequest frequest) { IPublishedContent node = null; @@ -51,7 +55,7 @@ namespace Umbraco.Web.Routing return node != null; } - private static IPublishedContent FindContentByAlias(IPublishedContentCache cache, int rootNodeId, string culture, string alias) + private IPublishedContent FindContentByAlias(IPublishedContentCache cache, int rootNodeId, string culture, string alias) { if (alias == null) throw new ArgumentNullException(nameof(alias)); @@ -84,11 +88,11 @@ namespace Umbraco.Web.Routing if (varies) { if (!c.HasCulture(culture)) return false; - v = c.Value(propertyAlias, culture); + v = c.Value(_publishedValueFallback, propertyAlias, culture); } else { - v = c.Value(propertyAlias); + v = c.Value(_publishedValueFallback, propertyAlias); } if (string.IsNullOrWhiteSpace(v)) return false; v = "," + v.Replace(" ", "") + ","; @@ -101,12 +105,12 @@ namespace Umbraco.Web.Routing if (rootNodeId > 0) { var rootNode = cache.GetById(rootNodeId); - return rootNode?.Descendants().FirstOrDefault(x => IsMatch(x, test1, test2)); + return rootNode?.Descendants(_variationContextAccessor).FirstOrDefault(x => IsMatch(x, test1, test2)); } foreach (var rootContent in cache.GetAtRoot()) { - var c = rootContent.DescendantsOrSelf().FirstOrDefault(x => IsMatch(x, test1, test2)); + var c = rootContent.DescendantsOrSelf(_variationContextAccessor).FirstOrDefault(x => IsMatch(x, test1, test2)); if (c != null) return c; } diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs similarity index 86% rename from src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs rename to src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs index 16dfa63596..7bcea4681e 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs @@ -1,9 +1,7 @@ using Umbraco.Core.Logging; -using Umbraco.Core.Models; using Umbraco.Core; -using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; using Umbraco.Core.Services; namespace Umbraco.Web.Routing @@ -20,10 +18,15 @@ namespace Umbraco.Web.Routing { private readonly IFileService _fileService; - public ContentFinderByUrlAndTemplate(ILogger logger, IFileService fileService) + private readonly IContentTypeService _contentTypeService; + private readonly IWebRoutingSettings _webRoutingSettings; + + public ContentFinderByUrlAndTemplate(ILogger logger, IFileService fileService, IContentTypeService contentTypeService, IWebRoutingSettings webRoutingSettings) : base(logger) { _fileService = fileService; + _contentTypeService = contentTypeService; + _webRoutingSettings = webRoutingSettings; } /// @@ -32,7 +35,7 @@ namespace Umbraco.Web.Routing /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. - public override bool TryFindContent(PublishedRequest frequest) + public override bool TryFindContent(IPublishedRequest frequest) { IPublishedContent node = null; var path = frequest.Uri.GetAbsolutePathDecoded(); @@ -73,7 +76,7 @@ namespace Umbraco.Web.Routing } // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings - if (!node.IsAllowedTemplate(template.Id)) + if (!node.IsAllowedTemplate(_contentTypeService, _webRoutingSettings, template.Id)) { Logger.Warn("Alternative template '{TemplateAlias}' is not allowed on node {NodeId}.", template.Alias, node.Id); frequest.PublishedContent = null; // clear diff --git a/src/Umbraco.Web/Routing/ContentFinderCollection.cs b/src/Umbraco.Core/Routing/ContentFinderCollection.cs similarity index 100% rename from src/Umbraco.Web/Routing/ContentFinderCollection.cs rename to src/Umbraco.Core/Routing/ContentFinderCollection.cs diff --git a/src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs b/src/Umbraco.Core/Routing/ContentFinderCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs rename to src/Umbraco.Core/Routing/ContentFinderCollectionBuilder.cs diff --git a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs similarity index 58% rename from src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs rename to src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs index 89abde0576..3e6413ff90 100644 --- a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -10,20 +9,17 @@ namespace Umbraco.Web.Routing /// public class DefaultMediaUrlProvider : IMediaUrlProvider { - private readonly PropertyEditorCollection _propertyEditors; + private readonly UriUtility _uriUtility; + private readonly MediaUrlGeneratorCollection _mediaPathGenerators; - public DefaultMediaUrlProvider(PropertyEditorCollection propertyEditors) - { - _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); - } - - [Obsolete("Use the constructor with all parameters instead")] - public DefaultMediaUrlProvider() : this(Current.PropertyEditors) + public DefaultMediaUrlProvider(MediaUrlGeneratorCollection mediaPathGenerators, UriUtility uriUtility) { + _mediaPathGenerators = mediaPathGenerators ?? throw new ArgumentNullException(nameof(mediaPathGenerators)); + _uriUtility = uriUtility; } /// - public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, + public virtual UrlInfo GetMediaUrl(IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); @@ -36,25 +32,23 @@ namespace Umbraco.Web.Routing } var propType = prop.PropertyType; - string path = null; - if (_propertyEditors.TryGet(propType.EditorAlias, out var editor) - && editor is IDataEditorWithMediaPath dataEditor) + if (_mediaPathGenerators.TryGetMediaPath(propType.EditorAlias, value, out var path)) { - path = dataEditor.GetMediaPath(value); + var url = AssembleUrl(path, current, mode); + return UrlInfo.Url(url.ToString(), culture); } - var url = AssembleUrl(path, current, mode); - return url == null ? null : UrlInfo.Url(url.ToString(), culture); + return null; } private Uri AssembleUrl(string path, Uri current, UrlMode mode) { - if (string.IsNullOrEmpty(path)) - return null; + if (string.IsNullOrWhiteSpace(path)) + throw new ArgumentException($"{nameof(path)} cannot be null or whitespace", nameof(path)); // the stored path is absolute so we just return it as is - if(Uri.IsWellFormedUriString(path, UriKind.Absolute)) + if (Uri.IsWellFormedUriString(path, UriKind.Absolute)) return new Uri(path); Uri uri; @@ -75,7 +69,7 @@ namespace Umbraco.Web.Routing throw new ArgumentOutOfRangeException(nameof(mode)); } - return UriUtility.MediaUriFromUmbraco(uri); + return _uriUtility.MediaUriFromUmbraco(uri); } } } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs similarity index 84% rename from src/Umbraco.Web/Routing/DefaultUrlProvider.cs rename to src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 4092538481..f56d96b6b3 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; @@ -13,33 +12,38 @@ namespace Umbraco.Web.Routing /// public class DefaultUrlProvider : IUrlProvider { - private readonly IRequestHandlerSection _requestSettings; + private readonly IRequestHandlerSettings _requestSettings; private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly UriUtility _uriUtility; - public DefaultUrlProvider(IRequestHandlerSection requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) + public DefaultUrlProvider(IRequestHandlerSettings requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) { _requestSettings = requestSettings; _logger = logger; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + _uriUtility = uriUtility; + _umbracoContextAccessor = umbracoContextAccessor; } #region GetUrl /// - public virtual UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current) + public virtual UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current)); + var umbracoContext = _umbracoContextAccessor.UmbracoContext; // will not use cache if previewing var route = umbracoContext.Content.GetRouteById(content.Id, culture); return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); } - internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture) + internal UrlInfo GetUrlFromRoute(string route, IUmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture) { if (string.IsNullOrWhiteSpace(route)) { @@ -68,7 +72,7 @@ namespace Umbraco.Web.Routing /// /// Gets the other urls of a published content. /// - /// The Umbraco context. + /// The Umbraco context. /// The published content id. /// The current absolute url. /// The other urls for the published content. @@ -76,8 +80,9 @@ namespace Umbraco.Web.Routing /// Other urls are those that GetUrl would not return in the current context, but would be valid /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). /// - public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) + public virtual IEnumerable GetOtherUrls(int id, Uri current) { + var umbracoContext = _umbracoContextAccessor.UmbracoContext; var node = umbracoContext.Content.GetById(id); if (node == null) yield break; @@ -108,7 +113,7 @@ namespace Umbraco.Web.Routing var path = pos == 0 ? route : route.Substring(pos); var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); - uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); + uri = _uriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); yield return UrlInfo.Url(uri.ToString(), culture); } } @@ -167,7 +172,7 @@ namespace Umbraco.Web.Routing // UriFromUmbraco will handle vdir // meaning it will add vdir into domain urls too! - return UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); + return _uriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); } string CombinePaths(string path1, string path2) diff --git a/src/Umbraco.Web/Routing/Domain.cs b/src/Umbraco.Core/Routing/Domain.cs similarity index 100% rename from src/Umbraco.Web/Routing/Domain.cs rename to src/Umbraco.Core/Routing/Domain.cs diff --git a/src/Umbraco.Web/Routing/DomainAndUri.cs b/src/Umbraco.Core/Routing/DomainAndUri.cs similarity index 100% rename from src/Umbraco.Web/Routing/DomainAndUri.cs rename to src/Umbraco.Core/Routing/DomainAndUri.cs diff --git a/src/Umbraco.Web/Routing/DomainUtilities.cs b/src/Umbraco.Core/Routing/DomainUtilities.cs similarity index 98% rename from src/Umbraco.Web/Routing/DomainUtilities.cs rename to src/Umbraco.Core/Routing/DomainUtilities.cs index fb0c56b28d..c459ae4d14 100644 --- a/src/Umbraco.Web/Routing/DomainUtilities.cs +++ b/src/Umbraco.Core/Routing/DomainUtilities.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Routing /// one document per culture), and domains, withing the context of a current Uri, assign /// a culture to that document. /// - internal static string GetCultureFromDomains(int contentId, string contentPath, Uri current, UmbracoContext umbracoContext, ISiteDomainHelper siteDomainHelper) + internal static string GetCultureFromDomains(int contentId, string contentPath, Uri current, IUmbracoContext umbracoContext, ISiteDomainHelper siteDomainHelper) { if (umbracoContext == null) throw new InvalidOperationException("A current UmbracoContext is required."); @@ -295,7 +295,7 @@ namespace Umbraco.Web.Routing ? currentUri.GetLeftPart(UriPartial.Authority) + domainName : domainName; var scheme = currentUri?.Scheme ?? Uri.UriSchemeHttp; - return new Uri(UriUtility.TrimPathEndSlash(UriUtility.StartWithScheme(name, scheme))); + return new Uri(UriUtilityCore.TrimPathEndSlash(UriUtilityCore.StartWithScheme(name, scheme))); } #endregion @@ -344,7 +344,7 @@ namespace Umbraco.Web.Routing /// The current domain root node identifier, or null. /// The deepest wildcard Domain in the path, or null. /// Looks _under_ rootNodeId but not _at_ rootNodeId. - internal static Domain FindWildcardDomainInPath(IEnumerable domains, string path, int? rootNodeId) + public static Domain FindWildcardDomainInPath(IEnumerable domains, string path, int? rootNodeId) { var stopNodeId = rootNodeId ?? -1; diff --git a/src/Umbraco.Web/Routing/EnsureRoutableOutcome.cs b/src/Umbraco.Core/Routing/EnsureRoutableOutcome.cs similarity index 100% rename from src/Umbraco.Web/Routing/EnsureRoutableOutcome.cs rename to src/Umbraco.Core/Routing/EnsureRoutableOutcome.cs diff --git a/src/Umbraco.Web/Routing/IContentFinder.cs b/src/Umbraco.Core/Routing/IContentFinder.cs similarity index 92% rename from src/Umbraco.Web/Routing/IContentFinder.cs rename to src/Umbraco.Core/Routing/IContentFinder.cs index 2e388c4814..39d31741a6 100644 --- a/src/Umbraco.Web/Routing/IContentFinder.cs +++ b/src/Umbraco.Core/Routing/IContentFinder.cs @@ -11,6 +11,6 @@ namespace Umbraco.Web.Routing /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. /// Optionally, can also assign the template or anything else on the document request, although that is not required. - bool TryFindContent(PublishedRequest request); + bool TryFindContent(IPublishedRequest request); } } diff --git a/src/Umbraco.Web/Routing/IContentLastChanceFinder.cs b/src/Umbraco.Core/Routing/IContentLastChanceFinder.cs similarity index 100% rename from src/Umbraco.Web/Routing/IContentLastChanceFinder.cs rename to src/Umbraco.Core/Routing/IContentLastChanceFinder.cs diff --git a/src/Umbraco.Web/Routing/IMediaUrlProvider.cs b/src/Umbraco.Core/Routing/IMediaUrlProvider.cs similarity index 85% rename from src/Umbraco.Web/Routing/IMediaUrlProvider.cs rename to src/Umbraco.Core/Routing/IMediaUrlProvider.cs index 8a81b27415..1b966c6fdd 100644 --- a/src/Umbraco.Web/Routing/IMediaUrlProvider.cs +++ b/src/Umbraco.Core/Routing/IMediaUrlProvider.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Models.PublishedContent; + namespace Umbraco.Web.Routing { /// @@ -11,7 +12,6 @@ namespace Umbraco.Web.Routing /// /// Gets the url of a media item. /// - /// The Umbraco context. /// The published content. /// The property alias to resolve the url from. /// The url mode. @@ -26,6 +26,6 @@ namespace Umbraco.Web.Routing /// e.g. a cdn url provider will most likely always return an absolute url. /// If the provider is unable to provide a url, it returns null. /// - UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current); + UrlInfo GetMediaUrl(IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current); } } diff --git a/src/Umbraco.Core/Routing/IPublishedRequest.cs b/src/Umbraco.Core/Routing/IPublishedRequest.cs new file mode 100644 index 0000000000..f357108a4e --- /dev/null +++ b/src/Umbraco.Core/Routing/IPublishedRequest.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Routing +{ + public interface IPublishedRequest + { + /// + /// Gets the UmbracoContext. + /// + IUmbracoContext UmbracoContext { get; } + + /// + /// Gets or sets the cleaned up Uri used for routing. + /// + /// The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc. + Uri Uri { get; set; } + + /// + /// Gets or sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request. + /// + bool IgnorePublishedContentCollisions { get; set; } + + /// + /// Gets or sets the requested content. + /// + /// Setting the requested content clears Template. + IPublishedContent PublishedContent { get; set; } + + /// + /// Gets the initial requested content. + /// + /// The initial requested content is the content that was found by the finders, + /// before anything such as 404, redirect... took place. + IPublishedContent InitialPublishedContent { get; } + + /// + /// Gets value indicating whether the current published content is the initial one. + /// + bool IsInitialPublishedContent { get; } + + /// + /// Gets or sets a value indicating whether the current published content has been obtained + /// from the initial published content following internal redirections exclusively. + /// + /// Used by PublishedContentRequestEngine.FindTemplate() to figure out whether to + /// apply the internal redirect or not, when content is not the initial content. + bool IsInternalRedirectPublishedContent { get; } + + /// + /// Gets a value indicating whether the content request has a content. + /// + bool HasPublishedContent { get; } + + ITemplate TemplateModel { get; set; } + + /// + /// Gets the alias of the template to use to display the requested content. + /// + string TemplateAlias { get; } + + /// + /// Gets a value indicating whether the content request has a template. + /// + bool HasTemplate { get; } + + void UpdateToNotFound(); + + /// + /// Gets or sets the content request's domain. + /// + /// Is a DomainAndUri object ie a standard Domain plus the fully qualified uri. For example, + /// the Domain may contain "example.com" whereas the Uri will be fully qualified eg "http://example.com/". + DomainAndUri Domain { get; set; } + + /// + /// Gets a value indicating whether the content request has a domain. + /// + bool HasDomain { get; } + + /// + /// Gets or sets the content request's culture. + /// + CultureInfo Culture { get; set; } + + /// + /// Gets or sets a value indicating whether the requested content could not be found. + /// + /// This is set in the PublishedContentRequestBuilder and can also be used in + /// custom content finders or Prepared event handlers, where we want to allow developers + /// to indicate a request is 404 but not to cancel it. + bool Is404 { get; set; } + + /// + /// Gets a value indicating whether the content request triggers a redirect (permanent or not). + /// + bool IsRedirect { get; } + + /// + /// Gets or sets a value indicating whether the redirect is permanent. + /// + bool IsRedirectPermanent { get; } + + /// + /// Gets or sets the url to redirect to, when the content request triggers a redirect. + /// + string RedirectUrl { get; } + + /// + /// Gets or sets the content request http response status code. + /// + /// Does not actually set the http response status code, only registers that the response + /// should use the specified code. The code will or will not be used, in due time. + int ResponseStatusCode { get; } + + /// + /// Gets or sets the content request http response status description. + /// + /// Does not actually set the http response status description, only registers that the response + /// should use the specified description. The description will or will not be used, in due time. + string ResponseStatusDescription { get; } + + /// + /// Gets or sets a list of Extensions to append to the Response.Cache object. + /// + List CacheExtensions { get; set; } + + /// + /// Gets or sets a dictionary of Headers to append to the Response object. + /// + Dictionary Headers { get; set; } + + bool CacheabilityNoCache { get; set; } + + /// + /// Prepares the request. + /// + void Prepare(); + + /// + /// Triggers the Preparing event. + /// + void OnPreparing(); + + /// + /// Triggers the Prepared event. + /// + void OnPrepared(); + + /// + /// Sets the requested content, following an internal redirect. + /// + /// The requested content. + /// Depending on UmbracoSettings.InternalRedirectPreservesTemplate, will + /// preserve or reset the template, if any. + void SetInternalRedirectPublishedContent(IPublishedContent content); + + /// + /// Indicates that the current PublishedContent is the initial one. + /// + void SetIsInitialPublishedContent(); + + /// + /// Tries to set the template to use to display the requested content. + /// + /// The alias of the template. + /// A value indicating whether a valid template with the specified alias was found. + /// + /// Successfully setting the template does refresh RenderingEngine. + /// If setting the template fails, then the previous template (if any) remains in place. + /// + bool TrySetTemplate(string alias); + + /// + /// Sets the template to use to display the requested content. + /// + /// The template. + /// Setting the template does refresh RenderingEngine. + void SetTemplate(ITemplate template); + + /// + /// Resets the template. + /// + void ResetTemplate(); + + /// + /// Indicates that the content request should trigger a redirect (302). + /// + /// The url to redirect to. + /// Does not actually perform a redirect, only registers that the response should + /// redirect. Redirect will or will not take place in due time. + void SetRedirect(string url); + + /// + /// Indicates that the content request should trigger a permanent redirect (301). + /// + /// The url to redirect to. + /// Does not actually perform a redirect, only registers that the response should + /// redirect. Redirect will or will not take place in due time. + void SetRedirectPermanent(string url); + + /// + /// Indicates that the content request should trigger a redirect, with a specified status code. + /// + /// The url to redirect to. + /// The status code (300-308). + /// Does not actually perform a redirect, only registers that the response should + /// redirect. Redirect will or will not take place in due time. + void SetRedirect(string url, int status); + + /// + /// Sets the http response status code, along with an optional associated description. + /// + /// The http status code. + /// The description. + /// Does not actually set the http response status code and description, only registers that + /// the response should use the specified code and description. The code and description will or will + /// not be used, in due time. + void SetResponseStatus(int code, string description = null); + } +} diff --git a/src/Umbraco.Web/Routing/IPublishedRouter.cs b/src/Umbraco.Core/Routing/IPublishedRouter.cs similarity index 86% rename from src/Umbraco.Web/Routing/IPublishedRouter.cs rename to src/Umbraco.Core/Routing/IPublishedRouter.cs index df06e42e55..db9d69df20 100644 --- a/src/Umbraco.Web/Routing/IPublishedRouter.cs +++ b/src/Umbraco.Core/Routing/IPublishedRouter.cs @@ -17,21 +17,21 @@ namespace Umbraco.Web.Routing /// The current Umbraco context. /// The (optional) request Uri. /// A published request. - PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null); + IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null); /// /// Prepares a request for rendering. /// /// The request. /// A value indicating whether the request was successfully prepared and can be rendered. - bool PrepareRequest(PublishedRequest request); + bool PrepareRequest(IPublishedRequest request); /// /// Tries to route a request. /// /// The request. /// A value indicating whether the request can be routed to a document. - bool TryRouteRequest(PublishedRequest request); + bool TryRouteRequest(IPublishedRequest request); /// /// Gets a template. @@ -49,6 +49,6 @@ namespace Umbraco.Web.Routing /// the request, for whatever reason, and wants to force it to be re-routed /// and rendered as if no document were found (404). /// - void UpdateRequestToNotFound(PublishedRequest request); + void UpdateRequestToNotFound(IPublishedRequest request); } } diff --git a/src/Umbraco.Core/Routing/IPublishedUrlProvider.cs b/src/Umbraco.Core/Routing/IPublishedUrlProvider.cs new file mode 100644 index 0000000000..45faf76772 --- /dev/null +++ b/src/Umbraco.Core/Routing/IPublishedUrlProvider.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Routing +{ + public interface IPublishedUrlProvider + { + /// + /// Gets or sets the provider url mode. + /// + UrlMode Mode { get; set; } + + /// + /// Gets the url of a published content. + /// + /// The published content identifier. + /// The url mode. + /// A culture. + /// The current absolute url. + /// The url for the published content. + string GetUrl(Guid id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null); + + /// + /// Gets the url of a published content. + /// + /// The published content identifier. + /// The url mode. + /// A culture. + /// The current absolute url. + /// The url for the published content. + string GetUrl(int id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null); + + /// + /// Gets the url of a published content. + /// + /// The published content. + /// The url mode. + /// A culture. + /// The current absolute url. + /// The url for the published content. + /// + /// The url is absolute or relative depending on mode and on current. + /// If the published content is multi-lingual, gets the url for the specified culture or, + /// when no culture is specified, the current culture. + /// If the provider is unable to provide a url, it returns "#". + /// + string GetUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null); + + string GetUrlFromRoute(int id, string route, string culture); + + /// + /// Gets the other urls of a published content. + /// + /// The published content id. + /// The other urls for the published content. + /// + /// Other urls are those that GetUrl would not return in the current context, but would be valid + /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). + /// The results depend on the current url. + /// + IEnumerable GetOtherUrls(int id); + + /// + /// Gets the other urls of a published content. + /// + /// The published content id. + /// The current absolute url. + /// The other urls for the published content. + /// + /// Other urls are those that GetUrl would not return in the current context, but would be valid + /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). + /// + IEnumerable GetOtherUrls(int id, Uri current); + + /// + /// Gets the url of a media item. + /// + /// + /// + /// + /// + /// + /// + string GetMediaUrl(Guid id, UrlMode mode = UrlMode.Default, string culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri current = null); + + /// + /// Gets the url of a media item. + /// + /// The published content. + /// The property alias to resolve the url from. + /// The url mode. + /// The variation language. + /// The current absolute url. + /// The url for the media. + /// + /// The url is absolute or relative depending on mode and on current. + /// If the media is multi-lingual, gets the url for the specified culture or, + /// when no culture is specified, the current culture. + /// If the provider is unable to provide a url, it returns . + /// + string GetMediaUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri current = null); + } +} diff --git a/src/Umbraco.Web/Routing/ISiteDomainHelper.cs b/src/Umbraco.Core/Routing/ISiteDomainHelper.cs similarity index 100% rename from src/Umbraco.Web/Routing/ISiteDomainHelper.cs rename to src/Umbraco.Core/Routing/ISiteDomainHelper.cs diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Core/Routing/IUrlProvider.cs similarity index 82% rename from src/Umbraco.Web/Routing/IUrlProvider.cs rename to src/Umbraco.Core/Routing/IUrlProvider.cs index c0ce1fef39..ba1d48a113 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Core/Routing/IUrlProvider.cs @@ -12,7 +12,6 @@ namespace Umbraco.Web.Routing /// /// Gets the url of a published content. /// - /// The Umbraco context. /// The published content. /// The url mode. /// A culture. @@ -24,12 +23,11 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it should return null. /// - UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current); + UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current); /// /// Gets the other urls of a published content. /// - /// The Umbraco context. /// The published content id. /// The current absolute url. /// The other urls for the published content. @@ -37,6 +35,6 @@ namespace Umbraco.Web.Routing /// Other urls are those that GetUrl would not return in the current context, but would be valid /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). /// - IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current); + IEnumerable GetOtherUrls(int id, Uri current); } } diff --git a/src/Umbraco.Web/Routing/MediaUrlProviderCollection.cs b/src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs similarity index 100% rename from src/Umbraco.Web/Routing/MediaUrlProviderCollection.cs rename to src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs diff --git a/src/Umbraco.Web/Routing/MediaUrlProviderCollectionBuilder.cs b/src/Umbraco.Core/Routing/MediaUrlProviderCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Routing/MediaUrlProviderCollectionBuilder.cs rename to src/Umbraco.Core/Routing/MediaUrlProviderCollectionBuilder.cs diff --git a/src/Umbraco.Web/Routing/PublishedRequest.cs b/src/Umbraco.Core/Routing/PublishedRequest.cs similarity index 93% rename from src/Umbraco.Web/Routing/PublishedRequest.cs rename to src/Umbraco.Core/Routing/PublishedRequest.cs index aee7ba4de8..6e4d0008a9 100644 --- a/src/Umbraco.Web/Routing/PublishedRequest.cs +++ b/src/Umbraco.Core/Routing/PublishedRequest.cs @@ -2,22 +2,21 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Threading; -using System.Web; -using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Macros; +using Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Web.Routing { + /// /// Represents a request for one specified Umbraco IPublishedContent to be rendered /// by one specified template, using one specified Culture and RenderingEngine. /// - public class PublishedRequest + public class PublishedRequest : IPublishedRequest { private readonly IPublishedRouter _publishedRouter; + private readonly IWebRoutingSettings _webRoutingSettings; private bool _readonly; // after prepared private bool _readonlyUri; // after preparing @@ -27,25 +26,25 @@ namespace Umbraco.Web.Routing private CultureInfo _culture; private IPublishedContent _publishedContent; private IPublishedContent _initialPublishedContent; // found by finders before 404, redirects, etc - private PublishedContentHashtableConverter _umbracoPage; // legacy /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The published router. /// The Umbraco context. /// The request Uri. - internal PublishedRequest(IPublishedRouter publishedRouter, UmbracoContext umbracoContext, Uri uri = null) + internal PublishedRequest(IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, IWebRoutingSettings webRoutingSettings, Uri uri = null) { UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); + _webRoutingSettings = webRoutingSettings; Uri = uri ?? umbracoContext.CleanedUmbracoUrl; } /// /// Gets the UmbracoContext. /// - public UmbracoContext UmbracoContext { get; } + public IUmbracoContext UmbracoContext { get; } /// /// Gets or sets the cleaned up Uri used for routing. @@ -63,12 +62,14 @@ namespace Umbraco.Web.Routing } // utility for ensuring it is ok to set some properties - private void EnsureWriteable() + public void EnsureWriteable() { if (_readonly) throw new InvalidOperationException("Cannot modify a PublishedRequest once it is read-only."); } + public bool CacheabilityNoCache { get; set; } + /// /// Prepares the request. /// @@ -103,7 +104,7 @@ namespace Umbraco.Web.Routing /// /// Triggers the Preparing event. /// - internal void OnPreparing() + public void OnPreparing() { Preparing?.Invoke(this, EventArgs.Empty); _readonlyUri = true; @@ -112,7 +113,7 @@ namespace Umbraco.Web.Routing /// /// Triggers the Prepared event. /// - internal void OnPrepared() + public void OnPrepared() { Prepared?.Invoke(this, EventArgs.Empty); @@ -177,7 +178,7 @@ namespace Umbraco.Web.Routing IsInternalRedirectPublishedContent = isInternalRedirect; // must restore the template if it's an internal redirect & the config option is set - if (isInternalRedirect && Current.Configs.Settings().WebRouting.InternalRedirectPreservesTemplate) + if (isInternalRedirect && _webRoutingSettings.InternalRedirectPreservesTemplate) { // restore TemplateModel = template; @@ -228,7 +229,7 @@ namespace Umbraco.Web.Routing /// /// Gets or sets the template model to use to display the requested content. /// - internal ITemplate TemplateModel { get; set; } + public ITemplate TemplateModel { get; set; } /// /// Gets the alias of the template to use to display the requested content. @@ -290,7 +291,7 @@ namespace Umbraco.Web.Routing /// public bool HasTemplate => TemplateModel != null; - internal void UpdateToNotFound() + public void UpdateToNotFound() { var __readonly = _readonly; _readonly = false; @@ -461,7 +462,7 @@ namespace Umbraco.Web.Routing // Note: we used to set a default value here but that would then be the default // for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example // see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752 - public HttpCacheability Cacheability { get; set; } + //public HttpCacheability Cacheability { get; set; } /// /// Gets or sets a list of Extensions to append to the Response.Cache object. @@ -474,23 +475,5 @@ namespace Umbraco.Web.Routing public Dictionary Headers { get; set; } = new Dictionary(); #endregion - - #region Legacy - - // for legacy/webforms/macro code - - // TODO: get rid of it eventually - internal PublishedContentHashtableConverter LegacyContentHashTable - { - get - { - if (_umbracoPage == null) - throw new InvalidOperationException("The UmbracoPage object has not been initialized yet."); - - return _umbracoPage; - } - set => _umbracoPage = value; - } - - #endregion } } diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs similarity index 81% rename from src/Umbraco.Web/Routing/PublishedRouter.cs rename to src/Umbraco.Core/Routing/PublishedRouter.cs index 30076ee2b0..af4ec60e4d 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -4,13 +4,12 @@ using System.Threading; using System.Globalization; using System.IO; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Request; using Umbraco.Core.Services; -using Umbraco.Web.Macros; using Umbraco.Web.Security; namespace Umbraco.Web.Routing @@ -20,44 +19,62 @@ namespace Umbraco.Web.Routing /// public class PublishedRouter : IPublishedRouter { - private readonly IWebRoutingSection _webRoutingSection; + private readonly IWebRoutingSettings _webRoutingSettings; private readonly ContentFinderCollection _contentFinders; private readonly IContentLastChanceFinder _contentLastChanceFinder; - private readonly ServiceContext _services; private readonly IProfilingLogger _profilingLogger; private readonly IVariationContextAccessor _variationContextAccessor; private readonly ILogger _logger; + private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IRequestAccessor _requestAccessor; + private readonly IPublishedValueFallback _publishedValueFallback; + private readonly IPublicAccessChecker _publicAccessChecker; + private readonly IFileService _fileService; + private readonly IContentTypeService _contentTypeService; + private readonly IPublicAccessService _publicAccessService; /// /// Initializes a new instance of the class. /// public PublishedRouter( - IWebRoutingSection webRoutingSection, + IWebRoutingSettings webRoutingSettings, ContentFinderCollection contentFinders, IContentLastChanceFinder contentLastChanceFinder, IVariationContextAccessor variationContextAccessor, - ServiceContext services, - IProfilingLogger proflog) + IProfilingLogger proflog, + IPublishedUrlProvider publishedUrlProvider, + IRequestAccessor requestAccessor, + IPublishedValueFallback publishedValueFallback, + IPublicAccessChecker publicAccessChecker, + IFileService fileService, + IContentTypeService contentTypeService, + IPublicAccessService publicAccessService) { - _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); + _webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings)); _contentFinders = contentFinders ?? throw new ArgumentNullException(nameof(contentFinders)); _contentLastChanceFinder = contentLastChanceFinder ?? throw new ArgumentNullException(nameof(contentLastChanceFinder)); - _services = services ?? throw new ArgumentNullException(nameof(services)); _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); _logger = proflog; + _publishedUrlProvider = publishedUrlProvider; + _requestAccessor = requestAccessor; + _publishedValueFallback = publishedValueFallback; + _publicAccessChecker = publicAccessChecker; + _fileService = fileService; + _contentTypeService = contentTypeService; + _publicAccessService = publicAccessService; } /// - public PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null) + public IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null) { - return new PublishedRequest(this, umbracoContext, uri ?? umbracoContext.CleanedUmbracoUrl); + return new PublishedRequest(this, umbracoContext, _webRoutingSettings, uri ?? umbracoContext.CleanedUmbracoUrl); } #region Request /// - public bool TryRouteRequest(PublishedRequest request) + public bool TryRouteRequest(IPublishedRequest request) { // disabled - is it going to change the routing? //_pcr.OnPreparing(); @@ -88,7 +105,7 @@ namespace Umbraco.Web.Routing } /// - public bool PrepareRequest(PublishedRequest request) + public bool PrepareRequest(IPublishedRequest request) { // note - at that point the original legacy module did something do handle IIS custom 404 errors // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support @@ -159,7 +176,7 @@ namespace Umbraco.Web.Routing /// This method logic has been put into it's own method in case developers have created a custom PCR or are assigning their own values /// but need to finalize it themselves. /// - public bool ConfigureRequest(PublishedRequest frequest) + public bool ConfigureRequest(IPublishedRequest frequest) { if (frequest.HasPublishedContent == false) { @@ -182,22 +199,11 @@ namespace Umbraco.Web.Routing // can't go beyond that point without a PublishedContent to render // it's ok not to have a template, in order to give MVC a chance to hijack routes - // note - the page() ctor below will cause the "page" to get the value of all its - // "elements" ie of all the IPublishedContent property. If we use the object value, - // that will trigger macro execution - which can't happen because macro execution - // requires that _pcr.UmbracoPage is already initialized = catch-22. The "legacy" - // pipeline did _not_ evaluate the macros, ie it is using the data value, and we - // have to keep doing it because of that catch-22. - - // assign the legacy page back to the request - // handlers like default.aspx will want it and most macros currently need it - frequest.LegacyContentHashTable = new PublishedContentHashtableConverter(frequest); - return true; } /// - public void UpdateRequestToNotFound(PublishedRequest request) + public void UpdateRequestToNotFound(IPublishedRequest request) { // clear content var content = request.PublishedContent; @@ -226,13 +232,6 @@ namespace Umbraco.Web.Routing // to Mvc since Mvc can't do much either return; } - - // see note in PrepareRequest() - - // assign the legacy page back to the docrequest - // handlers like default.aspx will want it and most macros currently need it - request.LegacyContentHashTable = new PublishedContentHashtableConverter(request); - } #endregion @@ -243,7 +242,7 @@ namespace Umbraco.Web.Routing /// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly. /// /// A value indicating whether a domain was found. - internal bool FindDomain(PublishedRequest request) + internal bool FindDomain(IPublishedRequest request) { const string tracePrefix = "FindDomain: "; @@ -314,7 +313,7 @@ namespace Umbraco.Web.Routing /// /// Looks for wildcard domains in the path and updates Culture accordingly. /// - internal void HandleWildcardDomains(PublishedRequest request) + internal void HandleWildcardDomains(IPublishedRequest request) { const string tracePrefix = "HandleWildcardDomains: "; @@ -367,14 +366,14 @@ namespace Umbraco.Web.Routing /// public ITemplate GetTemplate(string alias) { - return _services.FileService.GetTemplate(alias); + return _fileService.GetTemplate(alias); } /// /// Finds the Umbraco document (if any) matching the request, and updates the PublishedRequest accordingly. /// /// A value indicating whether a document and template were found. - private void FindPublishedContentAndTemplate(PublishedRequest request) + private void FindPublishedContentAndTemplate(IPublishedRequest request) { _logger.Debug("FindPublishedContentAndTemplate: Path={UriAbsolutePath}", request.Uri.AbsolutePath); @@ -404,7 +403,7 @@ namespace Umbraco.Web.Routing /// Tries to find the document matching the request, by running the IPublishedContentFinder instances. /// /// There is no finder collection. - internal void FindPublishedContent(PublishedRequest request) + internal void FindPublishedContent(IPublishedRequest request) { const string tracePrefix = "FindPublishedContent: "; @@ -436,7 +435,7 @@ namespace Umbraco.Web.Routing /// Handles "not found", internal redirects, access validation... /// things that must be handled in one place because they can create loops /// - private void HandlePublishedContent(PublishedRequest request) + private void HandlePublishedContent(IPublishedRequest request) { // because these might loop, we have to have some sort of infinite loop detection int i = 0, j = 0; @@ -495,7 +494,7 @@ namespace Umbraco.Web.Routing /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. /// As per legacy, if the redirect does not work, we just ignore it. /// - private bool FollowInternalRedirects(PublishedRequest request) + private bool FollowInternalRedirects(IPublishedRequest request) { if (request.PublishedContent == null) throw new InvalidOperationException("There is no PublishedContent."); @@ -507,7 +506,7 @@ namespace Umbraco.Web.Routing var redirect = false; var valid = false; IPublishedContent internalRedirectNode = null; - var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, defaultValue: -1); + var internalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.InternalRedirectId, defaultValue: -1); if (internalRedirectId > 0) { @@ -517,7 +516,7 @@ namespace Umbraco.Web.Routing } else { - var udiInternalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId); + var udiInternalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.InternalRedirectId); if (udiInternalRedirectId != null) { // try and get the redirect node from a UDI Guid @@ -557,65 +556,41 @@ namespace Umbraco.Web.Routing /// Ensures that access to current node is permitted. /// /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - private void EnsurePublishedContentAccess(PublishedRequest request) + private void EnsurePublishedContentAccess(IPublishedRequest request) { if (request.PublishedContent == null) throw new InvalidOperationException("There is no PublishedContent."); var path = request.PublishedContent.Path; - var publicAccessAttempt = _services.PublicAccessService.IsProtected(path); + var publicAccessAttempt = _publicAccessService.IsProtected(path); if (publicAccessAttempt) { _logger.Debug("EnsurePublishedContentAccess: Page is protected, check for access"); - var membershipHelper = Current.Factory.GetInstance(); - - if (membershipHelper.IsLoggedIn() == false) + var status = _publicAccessChecker.HasMemberAccessToContent(request.PublishedContent.Id); + switch (status) { - _logger.Debug("EnsurePublishedContentAccess: Not logged in, redirect to login page"); - - var loginPageId = publicAccessAttempt.Result.LoginNodeId; - - if (loginPageId != request.PublishedContent.Id) - request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId); - } - else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, membershipHelper.GetCurrentUserRoles()) == false) - { - _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); - var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; - if (errorPageId != request.PublishedContent.Id) - request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId); - } - else - { - // grab the current member - var member = membershipHelper.GetCurrentMember(); - // if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access - var memberIsActive = true; - if (member != null) - { - if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false) - memberIsActive = member.Value(Constants.Conventions.Member.IsApproved); - - if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false) - memberIsActive = member.Value(Constants.Conventions.Member.IsLockedOut) == false; - } - - if (memberIsActive == false) - { - _logger.Debug( - "Current member is either unapproved or locked out, redirect to error page"); - var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; - if (errorPageId != request.PublishedContent.Id) - request.PublishedContent = - request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId); - } - else - { + case PublicAccessStatus.NotLoggedIn: + _logger.Debug("EnsurePublishedContentAccess: Not logged in, redirect to login page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.LoginNodeId); + break; + case PublicAccessStatus.AccessDenied: + _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.LockedOut: + _logger.Debug("Current member is locked out, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.NotApproved: + _logger.Debug("Current member is unapproved, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.AccessAccepted: _logger.Debug("Current member has access"); - } + break; } } else @@ -624,10 +599,16 @@ namespace Umbraco.Web.Routing } } + private static void SetPublishedContentAsOtherPage(IPublishedRequest request, int errorPageId) + { + if (errorPageId != request.PublishedContent.Id) + request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId); + } + /// /// Finds a template for the current node, if any. /// - private void FindTemplate(PublishedRequest request) + private void FindTemplate(IPublishedRequest request) { // NOTE: at the moment there is only 1 way to find a template, and then ppl must // use the Prepared event to change the template if they wish. Should we also @@ -644,9 +625,9 @@ namespace Umbraco.Web.Routing // does not apply // + optionally, apply the alternate template on internal redirects var useAltTemplate = request.IsInitialPublishedContent - || (_webRoutingSection.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent); + || (_webRoutingSettings.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent); var altTemplate = useAltTemplate - ? request.UmbracoContext.HttpContext.Request[Constants.Conventions.Url.AltTemplate] + ? _requestAccessor.GetRequestValue(Constants.Conventions.Url.AltTemplate) : null; if (string.IsNullOrWhiteSpace(altTemplate)) @@ -657,7 +638,7 @@ namespace Umbraco.Web.Routing if (request.HasTemplate) { - _logger.Debug("FindTemplate: Has a template already, and no alternate template."); + _logger.Debug("FindTemplate: Has a template already, and no alternate template."); return; } @@ -683,10 +664,15 @@ namespace Umbraco.Web.Routing _logger.Debug("FindTemplate: Look for alternative template alias={AltTemplate}", altTemplate); // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings - if (request.PublishedContent.IsAllowedTemplate(altTemplate)) + if (request.PublishedContent.IsAllowedTemplate( + _fileService, + _contentTypeService, + _webRoutingSettings.DisableAlternativeTemplates, + _webRoutingSettings.ValidateAlternativeTemplates, + altTemplate)) { // allowed, use - var template = _services.FileService.GetTemplate(altTemplate); + var template = _fileService.GetTemplate(altTemplate); if (template != null) { @@ -740,7 +726,7 @@ namespace Umbraco.Web.Routing if (templateId == null) throw new InvalidOperationException("The template is not set, the page cannot render."); - var template = _services.FileService.GetTemplate(templateId.Value); + var template = _fileService.GetTemplate(templateId.Value); if (template == null) throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render."); _logger.Debug("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); @@ -751,7 +737,7 @@ namespace Umbraco.Web.Routing /// Follows external redirection through umbracoRedirect document property. /// /// As per legacy, if the redirect does not work, we just ignore it. - private void FollowExternalRedirect(PublishedRequest request) + private void FollowExternalRedirect(IPublishedRequest request) { if (request.HasPublishedContent == false) return; @@ -759,18 +745,18 @@ namespace Umbraco.Web.Routing if (request.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false) return; - var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, defaultValue: -1); + var redirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.Redirect, defaultValue: -1); var redirectUrl = "#"; if (redirectId > 0) { - redirectUrl = request.UmbracoContext.UrlProvider.GetUrl(redirectId); + redirectUrl = _publishedUrlProvider.GetUrl(redirectId); } else { // might be a UDI instead of an int Id - var redirectUdi = request.PublishedContent.Value(Constants.Conventions.Content.Redirect); + var redirectUdi = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.Redirect); if (redirectUdi != null) - redirectUrl = request.UmbracoContext.UrlProvider.GetUrl(redirectUdi.Guid); + redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid); } if (redirectUrl != "#") request.SetRedirect(redirectUrl); diff --git a/src/Umbraco.Web/Routing/RoutableAttemptEventArgs.cs b/src/Umbraco.Core/Routing/RoutableAttemptEventArgs.cs similarity index 80% rename from src/Umbraco.Web/Routing/RoutableAttemptEventArgs.cs rename to src/Umbraco.Core/Routing/RoutableAttemptEventArgs.cs index 96b377b103..54ea98b3a4 100644 --- a/src/Umbraco.Web/Routing/RoutableAttemptEventArgs.cs +++ b/src/Umbraco.Core/Routing/RoutableAttemptEventArgs.cs @@ -9,8 +9,8 @@ namespace Umbraco.Web.Routing { public EnsureRoutableOutcome Outcome { get; private set; } - public RoutableAttemptEventArgs(EnsureRoutableOutcome reason, UmbracoContext umbracoContext, HttpContextBase httpContext) - : base(umbracoContext, httpContext) + public RoutableAttemptEventArgs(EnsureRoutableOutcome reason, IUmbracoContext umbracoContext) + : base(umbracoContext) { Outcome = reason; } diff --git a/src/Umbraco.Web/Routing/SiteDomainHelper.cs b/src/Umbraco.Core/Routing/SiteDomainHelper.cs similarity index 99% rename from src/Umbraco.Web/Routing/SiteDomainHelper.cs rename to src/Umbraco.Core/Routing/SiteDomainHelper.cs index 6173dfb43c..35475ae292 100644 --- a/src/Umbraco.Web/Routing/SiteDomainHelper.cs +++ b/src/Umbraco.Core/Routing/SiteDomainHelper.cs @@ -266,7 +266,7 @@ namespace Umbraco.Web.Routing return _qualifiedSites[current.Scheme] = _sites .ToDictionary( kvp => kvp.Key, - kvp => kvp.Value.Select(d => new Uri(UriUtility.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray() + kvp => kvp.Value.Select(d => new Uri(UriUtilityCore.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray() ); // .ToDictionary will evaluate and create the dictionary immediately diff --git a/src/Umbraco.Web/Routing/UmbracoRequestEventArgs.cs b/src/Umbraco.Core/Routing/UmbracoRequestEventArgs.cs similarity index 54% rename from src/Umbraco.Web/Routing/UmbracoRequestEventArgs.cs rename to src/Umbraco.Core/Routing/UmbracoRequestEventArgs.cs index 5b2be178fd..4430d9689f 100644 --- a/src/Umbraco.Web/Routing/UmbracoRequestEventArgs.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestEventArgs.cs @@ -8,13 +8,11 @@ namespace Umbraco.Web.Routing /// public class UmbracoRequestEventArgs : EventArgs { - public UmbracoContext UmbracoContext { get; private set; } - public HttpContextBase HttpContext { get; private set; } + public IUmbracoContext UmbracoContext { get; private set; } - public UmbracoRequestEventArgs(UmbracoContext umbracoContext, HttpContextBase httpContext) + public UmbracoRequestEventArgs(IUmbracoContext umbracoContext) { UmbracoContext = umbracoContext; - HttpContext = httpContext; } } } diff --git a/src/Umbraco.Web/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs similarity index 71% rename from src/Umbraco.Web/UriUtility.cs rename to src/Umbraco.Core/Routing/UriUtility.cs index 9e94a4a20a..9cb6bf50f6 100644 --- a/src/Umbraco.Web/UriUtility.cs +++ b/src/Umbraco.Core/Routing/UriUtility.cs @@ -4,21 +4,22 @@ using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; namespace Umbraco.Web { - public static class UriUtility + public sealed class UriUtility { static string _appPath; static string _appPathPrefix; - static UriUtility() + public UriUtility(IHostingEnvironment hostingEnvironment) { - ResetAppDomainAppVirtualPath(); + ResetAppDomainAppVirtualPath(hostingEnvironment); } // internal for unit testing only - internal static void SetAppDomainAppVirtualPath(string appPath) + internal void SetAppDomainAppVirtualPath(string appPath) { _appPath = appPath ?? "/"; _appPathPrefix = _appPath; @@ -26,20 +27,20 @@ namespace Umbraco.Web _appPathPrefix = String.Empty; } - internal static void ResetAppDomainAppVirtualPath() + internal void ResetAppDomainAppVirtualPath(IHostingEnvironment hostingEnvironment) { - SetAppDomainAppVirtualPath(HttpRuntime.AppDomainAppVirtualPath); + SetAppDomainAppVirtualPath(hostingEnvironment.ApplicationVirtualPath); } // will be "/" or "/foo" - public static string AppPath => _appPath; + public string AppPath => _appPath; // will be "" or "/foo" - public static string AppPathPrefix => _appPathPrefix; + public string AppPathPrefix => _appPathPrefix; // adds the virtual directory if any // see also VirtualPathUtility.ToAbsolute - public static string ToAbsolute(string url) + public string ToAbsolute(string url) { //return ResolveUrl(url); url = url.TrimStart('~'); @@ -48,7 +49,7 @@ namespace Umbraco.Web // strips the virtual directory if any // see also VirtualPathUtility.ToAppRelative - public static string ToAppRelative(string virtualPath) + public string ToAppRelative(string virtualPath) { if (virtualPath.InvariantStartsWith(_appPathPrefix) && (virtualPath.Length == _appPathPrefix.Length || virtualPath[_appPathPrefix.Length] == '/')) @@ -60,7 +61,7 @@ namespace Umbraco.Web // maps an internal umbraco uri to a public uri // ie with virtual directory, .aspx if required... - public static Uri UriFromUmbraco(Uri uri, IGlobalSettings globalSettings, IRequestHandlerSection requestConfig) + public Uri UriFromUmbraco(Uri uri, IGlobalSettings globalSettings, IRequestHandlerSettings requestConfig) { var path = uri.GetSafeAbsolutePath(); @@ -74,7 +75,7 @@ namespace Umbraco.Web // maps a media umbraco uri to a public uri // ie with virtual directory - that is all for media - public static Uri MediaUriFromUmbraco(Uri uri) + public Uri MediaUriFromUmbraco(Uri uri) { var path = uri.GetSafeAbsolutePath(); path = ToAbsolute(path); @@ -83,7 +84,7 @@ namespace Umbraco.Web // maps a public uri to an internal umbraco uri // ie no virtual directory, no .aspx, lowercase... - public static Uri UriToUmbraco(Uri uri) + public Uri UriToUmbraco(Uri uri) { // note: no need to decode uri here because we're returning a uri // so it will be re-encoded anyway @@ -120,7 +121,7 @@ namespace Umbraco.Web // ResolveUrl("page2.aspx") returns "/page2.aspx" // Page.ResolveUrl("page2.aspx") returns "/sub/page2.aspx" (relative...) // - public static string ResolveUrl(string relativeUrl) + public string ResolveUrl(string relativeUrl) { if (relativeUrl == null) throw new ArgumentNullException("relativeUrl"); @@ -177,74 +178,27 @@ namespace Umbraco.Web #endregion - #region Uri string utilities - - public static bool HasScheme(string uri) - { - return uri.IndexOf("://") > 0; - } - - public static string StartWithScheme(string uri) - { - return StartWithScheme(uri, null); - } - - public static string StartWithScheme(string uri, string scheme) - { - return HasScheme(uri) ? uri : String.Format("{0}://{1}", scheme ?? Uri.UriSchemeHttp, uri); - } - - public static string EndPathWithSlash(string uri) - { - var pos1 = Math.Max(0, uri.IndexOf('?')); - var pos2 = Math.Max(0, uri.IndexOf('#')); - var pos = Math.Min(pos1, pos2); - - var path = pos > 0 ? uri.Substring(0, pos) : uri; - path = path.EnsureEndsWith('/'); - - if (pos > 0) - path += uri.Substring(pos); - - return path; - } - - public static string TrimPathEndSlash(string uri) - { - var pos1 = Math.Max(0, uri.IndexOf('?')); - var pos2 = Math.Max(0, uri.IndexOf('#')); - var pos = Math.Min(pos1, pos2); - - var path = pos > 0 ? uri.Substring(0, pos) : uri; - path = path.TrimEnd('/'); - - if (pos > 0) - path += uri.Substring(pos); - - return path; - } - - #endregion /// /// Returns an full url with the host, port, etc... /// /// An absolute path (i.e. starts with a '/' ) - /// + /// /// /// /// Based on http://stackoverflow.com/questions/3681052/get-absolute-url-from-relative-path-refactored-method /// - internal static Uri ToFullUrl(string absolutePath, HttpContextBase httpContext) + internal Uri ToFullUrl(string absolutePath, Uri curentRequestUrl) { - if (httpContext == null) throw new ArgumentNullException("httpContext"); if (string.IsNullOrEmpty(absolutePath)) - throw new ArgumentNullException("absolutePath"); + throw new ArgumentNullException(nameof(absolutePath)); if (!absolutePath.StartsWith("/")) throw new FormatException("The absolutePath specified does not start with a '/'"); - return new Uri(absolutePath, UriKind.Relative).MakeAbsolute(httpContext.Request.Url); + return new Uri(absolutePath, UriKind.Relative).MakeAbsolute(curentRequestUrl); } + + } } diff --git a/src/Umbraco.Web/Routing/UrlInfo.cs b/src/Umbraco.Core/Routing/UrlInfo.cs similarity index 100% rename from src/Umbraco.Web/Routing/UrlInfo.cs rename to src/Umbraco.Core/Routing/UrlInfo.cs diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs similarity index 79% rename from src/Umbraco.Web/Routing/UrlProvider.cs rename to src/Umbraco.Core/Routing/UrlProvider.cs index d42639b781..fa764cf7ff 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -4,30 +4,31 @@ using System.Linq; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; namespace Umbraco.Web.Routing { + /// /// Provides urls. /// - public class UrlProvider + public class UrlProvider : IPublishedUrlProvider { #region Ctor and configuration /// /// Initializes a new instance of the class with an Umbraco context and a list of url providers. /// - /// The Umbraco context. + /// The Umbraco context accessor. /// Routing settings. /// The list of url providers. /// The list of media url providers. /// The current variation accessor. - public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, IEnumerable mediaUrlProviders, IVariationContextAccessor variationContextAccessor) + /// + public UrlProvider(IUmbracoContextAccessor umbracoContextAccessor, IWebRoutingSettings routingSettings, UrlProviderCollection urlProviders, MediaUrlProviderCollection mediaUrlProviders, IVariationContextAccessor variationContextAccessor) { if (routingSettings == null) throw new ArgumentNullException(nameof(routingSettings)); - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _urlProviders = urlProviders; _mediaUrlProviders = mediaUrlProviders; _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); @@ -40,25 +41,8 @@ namespace Umbraco.Web.Routing } } - /// - /// Initializes a new instance of the class with an Umbraco context and a list of url providers. - /// - /// The Umbraco context. - /// The list of url providers. - /// The list of media url providers - /// The current variation accessor. - /// An optional provider mode. - public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, IEnumerable mediaUrlProviders, IVariationContextAccessor variationContextAccessor, UrlMode mode = UrlMode.Auto) - { - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - _urlProviders = urlProviders; - _mediaUrlProviders = mediaUrlProviders; - _variationContextAccessor = variationContextAccessor; - Mode = mode; - } - - private readonly UmbracoContext _umbracoContext; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IEnumerable _urlProviders; private readonly IEnumerable _mediaUrlProviders; private readonly IVariationContextAccessor _variationContextAccessor; @@ -72,10 +56,9 @@ namespace Umbraco.Web.Routing #region GetUrl - private UrlMode GetMode(bool absolute) => absolute ? UrlMode.Absolute : Mode; - private IPublishedContent GetDocument(int id) => _umbracoContext.Content.GetById(id); - private IPublishedContent GetDocument(Guid id) => _umbracoContext.Content.GetById(id); - private IPublishedContent GetMedia(Guid id) => _umbracoContext.Media.GetById(id); + private IPublishedContent GetDocument(int id) => _umbracoContextAccessor.UmbracoContext.Content.GetById(id); + private IPublishedContent GetDocument(Guid id) => _umbracoContextAccessor.UmbracoContext.Content.GetById(id); + private IPublishedContent GetMedia(Guid id) => _umbracoContextAccessor.UmbracoContext.Media.GetById(id); /// /// Gets the url of a published content. @@ -131,19 +114,19 @@ namespace Umbraco.Web.Routing } if (current == null) - current = _umbracoContext.CleanedUmbracoUrl; + current = _umbracoContextAccessor.UmbracoContext.CleanedUmbracoUrl; - var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, content, mode, culture, current)) + var url = _urlProviders.Select(provider => provider.GetUrl(content, mode, culture, current)) .FirstOrDefault(u => u != null); return url?.Text ?? "#"; // legacy wants this } - internal string GetUrlFromRoute(int id, string route, string culture) + public string GetUrlFromRoute(int id, string route, string culture) { var provider = _urlProviders.OfType().FirstOrDefault(); var url = provider == null ? route // what else? - : provider.GetUrlFromRoute(route, Current.UmbracoContext, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text; + : provider.GetUrlFromRoute(route, _umbracoContextAccessor.UmbracoContext, id, _umbracoContextAccessor.UmbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text; return url ?? "#"; } @@ -163,7 +146,7 @@ namespace Umbraco.Web.Routing /// public IEnumerable GetOtherUrls(int id) { - return GetOtherUrls(id, _umbracoContext.CleanedUmbracoUrl); + return GetOtherUrls(id, _umbracoContextAccessor.UmbracoContext.CleanedUmbracoUrl); } /// @@ -178,7 +161,7 @@ namespace Umbraco.Web.Routing /// public IEnumerable GetOtherUrls(int id, Uri current) { - return _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, id, current) ?? Enumerable.Empty()); + return _urlProviders.SelectMany(provider => provider.GetOtherUrls(id, current) ?? Enumerable.Empty()); } #endregion @@ -232,10 +215,10 @@ namespace Umbraco.Web.Routing } if (current == null) - current = _umbracoContext.CleanedUmbracoUrl; + current = _umbracoContextAccessor.UmbracoContext.CleanedUmbracoUrl; var url = _mediaUrlProviders.Select(provider => - provider.GetMediaUrl(_umbracoContext, content, propertyAlias, mode, culture, current)) + provider.GetMediaUrl(content, propertyAlias, mode, culture, current)) .FirstOrDefault(u => u != null); return url?.Text ?? ""; diff --git a/src/Umbraco.Web/Routing/UrlProviderCollection.cs b/src/Umbraco.Core/Routing/UrlProviderCollection.cs similarity index 100% rename from src/Umbraco.Web/Routing/UrlProviderCollection.cs rename to src/Umbraco.Core/Routing/UrlProviderCollection.cs diff --git a/src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs b/src/Umbraco.Core/Routing/UrlProviderCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs rename to src/Umbraco.Core/Routing/UrlProviderCollectionBuilder.cs diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs similarity index 84% rename from src/Umbraco.Web/Routing/UrlProviderExtensions.cs rename to src/Umbraco.Core/Routing/UrlProviderExtensions.cs index 077680d2e2..a7f8a97c6d 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { @@ -19,11 +20,14 @@ namespace Umbraco.Web.Routing /// public static IEnumerable GetContentUrls(this IContent content, IPublishedRouter publishedRouter, - UmbracoContext umbracoContext, + IUmbracoContext umbracoContext, ILocalizationService localizationService, ILocalizedTextService textService, IContentService contentService, - ILogger logger) + IVariationContextAccessor variationContextAccessor, + ILogger logger, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider) { if (content == null) throw new ArgumentNullException(nameof(content)); if (publishedRouter == null) throw new ArgumentNullException(nameof(publishedRouter)); @@ -32,6 +36,9 @@ namespace Umbraco.Web.Routing if (textService == null) throw new ArgumentNullException(nameof(textService)); if (contentService == null) throw new ArgumentNullException(nameof(contentService)); if (logger == null) throw new ArgumentNullException(nameof(logger)); + if (publishedUrlProvider == null) throw new ArgumentNullException(nameof(publishedUrlProvider)); + if (uriUtility == null) throw new ArgumentNullException(nameof(uriUtility)); + if (variationContextAccessor == null) throw new ArgumentNullException(nameof(variationContextAccessor)); if (content.Published == false) { @@ -56,7 +63,7 @@ namespace Umbraco.Web.Routing //get all URLs for all cultures //in a HashSet, so de-duplicates too - foreach (var cultureUrl in GetContentUrlsByCulture(content, cultures, publishedRouter, umbracoContext, contentService, textService, logger)) + foreach (var cultureUrl in GetContentUrlsByCulture(content, cultures, publishedRouter, umbracoContext, contentService, textService, variationContextAccessor, logger, uriUtility, publishedUrlProvider)) { urls.Add(cultureUrl); } @@ -74,11 +81,11 @@ namespace Umbraco.Web.Routing // get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless. // for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them. - foreach (var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) + foreach (var otherUrl in publishedUrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) if (urls.Add(otherUrl)) //avoid duplicates yield return otherUrl; } - + /// /// Tries to return a for each culture for the content while detecting collisions/errors /// @@ -93,10 +100,13 @@ namespace Umbraco.Web.Routing private static IEnumerable GetContentUrlsByCulture(IContent content, IEnumerable cultures, IPublishedRouter publishedRouter, - UmbracoContext umbracoContext, + IUmbracoContext umbracoContext, IContentService contentService, ILocalizedTextService textService, - ILogger logger) + IVariationContextAccessor variationContextAccessor, + ILogger logger, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider) { foreach (var culture in cultures) { @@ -109,7 +119,7 @@ namespace Umbraco.Web.Routing string url; try { - url = umbracoContext.UrlProvider.GetUrl(content.Id, culture: culture); + url = publishedUrlProvider.GetUrl(content.Id, culture: culture); } catch (Exception ex) { @@ -131,7 +141,7 @@ namespace Umbraco.Web.Routing // got a url, deal with collisions, add url default: - if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, out var urlInfo)) // detect collisions, etc + if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility, out var urlInfo)) // detect collisions, etc yield return urlInfo; else yield return UrlInfo.Url(url, culture); @@ -161,12 +171,12 @@ namespace Umbraco.Web.Routing return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture); } - private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) + private static bool DetectCollision(IContent content, string url, string culture, IUmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, UriUtility uriUtility, out UrlInfo urlInfo) { // test for collisions on the 'main' url var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri == false) uri = uri.MakeAbsolute(umbracoContext.CleanedUmbracoUrl); - uri = UriUtility.UriToUmbraco(uri); + uri = uriUtility.UriToUmbraco(uri); var pcr = publishedRouter.CreateRequest(umbracoContext, uri); publishedRouter.TryRouteRequest(pcr); @@ -187,7 +197,7 @@ namespace Umbraco.Web.Routing var l = new List(); while (o != null) { - l.Add(o.Name()); + l.Add(o.Name(variationContextAccessor)); o = o.Parent; } l.Reverse(); diff --git a/src/Umbraco.Abstractions/IMainDom.cs b/src/Umbraco.Core/Runtime/IMainDom.cs similarity index 96% rename from src/Umbraco.Abstractions/IMainDom.cs rename to src/Umbraco.Core/Runtime/IMainDom.cs index 31b2e2eee0..444fc1c7d0 100644 --- a/src/Umbraco.Abstractions/IMainDom.cs +++ b/src/Umbraco.Core/Runtime/IMainDom.cs @@ -1,5 +1,6 @@ using System; +// TODO: Can't change namespace due to breaking changes, change in netcore namespace Umbraco.Core { /// diff --git a/src/Umbraco.Core/Runtime/IMainDomLock.cs b/src/Umbraco.Core/Runtime/IMainDomLock.cs new file mode 100644 index 0000000000..6a62f48194 --- /dev/null +++ b/src/Umbraco.Core/Runtime/IMainDomLock.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; + +namespace Umbraco.Core.Runtime +{ + /// + /// An application-wide distributed lock + /// + /// + /// Disposing releases the lock + /// + public interface IMainDomLock : IDisposable + { + /// + /// Acquires an application-wide distributed lock + /// + /// + /// + /// An awaitable boolean value which will be false if the elapsed millsecondsTimeout value is exceeded + /// + Task AcquireLockAsync(int millisecondsTimeout); + + /// + /// Wait on a background thread to receive a signal from another AppDomain + /// + /// + Task ListenAsync(); + } +} diff --git a/src/Umbraco.Abstractions/Runtime/IUmbracoBootPermissionChecker.cs b/src/Umbraco.Core/Runtime/IUmbracoBootPermissionChecker.cs similarity index 100% rename from src/Umbraco.Abstractions/Runtime/IUmbracoBootPermissionChecker.cs rename to src/Umbraco.Core/Runtime/IUmbracoBootPermissionChecker.cs diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs similarity index 74% rename from src/Umbraco.Core/MainDom.cs rename to src/Umbraco.Core/Runtime/MainDom.cs index eba7492563..2c56852095 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Threading; -using System.Web.Hosting; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; -namespace Umbraco.Core +namespace Umbraco.Core.Runtime { + /// /// Provides the full implementation of . /// @@ -16,23 +16,17 @@ namespace Umbraco.Core /// When an AppDomain starts, it tries to acquire the main domain status. /// When an AppDomain stops (eg the application is restarting) it should release the main domain status. /// - internal class MainDom : IMainDom, IRegisteredObject, IDisposable + public class MainDom : IMainDom, IRegisteredObject, IDisposable { #region Vars private readonly ILogger _logger; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IMainDomLock _mainDomLock; // our own lock for local consistency private object _locko = new object(); - // async lock representing the main domain lock - private readonly SystemLock _systemLock; - private IDisposable _systemLocker; - - // event wait handle used to notify current main domain that it should - // release the lock because a new domain wants to be the main domain - private readonly EventWaitHandle _signal; - private bool _isInitialized; // indicates whether... private bool _isMainDom; // we are the main domain @@ -41,39 +35,20 @@ namespace Umbraco.Core // actions to run before releasing the main domain private readonly List> _callbacks = new List>(); - private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds + private const int LockTimeoutMilliseconds = 40000; // 40 seconds #endregion #region Ctor // initializes a new instance of MainDom - public MainDom(ILogger logger, IHostingEnvironment hostingEnvironment) + public MainDom(ILogger logger, IHostingEnvironment hostingEnvironment, IMainDomLock systemLock) { - HostingEnvironment.RegisterObject(this); + hostingEnvironment.RegisterObject(this); _logger = logger; - - // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail - var appId = hostingEnvironment.ApplicationId?.ReplaceNonAlphanumericChars(string.Empty); - - // combining with the physical path because if running on eg IIS Express, - // two sites could have the same appId even though they are different. - // - // now what could still collide is... two sites, running in two different processes - // and having the same appId, and running on the same app physical path - // - // we *cannot* use the process ID here because when an AppPool restarts it is - // a new process for the same application path - - var appPath = hostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty; - var hash = (appId + ":::" + appPath).GenerateHash(); - - var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; - _systemLock = new SystemLock(lockName); - - var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT"; - _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + _hostingEnvironment = hostingEnvironment; + _mainDomLock = systemLock; } #endregion @@ -142,13 +117,14 @@ namespace Umbraco.Core continue; } } + _logger.Debug("Stopped ({SignalSource})", source); } finally { // in any case... _isMainDom = false; - _systemLocker?.Dispose(); + _mainDomLock.Dispose(); _logger.Info("Released ({SignalSource})", source); } @@ -168,37 +144,34 @@ namespace Umbraco.Core _logger.Info("Acquiring."); - // signal other instances that we want the lock, then wait one the lock, - // which may timeout, and this is accepted - see comments below + // Get the lock + var acquired = _mainDomLock.AcquireLockAsync(LockTimeoutMilliseconds).GetAwaiter().GetResult(); - // signal, then wait for the lock, then make sure the event is - // reset (maybe there was noone listening..) - _signal.Set(); + if (!acquired) + { + _logger.Info("Cannot acquire (timeout)."); - // if more than 1 instance reach that point, one will get the lock - // and the other one will timeout, which is accepted + // In previous versions we'd let a TimeoutException be thrown + // and the appdomain would not start. We have the opportunity to allow it to + // start without having MainDom? This would mean that it couldn't write + // to nucache/examine and would only be ok if this was a super short lived appdomain. + // maybe safer to just keep throwing in this case. + + throw new TimeoutException("Cannot acquire MainDom"); + // return false; + } - //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. try { - _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); + // Listen for the signal from another AppDomain coming online to release the lock + _mainDomLock.ListenAsync().ContinueWith(_ => OnSignal("signal")); } - finally + catch (OperationCanceledException ex) { - // we need to reset the event, because otherwise we would end up - // signaling ourselves and committing suicide immediately. - // only 1 instance can reach that point, but other instances may - // have started and be trying to get the lock - they will timeout, - // which is accepted - - _signal.Reset(); + // the waiting task could be canceled if this appdomain is naturally shutting down, we'll just swallow this exception + _logger.Warn(ex, ex.Message); } - //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread - - _signal.WaitOneAsync() - .ContinueWith(_ => OnSignal("signal")); - _logger.Info("Acquired."); return true; } @@ -206,6 +179,10 @@ namespace Umbraco.Core /// /// Gets a value indicating whether the current domain is the main domain. /// + /// + /// The lazy initializer call will only call the Acquire callback when it's not been initialized, else it will just return + /// the value from _isMainDom which means when we set _isMainDom to false again after being signaled, this will return false; + /// public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire()); // IRegisteredObject @@ -216,7 +193,7 @@ namespace Umbraco.Core // The web app is stopping, need to wind down Dispose(true); - HostingEnvironment.UnregisterObject(this); + _hostingEnvironment.UnregisterObject(this); } #region IDisposable Support @@ -231,8 +208,7 @@ namespace Umbraco.Core { if (disposing) { - _signal?.Close(); - _signal?.Dispose(); + _mainDomLock.Dispose(); } disposedValue = true; @@ -245,5 +221,25 @@ namespace Umbraco.Core } #endregion + + public static string GetMainDomId(IHostingEnvironment hostingEnvironment) + { + // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail + var appId = hostingEnvironment.ApplicationId?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty; + + // combining with the physical path because if running on eg IIS Express, + // two sites could have the same appId even though they are different. + // + // now what could still collide is... two sites, running in two different processes + // and having the same appId, and running on the same app physical path + // + // we *cannot* use the process ID here because when an AppPool restarts it is + // a new process for the same application path + + var appPath = hostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty; + var hash = (appId + ":::" + appPath).GenerateHash(); + + return hash; + } } } diff --git a/src/Umbraco.Abstractions/RuntimeLevel.cs b/src/Umbraco.Core/RuntimeLevel.cs similarity index 100% rename from src/Umbraco.Abstractions/RuntimeLevel.cs rename to src/Umbraco.Core/RuntimeLevel.cs diff --git a/src/Umbraco.Abstractions/RuntimeLevelReason.cs b/src/Umbraco.Core/RuntimeLevelReason.cs similarity index 100% rename from src/Umbraco.Abstractions/RuntimeLevelReason.cs rename to src/Umbraco.Core/RuntimeLevelReason.cs diff --git a/src/Umbraco.Abstractions/SafeCallContext.cs b/src/Umbraco.Core/SafeCallContext.cs similarity index 100% rename from src/Umbraco.Abstractions/SafeCallContext.cs rename to src/Umbraco.Core/SafeCallContext.cs diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunnerOptions.cs b/src/Umbraco.Core/Scheduling/BackgroundTaskRunnerOptions.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/BackgroundTaskRunnerOptions.cs rename to src/Umbraco.Core/Scheduling/BackgroundTaskRunnerOptions.cs diff --git a/src/Umbraco.Web/Scheduling/IBackgroundTask.cs b/src/Umbraco.Core/Scheduling/IBackgroundTask.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/IBackgroundTask.cs rename to src/Umbraco.Core/Scheduling/IBackgroundTask.cs diff --git a/src/Umbraco.Web/Scheduling/IBackgroundTaskRunner.cs b/src/Umbraco.Core/Scheduling/IBackgroundTaskRunner.cs similarity index 96% rename from src/Umbraco.Web/Scheduling/IBackgroundTaskRunner.cs rename to src/Umbraco.Core/Scheduling/IBackgroundTaskRunner.cs index 9dd5722592..8cbf5fc8d6 100644 --- a/src/Umbraco.Web/Scheduling/IBackgroundTaskRunner.cs +++ b/src/Umbraco.Core/Scheduling/IBackgroundTaskRunner.cs @@ -1,5 +1,5 @@ using System; -using System.Web.Hosting; +using Umbraco.Core; namespace Umbraco.Web.Scheduling { diff --git a/src/Umbraco.Web/Scheduling/ILatchedBackgroundTask.cs b/src/Umbraco.Core/Scheduling/ILatchedBackgroundTask.cs similarity index 92% rename from src/Umbraco.Web/Scheduling/ILatchedBackgroundTask.cs rename to src/Umbraco.Core/Scheduling/ILatchedBackgroundTask.cs index c761e5f4de..e981623b34 100644 --- a/src/Umbraco.Web/Scheduling/ILatchedBackgroundTask.cs +++ b/src/Umbraco.Core/Scheduling/ILatchedBackgroundTask.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using System.Threading.Tasks; namespace Umbraco.Web.Scheduling @@ -11,7 +10,7 @@ namespace Umbraco.Web.Scheduling /// a condition is met. However if the tasks runner has to terminate, /// latched background tasks can be executed immediately, depending on /// the value returned by RunsOnShutdown. - internal interface ILatchedBackgroundTask : IBackgroundTask + public interface ILatchedBackgroundTask : IBackgroundTask { /// /// Gets a task on latch. diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Core/Scheduling/KeepAlive.cs similarity index 88% rename from src/Umbraco.Web/Scheduling/KeepAlive.cs rename to src/Umbraco.Core/Scheduling/KeepAlive.cs index 9a22c59566..515251b105 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Core/Scheduling/KeepAlive.cs @@ -3,26 +3,25 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Sync; namespace Umbraco.Web.Scheduling { - internal class KeepAlive : RecurringTaskBase + public class KeepAlive : RecurringTaskBase { private readonly IRuntimeState _runtime; - private readonly IKeepAliveSection _keepAliveSection; + private readonly IKeepAliveSettings _keepAliveSettings; private readonly IProfilingLogger _logger; private static HttpClient _httpClient; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IKeepAliveSection keepAliveSection, IProfilingLogger logger) + IRuntimeState runtime, IKeepAliveSettings keepAliveSettings, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; - _keepAliveSection = keepAliveSection; + _keepAliveSettings = keepAliveSettings; _logger = logger; if (_httpClient == null) _httpClient = new HttpClient(); @@ -50,7 +49,7 @@ namespace Umbraco.Web.Scheduling using (_logger.DebugDuration("Keep alive executing", "Keep alive complete")) { - var keepAlivePingUrl = _keepAliveSection.KeepAlivePingUrl; + var keepAlivePingUrl = _keepAliveSettings.KeepAlivePingUrl; try { if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}")) diff --git a/src/Umbraco.Web/Scheduling/LatchedBackgroundTaskBase.cs b/src/Umbraco.Core/Scheduling/LatchedBackgroundTaskBase.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/LatchedBackgroundTaskBase.cs rename to src/Umbraco.Core/Scheduling/LatchedBackgroundTaskBase.cs diff --git a/src/Umbraco.Web/Scheduling/RecurringTaskBase.cs b/src/Umbraco.Core/Scheduling/RecurringTaskBase.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/RecurringTaskBase.cs rename to src/Umbraco.Core/Scheduling/RecurringTaskBase.cs diff --git a/src/Umbraco.Web/Scheduling/TaskAndFactoryExtensions.cs b/src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/TaskAndFactoryExtensions.cs rename to src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs diff --git a/src/Umbraco.Web/Scheduling/TaskEventArgs.cs b/src/Umbraco.Core/Scheduling/TaskEventArgs.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/TaskEventArgs.cs rename to src/Umbraco.Core/Scheduling/TaskEventArgs.cs diff --git a/src/Umbraco.Web/Scheduling/TempFileCleanup.cs b/src/Umbraco.Core/Scheduling/TempFileCleanup.cs similarity index 97% rename from src/Umbraco.Web/Scheduling/TempFileCleanup.cs rename to src/Umbraco.Core/Scheduling/TempFileCleanup.cs index 5d28bbfdb1..aefaf605db 100644 --- a/src/Umbraco.Web/Scheduling/TempFileCleanup.cs +++ b/src/Umbraco.Core/Scheduling/TempFileCleanup.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Scheduling /// /// Used to cleanup temporary file locations /// - internal class TempFileCleanup : RecurringTaskBase + public class TempFileCleanup : RecurringTaskBase { private readonly DirectoryInfo[] _tempFolders; private readonly TimeSpan _age; diff --git a/src/Umbraco.Web/Scheduling/ThreadingTaskImmutable.cs b/src/Umbraco.Core/Scheduling/ThreadingTaskImmutable.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/ThreadingTaskImmutable.cs rename to src/Umbraco.Core/Scheduling/ThreadingTaskImmutable.cs diff --git a/src/Umbraco.Infrastructure/Scoping/CallContext.cs b/src/Umbraco.Core/Scoping/CallContext.cs similarity index 100% rename from src/Umbraco.Infrastructure/Scoping/CallContext.cs rename to src/Umbraco.Core/Scoping/CallContext.cs diff --git a/src/Umbraco.Abstractions/Scoping/IInstanceIdentifiable.cs b/src/Umbraco.Core/Scoping/IInstanceIdentifiable.cs similarity index 100% rename from src/Umbraco.Abstractions/Scoping/IInstanceIdentifiable.cs rename to src/Umbraco.Core/Scoping/IInstanceIdentifiable.cs diff --git a/src/Umbraco.Abstractions/Scoping/IScopeContext.cs b/src/Umbraco.Core/Scoping/IScopeContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Scoping/IScopeContext.cs rename to src/Umbraco.Core/Scoping/IScopeContext.cs diff --git a/src/Umbraco.Web/Search/SearchableTreeAttribute.cs b/src/Umbraco.Core/Search/SearchableTreeAttribute.cs similarity index 100% rename from src/Umbraco.Web/Search/SearchableTreeAttribute.cs rename to src/Umbraco.Core/Search/SearchableTreeAttribute.cs diff --git a/src/Umbraco.Abstractions/Sections/ContentSection.cs b/src/Umbraco.Core/Sections/ContentSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/ContentSection.cs rename to src/Umbraco.Core/Sections/ContentSection.cs diff --git a/src/Umbraco.Abstractions/Sections/FormsSection.cs b/src/Umbraco.Core/Sections/FormsSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/FormsSection.cs rename to src/Umbraco.Core/Sections/FormsSection.cs diff --git a/src/Umbraco.Abstractions/Sections/ISection.cs b/src/Umbraco.Core/Sections/ISection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/ISection.cs rename to src/Umbraco.Core/Sections/ISection.cs diff --git a/src/Umbraco.Abstractions/Sections/MediaSection.cs b/src/Umbraco.Core/Sections/MediaSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/MediaSection.cs rename to src/Umbraco.Core/Sections/MediaSection.cs diff --git a/src/Umbraco.Abstractions/Sections/MembersSection.cs b/src/Umbraco.Core/Sections/MembersSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/MembersSection.cs rename to src/Umbraco.Core/Sections/MembersSection.cs diff --git a/src/Umbraco.Abstractions/Sections/PackagesSection.cs b/src/Umbraco.Core/Sections/PackagesSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/PackagesSection.cs rename to src/Umbraco.Core/Sections/PackagesSection.cs diff --git a/src/Umbraco.Abstractions/Sections/SectionCollection.cs b/src/Umbraco.Core/Sections/SectionCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/SectionCollection.cs rename to src/Umbraco.Core/Sections/SectionCollection.cs diff --git a/src/Umbraco.Abstractions/Sections/SectionCollectionBuilder.cs b/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/SectionCollectionBuilder.cs rename to src/Umbraco.Core/Sections/SectionCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Sections/SettingsSection.cs b/src/Umbraco.Core/Sections/SettingsSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/SettingsSection.cs rename to src/Umbraco.Core/Sections/SettingsSection.cs diff --git a/src/Umbraco.Abstractions/Sections/TranslationSection.cs b/src/Umbraco.Core/Sections/TranslationSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/TranslationSection.cs rename to src/Umbraco.Core/Sections/TranslationSection.cs diff --git a/src/Umbraco.Abstractions/Sections/UsersSection.cs b/src/Umbraco.Core/Sections/UsersSection.cs similarity index 100% rename from src/Umbraco.Abstractions/Sections/UsersSection.cs rename to src/Umbraco.Core/Sections/UsersSection.cs diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs deleted file mode 100644 index 7c3e835a77..0000000000 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Globalization; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; - -namespace Umbraco.Core.Security -{ - public static class AuthenticationExtensions - { - /// - /// This will return the current back office identity if the IPrincipal is the correct type - /// - /// - /// - internal static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user) - { - //If it's already a UmbracoBackOfficeIdentity - if (user.Identity is UmbracoBackOfficeIdentity backOfficeIdentity) return backOfficeIdentity; - - //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that - if (user is ClaimsPrincipal claimsPrincipal) - { - backOfficeIdentity = claimsPrincipal.Identities.OfType().FirstOrDefault(); - if (backOfficeIdentity != null) return backOfficeIdentity; - } - - //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session - if (user.Identity is ClaimsIdentity claimsIdentity && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType)) - { - try - { - return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity); - } - catch (InvalidOperationException) - { - } - } - - return null; - } - - /// - /// Ensures that the thread culture is set based on the back office user's culture - /// - /// - internal static void EnsureCulture(this IIdentity identity) - { - if (identity is UmbracoBackOfficeIdentity umbIdentity && umbIdentity.IsAuthenticated) - { - Thread.CurrentThread.CurrentUICulture = - Thread.CurrentThread.CurrentCulture = UserCultures.GetOrAdd(umbIdentity.Culture, s => new CultureInfo(s)); - } - } - - - /// - /// Used so that we aren't creating a new CultureInfo object for every single request - /// - private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); - - } -} diff --git a/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs b/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs new file mode 100644 index 0000000000..439e7a82b8 --- /dev/null +++ b/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Security +{ + public interface IMemberUserKeyProvider + { + object GetMemberProviderUserKey(); + } +} diff --git a/src/Umbraco.Abstractions/Security/IPasswordHasher.cs b/src/Umbraco.Core/Security/IPasswordHasher.cs similarity index 100% rename from src/Umbraco.Abstractions/Security/IPasswordHasher.cs rename to src/Umbraco.Core/Security/IPasswordHasher.cs diff --git a/src/Umbraco.Core/Security/IPublicAccessChecker.cs b/src/Umbraco.Core/Security/IPublicAccessChecker.cs new file mode 100644 index 0000000000..a47186394e --- /dev/null +++ b/src/Umbraco.Core/Security/IPublicAccessChecker.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Web.Security +{ + public interface IPublicAccessChecker + { + PublicAccessStatus HasMemberAccessToContent(int publishedContentId); + } +} diff --git a/src/Umbraco.Core/Security/IWebSecurity.cs b/src/Umbraco.Core/Security/IWebSecurity.cs new file mode 100644 index 0000000000..0822b5cb69 --- /dev/null +++ b/src/Umbraco.Core/Security/IWebSecurity.cs @@ -0,0 +1,62 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.Security +{ + public interface IWebSecurity + { + /// + /// Gets the current user. + /// + /// The current user. + IUser CurrentUser { get; } + + [Obsolete("This needs to be removed, ASP.NET Identity should always be used for this operation, this is currently only used in the installer which needs to be updated")] + double PerformLogin(int userId); + + [Obsolete("This needs to be removed, ASP.NET Identity should always be used for this operation, this is currently only used in the installer which needs to be updated")] + void ClearCurrentLogin(); + + /// + /// Gets the current user's id. + /// + /// + Attempt GetUserId(); + + /// + /// Validates the currently logged in user and ensures they are not timed out + /// + /// + bool ValidateCurrentUser(); + + /// + /// Validates the current user assigned to the request and ensures the stored user data is valid + /// + /// set to true if you want exceptions to be thrown if failed + /// If true requires that the user is approved to be validated + /// + ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true); + + /// + /// Authorizes the full request, checks for SSL and validates the current user + /// + /// set to true if you want exceptions to be thrown if failed + /// + ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false); + + /// + /// Checks if the specified user as access to the app + /// + /// + /// + /// + bool UserHasSectionAccess(string section, IUser user); + + /// + /// Ensures that a back office user is logged in + /// + /// + bool IsAuthenticated(); + } +} diff --git a/src/Umbraco.Abstractions/Security/PasswordGenerator.cs b/src/Umbraco.Core/Security/PasswordGenerator.cs similarity index 100% rename from src/Umbraco.Abstractions/Security/PasswordGenerator.cs rename to src/Umbraco.Core/Security/PasswordGenerator.cs diff --git a/src/Umbraco.Core/Security/PublicAccessStatus.cs b/src/Umbraco.Core/Security/PublicAccessStatus.cs new file mode 100644 index 0000000000..57df423749 --- /dev/null +++ b/src/Umbraco.Core/Security/PublicAccessStatus.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Web.Security +{ + public enum PublicAccessStatus + { + NotLoggedIn, + AccessDenied, + NotApproved, + LockedOut, + AccessAccepted + } +} diff --git a/src/Umbraco.Web/Security/ValidateRequestAttempt.cs b/src/Umbraco.Core/Security/ValidateRequestAttempt.cs similarity index 100% rename from src/Umbraco.Web/Security/ValidateRequestAttempt.cs rename to src/Umbraco.Core/Security/ValidateRequestAttempt.cs diff --git a/src/Umbraco.Abstractions/SemVersionExtensions.cs b/src/Umbraco.Core/SemVersionExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/SemVersionExtensions.cs rename to src/Umbraco.Core/SemVersionExtensions.cs diff --git a/src/Umbraco.Abstractions/Semver/Semver.cs b/src/Umbraco.Core/Semver/Semver.cs similarity index 100% rename from src/Umbraco.Abstractions/Semver/Semver.cs rename to src/Umbraco.Core/Semver/Semver.cs diff --git a/src/Umbraco.Abstractions/Serialization/IJsonSerializer.cs b/src/Umbraco.Core/Serialization/IJsonSerializer.cs similarity index 100% rename from src/Umbraco.Abstractions/Serialization/IJsonSerializer.cs rename to src/Umbraco.Core/Serialization/IJsonSerializer.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/ContentTypeChange.cs b/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/ContentTypeChange.cs rename to src/Umbraco.Core/Services/Changes/ContentTypeChange.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/ContentTypeChangeExtensions.cs b/src/Umbraco.Core/Services/Changes/ContentTypeChangeExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/ContentTypeChangeExtensions.cs rename to src/Umbraco.Core/Services/Changes/ContentTypeChangeExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/ContentTypeChangeTypes.cs b/src/Umbraco.Core/Services/Changes/ContentTypeChangeTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/ContentTypeChangeTypes.cs rename to src/Umbraco.Core/Services/Changes/ContentTypeChangeTypes.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/DomainChangeTypes.cs b/src/Umbraco.Core/Services/Changes/DomainChangeTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/DomainChangeTypes.cs rename to src/Umbraco.Core/Services/Changes/DomainChangeTypes.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/TreeChange.cs b/src/Umbraco.Core/Services/Changes/TreeChange.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/TreeChange.cs rename to src/Umbraco.Core/Services/Changes/TreeChange.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/TreeChangeExtensions.cs b/src/Umbraco.Core/Services/Changes/TreeChangeExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/TreeChangeExtensions.cs rename to src/Umbraco.Core/Services/Changes/TreeChangeExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/Changes/TreeChangeTypes.cs b/src/Umbraco.Core/Services/Changes/TreeChangeTypes.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Changes/TreeChangeTypes.cs rename to src/Umbraco.Core/Services/Changes/TreeChangeTypes.cs diff --git a/src/Umbraco.Abstractions/Services/ContentServiceExtensions.cs b/src/Umbraco.Core/Services/ContentServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ContentServiceExtensions.cs rename to src/Umbraco.Core/Services/ContentServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/ContentTypeServiceExtensions.cs b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ContentTypeServiceExtensions.cs rename to src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/DateTypeServiceExtensions.cs b/src/Umbraco.Core/Services/DateTypeServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/DateTypeServiceExtensions.cs rename to src/Umbraco.Core/Services/DateTypeServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/IAuditService.cs b/src/Umbraco.Core/Services/IAuditService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IAuditService.cs rename to src/Umbraco.Core/Services/IAuditService.cs diff --git a/src/Umbraco.Abstractions/Services/IConsentService.cs b/src/Umbraco.Core/Services/IConsentService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IConsentService.cs rename to src/Umbraco.Core/Services/IConsentService.cs diff --git a/src/Umbraco.Abstractions/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs similarity index 98% rename from src/Umbraco.Abstractions/Services/IContentService.cs rename to src/Umbraco.Core/Services/IContentService.cs index 2a34fcb898..25428f420b 100644 --- a/src/Umbraco.Abstractions/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -312,13 +312,6 @@ namespace Umbraco.Core.Services /// OperationResult MoveToRecycleBin(IContent content, int userId = Constants.Security.SuperUserId); - /// - /// Empties the recycle bin. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] - OperationResult EmptyRecycleBin(); - /// /// Empties the Recycle Bin by deleting all that resides in the bin /// diff --git a/src/Umbraco.Abstractions/Services/IContentServiceBase.cs b/src/Umbraco.Core/Services/IContentServiceBase.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IContentServiceBase.cs rename to src/Umbraco.Core/Services/IContentServiceBase.cs diff --git a/src/Umbraco.Abstractions/Services/IContentTypeBaseServiceProvider.cs b/src/Umbraco.Core/Services/IContentTypeBaseServiceProvider.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IContentTypeBaseServiceProvider.cs rename to src/Umbraco.Core/Services/IContentTypeBaseServiceProvider.cs diff --git a/src/Umbraco.Abstractions/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IContentTypeService.cs rename to src/Umbraco.Core/Services/IContentTypeService.cs diff --git a/src/Umbraco.Abstractions/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs similarity index 86% rename from src/Umbraco.Abstractions/Services/IContentTypeServiceBase.cs rename to src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 51e5d756eb..4c818aa87c 100644 --- a/src/Umbraco.Abstractions/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -39,6 +39,11 @@ namespace Umbraco.Core.Services int Count(); + /// + /// Returns true or false depending on whether content nodes have been created based on the provided content type id. + /// + bool HasContentNodes(int id); + IEnumerable GetAll(params int[] ids); IEnumerable GetAll(IEnumerable ids); @@ -46,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); @@ -64,6 +72,13 @@ namespace Umbraco.Core.Services /// bool HasContainerInPath(string contentPath); + /// + /// Gets a value indicating whether there is a list view content item in the path. + /// + /// + /// + bool HasContainerInPath(params int[] ids); + Attempt> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); EntityContainer GetContainer(int containerId); diff --git a/src/Umbraco.Web/Services/IDashboardService.cs b/src/Umbraco.Core/Services/IDashboardService.cs similarity index 100% rename from src/Umbraco.Web/Services/IDashboardService.cs rename to src/Umbraco.Core/Services/IDashboardService.cs diff --git a/src/Umbraco.Abstractions/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IDataTypeService.cs rename to src/Umbraco.Core/Services/IDataTypeService.cs diff --git a/src/Umbraco.Abstractions/Services/IDomainService.cs b/src/Umbraco.Core/Services/IDomainService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IDomainService.cs rename to src/Umbraco.Core/Services/IDomainService.cs diff --git a/src/Umbraco.Abstractions/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IEntityService.cs rename to src/Umbraco.Core/Services/IEntityService.cs diff --git a/src/Umbraco.Abstractions/Services/IEntityXmlSerializer.cs b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IEntityXmlSerializer.cs rename to src/Umbraco.Core/Services/IEntityXmlSerializer.cs diff --git a/src/Umbraco.Abstractions/Services/IExternalLoginService.cs b/src/Umbraco.Core/Services/IExternalLoginService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IExternalLoginService.cs rename to src/Umbraco.Core/Services/IExternalLoginService.cs diff --git a/src/Umbraco.Abstractions/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IFileService.cs rename to src/Umbraco.Core/Services/IFileService.cs diff --git a/src/Umbraco.Core/Services/IIdKeyMap.cs b/src/Umbraco.Core/Services/IIdKeyMap.cs new file mode 100644 index 0000000000..05d1b2283d --- /dev/null +++ b/src/Umbraco.Core/Services/IIdKeyMap.cs @@ -0,0 +1,16 @@ +using System; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IIdKeyMap + { + Attempt GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType); + Attempt GetIdForUdi(Udi udi); + Attempt GetUdiForId(int id, UmbracoObjectTypes umbracoObjectType); + Attempt GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType); + void ClearCache(); + void ClearCache(int id); + void ClearCache(Guid key); + } +} diff --git a/src/Umbraco.Abstractions/Services/IKeyValueService.cs b/src/Umbraco.Core/Services/IKeyValueService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IKeyValueService.cs rename to src/Umbraco.Core/Services/IKeyValueService.cs diff --git a/src/Umbraco.Abstractions/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ILocalizationService.cs rename to src/Umbraco.Core/Services/ILocalizationService.cs diff --git a/src/Umbraco.Abstractions/Services/ILocalizedTextService.cs b/src/Umbraco.Core/Services/ILocalizedTextService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ILocalizedTextService.cs rename to src/Umbraco.Core/Services/ILocalizedTextService.cs diff --git a/src/Umbraco.Abstractions/Services/IMacroService.cs b/src/Umbraco.Core/Services/IMacroService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMacroService.cs rename to src/Umbraco.Core/Services/IMacroService.cs diff --git a/src/Umbraco.Abstractions/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs similarity index 98% rename from src/Umbraco.Abstractions/Services/IMediaService.cs rename to src/Umbraco.Core/Services/IMediaService.cs index 6de2765644..c1537adbc8 100644 --- a/src/Umbraco.Abstractions/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -156,13 +156,6 @@ namespace Umbraco.Core.Services /// Id of the User deleting the Media Attempt MoveToRecycleBin(IMedia media, int userId = Constants.Security.SuperUserId); - /// - /// Empties the Recycle Bin by deleting all that resides in the bin - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] - OperationResult EmptyRecycleBin(); - /// /// Empties the Recycle Bin by deleting all that resides in the bin /// diff --git a/src/Umbraco.Abstractions/Services/IMediaTypeService.cs b/src/Umbraco.Core/Services/IMediaTypeService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMediaTypeService.cs rename to src/Umbraco.Core/Services/IMediaTypeService.cs diff --git a/src/Umbraco.Abstractions/Services/IMemberGroupService.cs b/src/Umbraco.Core/Services/IMemberGroupService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMemberGroupService.cs rename to src/Umbraco.Core/Services/IMemberGroupService.cs diff --git a/src/Umbraco.Abstractions/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMemberService.cs rename to src/Umbraco.Core/Services/IMemberService.cs diff --git a/src/Umbraco.Abstractions/Services/IMemberTypeService.cs b/src/Umbraco.Core/Services/IMemberTypeService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMemberTypeService.cs rename to src/Umbraco.Core/Services/IMemberTypeService.cs diff --git a/src/Umbraco.Abstractions/Services/IMembershipMemberService.cs b/src/Umbraco.Core/Services/IMembershipMemberService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMembershipMemberService.cs rename to src/Umbraco.Core/Services/IMembershipMemberService.cs diff --git a/src/Umbraco.Abstractions/Services/IMembershipRoleService.cs b/src/Umbraco.Core/Services/IMembershipRoleService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMembershipRoleService.cs rename to src/Umbraco.Core/Services/IMembershipRoleService.cs diff --git a/src/Umbraco.Abstractions/Services/IMembershipUserService.cs b/src/Umbraco.Core/Services/IMembershipUserService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IMembershipUserService.cs rename to src/Umbraco.Core/Services/IMembershipUserService.cs diff --git a/src/Umbraco.Abstractions/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/INotificationService.cs rename to src/Umbraco.Core/Services/INotificationService.cs diff --git a/src/Umbraco.Abstractions/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IPackagingService.cs rename to src/Umbraco.Core/Services/IPackagingService.cs diff --git a/src/Umbraco.Abstractions/Services/IPropertyValidationService.cs b/src/Umbraco.Core/Services/IPropertyValidationService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IPropertyValidationService.cs rename to src/Umbraco.Core/Services/IPropertyValidationService.cs diff --git a/src/Umbraco.Abstractions/Services/IPublicAccessService.cs b/src/Umbraco.Core/Services/IPublicAccessService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IPublicAccessService.cs rename to src/Umbraco.Core/Services/IPublicAccessService.cs diff --git a/src/Umbraco.Abstractions/Services/IRedirectUrlService.cs b/src/Umbraco.Core/Services/IRedirectUrlService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IRedirectUrlService.cs rename to src/Umbraco.Core/Services/IRedirectUrlService.cs diff --git a/src/Umbraco.Abstractions/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IRelationService.cs rename to src/Umbraco.Core/Services/IRelationService.cs diff --git a/src/Umbraco.Abstractions/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IRuntime.cs rename to src/Umbraco.Core/Services/IRuntime.cs diff --git a/src/Umbraco.Abstractions/Services/IRuntimeState.cs b/src/Umbraco.Core/Services/IRuntimeState.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IRuntimeState.cs rename to src/Umbraco.Core/Services/IRuntimeState.cs diff --git a/src/Umbraco.Web/Services/ISectionService.cs b/src/Umbraco.Core/Services/ISectionService.cs similarity index 100% rename from src/Umbraco.Web/Services/ISectionService.cs rename to src/Umbraco.Core/Services/ISectionService.cs diff --git a/src/Umbraco.Abstractions/Services/IServerRegistrationService.cs b/src/Umbraco.Core/Services/IServerRegistrationService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IServerRegistrationService.cs rename to src/Umbraco.Core/Services/IServerRegistrationService.cs diff --git a/src/Umbraco.Abstractions/Services/IService.cs b/src/Umbraco.Core/Services/IService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IService.cs rename to src/Umbraco.Core/Services/IService.cs diff --git a/src/Umbraco.Abstractions/Services/ITagService.cs b/src/Umbraco.Core/Services/ITagService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ITagService.cs rename to src/Umbraco.Core/Services/ITagService.cs diff --git a/src/Umbraco.Web/Services/ITreeService.cs b/src/Umbraco.Core/Services/ITreeService.cs similarity index 100% rename from src/Umbraco.Web/Services/ITreeService.cs rename to src/Umbraco.Core/Services/ITreeService.cs diff --git a/src/Umbraco.Core/Services/IUpgradeService.cs b/src/Umbraco.Core/Services/IUpgradeService.cs new file mode 100644 index 0000000000..70bbb68085 --- /dev/null +++ b/src/Umbraco.Core/Services/IUpgradeService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Semver; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IUpgradeService + { + Task CheckUpgrade(SemVersion version); + } +} diff --git a/src/Umbraco.Abstractions/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/IUserService.cs rename to src/Umbraco.Core/Services/IUserService.cs diff --git a/src/Umbraco.Abstractions/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/LocalizedTextServiceExtensions.cs rename to src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/MediaServiceExtensions.cs b/src/Umbraco.Core/Services/MediaServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/MediaServiceExtensions.cs rename to src/Umbraco.Core/Services/MediaServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/MoveOperationStatusType.cs b/src/Umbraco.Core/Services/MoveOperationStatusType.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/MoveOperationStatusType.cs rename to src/Umbraco.Core/Services/MoveOperationStatusType.cs diff --git a/src/Umbraco.Abstractions/Services/OperationResult.cs b/src/Umbraco.Core/Services/OperationResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/OperationResult.cs rename to src/Umbraco.Core/Services/OperationResult.cs diff --git a/src/Umbraco.Abstractions/Services/OperationResultType.cs b/src/Umbraco.Core/Services/OperationResultType.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/OperationResultType.cs rename to src/Umbraco.Core/Services/OperationResultType.cs diff --git a/src/Umbraco.Abstractions/Services/Ordering.cs b/src/Umbraco.Core/Services/Ordering.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/Ordering.cs rename to src/Umbraco.Core/Services/Ordering.cs diff --git a/src/Umbraco.Abstractions/Services/PublicAccessServiceExtensions.cs b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/PublicAccessServiceExtensions.cs rename to src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs diff --git a/src/Umbraco.Abstractions/Services/PublishResult.cs b/src/Umbraco.Core/Services/PublishResult.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/PublishResult.cs rename to src/Umbraco.Core/Services/PublishResult.cs diff --git a/src/Umbraco.Abstractions/Services/PublishResultType.cs b/src/Umbraco.Core/Services/PublishResultType.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/PublishResultType.cs rename to src/Umbraco.Core/Services/PublishResultType.cs diff --git a/src/Umbraco.Web/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs similarity index 98% rename from src/Umbraco.Web/Services/SectionService.cs rename to src/Umbraco.Core/Services/SectionService.cs index 2696186301..7f97b7b71a 100644 --- a/src/Umbraco.Web/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -4,7 +4,6 @@ using System.Linq; using Umbraco.Core.Models.Sections; using Umbraco.Core.Services; using Umbraco.Web.Sections; -using Umbraco.Web.Trees; namespace Umbraco.Web.Services { diff --git a/src/Umbraco.Abstractions/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/ServiceContext.cs rename to src/Umbraco.Core/Services/ServiceContext.cs diff --git a/src/Umbraco.Web/Services/TreeService.cs b/src/Umbraco.Core/Services/TreeService.cs similarity index 100% rename from src/Umbraco.Web/Services/TreeService.cs rename to src/Umbraco.Core/Services/TreeService.cs diff --git a/src/Umbraco.Core/Services/UpgradeService.cs b/src/Umbraco.Core/Services/UpgradeService.cs new file mode 100644 index 0000000000..ead5270b41 --- /dev/null +++ b/src/Umbraco.Core/Services/UpgradeService.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Semver; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Services; + +namespace Umbraco.Core +{ + public class UpgradeService : IUpgradeService + { + private readonly IUpgradeCheckRepository _upgradeCheckRepository; + + public UpgradeService(IUpgradeCheckRepository upgradeCheckRepository) + { + _upgradeCheckRepository = upgradeCheckRepository; + } + + public async Task CheckUpgrade(SemVersion version) + { + return await _upgradeCheckRepository.CheckUpgradeAsync(version); + } + } +} diff --git a/src/Umbraco.Abstractions/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Services/UserServiceExtensions.cs rename to src/Umbraco.Core/Services/UserServiceExtensions.cs diff --git a/src/Umbraco.Core/Session/ISessionManager.cs b/src/Umbraco.Core/Session/ISessionManager.cs new file mode 100644 index 0000000000..f3a47202ee --- /dev/null +++ b/src/Umbraco.Core/Session/ISessionManager.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Session +{ + public interface ISessionManager + { + object GetSessionValue(string sessionName); + void SetSessionValue(string sessionName, object value); + } +} diff --git a/src/Umbraco.Abstractions/Settable.cs b/src/Umbraco.Core/Settable.cs similarity index 100% rename from src/Umbraco.Abstractions/Settable.cs rename to src/Umbraco.Core/Settable.cs diff --git a/src/Umbraco.Abstractions/SimpleMainDom.cs b/src/Umbraco.Core/SimpleMainDom.cs similarity index 100% rename from src/Umbraco.Abstractions/SimpleMainDom.cs rename to src/Umbraco.Core/SimpleMainDom.cs diff --git a/src/Umbraco.Abstractions/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/StringExtensions.cs rename to src/Umbraco.Core/StringExtensions.cs diff --git a/src/Umbraco.Abstractions/StringUdi.cs b/src/Umbraco.Core/StringUdi.cs similarity index 100% rename from src/Umbraco.Abstractions/StringUdi.cs rename to src/Umbraco.Core/StringUdi.cs diff --git a/src/Umbraco.Abstractions/Strings/CleanStringType.cs b/src/Umbraco.Core/Strings/CleanStringType.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/CleanStringType.cs rename to src/Umbraco.Core/Strings/CleanStringType.cs diff --git a/src/Umbraco.Abstractions/Strings/Css/StylesheetHelper.cs b/src/Umbraco.Core/Strings/Css/StylesheetHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/Css/StylesheetHelper.cs rename to src/Umbraco.Core/Strings/Css/StylesheetHelper.cs diff --git a/src/Umbraco.Abstractions/Strings/Css/StylesheetRule.cs b/src/Umbraco.Core/Strings/Css/StylesheetRule.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/Css/StylesheetRule.cs rename to src/Umbraco.Core/Strings/Css/StylesheetRule.cs diff --git a/src/Umbraco.Abstractions/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs similarity index 99% rename from src/Umbraco.Abstractions/Strings/DefaultShortStringHelper.cs rename to src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 6361186604..c3e7fa85c3 100644 --- a/src/Umbraco.Abstractions/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Strings { #region Ctor, consts and vars - public DefaultShortStringHelper(IUmbracoSettingsSection settings) + public DefaultShortStringHelper(IRequestHandlerSettings settings) { _config = new DefaultShortStringHelperConfig().WithDefault(settings); } @@ -619,6 +619,6 @@ namespace Umbraco.Core.Strings return new string(output, 0, opos); } - #endregion + #endregion } } diff --git a/src/Umbraco.Abstractions/Strings/DefaultShortStringHelperConfig.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs similarity index 96% rename from src/Umbraco.Abstractions/Strings/DefaultShortStringHelperConfig.cs rename to src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs index e75e00defb..25ee781ae9 100644 --- a/src/Umbraco.Abstractions/Strings/DefaultShortStringHelperConfig.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Strings public string DefaultCulture { get; set; } = ""; // invariant public Dictionary UrlReplaceCharacters { get; set; } - + public DefaultShortStringHelperConfig WithConfig(Config config) { return WithConfig(DefaultCulture, CleanStringType.RoleMask, config); @@ -57,16 +57,16 @@ namespace Umbraco.Core.Strings /// Sets the default configuration. /// /// The short string helper. - public DefaultShortStringHelperConfig WithDefault(IUmbracoSettingsSection umbracoSettings) + public DefaultShortStringHelperConfig WithDefault(IRequestHandlerSettings requestHandlerSettings) { - UrlReplaceCharacters = umbracoSettings.RequestHandler.CharCollection + UrlReplaceCharacters = requestHandlerSettings.CharCollection .Where(x => string.IsNullOrEmpty(x.Char) == false) .ToDictionary(x => x.Char, x => x.Replacement); var urlSegmentConvertTo = CleanStringType.Utf8; - if (umbracoSettings.RequestHandler.ConvertUrlsToAscii) + if (requestHandlerSettings.ConvertUrlsToAscii) urlSegmentConvertTo = CleanStringType.Ascii; - if (umbracoSettings.RequestHandler.TryConvertUrlsToAscii) + if (requestHandlerSettings.TryConvertUrlsToAscii) urlSegmentConvertTo = CleanStringType.TryAscii; return WithConfig(CleanStringType.UrlSegment, new Config diff --git a/src/Umbraco.Abstractions/Strings/DefaultUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/DefaultUrlSegmentProvider.cs rename to src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs diff --git a/src/Umbraco.Abstractions/Strings/Diff.cs b/src/Umbraco.Core/Strings/Diff.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/Diff.cs rename to src/Umbraco.Core/Strings/Diff.cs diff --git a/src/Umbraco.Abstractions/Strings/HtmlEncodedString.cs b/src/Umbraco.Core/Strings/HtmlEncodedString.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/HtmlEncodedString.cs rename to src/Umbraco.Core/Strings/HtmlEncodedString.cs diff --git a/src/Umbraco.Abstractions/Strings/IHtmlEncodedString.cs b/src/Umbraco.Core/Strings/IHtmlEncodedString.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/IHtmlEncodedString.cs rename to src/Umbraco.Core/Strings/IHtmlEncodedString.cs diff --git a/src/Umbraco.Abstractions/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/IShortStringHelper.cs rename to src/Umbraco.Core/Strings/IShortStringHelper.cs diff --git a/src/Umbraco.Abstractions/Strings/IUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/IUrlSegmentProvider.cs rename to src/Umbraco.Core/Strings/IUrlSegmentProvider.cs diff --git a/src/Umbraco.Abstractions/Strings/PathUtility.cs b/src/Umbraco.Core/Strings/PathUtility.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/PathUtility.cs rename to src/Umbraco.Core/Strings/PathUtility.cs diff --git a/src/Umbraco.Abstractions/Strings/UrlSegmentProviderCollection.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/UrlSegmentProviderCollection.cs rename to src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs diff --git a/src/Umbraco.Abstractions/Strings/UrlSegmentProviderCollectionBuilder.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/UrlSegmentProviderCollectionBuilder.cs rename to src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs diff --git a/src/Umbraco.Abstractions/Strings/Utf8ToAsciiConverter.cs b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/Strings/Utf8ToAsciiConverter.cs rename to src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs diff --git a/src/Umbraco.Abstractions/Sync/DatabaseServerMessengerOptions.cs b/src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/DatabaseServerMessengerOptions.cs rename to src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs diff --git a/src/Umbraco.Abstractions/Sync/DatabaseServerRegistrar.cs b/src/Umbraco.Core/Sync/DatabaseServerRegistrar.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/DatabaseServerRegistrar.cs rename to src/Umbraco.Core/Sync/DatabaseServerRegistrar.cs diff --git a/src/Umbraco.Abstractions/Sync/DatabaseServerRegistrarOptions.cs b/src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/DatabaseServerRegistrarOptions.cs rename to src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs diff --git a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs new file mode 100644 index 0000000000..d8ec82818d --- /dev/null +++ b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core.Sync +{ + /// + /// An implementation that works by storing messages in the database. + /// + public interface IBatchedDatabaseServerMessenger : IDatabaseServerMessenger + { + void FlushBatch(); + DatabaseServerMessengerOptions Options { get; } + void Startup(); + } +} diff --git a/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs new file mode 100644 index 0000000000..a49cfdd023 --- /dev/null +++ b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Sync +{ + public interface IDatabaseServerMessenger: IServerMessenger + { + void Sync(); + } +} diff --git a/src/Umbraco.Abstractions/Sync/IServerAddress.cs b/src/Umbraco.Core/Sync/IServerAddress.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/IServerAddress.cs rename to src/Umbraco.Core/Sync/IServerAddress.cs diff --git a/src/Umbraco.Abstractions/Sync/IServerMessenger.cs b/src/Umbraco.Core/Sync/IServerMessenger.cs similarity index 91% rename from src/Umbraco.Abstractions/Sync/IServerMessenger.cs rename to src/Umbraco.Core/Sync/IServerMessenger.cs index 37d08723f4..b8300b2d6d 100644 --- a/src/Umbraco.Abstractions/Sync/IServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IServerMessenger.cs @@ -17,13 +17,6 @@ namespace Umbraco.Core.Sync /// The notification content. void PerformRefresh(ICacheRefresher refresher, TPayload[] payload); - /// - /// Notifies the distributed cache, for a specified . - /// - /// The ICacheRefresher. - /// The notification content. - void PerformRefresh(ICacheRefresher refresher, string jsonPayload); - /// /// Notifies the distributed cache of specified item invalidation, for a specified . /// diff --git a/src/Umbraco.Abstractions/Sync/IServerRegistrar.cs b/src/Umbraco.Core/Sync/IServerRegistrar.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/IServerRegistrar.cs rename to src/Umbraco.Core/Sync/IServerRegistrar.cs diff --git a/src/Umbraco.Abstractions/Sync/MessageType.cs b/src/Umbraco.Core/Sync/MessageType.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/MessageType.cs rename to src/Umbraco.Core/Sync/MessageType.cs diff --git a/src/Umbraco.Abstractions/Sync/RefreshMethodType.cs b/src/Umbraco.Core/Sync/RefreshMethodType.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/RefreshMethodType.cs rename to src/Umbraco.Core/Sync/RefreshMethodType.cs diff --git a/src/Umbraco.Abstractions/Sync/ServerRole.cs b/src/Umbraco.Core/Sync/ServerRole.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/ServerRole.cs rename to src/Umbraco.Core/Sync/ServerRole.cs diff --git a/src/Umbraco.Abstractions/Sync/SingleServerRegistrar.cs b/src/Umbraco.Core/Sync/SingleServerRegistrar.cs similarity index 100% rename from src/Umbraco.Abstractions/Sync/SingleServerRegistrar.cs rename to src/Umbraco.Core/Sync/SingleServerRegistrar.cs diff --git a/src/Umbraco.Abstractions/SystemLock.cs b/src/Umbraco.Core/SystemLock.cs similarity index 100% rename from src/Umbraco.Abstractions/SystemLock.cs rename to src/Umbraco.Core/SystemLock.cs diff --git a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs b/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs similarity index 81% rename from src/Umbraco.Web/Templates/HtmlImageSourceParser.cs rename to src/Umbraco.Core/Templates/HtmlImageSourceParser.cs index 9019e6c0bc..79d65e0829 100644 --- a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs +++ b/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs @@ -1,24 +1,34 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using Umbraco.Core; +using Umbraco.Web.Routing; namespace Umbraco.Web.Templates { public sealed class HtmlImageSourceParser { - public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor) + public HtmlImageSourceParser(Func getMediaUrl) { - _umbracoContextAccessor = umbracoContextAccessor; + this._getMediaUrl = getMediaUrl; } - private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", + private readonly IPublishedUrlProvider _publishedUrlProvider; + + public HtmlImageSourceParser(IPublishedUrlProvider publishedUrlProvider) + { + _publishedUrlProvider = publishedUrlProvider; + } + + private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)((?:\?[^""]*)?""[^>]*data-udi="")([^""]*)(""[^>]*>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private Func _getMediaUrl; + /// /// Parses out media UDIs from an html string based on 'data-udi' html attributes /// @@ -45,13 +55,8 @@ namespace Umbraco.Web.Templates /// Umbraco image tags are identified by their data-udi attributes public string EnsureImageSources(string text) { - // don't attempt to proceed without a context - if (_umbracoContextAccessor?.UmbracoContext?.UrlProvider == null) - { - return text; - } - - var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; + if(_getMediaUrl == null) + _getMediaUrl = (guid) => _publishedUrlProvider.GetMediaUrl(guid); return ResolveImgPattern.Replace(text, match => { @@ -66,7 +71,7 @@ namespace Umbraco.Web.Templates { return match.Value; } - var mediaUrl = urlProvider.GetMediaUrl(guidUdi.Guid); + var mediaUrl = _getMediaUrl(guidUdi.Guid); if (mediaUrl == null) { // image does not exist - we could choose to remove the image entirely here (return empty string), @@ -86,7 +91,5 @@ namespace Umbraco.Web.Templates public string RemoveImageSources(string text) // see comment in ResolveMediaFromTextString for group reference => ResolveImgPattern.Replace(text, "$1$3$4$5"); - - } } diff --git a/src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs b/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs similarity index 89% rename from src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs rename to src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs index ef89088a7a..2ecd61f7d2 100644 --- a/src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs +++ b/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; namespace Umbraco.Web.Templates @@ -18,13 +16,15 @@ namespace Umbraco.Web.Templates RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; - public HtmlLocalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) + public HtmlLocalLinkParser(IUmbracoContextAccessor umbracoContextAccessor, IPublishedUrlProvider publishedUrlProvider) { _umbracoContextAccessor = umbracoContextAccessor; + _publishedUrlProvider = publishedUrlProvider; } - internal IEnumerable FindUdisFromLocalLinks(string text) + public IEnumerable FindUdisFromLocalLinks(string text) { foreach ((int? intId, GuidUdi udi, string tagValue) in FindLocalLinkIds(text)) { @@ -64,7 +64,6 @@ namespace Umbraco.Web.Templates if (_umbracoContextAccessor.UmbracoContext == null) throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext"); - var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; foreach((int? intId, GuidUdi udi, string tagValue) in FindLocalLinkIds(text)) { @@ -72,9 +71,9 @@ namespace Umbraco.Web.Templates { var newLink = "#"; if (udi.EntityType == Constants.UdiEntityType.Document) - newLink = urlProvider.GetUrl(udi.Guid); + newLink = _publishedUrlProvider.GetUrl(udi.Guid); else if (udi.EntityType == Constants.UdiEntityType.Media) - newLink = urlProvider.GetMediaUrl(udi.Guid); + newLink = _publishedUrlProvider.GetMediaUrl(udi.Guid); if (newLink == null) newLink = "#"; @@ -83,7 +82,7 @@ namespace Umbraco.Web.Templates } else if (intId.HasValue) { - var newLink = urlProvider.GetUrl(intId.Value); + var newLink = _publishedUrlProvider.GetUrl(intId.Value); text = text.Replace(tagValue, "href=\"" + newLink); } } diff --git a/src/Umbraco.Web/Templates/HtmlUrlParser.cs b/src/Umbraco.Core/Templates/HtmlUrlParser.cs similarity index 89% rename from src/Umbraco.Web/Templates/HtmlUrlParser.cs rename to src/Umbraco.Core/Templates/HtmlUrlParser.cs index a2fdb93292..566fce8b87 100644 --- a/src/Umbraco.Web/Templates/HtmlUrlParser.cs +++ b/src/Umbraco.Core/Templates/HtmlUrlParser.cs @@ -7,16 +7,16 @@ namespace Umbraco.Web.Templates { public sealed class HtmlUrlParser { - private readonly IContentSection _contentSection; + private readonly IContentSettings _contentSettings; private readonly IIOHelper _ioHelper; private readonly IProfilingLogger _logger; private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - public HtmlUrlParser(IContentSection contentSection, IProfilingLogger logger, IIOHelper ioHelper) + public HtmlUrlParser(IContentSettings contentSettings, IProfilingLogger logger, IIOHelper ioHelper) { - _contentSection = contentSection; + _contentSettings = contentSettings; _ioHelper = ioHelper; _logger = logger; } @@ -32,7 +32,7 @@ namespace Umbraco.Web.Templates /// public string EnsureUrls(string text) { - if (_contentSection.ResolveUrlsFromTextString == false) return text; + if (_contentSettings.ResolveUrlsFromTextString == false) return text; using (var timer = _logger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { diff --git a/src/Umbraco.Web/Templates/ITemplateRenderer.cs b/src/Umbraco.Core/Templates/ITemplateRenderer.cs similarity index 100% rename from src/Umbraco.Web/Templates/ITemplateRenderer.cs rename to src/Umbraco.Core/Templates/ITemplateRenderer.cs diff --git a/src/Umbraco.Abstractions/ThreadExtensions.cs b/src/Umbraco.Core/ThreadExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/ThreadExtensions.cs rename to src/Umbraco.Core/ThreadExtensions.cs diff --git a/src/Umbraco.Abstractions/Tour/BackOfficeTourFilter.cs b/src/Umbraco.Core/Tour/BackOfficeTourFilter.cs similarity index 100% rename from src/Umbraco.Abstractions/Tour/BackOfficeTourFilter.cs rename to src/Umbraco.Core/Tour/BackOfficeTourFilter.cs diff --git a/src/Umbraco.Abstractions/Tour/TourFilterCollection.cs b/src/Umbraco.Core/Tour/TourFilterCollection.cs similarity index 100% rename from src/Umbraco.Abstractions/Tour/TourFilterCollection.cs rename to src/Umbraco.Core/Tour/TourFilterCollection.cs diff --git a/src/Umbraco.Abstractions/Tour/TourFilterCollectionBuilder.cs b/src/Umbraco.Core/Tour/TourFilterCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Abstractions/Tour/TourFilterCollectionBuilder.cs rename to src/Umbraco.Core/Tour/TourFilterCollectionBuilder.cs diff --git a/src/Umbraco.Web/Trees/ActionUrlMethod.cs b/src/Umbraco.Core/Trees/ActionUrlMethod.cs similarity index 100% rename from src/Umbraco.Web/Trees/ActionUrlMethod.cs rename to src/Umbraco.Core/Trees/ActionUrlMethod.cs diff --git a/src/Umbraco.Web/Trees/CoreTreeAttribute.cs b/src/Umbraco.Core/Trees/CoreTreeAttribute.cs similarity index 100% rename from src/Umbraco.Web/Trees/CoreTreeAttribute.cs rename to src/Umbraco.Core/Trees/CoreTreeAttribute.cs diff --git a/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs b/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs new file mode 100644 index 0000000000..dc4c53bac9 --- /dev/null +++ b/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs @@ -0,0 +1,17 @@ +using Umbraco.Web.Models.Trees; + +namespace Umbraco.Web.Trees +{ + + /// + /// Represents a factory to create . + /// + public interface IMenuItemCollectionFactory + { + /// + /// Creates an empty . + /// + /// An empty . + MenuItemCollection Create(); + } +} diff --git a/src/Umbraco.Web/Trees/ITree.cs b/src/Umbraco.Core/Trees/ITree.cs similarity index 100% rename from src/Umbraco.Web/Trees/ITree.cs rename to src/Umbraco.Core/Trees/ITree.cs diff --git a/src/Umbraco.Web/Models/Trees/MenuItemCollection.cs b/src/Umbraco.Core/Trees/MenuItemCollection.cs similarity index 72% rename from src/Umbraco.Web/Models/Trees/MenuItemCollection.cs rename to src/Umbraco.Core/Trees/MenuItemCollection.cs index e1fd2218e0..84d46c0d11 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemCollection.cs +++ b/src/Umbraco.Core/Trees/MenuItemCollection.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using Umbraco.Web.Actions; namespace Umbraco.Web.Models.Trees { @@ -9,17 +10,16 @@ namespace Umbraco.Web.Models.Trees [DataContract(Name = "menuItems", Namespace = "")] public class MenuItemCollection { - public static MenuItemCollection Empty => new MenuItemCollection(); + private readonly MenuItemList _menuItems; - private readonly MenuItemList _menuItems = new MenuItemList(); - - public MenuItemCollection() + public MenuItemCollection(ActionCollection actionCollection) { + _menuItems = new MenuItemList(actionCollection); } - public MenuItemCollection(IEnumerable items) + public MenuItemCollection(ActionCollection actionCollection, IEnumerable items) { - _menuItems = new MenuItemList(items); + _menuItems = new MenuItemList(actionCollection, items); } /// @@ -39,8 +39,5 @@ namespace Umbraco.Web.Models.Trees { get { return _menuItems; } } - - - } } diff --git a/src/Umbraco.Core/Trees/MenuItemCollectionFactory.cs b/src/Umbraco.Core/Trees/MenuItemCollectionFactory.cs new file mode 100644 index 0000000000..0a8ea000e3 --- /dev/null +++ b/src/Umbraco.Core/Trees/MenuItemCollectionFactory.cs @@ -0,0 +1,21 @@ +using Umbraco.Web.Actions; +using Umbraco.Web.Models.Trees; + +namespace Umbraco.Web.Trees +{ + public class MenuItemCollectionFactory: IMenuItemCollectionFactory + { + private readonly ActionCollection _actionCollection; + + public MenuItemCollectionFactory(ActionCollection actionCollection) + { + _actionCollection = actionCollection; + } + + public MenuItemCollection Create() + { + return new MenuItemCollection(_actionCollection); + } + + } +} diff --git a/src/Umbraco.Core/Trees/MenuItemList.cs b/src/Umbraco.Core/Trees/MenuItemList.cs new file mode 100644 index 0000000000..546fa0390f --- /dev/null +++ b/src/Umbraco.Core/Trees/MenuItemList.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using Umbraco.Core.Services; +using Umbraco.Web.Actions; + +namespace Umbraco.Web.Models.Trees +{ + /// + /// A custom menu list + /// + /// + /// NOTE: We need a sub collection to the MenuItemCollection object due to how json serialization works. + /// + public class MenuItemList : List + { + private readonly ActionCollection _actionCollection; + + public MenuItemList(ActionCollection actionCollection) + { + _actionCollection = actionCollection; + } + + public MenuItemList(ActionCollection actionCollection, IEnumerable items) + : base(items) + { + _actionCollection = actionCollection; + } + + /// + /// Adds a menu item with a dictionary which is merged to the AdditionalData bag + /// + /// + /// + /// The used to localize the action name based on its alias + /// Whether or not this action opens a dialog + public MenuItem Add(ILocalizedTextService textService, bool hasSeparator = false, bool opensDialog = false) + where T : IAction + { + var item = CreateMenuItem(textService, hasSeparator, opensDialog); + if (item != null) + { + Add(item); + return item; + } + return null; + } + + private MenuItem CreateMenuItem(ILocalizedTextService textService, bool hasSeparator = false, bool opensDialog = false) + where T : IAction + { + var item = _actionCollection.GetAction(); + if (item == null) return null; + + var menuItem = new MenuItem(item, textService.Localize($"actions/{item.Alias}")) + { + SeparatorBefore = hasSeparator, + OpensDialog = opensDialog + }; + + return menuItem; + } + + } +} diff --git a/src/Umbraco.Web/Trees/Tree.cs b/src/Umbraco.Core/Trees/Tree.cs similarity index 100% rename from src/Umbraco.Web/Trees/Tree.cs rename to src/Umbraco.Core/Trees/Tree.cs diff --git a/src/Umbraco.Web/Trees/TreeCollection.cs b/src/Umbraco.Core/Trees/TreeCollection.cs similarity index 100% rename from src/Umbraco.Web/Trees/TreeCollection.cs rename to src/Umbraco.Core/Trees/TreeCollection.cs diff --git a/src/Umbraco.Web/Trees/TreeUse.cs b/src/Umbraco.Core/Trees/TreeUse.cs similarity index 100% rename from src/Umbraco.Web/Trees/TreeUse.cs rename to src/Umbraco.Core/Trees/TreeUse.cs diff --git a/src/Umbraco.Abstractions/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs similarity index 85% rename from src/Umbraco.Abstractions/TypeExtensions.cs rename to src/Umbraco.Core/TypeExtensions.cs index c656aa6373..15f3e56924 100644 --- a/src/Umbraco.Abstractions/TypeExtensions.cs +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using Umbraco.Core.Composing; +using Umbraco.Core.Strings; namespace Umbraco.Core { @@ -427,5 +428,54 @@ namespace Umbraco.Core return type.GetCustomAttributes(typeof (T), inherited).OfType(); } + /// + /// Tries to return a value based on a property name for an object but ignores case sensitivity + /// + /// + /// + /// + /// + /// + /// + /// Currently this will only work for ProperCase and camelCase properties, see the TODO below to enable complete case insensitivity + /// + internal static Attempt GetMemberIgnoreCase(this Type type, IShortStringHelper shortStringHelper, object target, string memberName) + { + Func> getMember = + memberAlias => + { + try + { + return Attempt.Succeed( + type.InvokeMember(memberAlias, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public, + null, + target, + null)); + } + catch (MissingMethodException ex) + { + return Attempt.Fail(ex); + } + }; + + //try with the current casing + var attempt = getMember(memberName); + if (attempt.Success == false) + { + //if we cannot get with the current alias, try changing it's case + attempt = memberName[0].IsUpperCase() + ? getMember(memberName.ToCleanString(shortStringHelper, CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.CamelCase)) + : getMember(memberName.ToCleanString(shortStringHelper, CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.PascalCase)); + + // TODO: If this still fails then we should get a list of properties from the object and then compare - doing the above without listing + // all properties will surely be faster than using reflection to get ALL properties first and then query against them. + } + + return attempt; + } + } } diff --git a/src/Umbraco.Abstractions/TypeLoaderExtensions.cs b/src/Umbraco.Core/TypeLoaderExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/TypeLoaderExtensions.cs rename to src/Umbraco.Core/TypeLoaderExtensions.cs diff --git a/src/Umbraco.Abstractions/Udi.cs b/src/Umbraco.Core/Udi.cs similarity index 97% rename from src/Umbraco.Abstractions/Udi.cs rename to src/Umbraco.Core/Udi.cs index 80e58b2f86..8a0ce83811 100644 --- a/src/Umbraco.Abstractions/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -11,8 +11,8 @@ namespace Umbraco.Core /// An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document. [TypeConverter(typeof(UdiTypeConverter))] public abstract class Udi : IComparable - { - public Uri UriValue { get; } + { + public Uri UriValue { get; } /// /// Initializes a new instance of the Udi class. @@ -35,7 +35,7 @@ namespace Umbraco.Core UriValue = uriValue; } - + /// /// Gets the entity type part of the identifier. @@ -54,7 +54,7 @@ namespace Umbraco.Core return UriValue.AbsoluteUri; } - + /// /// Creates a root Udi for an entity type. @@ -114,7 +114,7 @@ namespace Umbraco.Core if (udiType == UdiType.GuidUdi) return new GuidUdi(uri); - if (udiType == UdiType.GuidUdi) + if (udiType == UdiType.StringUdi) return new StringUdi(uri); throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri)); @@ -166,6 +166,6 @@ namespace Umbraco.Core return (udi1 == udi2) == false; } - + } } diff --git a/src/Umbraco.Abstractions/UdiDefinitionAttribute.cs b/src/Umbraco.Core/UdiDefinitionAttribute.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiDefinitionAttribute.cs rename to src/Umbraco.Core/UdiDefinitionAttribute.cs diff --git a/src/Umbraco.Abstractions/UdiEntityTypeHelper.cs b/src/Umbraco.Core/UdiEntityTypeHelper.cs similarity index 95% rename from src/Umbraco.Abstractions/UdiEntityTypeHelper.cs rename to src/Umbraco.Core/UdiEntityTypeHelper.cs index 12e934b9ec..39ee28488f 100644 --- a/src/Umbraco.Abstractions/UdiEntityTypeHelper.cs +++ b/src/Umbraco.Core/UdiEntityTypeHelper.cs @@ -38,8 +38,6 @@ namespace Umbraco.Core return Constants.UdiEntityType.MemberType; case UmbracoObjectTypes.MemberGroup: return Constants.UdiEntityType.MemberGroup; - case UmbracoObjectTypes.Stylesheet: - return Constants.UdiEntityType.Stylesheet; case UmbracoObjectTypes.RelationType: return Constants.UdiEntityType.RelationType; case UmbracoObjectTypes.FormsForm: @@ -86,8 +84,6 @@ namespace Umbraco.Core return UmbracoObjectTypes.MemberType; case Constants.UdiEntityType.MemberGroup: return UmbracoObjectTypes.MemberGroup; - case Constants.UdiEntityType.Stylesheet: - return UmbracoObjectTypes.Stylesheet; case Constants.UdiEntityType.RelationType: return UmbracoObjectTypes.RelationType; case Constants.UdiEntityType.FormsForm: diff --git a/src/Umbraco.Abstractions/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiGetterExtensions.cs rename to src/Umbraco.Core/UdiGetterExtensions.cs diff --git a/src/Umbraco.Abstractions/UdiParser.cs b/src/Umbraco.Core/UdiParser.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiParser.cs rename to src/Umbraco.Core/UdiParser.cs diff --git a/src/Umbraco.Abstractions/UdiParserServiceConnectors.cs b/src/Umbraco.Core/UdiParserServiceConnectors.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiParserServiceConnectors.cs rename to src/Umbraco.Core/UdiParserServiceConnectors.cs diff --git a/src/Umbraco.Abstractions/UdiRange.cs b/src/Umbraco.Core/UdiRange.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiRange.cs rename to src/Umbraco.Core/UdiRange.cs diff --git a/src/Umbraco.Abstractions/UdiType.cs b/src/Umbraco.Core/UdiType.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiType.cs rename to src/Umbraco.Core/UdiType.cs diff --git a/src/Umbraco.Abstractions/UdiTypeConverter.cs b/src/Umbraco.Core/UdiTypeConverter.cs similarity index 100% rename from src/Umbraco.Abstractions/UdiTypeConverter.cs rename to src/Umbraco.Core/UdiTypeConverter.cs diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj old mode 100755 new mode 100644 index 28e370ab66..c083cccf8f --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1,178 +1,28 @@ - - - - - v4.7.2 - false - {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} - Library - Umbraco.Core - Umbraco.Core - ..\ - - $(AdditionalFileItemNames);Content - - - true - portable - false - bin\Debug\ - TRACE;DEBUG - prompt - 4 - false - latest - - - portable - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Core.xml - false - latest - - - - - - - - - - - - - - - - - - - - - - 1.0.0-beta2-19324-01 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - 3.3.0 - runtime; build; native; contentfiles; analyzers - all - - - 1.4.0 - - - 1.0.0 - - - 4.6.0 - - - 1.0.5 - runtime; build; native; contentfiles; analyzers - all - - - - - - 2.2.2 - - - 5.2.7 - - - 4.0.1 - - - - - - 2.9.0 - - - 2.0.1 - - - 3.1.0 - - - 2.0.0 - - - 1.1.0 - - - 1.0.3 - - - 2.2.2 - - - 4.1.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\SolutionInfo.cs - - - - - - - - - - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - - - {3ae7bf57-966b-45a5-910a-954d7c554441} - Umbraco.Infrastructure - - - - \ No newline at end of file + + + + netstandard2.0 + 8 + Umbraco.Core + 9.0.0 + 9.0.0 + 9.0.0 + Umbraco CMS + + + + + + + + + + + + <_Parameter1>Umbraco.Tests + + + <_Parameter1>Umbraco.Tests.Benchmarks + + + diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs b/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs similarity index 100% rename from src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs rename to src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs 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.Web/UmbracoContextReference.cs b/src/Umbraco.Core/UmbracoContextReference.cs similarity index 90% rename from src/Umbraco.Web/UmbracoContextReference.cs rename to src/Umbraco.Core/UmbracoContextReference.cs index 6c4ac7e54a..bd021494e6 100644 --- a/src/Umbraco.Web/UmbracoContextReference.cs +++ b/src/Umbraco.Core/UmbracoContextReference.cs @@ -20,18 +20,18 @@ namespace Umbraco.Web /// /// Initializes a new instance of the class. /// - internal UmbracoContextReference(UmbracoContext umbracoContext, bool isRoot, IUmbracoContextAccessor umbracoContextAccessor) + internal UmbracoContextReference(IUmbracoContext umbracoContext, bool isRoot, IUmbracoContextAccessor umbracoContextAccessor) { - UmbracoContext = umbracoContext; IsRoot = isRoot; + UmbracoContext = umbracoContext; _umbracoContextAccessor = umbracoContextAccessor; } /// /// Gets the . /// - public UmbracoContext UmbracoContext { get; } + public IUmbracoContext UmbracoContext { get; } /// /// Gets a value indicating whether the reference is a root reference. diff --git a/src/Umbraco.Abstractions/UnknownTypeUdi.cs b/src/Umbraco.Core/UnknownTypeUdi.cs similarity index 100% rename from src/Umbraco.Abstractions/UnknownTypeUdi.cs rename to src/Umbraco.Core/UnknownTypeUdi.cs diff --git a/src/Umbraco.Core/UpgradeResult.cs b/src/Umbraco.Core/UpgradeResult.cs new file mode 100644 index 0000000000..a27f6bb6a3 --- /dev/null +++ b/src/Umbraco.Core/UpgradeResult.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Core.Models +{ + public class UpgradeResult + { + public string UpgradeType { get; } + public string Comment { get; } + public string UpgradeUrl { get; } + + public UpgradeResult(string upgradeType, string comment, string upgradeUrl) + { + UpgradeType = upgradeType; + Comment = comment; + UpgradeUrl = upgradeUrl; + } + } +} diff --git a/src/Umbraco.Abstractions/UpgradeableReadLock.cs b/src/Umbraco.Core/UpgradeableReadLock.cs similarity index 100% rename from src/Umbraco.Abstractions/UpgradeableReadLock.cs rename to src/Umbraco.Core/UpgradeableReadLock.cs diff --git a/src/Umbraco.Abstractions/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs similarity index 94% rename from src/Umbraco.Abstractions/UriExtensions.cs rename to src/Umbraco.Core/UriExtensions.cs index 94ef3b46fc..4321aefd7f 100644 --- a/src/Umbraco.Abstractions/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using Umbraco.Composing; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -41,7 +40,7 @@ namespace Umbraco.Core /// But if we've got this far we'll just have to assume it's front-end anyways. /// /// - internal static bool IsBackOfficeRequest(this Uri url, string applicationPath, IGlobalSettings globalSettings, IIOHelper ioHelper) + internal static bool IsBackOfficeRequest(this Uri url, string applicationPath, IIOHelper ioHelper) { applicationPath = applicationPath ?? string.Empty; @@ -50,11 +49,11 @@ namespace Umbraco.Core var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/'); //check if this is in the umbraco back office - var isUmbracoPath = urlPath.InvariantStartsWith(globalSettings.Path.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/')); + var isUmbracoPath = urlPath.InvariantStartsWith(ioHelper.BackOfficePath.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/')); //if not, then def not back office if (isUmbracoPath == false) return false; - var mvcArea = globalSettings.GetUmbracoMvcArea(ioHelper); + var mvcArea = ioHelper.GetUmbracoMvcArea(); //if its the normal /umbraco path if (urlPath.InvariantEquals("/" + mvcArea) || urlPath.InvariantEquals("/" + mvcArea + "/")) @@ -128,12 +127,12 @@ namespace Umbraco.Core /// /// /// - internal static bool IsDefaultBackOfficeRequest(this Uri url, IGlobalSettings globalSettings) + internal static bool IsDefaultBackOfficeRequest(this Uri url, IIOHelper ioHelper) { - if (url.AbsolutePath.InvariantEquals(globalSettings.Path.TrimEnd("/")) - || url.AbsolutePath.InvariantEquals(globalSettings.Path.EnsureEndsWith('/')) - || url.AbsolutePath.InvariantEquals(globalSettings.Path.EnsureEndsWith('/') + "Default") - || url.AbsolutePath.InvariantEquals(globalSettings.Path.EnsureEndsWith('/') + "Default/")) + if (url.AbsolutePath.InvariantEquals(ioHelper.BackOfficePath.TrimEnd("/")) + || url.AbsolutePath.InvariantEquals(ioHelper.BackOfficePath.EnsureEndsWith('/')) + || url.AbsolutePath.InvariantEquals(ioHelper.BackOfficePath.EnsureEndsWith('/') + "Default") + || url.AbsolutePath.InvariantEquals(ioHelper.BackOfficePath.EnsureEndsWith('/') + "Default/")) { return true; } @@ -241,7 +240,7 @@ namespace Umbraco.Core /// Default uri.AbsolutePath does not support relative uris. public static string GetSafeAbsolutePathDecoded(this Uri uri) { - return System.Web.HttpUtility.UrlDecode(uri.GetSafeAbsolutePath()); + return System.Net.WebUtility.UrlDecode(uri.GetSafeAbsolutePath()); } /// diff --git a/src/Umbraco.Core/UriUtilityCore.cs b/src/Umbraco.Core/UriUtilityCore.cs new file mode 100644 index 0000000000..9ca9d66229 --- /dev/null +++ b/src/Umbraco.Core/UriUtilityCore.cs @@ -0,0 +1,59 @@ +using System; +using Umbraco.Core; + +namespace Umbraco.Web +{ + public static class UriUtilityCore + { + + #region Uri string utilities + + public static bool HasScheme(string uri) + { + return uri.IndexOf("://") > 0; + } + + public static string StartWithScheme(string uri) + { + return StartWithScheme(uri, null); + } + + public static string StartWithScheme(string uri, string scheme) + { + return HasScheme(uri) ? uri : String.Format("{0}://{1}", scheme ?? Uri.UriSchemeHttp, uri); + } + + public static string EndPathWithSlash(string uri) + { + var pos1 = Math.Max(0, uri.IndexOf('?')); + var pos2 = Math.Max(0, uri.IndexOf('#')); + var pos = Math.Min(pos1, pos2); + + var path = pos > 0 ? uri.Substring(0, pos) : uri; + path = path.EnsureEndsWith('/'); + + if (pos > 0) + path += uri.Substring(pos); + + return path; + } + + public static string TrimPathEndSlash(string uri) + { + var pos1 = Math.Max(0, uri.IndexOf('?')); + var pos2 = Math.Max(0, uri.IndexOf('#')); + var pos = Math.Min(pos1, pos2); + + var path = pos > 0 ? uri.Substring(0, pos) : uri; + path = path.TrimEnd('/'); + + if (pos > 0) + path += uri.Substring(pos); + + return path; + } + + #endregion + + } +} diff --git a/src/Umbraco.Abstractions/VersionExtensions.cs b/src/Umbraco.Core/VersionExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/VersionExtensions.cs rename to src/Umbraco.Core/VersionExtensions.cs diff --git a/src/Umbraco.Abstractions/WaitHandleExtensions.cs b/src/Umbraco.Core/WaitHandleExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/WaitHandleExtensions.cs rename to src/Umbraco.Core/WaitHandleExtensions.cs diff --git a/src/Umbraco.Abstractions/WriteLock.cs b/src/Umbraco.Core/WriteLock.cs similarity index 100% rename from src/Umbraco.Abstractions/WriteLock.cs rename to src/Umbraco.Core/WriteLock.cs diff --git a/src/Umbraco.Abstractions/Xml/DynamicContext.cs b/src/Umbraco.Core/Xml/DynamicContext.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/DynamicContext.cs rename to src/Umbraco.Core/Xml/DynamicContext.cs diff --git a/src/Umbraco.Abstractions/Xml/UmbracoXPathPathSyntaxParser.cs b/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/UmbracoXPathPathSyntaxParser.cs rename to src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/INavigableContent.cs b/src/Umbraco.Core/Xml/XPath/INavigableContent.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/INavigableContent.cs rename to src/Umbraco.Core/Xml/XPath/INavigableContent.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/INavigableContentType.cs b/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/INavigableContentType.cs rename to src/Umbraco.Core/Xml/XPath/INavigableContentType.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/INavigableFieldType.cs b/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/INavigableFieldType.cs rename to src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/INavigableSource.cs b/src/Umbraco.Core/Xml/XPath/INavigableSource.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/INavigableSource.cs rename to src/Umbraco.Core/Xml/XPath/INavigableSource.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/MacroNavigator.cs b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/MacroNavigator.cs rename to src/Umbraco.Core/Xml/XPath/MacroNavigator.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/NavigableNavigator.cs b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/NavigableNavigator.cs rename to src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs diff --git a/src/Umbraco.Abstractions/Xml/XPath/RenamedRootNavigator.cs b/src/Umbraco.Core/Xml/XPath/RenamedRootNavigator.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPath/RenamedRootNavigator.cs rename to src/Umbraco.Core/Xml/XPath/RenamedRootNavigator.cs diff --git a/src/Umbraco.Abstractions/Xml/XPathNavigatorExtensions.cs b/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPathNavigatorExtensions.cs rename to src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs diff --git a/src/Umbraco.Abstractions/Xml/XPathVariable.cs b/src/Umbraco.Core/Xml/XPathVariable.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XPathVariable.cs rename to src/Umbraco.Core/Xml/XPathVariable.cs diff --git a/src/Umbraco.Abstractions/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs similarity index 99% rename from src/Umbraco.Abstractions/Xml/XmlHelper.cs rename to src/Umbraco.Core/Xml/XmlHelper.cs index 099b73ef36..4b76dc3799 100644 --- a/src/Umbraco.Abstractions/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.Abstractions/Xml/XmlNamespaces.cs b/src/Umbraco.Core/Xml/XmlNamespaces.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XmlNamespaces.cs rename to src/Umbraco.Core/Xml/XmlNamespaces.cs diff --git a/src/Umbraco.Abstractions/Xml/XmlNodeListFactory.cs b/src/Umbraco.Core/Xml/XmlNodeListFactory.cs similarity index 100% rename from src/Umbraco.Abstractions/Xml/XmlNodeListFactory.cs rename to src/Umbraco.Core/Xml/XmlNodeListFactory.cs diff --git a/src/Umbraco.Abstractions/XmlExtensions.cs b/src/Umbraco.Core/XmlExtensions.cs similarity index 100% rename from src/Umbraco.Abstractions/XmlExtensions.cs rename to src/Umbraco.Core/XmlExtensions.cs diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs similarity index 60% rename from src/Umbraco.Web/Search/UmbracoTreeSearcher.cs rename to src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index cf9d390768..c4e7f264fb 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -5,76 +5,41 @@ using System.Text; using System.Text.RegularExpressions; using Examine; using Umbraco.Core; -using Umbraco.Core.Mapping; using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Examine; +using Umbraco.Web; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Trees; -namespace Umbraco.Web.Search +namespace Umbraco.Examine { - - /// - /// Used for internal Umbraco implementations of - /// - public class UmbracoTreeSearcher + public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher { private readonly IExamineManager _examineManager; - private readonly UmbracoContext _umbracoContext; private readonly ILocalizationService _languageService; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IEntityService _entityService; - private readonly UmbracoMapper _mapper; - private readonly ISqlContext _sqlContext; + private readonly IUmbracoTreeSearcherFields _treeSearcherFields; - public UmbracoTreeSearcher(IExamineManager examineManager, - UmbracoContext umbracoContext, - ILocalizationService languageService, - IEntityService entityService, - UmbracoMapper mapper, - ISqlContext sqlContext) + public BackOfficeExamineSearcher(IExamineManager examineManager, + ILocalizationService languageService, + IUmbracoContextAccessor umbracoContextAccessor, + IEntityService entityService, + IUmbracoTreeSearcherFields treeSearcherFields) { - _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); - _umbracoContext = umbracoContext; + _examineManager = examineManager; _languageService = languageService; + _umbracoContextAccessor = umbracoContextAccessor; _entityService = entityService; - _mapper = mapper; - _sqlContext = sqlContext; + _treeSearcherFields = treeSearcherFields; } - /// - /// Searches Examine for results based on the entity type - /// - /// - /// - /// - /// - /// A starting point for the search, generally a node id, but for members this is a member type alias - /// - /// - /// - /// If set to true, user and group start node permissions will be ignored. - /// - public IEnumerable ExamineSearch( - string query, - UmbracoEntityTypes entityType, - int pageSize, - long pageIndex, out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false) + public IEnumerable Search(string query, UmbracoEntityTypes entityType, int pageSize, long pageIndex, out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false) { var sb = new StringBuilder(); string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; - var fields = new List { "id", "__NodeId", "__Key" }; - - // TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string - // manipulation for things like start paths, member types, etc... - //if (Examine.ExamineExtensions.TryParseLuceneQuery(query)) - //{ - - //} + var fields = _treeSearcherFields.GetBackOfficeFields().ToList(); //special GUID check since if a user searches on one specifically we need to escape it if (Guid.TryParse(query, out var g)) @@ -82,12 +47,14 @@ namespace Umbraco.Web.Search query = "\"" + g.ToString() + "\""; } + var currentUser = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser; + switch (entityType) { case UmbracoEntityTypes.Member: indexName = Constants.UmbracoIndexes.MembersIndexName; type = "member"; - fields.AddRange(new[]{ "email", "loginName"}); + fields.AddRange(_treeSearcherFields.GetBackOfficeMembersFields()); if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") { sb.Append("+__NodeTypeAlias:"); @@ -97,17 +64,22 @@ namespace Umbraco.Web.Search break; case UmbracoEntityTypes.Media: type = "media"; - fields.AddRange(new[] { UmbracoExamineIndex.UmbracoFileFieldName }); - var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); + fields.AddRange(_treeSearcherFields.GetBackOfficeMediaFields()); + var allMediaStartNodes = currentUser != null + ? currentUser.CalculateMediaStartNodeIds(_entityService) + : Array.Empty(); AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; case UmbracoEntityTypes.Document: type = "content"; - var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService); + fields.AddRange(_treeSearcherFields.GetBackOfficeDocumentFields()); + var allContentStartNodes = currentUser != null + ? currentUser.CalculateContentStartNodeIds(_entityService) + : Array.Empty(); AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; default: - throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); + throw new NotSupportedException("The " + typeof(BackOfficeExamineSearcher) + " currently does not support searching against object type " + entityType); } if (!_examineManager.TryGetIndex(indexName, out var index)) @@ -118,7 +90,7 @@ namespace Umbraco.Web.Search if (!BuildQuery(sb, query, searchFrom, fields, type)) { totalFound = 0; - return Enumerable.Empty(); + return Enumerable.Empty(); } var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()) @@ -129,37 +101,7 @@ namespace Umbraco.Web.Search var pagedResult = result.Skip(Convert.ToInt32(pageIndex)); - switch (entityType) - { - case UmbracoEntityTypes.Member: - return MemberFromSearchResults(pagedResult.ToArray()); - case UmbracoEntityTypes.Media: - return MediaFromSearchResults(pagedResult); - case UmbracoEntityTypes.Document: - return ContentFromSearchResults(pagedResult); - default: - throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); - } - } - - /// - /// Searches with the for results based on the entity type - /// - /// - /// - /// - /// - /// - /// - /// - public IEnumerable EntitySearch(UmbracoObjectTypes objectType, string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) - { - //if it's a GUID, match it - Guid.TryParse(query, out var g); - - var results = _entityService.GetPagedDescendants(objectType, pageIndex, pageSize, out totalFound, - filter: _sqlContext.Query().Where(x => x.Name.Contains(query) || x.Key == g)); - return _mapper.MapEnumerable(results); + return pagedResult; } private bool BuildQuery(StringBuilder sb, string query, string searchFrom, List fields, string type) @@ -238,7 +180,7 @@ namespace Umbraco.Web.Search var queryWordsReplaced = new string[querywords.Length]; // when searching file names containing hyphens we need to replace the hyphens with spaces - if (f.Equals(UmbracoExamineIndex.UmbracoFileFieldName)) + if (f.Equals(UmbracoExamineFieldNames.UmbracoFileFieldName)) { for (var index = 0; index < querywords.Length; index++) { @@ -391,79 +333,5 @@ namespace Umbraco.Web.Search sb.Append(path); sb.Append("\\,*"); } - - /// - /// Returns a collection of entities for media based on search results - /// - /// - /// - private IEnumerable MemberFromSearchResults(IEnumerable results) - { - //add additional data - foreach (var result in results) - { - var m = _mapper.Map(result); - - //if no icon could be mapped, it will be set to document, so change it to picture - if (m.Icon == Constants.Icons.DefaultIcon) - { - m.Icon = Constants.Icons.Member; - } - - if (result.Values.ContainsKey("email") && result.Values["email"] != null) - { - m.AdditionalData["Email"] = result.Values["email"]; - } - if (result.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName) && result.Values[UmbracoExamineIndex.NodeKeyFieldName] != null) - { - if (Guid.TryParse(result.Values[UmbracoExamineIndex.NodeKeyFieldName], out var key)) - { - m.Key = key; - } - } - - yield return m; - } - } - - /// - /// Returns a collection of entities for media based on search results - /// - /// - /// - private IEnumerable MediaFromSearchResults(IEnumerable results) - => _mapper.Map>(results); - - /// - /// Returns a collection of entities for content based on search results - /// - /// - /// - private IEnumerable ContentFromSearchResults(IEnumerable results) - { - var defaultLang = _languageService.GetDefaultLanguageIsoCode(); - - foreach (var result in results) - { - var entity = _mapper.Map(result); - - var intId = entity.Id.TryConvertTo(); - if (intId.Success) - { - //if it varies by culture, return the default language URL - if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "y") - { - entity.AdditionalData["Url"] = _umbracoContext.Url(intId.Result, defaultLang); - } - else - { - entity.AdditionalData["Url"] = _umbracoContext.Url(intId.Result); - } - } - - yield return entity; - } - } - } } diff --git a/src/Umbraco.Examine/ExamineExtensions.cs b/src/Umbraco.Examine.Lucene/ExamineExtensions.cs similarity index 68% rename from src/Umbraco.Examine/ExamineExtensions.cs rename to src/Umbraco.Examine.Lucene/ExamineExtensions.cs index d231a86f69..d697bf6f0d 100644 --- a/src/Umbraco.Examine/ExamineExtensions.cs +++ b/src/Umbraco.Examine.Lucene/ExamineExtensions.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using Examine; using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; using Lucene.Net.Index; using Lucene.Net.QueryParsers; using Lucene.Net.Search; -using Lucene.Net.Store; using Umbraco.Core; using Version = Lucene.Net.Util.Version; using Umbraco.Core.Logging; @@ -16,19 +13,12 @@ using System.Threading; namespace Umbraco.Examine { + /// /// Extension methods for the LuceneIndex /// public static class ExamineExtensions { - /// - /// Matches a culture iso name suffix - /// - /// - /// myFieldName_en-us will match the "en-us" - /// - internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled); - private static bool _isConfigured = false; private static object _configuredInit = null; private static object _isConfiguredLocker = new object(); @@ -52,51 +42,6 @@ namespace Umbraco.Examine }); } - //TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression - - /// - /// Returns all index fields that are culture specific (suffixed) - /// - /// - /// - /// - public static IEnumerable GetCultureFields(this IUmbracoIndex index, string culture) - { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var field in allFields) - { - var match = CultureIsoCodeFieldNameMatchExpression.Match(field); - if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) - yield return field; - } - } - - /// - /// Returns all index fields that are culture specific (suffixed) or invariant - /// - /// - /// - /// - public static IEnumerable GetCultureAndInvariantFields(this IUmbracoIndex index, string culture) - { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var field in allFields) - { - var match = CultureIsoCodeFieldNameMatchExpression.Match(field); - if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) - { - yield return field; //matches this culture field - } - else if (!match.Success) - { - yield return field; //matches no culture field (invariant) - } - - } - } - internal static bool TryParseLuceneQuery(string query) { // TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll @@ -107,7 +52,7 @@ namespace Umbraco.Examine try { //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse - var parsed = new QueryParser(Version.LUCENE_30, "nodeName", new KeywordAnalyzer()).Parse(query); + var parsed = new QueryParser(Version.LUCENE_30, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query); return true; } catch (ParseException) @@ -126,7 +71,7 @@ namespace Umbraco.Examine /// /// This is not thread safe, use with care /// - internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing) + private static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing) { foreach (var luceneIndexer in examineManager.Indexes.OfType()) { diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs new file mode 100644 index 0000000000..0d701d388d --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs @@ -0,0 +1,50 @@ +using Examine; +using Examine.LuceneEngine.Directories; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Examine +{ + + public sealed class ExamineLuceneComponent : IComponent + { + private readonly IndexRebuilder _indexRebuilder; + private readonly IExamineManager _examineManager; + private readonly IMainDom _mainDom; + private readonly ILogger _logger; + + public ExamineLuceneComponent(IndexRebuilder indexRebuilder, IExamineManager examineManager, IMainDom mainDom, ILogger logger) + { + _indexRebuilder = indexRebuilder; + _examineManager = examineManager; + _mainDom = mainDom; + _logger = logger; + } + + public void Initialize() + { + //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the AppDomain + //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock + //which simply checks the existence of the lock file + DirectoryFactory.DefaultLockFactory = d => + { + var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); + return simpleFsLockFactory; + }; + + _indexRebuilder.RebuildingIndexes += IndexRebuilder_RebuildingIndexes; + } + + /// + /// Handles event to ensure that all lucene based indexes are properly configured before rebuilding + /// + /// + /// + private void IndexRebuilder_RebuildingIndexes(object sender, IndexRebuildingEventArgs e) => _examineManager.ConfigureIndexes(_mainDom, _logger); + + public void Terminate() + { + } + } +} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs new file mode 100644 index 0000000000..724149a01d --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs @@ -0,0 +1,20 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; + +namespace Umbraco.Examine +{ + // We want to run after core composers since we are replacing some items + [ComposeAfter(typeof(ICoreComposer))] + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class ExamineLuceneComposer : ComponentComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs new file mode 100644 index 0000000000..e1e80ead2f --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs @@ -0,0 +1,33 @@ +using Examine; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Examine +{ + public class ExamineLuceneFinalComponent : IComponent + { + private readonly IProfilingLogger _logger; + private readonly IExamineManager _examineManager; + private readonly IMainDom _mainDom; + + public ExamineLuceneFinalComponent(IProfilingLogger logger, IExamineManager examineManager, IMainDom mainDom) + { + _logger = logger; + _examineManager = examineManager; + _mainDom = mainDom; + } + + public void Initialize() + { + if (!_mainDom.IsMainDom) return; + + // Ensures all lucene based indexes are unlocked and ready to go + _examineManager.ConfigureIndexes(_mainDom, _logger); + } + + public void Terminate() + { + } + } +} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs new file mode 100644 index 0000000000..1a73426568 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs @@ -0,0 +1,13 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; + +namespace Umbraco.Examine +{ + // examine's Lucene final composer composes after all user composers + // and *also* after ICoreComposer (in case IUserComposer is disabled) + [ComposeAfter(typeof(IUserComposer))] + [ComposeAfter(typeof(ICoreComposer))] + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class ExamineLuceneFinalComposer : ComponentComposer + { } +} diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs similarity index 91% rename from src/Umbraco.Examine/LuceneIndexCreator.cs rename to src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs index ccc0248868..b1d529e6c6 100644 --- a/src/Umbraco.Examine/LuceneIndexCreator.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO; using Examine; using Examine.LuceneEngine.Directories; using Lucene.Net.Store; +using Umbraco.Core.Configuration; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.IO; @@ -19,11 +19,13 @@ namespace Umbraco.Examine { private readonly ITypeFinder _typeFinder; private readonly IIOHelper _ioHelper; + private readonly IIndexCreatorSettings _settings; - protected LuceneIndexCreator(ITypeFinder typeFinder, IIOHelper ioHelper) + protected LuceneIndexCreator(ITypeFinder typeFinder, IIOHelper ioHelper, IIndexCreatorSettings settings) { _typeFinder = typeFinder; _ioHelper = ioHelper; + _settings = settings; } public abstract IEnumerable Create(); @@ -43,7 +45,8 @@ namespace Umbraco.Examine System.IO.Directory.CreateDirectory(dirInfo.FullName); //check if there's a configured directory factory, if so create it and use that to create the lucene dir - var configuredDirectoryFactory = ConfigurationManager.AppSettings["Umbraco.Examine.LuceneDirectoryFactory"]; + var configuredDirectoryFactory = _settings.LuceneDirectoryFactory; + if (!configuredDirectoryFactory.IsNullOrWhiteSpace()) { //this should be a fully qualified type diff --git a/src/Umbraco.Examine/LuceneIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs similarity index 100% rename from src/Umbraco.Examine/LuceneIndexDiagnostics.cs rename to src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs new file mode 100644 index 0000000000..cc1fc115ca --- /dev/null +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs @@ -0,0 +1,35 @@ +using Examine; +using Examine.LuceneEngine.Providers; +using Umbraco.Core.Logging; +using Umbraco.Core.IO; + +namespace Umbraco.Examine +{ + /// + /// Implementation of which returns + /// for lucene based indexes that don't have an implementation else fallsback to the default implementation. + /// + public class LuceneIndexDiagnosticsFactory : IndexDiagnosticsFactory + { + private readonly ILogger _logger; + private readonly IIOHelper _ioHelper; + + public LuceneIndexDiagnosticsFactory(ILogger logger, IIOHelper ioHelper) + { + _logger = logger; + _ioHelper = ioHelper; + } + + public override IIndexDiagnostics Create(IIndex index) + { + if (!(index is IIndexDiagnostics indexDiag)) + { + if (index is LuceneIndex luceneIndex) + indexDiag = new LuceneIndexDiagnostics(luceneIndex, _logger, _ioHelper); + else + indexDiag = base.Create(index); + } + return indexDiag; + } + } +} diff --git a/src/Umbraco.Examine/NoPrefixSimpleFsLockFactory.cs b/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs similarity index 100% rename from src/Umbraco.Examine/NoPrefixSimpleFsLockFactory.cs rename to src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj new file mode 100644 index 0000000000..932d6d318b --- /dev/null +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -0,0 +1,49 @@ + + + + net472 + Umbraco.Examine + Umbraco CMS + Umbraco.Examine.Lucene + 8 + + + + true + bin\Release\Umbraco.Examine.Lucene.xml + + + + + + + + + + + + + + + + + + + + + + + 1.0.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + 3.4.0 + runtime; build; native; contentfiles; analyzers + all + + + + diff --git a/src/Umbraco.Examine/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs similarity index 97% rename from src/Umbraco.Examine/UmbracoContentIndex.cs rename to src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs index 33fd2d0ee7..1c8cd4b074 100644 --- a/src/Umbraco.Examine/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs @@ -20,7 +20,7 @@ namespace Umbraco.Examine /// public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex { - public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture"; + protected ILocalizationService LanguageService { get; } #region Constructors @@ -132,7 +132,7 @@ namespace Umbraco.Examine { //find all descendants based on path var descendantPath = $@"\-1\,*{nodeId}\,*"; - var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; + var rawQuery = $"{UmbracoExamineFieldNames.IndexPathFieldName}:{descendantPath}"; var searcher = GetSearcher(); var c = searcher.CreateQuery(); var filtered = c.NativeQuery(rawQuery); diff --git a/src/Umbraco.Examine/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs similarity index 88% rename from src/Umbraco.Examine/UmbracoExamineIndex.cs rename to src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index f7dfcf6375..880440f4f9 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -28,19 +28,7 @@ namespace Umbraco.Examine // call context (and the database it can contain)! ideally we should be able to override // SafelyProcessQueueItems but that's not possible in the current version of Examine. - /// - /// Used to store the path of a content object - /// - public const string IndexPathFieldName = SpecialFieldPrefix + "Path"; - public const string NodeKeyFieldName = SpecialFieldPrefix + "Key"; - public const string UmbracoFileFieldName = "umbracoFileSrc"; - public const string IconFieldName = SpecialFieldPrefix + "Icon"; - public const string PublishedFieldName = SpecialFieldPrefix + "Published"; - - /// - /// The prefix added to a field when it is duplicated in order to store the original raw value. - /// - public const string RawFieldPrefix = SpecialFieldPrefix + "Raw_"; + /// /// Create a new @@ -141,7 +129,7 @@ namespace Umbraco.Examine { var d = docArgs.Document; - foreach (var f in docArgs.ValueSet.Values.Where(x => x.Key.StartsWith(RawFieldPrefix)).ToList()) + foreach (var f in docArgs.ValueSet.Values.Where(x => x.Key.StartsWith(UmbracoExamineFieldNames.RawFieldPrefix)).ToList()) { if (f.Value.Count > 0) { @@ -182,13 +170,13 @@ namespace Umbraco.Examine var path = e.ValueSet.GetValue("path"); if (path != null) { - e.ValueSet.Set(IndexPathFieldName, path); + e.ValueSet.Set(UmbracoExamineFieldNames.IndexPathFieldName, path); } //icon - if (e.ValueSet.Values.TryGetValue("icon", out var icon) && e.ValueSet.Values.ContainsKey(IconFieldName) == false) + if (e.ValueSet.Values.TryGetValue("icon", out var icon) && e.ValueSet.Values.ContainsKey(UmbracoExamineFieldNames.IconFieldName) == false) { - e.ValueSet.Values[IconFieldName] = icon; + e.ValueSet.Values[UmbracoExamineFieldNames.IconFieldName] = icon; } } diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs similarity index 100% rename from src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs rename to src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs similarity index 72% rename from src/Umbraco.Web/Search/UmbracoIndexesCreator.cs rename to src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs index aad8303f40..0f60a7580c 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs @@ -1,17 +1,15 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core.Logging; using Umbraco.Core.Services; -using Umbraco.Examine; using Lucene.Net.Analysis.Standard; using Examine.LuceneEngine; using Examine; +using Umbraco.Core.Configuration; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.IO; -using Umbraco.Web.PublishedCache.NuCache; -namespace Umbraco.Web.Search +namespace Umbraco.Examine { /// /// Creates the indexes used by Umbraco @@ -28,7 +26,8 @@ namespace Umbraco.Web.Search IMemberService memberService, IUmbracoIndexConfig umbracoIndexConfig, IIOHelper ioHelper, - IRuntimeState runtimeState) : base(typeFinder, ioHelper) + IRuntimeState runtimeState, + IIndexCreatorSettings settings) : base(typeFinder, ioHelper, settings) { ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); @@ -53,7 +52,7 @@ namespace Umbraco.Web.Search /// public override IEnumerable Create() { - return new [] + return new[] { CreateInternalIndex(), CreateExternalIndex(), @@ -72,7 +71,7 @@ namespace Umbraco.Web.Search IOHelper, RuntimeState, LanguageService, - GetContentValueSetValidator() + UmbracoIndexConfig.GetContentValueSetValidator() ); return index; } @@ -88,7 +87,7 @@ namespace Umbraco.Web.Search IOHelper, RuntimeState, LanguageService, - GetPublishedContentValueSetValidator()); + UmbracoIndexConfig.GetPublishedContentValueSetValidator()); return index; } @@ -102,31 +101,9 @@ namespace Umbraco.Web.Search ProfilingLogger, IOHelper, RuntimeState, - GetMemberValueSetValidator() + UmbracoIndexConfig.GetMemberValueSetValidator() ); return index; } - [Obsolete("This method should not be used and will be removed in future versions. GetContentValueSetValidator was moved to IUmbracoIndexConfig")] - public virtual IContentValueSetValidator GetContentValueSetValidator() - { - return UmbracoIndexConfig.GetContentValueSetValidator(); - } - [Obsolete("This method should not be used and will be removed in future versions. GetPublishedContentValueSetValidator was moved to IUmbracoIndexConfig")] - public virtual IContentValueSetValidator GetPublishedContentValueSetValidator() - { - return UmbracoIndexConfig.GetPublishedContentValueSetValidator(); - } - - /// - /// Returns the for the member indexer - /// - /// - [Obsolete("This method should not be used and will be removed in future versions. GetMemberValueSetValidator was moved to IUmbracoIndexConfig")] - public virtual IValueSetValidator GetMemberValueSetValidator() - { - return UmbracoIndexConfig.GetMemberValueSetValidator(); - } - - } } diff --git a/src/Umbraco.Examine/UmbracoMemberIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs similarity index 94% rename from src/Umbraco.Examine/UmbracoMemberIndex.cs rename to src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs index 494c661062..0e9128d31a 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using Examine; -using Examine.LuceneEngine; +using Examine; using Lucene.Net.Analysis; using Umbraco.Core; using Umbraco.Core.IO; diff --git a/src/Umbraco.Examine/Properties/AssemblyInfo.cs b/src/Umbraco.Examine/Properties/AssemblyInfo.cs deleted file mode 100644 index 5c42a236f4..0000000000 --- a/src/Umbraco.Examine/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("Umbraco.Examine")] -[assembly: AssemblyDescription("Umbraco Examine")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Umbraco CMS")] - -// Umbraco Cms -[assembly: InternalsVisibleTo("Umbraco.Tests")] -[assembly: InternalsVisibleTo("Umbraco.Web")] - -// code analysis -// IDE1006 is broken, wants _value syntax for consts, etc - and it's even confusing ppl at MS, kill it -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "~_~")] diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj deleted file mode 100644 index 91c200bdfe..0000000000 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ /dev/null @@ -1,123 +0,0 @@ - - - - - v4.7.2 - false - {07FBC26B-2927-4A22-8D96-D644C667FECC} - Library - Umbraco.Examine - Umbraco.Examine - ..\ - - $(AdditionalFileItemNames);Content - - - true - portable - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - portable - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Examine.xml - false - latest - - - - - - - - - - - - - - - - - - - 1.0.0-beta2-19324-01 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - 3.3.0 - runtime; build; native; contentfiles; analyzers - all - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\SolutionInfo.cs - - - - - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} - Umbraco.Core - - - {3ae7bf57-966b-45a5-910a-954d7c554441} - Umbraco.Infrastructure - - - - \ No newline at end of file diff --git a/src/Umbraco.Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Examine/UmbracoExamineExtensions.cs deleted file mode 100644 index f33b7587e0..0000000000 --- a/src/Umbraco.Examine/UmbracoExamineExtensions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Examine.LuceneEngine.Search; -using Examine.Search; -using Umbraco.Core; - -namespace Umbraco.Examine -{ - public static class UmbracoExamineExtensions - { - public static IBooleanOperation Id(this IQuery query, int id) - { - var fieldQuery = query.Id(id.ToInvariantString()); - return fieldQuery; - } - - /// - /// Query method to search on parent id - /// - /// - /// - /// - public static IBooleanOperation ParentId(this IQuery query, int id) - { - var fieldQuery = query.Field("parentID", id); - return fieldQuery; - } - - /// - /// Query method to search on node name - /// - /// - /// - /// - public static IBooleanOperation NodeName(this IQuery query, string nodeName) - { - var fieldQuery = query.Field("nodeName", (IExamineValue)new ExamineValue(Examineness.Explicit, nodeName)); - return fieldQuery; - } - - /// - /// Query method to search on node name - /// - /// - /// - /// - public static IBooleanOperation NodeName(this IQuery query, IExamineValue nodeName) - { - var fieldQuery = query.Field("nodeName", nodeName); - return fieldQuery; - } - - /// - /// Query method to search on node type alias - /// - /// - /// - /// - public static IBooleanOperation NodeTypeAlias(this IQuery query, string nodeTypeAlias) - { - var fieldQuery = query.Field("__NodeTypeAlias", (IExamineValue)new ExamineValue(Examineness.Explicit, nodeTypeAlias)); - return fieldQuery; - } - - /// - /// Query method to search on node type alias - /// - /// - /// - /// - public static IBooleanOperation NodeTypeAlias(this IQuery query, IExamineValue nodeTypeAlias) - { - var fieldQuery = query.Field("__NodeTypeAlias", nodeTypeAlias); - return fieldQuery; - } - - - } -} diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs similarity index 73% rename from src/Umbraco.Web/BatchedDatabaseServerMessenger.cs rename to src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs index 228e3144b2..78b9589a2e 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Sync; using Umbraco.Web.Routing; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Scoping; -using Umbraco.Web.Composing; -using System.ComponentModel; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; +using Umbraco.Core.Request; namespace Umbraco.Web { @@ -25,21 +21,34 @@ namespace Umbraco.Web /// /// This binds to appropriate umbraco events in order to trigger the Boot(), Sync() & FlushBatch() calls /// - public class BatchedDatabaseServerMessenger : DatabaseServerMessenger + public class BatchedDatabaseServerMessenger : DatabaseServerMessenger, IBatchedDatabaseServerMessenger { private readonly IUmbracoDatabaseFactory _databaseFactory; + private readonly IRequestCache _requestCache; + private readonly IRequestAccessor _requestAccessor; public BatchedDatabaseServerMessenger( - IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment) - : base(runtime, scopeProvider, sqlContext, proflog, true, options, hostingEnvironment, Current.CacheRefreshers) + IRuntimeState runtime, + IUmbracoDatabaseFactory databaseFactory, + IScopeProvider scopeProvider, + ISqlContext sqlContext, + IProfilingLogger proflog, + DatabaseServerMessengerOptions options, + IHostingEnvironment hostingEnvironment, + CacheRefresherCollection cacheRefreshers, + IRequestCache requestCache, + IRequestAccessor requestAccessor) + : base(runtime, scopeProvider, sqlContext, proflog, true, options, hostingEnvironment, cacheRefreshers) { _databaseFactory = databaseFactory; + _requestCache = requestCache; + _requestAccessor = requestAccessor; } // invoked by DatabaseServerRegistrarAndMessengerComponent - internal void Startup() + public void Startup() { - UmbracoModule.EndRequest += UmbracoModule_EndRequest; + _requestAccessor.EndRequest += UmbracoModule_EndRequest; if (_databaseFactory.CanConnect == false) { @@ -102,24 +111,18 @@ namespace Umbraco.Web protected ICollection GetBatch(bool create) { - // try get the http context from the UmbracoContext, we do this because in the case we are launching an async - // thread and we know that the cache refreshers will execute, we will ensure the UmbracoContext and therefore we - // can get the http context from it - var httpContext = (Current.UmbracoContext == null ? null : Current.UmbracoContext.HttpContext) - // if this is null, it could be that an async thread is calling this method that we weren't aware of and the UmbracoContext - // wasn't ensured at the beginning of the thread. We can try to see if the HttpContext.Current is available which might be - // the case if the asp.net synchronization context has kicked in - ?? (HttpContext.Current == null ? null : new HttpContextWrapper(HttpContext.Current)); + var key = nameof(BatchedDatabaseServerMessenger); - // if no context was found, return null - we cannot not batch - if (httpContext == null) return null; - - var key = typeof (BatchedDatabaseServerMessenger).Name; + if (!_requestCache.IsAvailable) return null; // no thread-safety here because it'll run in only 1 thread (request) at a time - var batch = (ICollection)httpContext.Items[key]; + var batch = (ICollection)_requestCache.Get(key); if (batch == null && create) - httpContext.Items[key] = batch = new List(); + { + batch = new List(); + _requestCache.Set(key, batch); + } + return batch; } diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder.cs similarity index 100% rename from src/Umbraco.Web/Cache/DistributedCacheBinder.cs rename to src/Umbraco.Infrastructure/Cache/DistributedCacheBinder.cs diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinderComposer.cs similarity index 100% rename from src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs rename to src/Umbraco.Infrastructure/Cache/DistributedCacheBinderComposer.cs diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs similarity index 99% rename from src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs rename to src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index c56b7a094f..c0200933ab 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -8,7 +8,6 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; -using Umbraco.Web.Services; namespace Umbraco.Web.Cache { @@ -153,6 +152,7 @@ namespace Umbraco.Web.Cache private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs e) { + _distributedCache.RefreshPublicAccess(); } @@ -349,6 +349,7 @@ namespace Umbraco.Web.Cache private void UserService_DeletedUserGroup(IUserService sender, DeleteEventArgs e) { + foreach (var entity in e.DeletedEntities) _distributedCache.RemoveUserGroupCache(entity.Id); } diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheExtensions.cs similarity index 96% rename from src/Umbraco.Web/Cache/DistributedCacheExtensions.cs rename to src/Umbraco.Infrastructure/Cache/DistributedCacheExtensions.cs index b00a4818f6..7cfac1b9a0 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheExtensions.cs @@ -7,7 +7,7 @@ namespace Umbraco.Web.Cache /// /// Extension methods for . /// - internal static class DistributedCacheExtensions + public static class DistributedCacheExtensions { #region PublicAccessCache @@ -192,13 +192,15 @@ namespace Umbraco.Web.Cache public static void RefreshMacroCache(this DistributedCache dc, IMacro macro) { if (macro == null) return; - dc.RefreshByJson(MacroCacheRefresher.UniqueId, MacroCacheRefresher.Serialize(macro)); + var payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) }; + dc.RefreshByPayload(MacroCacheRefresher.UniqueId, payloads); } public static void RemoveMacroCache(this DistributedCache dc, IMacro macro) { if (macro == null) return; - dc.RefreshByJson(MacroCacheRefresher.UniqueId, MacroCacheRefresher.Serialize(macro)); + var payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) }; + dc.RefreshByPayload(MacroCacheRefresher.UniqueId, payloads); } #endregion @@ -298,5 +300,7 @@ namespace Umbraco.Web.Cache } #endregion + + } } diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs similarity index 83% rename from src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs rename to src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 6dfad8c45b..2a24e6f318 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -2,16 +2,18 @@ using System.Threading; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; using Umbraco.Examine; using Umbraco.Web.Cache; +using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Scheduling; using Umbraco.Web.Search; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Compose { @@ -36,9 +38,6 @@ namespace Umbraco.Web.Compose { public static DatabaseServerMessengerOptions GetDefaultOptions(IFactory factory) { - var logger = factory.GetInstance(); - var indexRebuilder = factory.GetInstance(); - return new DatabaseServerMessengerOptions { //These callbacks will be executed if the server has not been synced @@ -48,20 +47,25 @@ namespace Umbraco.Web.Compose //rebuild the xml cache file if the server is not synced () => { + var publishedSnapshotService = factory.GetInstance(); + // rebuild the published snapshot caches entirely, if the server is not synced // this is equivalent to DistributedCache RefreshAll... but local only // (we really should have a way to reuse RefreshAll... locally) // note: refresh all content & media caches does refresh content types too - var svc = Current.PublishedSnapshotService; - svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }); - svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); - svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _); + publishedSnapshotService.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }); + publishedSnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); + publishedSnapshotService.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _); }, //rebuild indexes if the server is not synced // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific // indexes then they can adjust this logic themselves. - () => { ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000); } + () => + { + var indexRebuilder = factory.GetInstance(); + indexRebuilder.RebuildIndexes(false, 5000); + } } }; } @@ -79,7 +83,7 @@ namespace Umbraco.Web.Compose { private object _locker = new object(); private readonly DatabaseServerRegistrar _registrar; - private readonly BatchedDatabaseServerMessenger _messenger; + private readonly IBatchedDatabaseServerMessenger _messenger; private readonly IRuntimeState _runtime; private readonly ILogger _logger; private readonly IServerRegistrationService _registrationService; @@ -88,28 +92,38 @@ namespace Umbraco.Web.Compose private bool _started; private IBackgroundTask[] _tasks; private IndexRebuilder _indexRebuilder; + private readonly IRequestAccessor _requestAccessor; - public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) + public DatabaseServerRegistrarAndMessengerComponent( + IRuntimeState runtime, + IServerRegistrar serverRegistrar, + IServerMessenger serverMessenger, + IServerRegistrationService registrationService, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IndexRebuilder indexRebuilder, + IRequestAccessor requestAccessor) { _runtime = runtime; _logger = logger; _registrationService = registrationService; _indexRebuilder = indexRebuilder; + _requestAccessor = requestAccessor; // create task runner for DatabaseServerRegistrar _registrar = serverRegistrar as DatabaseServerRegistrar; if (_registrar != null) { _touchTaskRunner = new BackgroundTaskRunner("ServerRegistration", - new BackgroundTaskRunnerOptions { AutoStart = true }, logger); + new BackgroundTaskRunnerOptions { AutoStart = true }, logger, hostingEnvironment); } // create task runner for BatchedDatabaseServerMessenger - _messenger = serverMessenger as BatchedDatabaseServerMessenger; + _messenger = serverMessenger as IBatchedDatabaseServerMessenger; if (_messenger != null) { _processTaskRunner = new BackgroundTaskRunner("ServerInstProcess", - new BackgroundTaskRunnerOptions { AutoStart = true }, logger); + new BackgroundTaskRunnerOptions { AutoStart = true }, logger, hostingEnvironment); } } @@ -117,7 +131,7 @@ namespace Umbraco.Web.Compose { //We will start the whole process when a successful request is made if (_registrar != null || _messenger != null) - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; // must come last, as it references some _variables _messenger?.Startup(); @@ -134,7 +148,7 @@ namespace Umbraco.Web.Compose /// /// We require this because: /// - ApplicationContext.UmbracoApplicationUrl is initialized by UmbracoModule in BeginRequest - /// - RegisterServer is called on UmbracoModule.RouteAttempt which is triggered in ProcessRequest + /// - RegisterServer is called on _requestAccessor.RouteAttempt which is triggered in ProcessRequest /// we are safe, UmbracoApplicationUrl has been initialized /// private void RegisterBackgroundTasksOnce(object sender, RoutableAttemptEventArgs e) @@ -143,7 +157,7 @@ namespace Umbraco.Web.Compose { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } @@ -193,11 +207,11 @@ namespace Umbraco.Web.Compose private class InstructionProcessTask : RecurringTaskBase { - private readonly DatabaseServerMessenger _messenger; + private readonly IDatabaseServerMessenger _messenger; private readonly ILogger _logger; public InstructionProcessTask(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - DatabaseServerMessenger messenger, ILogger logger) + IDatabaseServerMessenger messenger, ILogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _messenger = messenger; 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/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs similarity index 96% rename from src/Umbraco.Web/Compose/NotificationsComponent.cs rename to src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs index 3144895ba3..b39e5c03e9 100644 --- a/src/Umbraco.Web/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; @@ -153,7 +154,6 @@ namespace Umbraco.Web.Compose private readonly IUserService _userService; private readonly ILocalizedTextService _textService; private readonly IGlobalSettings _globalSettings; - private readonly IContentSection _contentConfig; private readonly ILogger _logger; /// @@ -166,7 +166,7 @@ namespace Umbraco.Web.Compose /// /// /// - public Notifier(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtimeState, INotificationService notificationService, IUserService userService, ILocalizedTextService textService, IGlobalSettings globalSettings, IContentSection contentConfig, ILogger logger) + public Notifier(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtimeState, INotificationService notificationService, IUserService userService, ILocalizedTextService textService, IGlobalSettings globalSettings, ILogger logger) { _umbracoContextAccessor = umbracoContextAccessor; _runtimeState = runtimeState; @@ -174,17 +174,12 @@ namespace Umbraco.Web.Compose _userService = userService; _textService = textService; _globalSettings = globalSettings; - _contentConfig = contentConfig; _logger = logger; } public void Notify(IAction action, params IContent[] entities) { - IUser user = null; - if (_umbracoContextAccessor.UmbracoContext != null) - { - user = _umbracoContextAccessor.UmbracoContext.Security.CurrentUser; - } + var user = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser; //if there is no current user, then use the admin if (user == null) diff --git a/src/Umbraco.Web/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs similarity index 100% rename from src/Umbraco.Web/Compose/NotificationsComposer.cs rename to src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs diff --git a/src/Umbraco.Web/Compose/PublicAccessComponent.cs b/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs similarity index 56% rename from src/Umbraco.Web/Compose/PublicAccessComponent.cs rename to src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs index ef7532253b..37bcfb1ceb 100644 --- a/src/Umbraco.Web/Compose/PublicAccessComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs @@ -1,22 +1,28 @@ -using Umbraco.Core; +using System; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Compose { public sealed class PublicAccessComponent : IComponent { + private readonly IPublicAccessService _publicAccessService; + public PublicAccessComponent(IPublicAccessService publicAccessService) + { + _publicAccessService = publicAccessService ?? throw new ArgumentNullException(nameof(publicAccessService)); + } + public void Initialize() { - MemberGroupService.Saved += MemberGroupService_Saved; + MemberGroupService.Saved += (s, e) => MemberGroupService_Saved(s, e, _publicAccessService); } public void Terminate() { } - static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e) + static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e, IPublicAccessService publicAccessService) { foreach (var grp in e.SavedEntities) { @@ -26,7 +32,7 @@ namespace Umbraco.Web.Compose && grp.AdditionalData["previousName"].ToString().IsNullOrWhiteSpace() == false && grp.AdditionalData["previousName"].ToString() != grp.Name) { - Current.Services.PublicAccessService.RenameMemberGroupRoleRules(grp.AdditionalData["previousName"].ToString(), grp.Name); + publicAccessService.RenameMemberGroupRoleRules(grp.AdditionalData["previousName"].ToString(), grp.Name); } } } diff --git a/src/Umbraco.Web/Compose/PublicAccessComposer.cs b/src/Umbraco.Infrastructure/Compose/PublicAccessComposer.cs similarity index 100% rename from src/Umbraco.Web/Compose/PublicAccessComposer.cs rename to src/Umbraco.Infrastructure/Compose/PublicAccessComposer.cs diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs deleted file mode 100644 index 7169b93cb4..0000000000 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Umbraco.Core.Configuration.UmbracoSettings; - -namespace Umbraco.Core.Composing.CompositionExtensions -{ - /// - /// Compose configurations. - /// - internal static class Configuration - { - public static Composition ComposeConfiguration(this Composition composition) - { - // common configurations are already registered - // register others - - composition.RegisterUnique(factory => factory.GetInstance().Content); - composition.RegisterUnique(factory => factory.GetInstance().RequestHandler); - composition.RegisterUnique(factory => factory.GetInstance().Security); - - return composition; - } - } -} diff --git a/src/Umbraco.Core/Composing/CompositionExtensions/CoreMappingProfiles.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/CoreMappingProfiles.cs similarity index 68% rename from src/Umbraco.Core/Composing/CompositionExtensions/CoreMappingProfiles.cs rename to src/Umbraco.Infrastructure/Composing/CompositionExtensions/CoreMappingProfiles.cs index 165fa10be7..9835d17fb6 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions/CoreMappingProfiles.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/CoreMappingProfiles.cs @@ -1,5 +1,4 @@ using Umbraco.Core.Mapping; -using Umbraco.Core.Models.Identity; namespace Umbraco.Core.Composing.CompositionExtensions @@ -9,8 +8,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions public static Composition ComposeCoreMappingProfiles(this Composition composition) { composition.RegisterUnique(); - composition.WithCollectionBuilder() - .Add(); + return composition; } } diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/FileSystems.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/FileSystems.cs index e13b4f9477..353c60d280 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/FileSystems.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/FileSystems.cs @@ -74,10 +74,10 @@ namespace Umbraco.Core.Composing.CompositionExtensions // it needs to be registered (not only the interface) because it provides additional // functionality eg for scoping, and is injected in the scope provider - whereas the // interface is really for end-users to get access to filesystems. - composition.RegisterUnique(factory => factory.CreateInstance(factory)); + composition.RegisterUnique(factory => factory.CreateInstance(factory)); // register IFileSystems, which gives access too all filesystems - composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); // register the scheme for media paths composition.RegisterUnique(); diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs index 0939dd0f71..61ff4ecb6d 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs @@ -47,6 +47,8 @@ namespace Umbraco.Core.Composing.CompositionExtensions composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); return composition; } diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs index c746c2b8b4..a41015c4e8 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; -using Umbraco.Core.Dictionary; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -25,7 +24,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions composition.RegisterUnique(); // register the special idk map - composition.RegisterUnique(); + composition.RegisterUnique(); // register the services composition.RegisterUnique(); diff --git a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs index d8a554ee8c..5200dced90 100644 --- a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs @@ -244,11 +244,11 @@ namespace Umbraco.Core.Composing.LightInject { } /// - public void EnablePerWebRequestScope() + public virtual void EnablePerWebRequestScope() { if (!(Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); - smp.EnablePerWebRequestScope(); + smp.EnablePerWebRequestScope(new PerLogicalCallContextScopeManagerProvider()); } private class AssemblyScanner : IAssemblyScanner diff --git a/src/Umbraco.Infrastructure/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs b/src/Umbraco.Infrastructure/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs index 897c58dd43..3175376b33 100644 --- a/src/Umbraco.Infrastructure/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs +++ b/src/Umbraco.Infrastructure/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs @@ -1,5 +1,4 @@ using LightInject; -using LightInject.Web; namespace Umbraco.Core.Composing.LightInject { @@ -29,10 +28,10 @@ namespace Umbraco.Core.Composing.LightInject _provider = new PerThreadScopeManagerProvider(); } - public void EnablePerWebRequestScope() + public void EnablePerWebRequestScope(IScopeManagerProvider perRequestScopeProvider) { - if (_provider is PerWebRequestScopeManagerProvider) return; - _provider = new PerWebRequestScopeManagerProvider(); + if (perRequestScopeProvider.GetType().IsAssignableFrom(_provider.GetType())) return; + _provider = perRequestScopeProvider; } public IScopeManager GetScopeManager(IServiceFactory factory) diff --git a/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs b/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs index 401982169f..eb74d37590 100644 --- a/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs +++ b/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs @@ -25,8 +25,7 @@ namespace Umbraco.Core ITypeFinder typeFinder, IIOHelper ioHelper, IUmbracoVersion umbracoVersion, - IDbProviderFactoryCreator dbProviderFactoryCreator, - IBulkSqlInsertProvider bulkSqlInsertProvider) + IDbProviderFactoryCreator dbProviderFactoryCreator) { composition.RegisterUnique(logger); composition.RegisterUnique(profiler); @@ -42,7 +41,7 @@ namespace Umbraco.Core composition.RegisterUnique(ioHelper); composition.RegisterUnique(umbracoVersion); composition.RegisterUnique(dbProviderFactoryCreator); - composition.RegisterUnique(bulkSqlInsertProvider); + composition.RegisterUnique(factory => factory.GetInstance().BulkSqlInsertProvider); } } } diff --git a/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs b/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs similarity index 99% rename from src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs rename to src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs index 320580aaf9..63b5cc90dc 100644 --- a/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs +++ b/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.Editors { - internal class UserEditorAuthorizationHelper + public class UserEditorAuthorizationHelper { private readonly IContentService _contentService; private readonly IMediaService _mediaService; diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs similarity index 99% rename from src/Umbraco.Examine/BaseValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs index 4a306aa5ff..2350d3cb84 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Examine; using Umbraco.Core; using Umbraco.Core.Models; diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs similarity index 98% rename from src/Umbraco.Examine/ContentIndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index 99ff4d7f87..8b34c3315a 100644 --- a/src/Umbraco.Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -6,7 +6,6 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; namespace Umbraco.Examine diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs similarity index 87% rename from src/Umbraco.Examine/ContentValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs index b2f8ae720b..c115939b7d 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs @@ -46,16 +46,16 @@ namespace Umbraco.Examine var values = new Dictionary> { {"icon", c.ContentType.Icon?.Yield() ?? Enumerable.Empty()}, - {UmbracoExamineIndex.PublishedFieldName, new object[] {c.Published ? "y" : "n"}}, //Always add invariant published value + {UmbracoExamineFieldNames.PublishedFieldName, new object[] {c.Published ? "y" : "n"}}, //Always add invariant published value {"id", new object[] {c.Id}}, - {UmbracoExamineIndex.NodeKeyFieldName, new object[] {c.Key}}, + {UmbracoExamineFieldNames.NodeKeyFieldName, new object[] {c.Key}}, {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, {"level", new object[] {c.Level}}, {"creatorID", new object[] {c.CreatorId}}, {"sortOrder", new object[] {c.SortOrder}}, {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate - {"nodeName", (PublishedValuesOnly //Always add invariant nodeName + {UmbracoExamineFieldNames.NodeNameFieldName, (PublishedValuesOnly //Always add invariant nodeName ? c.PublishName?.Yield() : c.Name?.Yield()) ?? Enumerable.Empty()}, {"urlName", urlValue?.Yield() ?? Enumerable.Empty()}, //Always add invariant urlName @@ -65,12 +65,12 @@ namespace Umbraco.Examine {"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() }, {"writerID", new object[] {c.WriterId}}, {"templateID", new object[] {c.TemplateId ?? 0}}, - {UmbracoContentIndex.VariesByCultureFieldName, new object[] {"n"}}, + {UmbracoExamineFieldNames.VariesByCultureFieldName, new object[] {"n"}}, }; if (isVariant) { - values[UmbracoContentIndex.VariesByCultureFieldName] = new object[] { "y" }; + values[UmbracoExamineFieldNames.VariesByCultureFieldName] = new object[] { "y" }; foreach (var culture in c.AvailableCultures) { @@ -80,7 +80,7 @@ namespace Umbraco.Examine values[$"nodeName_{lowerCulture}"] = (PublishedValuesOnly ? c.GetPublishName(culture)?.Yield() : c.GetCultureName(culture)?.Yield()) ?? Enumerable.Empty(); - values[$"{UmbracoExamineIndex.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? "y" : "n").Yield(); + values[$"{UmbracoExamineFieldNames.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? "y" : "n").Yield(); values[$"updateDate_{lowerCulture}"] = (PublishedValuesOnly ? c.GetPublishDate(culture) : c.GetUpdateDate(culture))?.Yield() ?? Enumerable.Empty(); diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs similarity index 93% rename from src/Umbraco.Examine/ContentValueSetValidator.cs rename to src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs index 9555566c53..24c9ab2c84 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine.Providers; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Services; namespace Umbraco.Examine @@ -92,18 +89,18 @@ namespace Umbraco.Examine //check for published content if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly) { - if (!valueSet.Values.TryGetValue(UmbracoExamineIndex.PublishedFieldName, out var published)) + if (!valueSet.Values.TryGetValue(UmbracoExamineFieldNames.PublishedFieldName, out var published)) return ValueSetValidationResult.Failed; if (!published[0].Equals("y")) return ValueSetValidationResult.Failed; //deal with variants, if there are unpublished variants than we need to remove them from the value set - if (valueSet.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var variesByCulture) + if (valueSet.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out var variesByCulture) && variesByCulture.Count > 0 && variesByCulture[0].Equals("y")) { //so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values - foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndex.PublishedFieldName}_")).ToList()) + foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineFieldNames.PublishedFieldName}_")).ToList()) { if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals("y")) { diff --git a/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs b/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs new file mode 100644 index 0000000000..5da21de6df --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using Examine; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Examine; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web +{ + /// + /// Extension methods for Examine. + /// + public static class ExamineExtensions + { + /// + /// Creates an containing all content from the . + /// + /// The search results. + /// The cache to fetch the content from. + /// + /// An containing all content. + /// + /// cache + /// + /// Search results are skipped if it can't be fetched from the by its integer id. + /// + public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache) + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + + var publishedSearchResults = new List(); + + foreach (var result in results) + { + if (int.TryParse(result.Id, out var contentId) && + cache.GetById(contentId) is IPublishedContent content) + { + publishedSearchResults.Add(new PublishedSearchResult(content, result.Score)); + } + } + + return publishedSearchResults; + } + + /// + /// Creates an containing all content, media or members from the . + /// + /// The search results. + /// The snapshot. + /// + /// An containing all content, media or members. + /// + /// snapshot + /// + /// Search results are skipped if it can't be fetched from the respective cache by its integer id. + /// + public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedSnapshot snapshot) + { + if (snapshot == null) throw new ArgumentNullException(nameof(snapshot)); + + var publishedSearchResults = new List(); + + foreach (var result in results) + { + if (int.TryParse(result.Id, out var contentId) && + result.Values.TryGetValue(ExamineFieldNames.CategoryFieldName, out var indexType)) + { + IPublishedContent content; + switch (indexType) + { + case IndexTypes.Content: + content = snapshot.Content.GetById(contentId); + break; + case IndexTypes.Media: + content = snapshot.Media.GetById(contentId); + break; + case IndexTypes.Member: + content = snapshot.Members.GetById(contentId); + break; + default: + continue; + } + + if (content != null) + { + publishedSearchResults.Add(new PublishedSearchResult(content, result.Score)); + } + } + } + + return publishedSearchResults; + } + } +} diff --git a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs similarity index 97% rename from src/Umbraco.Web/Search/GenericIndexDiagnostics.cs rename to src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs index cb25e1242a..c384392710 100644 --- a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs @@ -4,9 +4,8 @@ using System.Linq; using Examine; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Examine; -namespace Umbraco.Web.Search +namespace Umbraco.Examine { /// diff --git a/src/Umbraco.Infrastructure/Examine/IBackOfficeExamineSearcher.cs b/src/Umbraco.Infrastructure/Examine/IBackOfficeExamineSearcher.cs new file mode 100644 index 0000000000..719d5a33f2 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IBackOfficeExamineSearcher.cs @@ -0,0 +1,17 @@ +using Examine; +using System.Collections.Generic; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Examine +{ + /// + /// Used to search the back office for Examine indexed entities (Documents, Media and Members) + /// + public interface IBackOfficeExamineSearcher + { + IEnumerable Search(string query, + UmbracoEntityTypes entityType, + int pageSize, + long pageIndex, out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false); + } +} diff --git a/src/Umbraco.Examine/IContentValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/IContentValueSetBuilder.cs similarity index 100% rename from src/Umbraco.Examine/IContentValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/IContentValueSetBuilder.cs diff --git a/src/Umbraco.Examine/IContentValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/IContentValueSetValidator.cs similarity index 100% rename from src/Umbraco.Examine/IContentValueSetValidator.cs rename to src/Umbraco.Infrastructure/Examine/IContentValueSetValidator.cs diff --git a/src/Umbraco.Examine/IIndexCreator.cs b/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs similarity index 100% rename from src/Umbraco.Examine/IIndexCreator.cs rename to src/Umbraco.Infrastructure/Examine/IIndexCreator.cs diff --git a/src/Umbraco.Examine/IIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs similarity index 95% rename from src/Umbraco.Examine/IIndexDiagnostics.cs rename to src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs index 29d530c2d0..fa9dde25b8 100644 --- a/src/Umbraco.Examine/IIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core; namespace Umbraco.Examine { + /// /// Exposes diagnostic information about an index /// diff --git a/src/Umbraco.Infrastructure/Examine/IIndexDiagnosticsFactory.cs b/src/Umbraco.Infrastructure/Examine/IIndexDiagnosticsFactory.cs new file mode 100644 index 0000000000..f7922e64cb --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IIndexDiagnosticsFactory.cs @@ -0,0 +1,13 @@ +using Examine; + +namespace Umbraco.Examine +{ + + /// + /// Creates for an index if it doesn't implement + /// + public interface IIndexDiagnosticsFactory + { + IIndexDiagnostics Create(IIndex index); + } +} diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/IIndexPopulator.cs similarity index 100% rename from src/Umbraco.Examine/IIndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/IIndexPopulator.cs diff --git a/src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/IPublishedContentValueSetBuilder.cs similarity index 100% rename from src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/IPublishedContentValueSetBuilder.cs diff --git a/src/Umbraco.Examine/IUmbracoContentIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs similarity index 100% rename from src/Umbraco.Examine/IUmbracoContentIndex.cs rename to src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs diff --git a/src/Umbraco.Examine/IUmbracoIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs similarity index 100% rename from src/Umbraco.Examine/IUmbracoIndex.cs rename to src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs diff --git a/src/Umbraco.Examine/IUmbracoIndexConfig.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndexConfig.cs similarity index 100% rename from src/Umbraco.Examine/IUmbracoIndexConfig.cs rename to src/Umbraco.Infrastructure/Examine/IUmbracoIndexConfig.cs diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs similarity index 63% rename from src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs rename to src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs index d654e4effd..d64046a940 100644 --- a/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using Examine; -using Umbraco.Examine; - -namespace Umbraco.Web.Search +namespace Umbraco.Examine { /// /// diff --git a/src/Umbraco.Examine/IUmbracoMemberIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs similarity index 100% rename from src/Umbraco.Examine/IUmbracoMemberIndex.cs rename to src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoTreeSearcherFields.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoTreeSearcherFields.cs new file mode 100644 index 0000000000..d873d01972 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoTreeSearcherFields.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace Umbraco.Examine +{ + /// + /// Used to propagate hardcoded internal Field lists + /// + public interface IUmbracoTreeSearcherFields + { + /// + /// Propagate list of searchable fields for all node types + /// + IEnumerable GetBackOfficeFields(); + /// + /// Propagate list of searchable fields for Members + /// + IEnumerable GetBackOfficeMembersFields(); + /// + /// Propagate list of searchable fields for Media + /// + IEnumerable GetBackOfficeMediaFields(); + /// + /// Propagate list of searchable fields for Documents + /// + IEnumerable GetBackOfficeDocumentFields(); + } +} diff --git a/src/Umbraco.Examine/IValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/IValueSetBuilder.cs similarity index 100% rename from src/Umbraco.Examine/IValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/IValueSetBuilder.cs diff --git a/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs new file mode 100644 index 0000000000..9daa1705a9 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs @@ -0,0 +1,17 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// Default implementation of which returns for indexes that don't have an implementation + /// + public class IndexDiagnosticsFactory : IIndexDiagnosticsFactory + { + public virtual IIndexDiagnostics Create(IIndex index) + { + if (!(index is IIndexDiagnostics indexDiag)) + indexDiag = new GenericIndexDiagnostics(index); + return indexDiag; + } + } +} diff --git a/src/Umbraco.Examine/IndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs similarity index 100% rename from src/Umbraco.Examine/IndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/IndexPopulator.cs diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs similarity index 82% rename from src/Umbraco.Examine/IndexRebuilder.cs rename to src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs index 786aecac71..d87241e6f6 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Examine; namespace Umbraco.Examine -{ +{ /// /// Utility to rebuild all indexes ensuring minimal data queries @@ -45,6 +45,8 @@ namespace Umbraco.Examine if (indexes.Length == 0) return; + OnRebuildingIndexes(new IndexRebuildingEventArgs(indexes)); + foreach (var index in indexes) { index.CreateIndex(); // clear the index @@ -54,5 +56,11 @@ namespace Umbraco.Examine Parallel.ForEach(_populators, populator => populator.Populate(indexes)); } + /// + /// Event raised when indexes are being rebuilt + /// + public event EventHandler RebuildingIndexes; + + private void OnRebuildingIndexes(IndexRebuildingEventArgs args) => RebuildingIndexes?.Invoke(this, args); } } diff --git a/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs new file mode 100644 index 0000000000..20141a7194 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using Examine; + +namespace Umbraco.Examine +{ + public class IndexRebuildingEventArgs : EventArgs + { + public IndexRebuildingEventArgs(IEnumerable indexes) + { + Indexes = indexes; + } + + /// + /// The indexes being rebuilt + /// + public IEnumerable Indexes { get; } + } +} diff --git a/src/Umbraco.Examine/IndexTypes.cs b/src/Umbraco.Infrastructure/Examine/IndexTypes.cs similarity index 100% rename from src/Umbraco.Examine/IndexTypes.cs rename to src/Umbraco.Infrastructure/Examine/IndexTypes.cs diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs similarity index 99% rename from src/Umbraco.Examine/MediaIndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs index 1f5b11e54f..03fbe392b6 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Examine; -using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs similarity index 87% rename from src/Umbraco.Examine/MediaValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs index fd77c180dc..e9aa87a25c 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs @@ -2,7 +2,6 @@ using Examine; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -10,6 +9,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Core.Serialization; namespace Umbraco.Examine { @@ -19,16 +19,18 @@ namespace Umbraco.Examine private readonly IUserService _userService; private readonly ILogger _logger; private readonly IShortStringHelper _shortStringHelper; + private readonly IJsonSerializer _serializer; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, - IUserService userService, ILogger logger, IShortStringHelper shortStringHelper) + IUserService userService, ILogger logger, IShortStringHelper shortStringHelper, IJsonSerializer serializer) : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; _userService = userService; _logger = logger; _shortStringHelper = shortStringHelper; + _serializer = serializer; } /// @@ -48,7 +50,7 @@ namespace Umbraco.Examine ImageCropperValue cropper = null; try { - cropper = JsonConvert.DeserializeObject( + cropper = _serializer.Deserialize( m.GetValue(Constants.Conventions.Media.File)); } catch (Exception ex) @@ -77,19 +79,19 @@ namespace Umbraco.Examine { {"icon", m.ContentType.Icon?.Yield() ?? Enumerable.Empty()}, {"id", new object[] {m.Id}}, - {UmbracoExamineIndex.NodeKeyFieldName, new object[] {m.Key}}, + {UmbracoExamineFieldNames.NodeKeyFieldName, new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, {"level", new object[] {m.Level}}, {"creatorID", new object[] {m.CreatorId}}, {"sortOrder", new object[] {m.SortOrder}}, {"createDate", new object[] {m.CreateDate}}, {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", m.Name?.Yield() ?? Enumerable.Empty()}, + {UmbracoExamineFieldNames.NodeNameFieldName, m.Name?.Yield() ?? Enumerable.Empty()}, {"urlName", urlValue?.Yield() ?? Enumerable.Empty()}, {"path", m.Path?.Yield() ?? Enumerable.Empty()}, {"nodeType", m.ContentType.Id.ToString().Yield() }, {"creatorName", (m.GetCreatorProfile(_userService)?.Name ?? "??").Yield()}, - {UmbracoExamineIndex.UmbracoFileFieldName, umbracoFile.Yield()} + {UmbracoExamineFieldNames.UmbracoFileFieldName, umbracoFile.Yield()} }; foreach (var property in m.Properties) diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MemberIndexPopulator.cs similarity index 98% rename from src/Umbraco.Examine/MemberIndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/MemberIndexPopulator.cs index 26a3b0aedd..270d93d80d 100644 --- a/src/Umbraco.Examine/MemberIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MemberIndexPopulator.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Examine; -using Lucene.Net.Util; using Umbraco.Core.Models; using Umbraco.Core.Services; diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs similarity index 90% rename from src/Umbraco.Examine/MemberValueSetBuilder.cs rename to src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs index 06fe78105d..12d886eaf1 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs @@ -24,14 +24,14 @@ namespace Umbraco.Examine { {"icon", m.ContentType.Icon?.Yield() ?? Enumerable.Empty()}, {"id", new object[] {m.Id}}, - {UmbracoExamineIndex.NodeKeyFieldName, new object[] {m.Key}}, + {UmbracoExamineFieldNames.NodeKeyFieldName, new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, {"level", new object[] {m.Level}}, {"creatorID", new object[] {m.CreatorId}}, {"sortOrder", new object[] {m.SortOrder}}, {"createDate", new object[] {m.CreateDate}}, {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", m.Name?.Yield() ?? Enumerable.Empty()}, + {UmbracoExamineFieldNames.NodeNameFieldName, m.Name?.Yield() ?? Enumerable.Empty()}, {"path", m.Path?.Yield() ?? Enumerable.Empty()}, {"nodeType", m.ContentType.Id.ToString().Yield() }, {"loginName", m.Username?.Yield() ?? Enumerable.Empty()}, diff --git a/src/Umbraco.Examine/MemberValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/MemberValueSetValidator.cs similarity index 89% rename from src/Umbraco.Examine/MemberValueSetValidator.cs rename to src/Umbraco.Infrastructure/Examine/MemberValueSetValidator.cs index 0c585854e9..6ce650fc1d 100644 --- a/src/Umbraco.Examine/MemberValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/MemberValueSetValidator.cs @@ -23,7 +23,7 @@ namespace Umbraco.Examine /// /// By default these are the member fields we index /// - public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email", UmbracoExamineIndex.NodeKeyFieldName }; + public static readonly string[] DefaultMemberIndexFields = { "id", UmbracoExamineFieldNames.NodeNameFieldName, "updateDate", "loginName", "email", UmbracoExamineFieldNames.NodeKeyFieldName }; private static readonly IEnumerable ValidCategories = new[] { IndexTypes.Member }; protected override IEnumerable ValidIndexCategories => ValidCategories; diff --git a/src/Umbraco.Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs similarity index 100% rename from src/Umbraco.Examine/PublishedContentIndexPopulator.cs rename to src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs diff --git a/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs new file mode 100644 index 0000000000..c1932e0514 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs @@ -0,0 +1,133 @@ +using Examine; +using Examine.Search; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Umbraco.Core; + +namespace Umbraco.Examine +{ + public static class UmbracoExamineExtensions + { + /// + /// Matches a culture iso name suffix + /// + /// + /// myFieldName_en-us will match the "en-us" + /// + internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled); + + + + //TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression + + /// + /// Returns all index fields that are culture specific (suffixed) + /// + /// + /// + /// + public static IEnumerable GetCultureFields(this IUmbracoIndex index, string culture) + { + var allFields = index.GetFields(); + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var field in allFields) + { + var match = CultureIsoCodeFieldNameMatchExpression.Match(field); + if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) + yield return field; + } + } + + /// + /// Returns all index fields that are culture specific (suffixed) or invariant + /// + /// + /// + /// + public static IEnumerable GetCultureAndInvariantFields(this IUmbracoIndex index, string culture) + { + var allFields = index.GetFields(); + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var field in allFields) + { + var match = CultureIsoCodeFieldNameMatchExpression.Match(field); + if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) + { + yield return field; //matches this culture field + } + else if (!match.Success) + { + yield return field; //matches no culture field (invariant) + } + + } + } + + public static IBooleanOperation Id(this IQuery query, int id) + { + var fieldQuery = query.Id(id.ToInvariantString()); + return fieldQuery; + } + + /// + /// Query method to search on parent id + /// + /// + /// + /// + public static IBooleanOperation ParentId(this IQuery query, int id) + { + var fieldQuery = query.Field("parentID", id); + return fieldQuery; + } + + /// + /// Query method to search on node name + /// + /// + /// + /// + public static IBooleanOperation NodeName(this IQuery query, string nodeName) + { + var fieldQuery = query.Field(UmbracoExamineFieldNames.NodeNameFieldName, (IExamineValue)new ExamineValue(Examineness.Explicit, nodeName)); + return fieldQuery; + } + + /// + /// Query method to search on node name + /// + /// + /// + /// + public static IBooleanOperation NodeName(this IQuery query, IExamineValue nodeName) + { + var fieldQuery = query.Field(UmbracoExamineFieldNames.NodeNameFieldName, nodeName); + return fieldQuery; + } + + /// + /// Query method to search on node type alias + /// + /// + /// + /// + public static IBooleanOperation NodeTypeAlias(this IQuery query, string nodeTypeAlias) + { + var fieldQuery = query.Field(ExamineFieldNames.ItemTypeFieldName, (IExamineValue)new ExamineValue(Examineness.Explicit, nodeTypeAlias)); + return fieldQuery; + } + + /// + /// Query method to search on node type alias + /// + /// + /// + /// + public static IBooleanOperation NodeTypeAlias(this IQuery query, IExamineValue nodeTypeAlias) + { + var fieldQuery = query.Field(ExamineFieldNames.ItemTypeFieldName, nodeTypeAlias); + return fieldQuery; + } + + } +} diff --git a/src/Umbraco.Infrastructure/Examine/UmbracoExamineFieldNames.cs b/src/Umbraco.Infrastructure/Examine/UmbracoExamineFieldNames.cs new file mode 100644 index 0000000000..9b6d086ae4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/UmbracoExamineFieldNames.cs @@ -0,0 +1,25 @@ +using Examine; + +namespace Umbraco.Examine +{ + public static class UmbracoExamineFieldNames + { + /// + /// Used to store the path of a content object + /// + public const string IndexPathFieldName = ExamineFieldNames.SpecialFieldPrefix + "Path"; + public const string NodeKeyFieldName = ExamineFieldNames.SpecialFieldPrefix + "Key"; + public const string UmbracoFileFieldName = "umbracoFileSrc"; + public const string IconFieldName = ExamineFieldNames.SpecialFieldPrefix + "Icon"; + public const string PublishedFieldName = ExamineFieldNames.SpecialFieldPrefix + "Published"; + + /// + /// The prefix added to a field when it is duplicated in order to store the original raw value. + /// + public const string RawFieldPrefix = ExamineFieldNames.SpecialFieldPrefix + "Raw_"; + + public const string VariesByCultureFieldName = ExamineFieldNames.SpecialFieldPrefix + "VariesByCulture"; + + public const string NodeNameFieldName = "nodeName"; + } +} diff --git a/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs b/src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs similarity index 88% rename from src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs rename to src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs index 1e7b51aa14..0ea563e9cc 100644 --- a/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs +++ b/src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs @@ -33,7 +33,7 @@ namespace Umbraco.Examine new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), - new FieldDefinition(UmbracoExamineIndex.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase), + new FieldDefinition(UmbracoExamineFieldNames.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("version", FieldDefinitionTypes.Raw), new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("template", FieldDefinitionTypes.Raw), @@ -42,10 +42,10 @@ namespace Umbraco.Examine new FieldDefinition("email", FieldDefinitionTypes.EmailAddress), - new FieldDefinition(UmbracoExamineIndex.PublishedFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(UmbracoExamineIndex.IndexPathFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(UmbracoExamineIndex.IconFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(UmbracoContentIndex.VariesByCultureFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineFieldNames.PublishedFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineFieldNames.IndexPathFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineFieldNames.IconFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineFieldNames.VariesByCultureFieldName, FieldDefinitionTypes.Raw), }; @@ -76,7 +76,7 @@ namespace Umbraco.Examine if (!fieldName.Contains("_") || !fieldName.Contains("-")) return false; - var match = ExamineExtensions.CultureIsoCodeFieldNameMatchExpression.Match(fieldName); + var match = UmbracoExamineExtensions.CultureIsoCodeFieldNameMatchExpression.Match(fieldName); if (match.Success && match.Groups.Count == 3) { var nonCultureFieldName = match.Groups[1].Value; diff --git a/src/Umbraco.Examine/UmbracoIndexConfig.cs b/src/Umbraco.Infrastructure/Examine/UmbracoIndexConfig.cs similarity index 100% rename from src/Umbraco.Examine/UmbracoIndexConfig.cs rename to src/Umbraco.Infrastructure/Examine/UmbracoIndexConfig.cs diff --git a/src/Umbraco.Examine/ValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/ValueSetValidator.cs similarity index 97% rename from src/Umbraco.Examine/ValueSetValidator.cs rename to src/Umbraco.Infrastructure/Examine/ValueSetValidator.cs index 4db251c0f1..f6538dfacd 100644 --- a/src/Umbraco.Examine/ValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/ValueSetValidator.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine.Providers; using Umbraco.Core; namespace Umbraco.Examine diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollection.cs b/src/Umbraco.Infrastructure/HealthCheck/HealthCheckNotificationMethodCollection.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollection.cs rename to src/Umbraco.Infrastructure/HealthCheck/HealthCheckNotificationMethodCollection.cs diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs b/src/Umbraco.Infrastructure/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs similarity index 50% rename from src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs rename to src/Umbraco.Infrastructure/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs index b78c188660..d498716b71 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs +++ b/src/Umbraco.Infrastructure/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs @@ -3,7 +3,7 @@ using Umbraco.Web.HealthCheck.NotificationMethods; namespace Umbraco.Web.HealthCheck { - internal class HealthCheckNotificationMethodCollectionBuilder : LazyCollectionBuilderBase + public class HealthCheckNotificationMethodCollectionBuilder : LazyCollectionBuilderBase { protected override HealthCheckNotificationMethodCollectionBuilder This => this; } diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs b/src/Umbraco.Infrastructure/HealthCheck/HealthCheckResults.cs similarity index 99% rename from src/Umbraco.Web/HealthCheck/HealthCheckResults.cs rename to src/Umbraco.Infrastructure/HealthCheck/HealthCheckResults.cs index 61028699f0..8f3c05f5bd 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs +++ b/src/Umbraco.Infrastructure/HealthCheck/HealthCheckResults.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using HeyRed.MarkdownSharp; -using Umbraco.Core.Composing; +using Umbraco.Composing; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Logging; diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs similarity index 86% rename from src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs rename to src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs index ddde3f7b98..f76a69c0fa 100644 --- a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs +++ b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs @@ -1,11 +1,11 @@ using System; -using System.Collections.Generic; using System.Net.Mail; using System.Threading; using System.Threading.Tasks; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.HealthChecks; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Services; @@ -18,8 +18,9 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods private readonly IRuntimeState _runtimeState; private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; + private readonly IContentSettings _contentSettings; - public EmailNotificationMethod(ILocalizedTextService textService, IRuntimeState runtimeState, ILogger logger, IGlobalSettings globalSettings) + public EmailNotificationMethod(ILocalizedTextService textService, IRuntimeState runtimeState, ILogger logger, IGlobalSettings globalSettings, IHealthChecks healthChecks, IContentSettings contentSettings) : base(healthChecks) { var recipientEmail = Settings["recipientEmail"]?.Value; if (string.IsNullOrWhiteSpace(recipientEmail)) @@ -34,6 +35,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods _runtimeState = runtimeState; _logger = logger; _globalSettings = globalSettings; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } public string RecipientEmail { get; } @@ -72,7 +74,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods private MailMessage CreateMailMessage(string subject, string message) { - var to = Current.Configs.Settings().Content.NotificationEmailAddress; + var to = _contentSettings.NotificationEmailAddress; if (string.IsNullOrWhiteSpace(subject)) subject = "Umbraco Health Check Status"; diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/IHealthCheckNotificationMethod.cs b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/IHealthCheckNotificationMethod.cs similarity index 100% rename from src/Umbraco.Web/HealthCheck/NotificationMethods/IHealthCheckNotificationMethod.cs rename to src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/IHealthCheckNotificationMethod.cs diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/NotificationMethodBase.cs similarity index 89% rename from src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs rename to src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/NotificationMethodBase.cs index cf71be3c34..ff6fbe2371 100644 --- a/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs +++ b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/NotificationMethodBase.cs @@ -2,16 +2,13 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.HealthChecks; namespace Umbraco.Web.HealthCheck.NotificationMethods { public abstract class NotificationMethodBase : IHealthCheckNotificationMethod { - protected NotificationMethodBase() + protected NotificationMethodBase(IHealthChecks healthCheckConfig) { var type = GetType(); var attribute = type.GetCustomAttribute(); @@ -21,7 +18,6 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods return; } - var healthCheckConfig = Current.Configs.HealthChecks(); var notificationMethods = healthCheckConfig.NotificationSettings.NotificationMethods; var notificationMethod = notificationMethods[attribute.Alias]; if (notificationMethod == null) diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Infrastructure/IPublishedContentQuery.cs similarity index 55% rename from src/Umbraco.Web/IPublishedContentQuery.cs rename to src/Umbraco.Infrastructure/IPublishedContentQuery.cs index 8a8d678aba..c5f49c3e0b 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/IPublishedContentQuery.cs @@ -8,19 +8,23 @@ using Umbraco.Core.Xml; namespace Umbraco.Web { - using Examine = global::Examine; - /// /// Query methods used for accessing strongly typed content in templates /// public interface IPublishedContentQuery { + + + IPublishedContent Content(int id); IPublishedContent Content(Guid id); IPublishedContent Content(Udi id); + IPublishedContent Content(object id); IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars); IEnumerable Content(IEnumerable ids); IEnumerable Content(IEnumerable ids); + + IEnumerable Content(IEnumerable ids); IEnumerable ContentAtXPath(string xpath, params XPathVariable[] vars); IEnumerable ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars); IEnumerable ContentAtRoot(); @@ -28,59 +32,73 @@ namespace Umbraco.Web IPublishedContent Media(int id); IPublishedContent Media(Guid id); IPublishedContent Media(Udi id); + + IPublishedContent Media(object id); IEnumerable Media(IEnumerable ids); + IEnumerable Media(IEnumerable ids); IEnumerable Media(IEnumerable ids); IEnumerable MediaAtRoot(); /// /// Searches content. /// - /// Term to search. - /// Optional culture. - /// Optional index name. + /// The term to search. + /// The culture (defaults to a culture insensitive search). + /// The name of the index to search (defaults to ). + /// + /// The search results. + /// /// /// - /// When the is not specified or is *, all cultures are searched. + /// When the is not specified or is *, all cultures are searched. /// To search for only invariant documents and fields use null. /// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. /// /// While enumerating results, the ambient culture is changed to be the searched culture. /// - IEnumerable Search(string term, string culture = "*", string indexName = null); + IEnumerable Search(string term, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName); /// /// Searches content. /// - /// Term to search. - /// Numbers of items to skip. - /// Numbers of items to return. - /// Total number of matching items. - /// Optional culture. - /// Optional index name. + /// The term to search. + /// The amount of results to skip. + /// The amount of results to take/return. + /// The total amount of records. + /// The culture (defaults to a culture insensitive search). + /// The name of the index to search (defaults to ). + /// + /// The search results. + /// /// /// - /// When the is not specified or is *, all cultures are searched. + /// When the is not specified or is *, all cultures are searched. /// To search for only invariant documents and fields use null. /// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. /// /// While enumerating results, the ambient culture is changed to be the searched culture. /// - IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null); + IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName); /// - /// Executes the query and converts the results to PublishedSearchResult. + /// Executes the query and converts the results to . /// - /// - /// While enumerating results, the ambient culture is changed to be the searched culture. - /// + /// The query. + /// + /// The search results. + /// IEnumerable Search(IQueryExecutor query); /// - /// Executes the query and converts the results to PublishedSearchResult. + /// Executes the query and converts the results to . /// - /// - /// While enumerating results, the ambient culture is changed to be the searched culture. - /// + /// The query. + /// The amount of results to skip. + /// The amount of results to take/return. + /// The total amount of records. + /// + /// The search results. + /// IEnumerable Search(IQueryExecutor query, int skip, int take, out long totalRecords); } } diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs similarity index 57% rename from src/Umbraco.Web/Install/InstallHelper.cs rename to src/Umbraco.Infrastructure/Intall/InstallHelper.cs index 46efb69124..a5b4f71d8e 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs @@ -2,14 +2,17 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Web; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Cookie; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; +using Umbraco.Core.Models; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Web.Composing; +using Umbraco.Core.Serialization; +using Umbraco.Core.Services; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install @@ -18,21 +21,38 @@ namespace Umbraco.Web.Install { private static HttpClient _httpClient; private readonly DatabaseBuilder _databaseBuilder; - private readonly HttpContextBase _httpContext; 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(IUmbracoContextAccessor umbracoContextAccessor, - DatabaseBuilder databaseBuilder, - ILogger logger, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) + public InstallHelper(DatabaseBuilder databaseBuilder, + ILogger logger, + IGlobalSettings globalSettings, + IUmbracoVersion umbracoVersion, + IConnectionStrings connectionStrings, + IInstallationService installationService, + ICookieManager cookieManager, + IUserAgentProvider userAgentProvider, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IJsonSerializer jsonSerializer) { - _httpContext = umbracoContextAccessor.UmbracoContext.HttpContext; _logger = logger; _globalSettings = globalSettings; _umbracoVersion = umbracoVersion; _databaseBuilder = databaseBuilder; + _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); + _installationService = installationService; + _cookieManager = cookieManager; + _userAgentProvider = userAgentProvider; + _umbracoDatabaseFactory = umbracoDatabaseFactory; + _jsonSerializer = jsonSerializer; } public InstallationType GetInstallationType() @@ -40,16 +60,16 @@ namespace Umbraco.Web.Install return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value; } - internal void InstallStatus(bool isCompleted, string errorMsg) + public async Task InstallStatus(bool isCompleted, string errorMsg) { try { - var userAgent = _httpContext.Request.UserAgent; + var userAgent = _userAgentProvider.GetUserAgent(); // Check for current install Id var installId = Guid.NewGuid(); - var installCookie = _httpContext.Request.GetCookieValue(Constants.Web.InstallerCookieName); + var installCookie = _cookieManager.GetCookieValue(Constants.Web.InstallerCookieName); if (string.IsNullOrEmpty(installCookie) == false) { if (Guid.TryParse(installCookie, out installId)) @@ -58,29 +78,29 @@ namespace Umbraco.Web.Install if (installId == Guid.Empty) installId = Guid.NewGuid(); } + else + { + installId = Guid.NewGuid(); // Guid.TryParse will have reset installId to Guid.Empty + } } - _httpContext.Response.Cookies.Set(new HttpCookie(Constants.Web.InstallerCookieName, "1")); + + _cookieManager.SetCookieValue(Constants.Web.InstallerCookieName, installId.ToString()); var dbProvider = string.Empty; if (IsBrandNewInstall == false) { // 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 check = new org.umbraco.update.CheckForUpgrade(); - check.Install(installId, - IsBrandNewInstall == false, - isCompleted, - DateTime.Now, - _umbracoVersion.Current.Major, - _umbracoVersion.Current.Minor, - _umbracoVersion.Current.Build, - _umbracoVersion.Comment, - errorMsg, - userAgent, - dbProvider); + var installLog = new InstallLog(installId: installId, isUpgrade: IsBrandNewInstall == false, + installCompleted: isCompleted, timestamp: DateTime.Now, versionMajor: _umbracoVersion.Current.Major, + versionMinor: _umbracoVersion.Current.Minor, versionPatch: _umbracoVersion.Current.Build, + versionComment: _umbracoVersion.Comment, error: errorMsg, userAgent: userAgent, + dbProvider: dbProvider); + + await _installationService.LogInstall(installLog); } catch (Exception ex) { @@ -88,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 /// @@ -112,7 +115,7 @@ namespace Umbraco.Web.Install { get { - var databaseSettings = Current.Configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; + var databaseSettings = _connectionStrings[Constants.System.UmbracoConnectionName]; if (_globalSettings.ConfigurationStatus.IsNullOrWhiteSpace() && databaseSettings.IsConnectionStringConfigured() == false) { @@ -132,7 +135,7 @@ namespace Umbraco.Web.Install } } - internal IEnumerable GetStarterKits() + public IEnumerable GetStarterKits() { if (_httpClient == null) _httpClient = new HttpClient(); @@ -145,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 78% rename from src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs index 7ea8f088fe..fb8201600c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs @@ -1,8 +1,7 @@ using System.Linq; using System.Threading.Tasks; -using System.Web.Configuration; using System.Xml.Linq; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Security; using Umbraco.Web.Install.Models; @@ -13,18 +12,26 @@ 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, IMachineKeyConfig machineKeyConfig) + { + _ioHelper = ioHelper; + _machineKeyConfig = machineKeyConfig; + } + public override string View => HasMachineKey() == false ? base.View : ""; /// /// 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; } /// @@ -37,7 +44,7 @@ namespace Umbraco.Web.Install.InstallSteps if (model.HasValue && model.Value == false) return Task.FromResult(null); //install the machine key - var fileName = Current.IOHelper.MapPath($"{Current.IOHelper.Root}/web.config"); + var fileName = _ioHelper.MapPath($"{_ioHelper.Root}/web.config"); var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); // we only want to get the element that is under the root, (there may be more under tags we don't want them) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseConfigureStep.cs similarity index 89% rename from src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseConfigureStep.cs index 8d1e161811..3a978cc47a 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseConfigureStep.cs @@ -1,24 +1,26 @@ using System; using System.Threading.Tasks; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Web.Install.Models; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.NewInstall, "DatabaseConfigure", "database", 10, "Setting up a database, so Umbraco has a place to store your website", PerformsAppRestart = true)] - internal class DatabaseConfigureStep : InstallSetupStep + public class DatabaseConfigureStep : InstallSetupStep { private readonly DatabaseBuilder _databaseBuilder; private readonly ILogger _logger; + private readonly IConnectionStrings _connectionStrings; - public DatabaseConfigureStep(DatabaseBuilder databaseBuilder) + public DatabaseConfigureStep(DatabaseBuilder databaseBuilder, IConnectionStrings connectionStrings) { _databaseBuilder = databaseBuilder; + _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); } public override Task ExecuteAsync(DatabaseModel database) @@ -72,7 +74,7 @@ namespace Umbraco.Web.Install.InstallSteps private bool ShouldDisplayView() { //If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading. - var databaseSettings = Current.Configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; + var databaseSettings = _connectionStrings[Constants.System.UmbracoConnectionName]; if (databaseSettings.IsConnectionStringConfigured()) { diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseInstallStep.cs similarity index 75% rename from src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseInstallStep.cs index 7d8c7f1226..8c73e63b78 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseInstallStep.cs @@ -1,29 +1,32 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; -using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, "DatabaseInstall", 11, "")] - internal class DatabaseInstallStep : InstallSetupStep + public class DatabaseInstallStep : InstallSetupStep { private readonly DatabaseBuilder _databaseBuilder; private readonly IRuntimeState _runtime; private readonly ILogger _logger; + private readonly IIOHelper _ioHelper; + private readonly IConnectionStrings _connectionStrings; - public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger) + public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings) { _databaseBuilder = databaseBuilder; _runtime = runtime; _logger = logger; + _ioHelper = ioHelper; + _connectionStrings = connectionStrings; } public override Task ExecuteAsync(object model) @@ -40,7 +43,7 @@ namespace Umbraco.Web.Install.InstallSteps if (result.RequiresUpgrade == false) { - HandleConnectionStrings(_logger); + HandleConnectionStrings(_logger, _ioHelper, _connectionStrings); return Task.FromResult(null); } @@ -51,12 +54,18 @@ namespace Umbraco.Web.Install.InstallSteps })); } - internal static void HandleConnectionStrings(ILogger logger) + internal static void HandleConnectionStrings(ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings) { + + + var databaseSettings = connectionStrings[Constants.System.UmbracoConnectionName]; + + + // Remove legacy umbracoDbDsn configuration setting if it exists and connectionstring also exists - if (ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName] != null) + if (databaseSettings != null) { - GlobalSettings.RemoveSetting(Constants.System.UmbracoConnectionName, Current.IOHelper); + connectionStrings.RemoveConnectionString(Constants.System.UmbracoConnectionName, ioHelper); } else { diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseUpgradeStep.cs similarity index 84% rename from src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseUpgradeStep.cs index 34d630c6b6..3cd3c1ca56 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/DatabaseUpgradeStep.cs @@ -1,11 +1,12 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Migrations.Upgrade; -using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; using Umbraco.Web.Migrations.PostMigrations; @@ -13,21 +14,25 @@ namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.Upgrade | InstallationType.NewInstall, "DatabaseUpgrade", 12, "")] - internal class DatabaseUpgradeStep : InstallSetupStep + public class DatabaseUpgradeStep : InstallSetupStep { private readonly DatabaseBuilder _databaseBuilder; private readonly IRuntimeState _runtime; private readonly ILogger _logger; private readonly IUmbracoVersion _umbracoVersion; private readonly IGlobalSettings _globalSettings; + private readonly IConnectionStrings _connectionStrings; + private readonly IIOHelper _ioHelper; - public DatabaseUpgradeStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IUmbracoVersion umbracoVersion, IGlobalSettings globalSettings) + public DatabaseUpgradeStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IUmbracoVersion umbracoVersion, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, IIOHelper ioHelper) { _databaseBuilder = databaseBuilder; _runtime = runtime; _logger = logger; _umbracoVersion = umbracoVersion; _globalSettings = globalSettings; + _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); + _ioHelper = ioHelper; } public override Task ExecuteAsync(object model) @@ -50,7 +55,7 @@ namespace Umbraco.Web.Install.InstallSteps throw new InstallException("The database failed to upgrade. ERROR: " + result.Message); } - DatabaseInstallStep.HandleConnectionStrings(_logger); + DatabaseInstallStep.HandleConnectionStrings(_logger, _ioHelper, _connectionStrings); } return Task.FromResult(null); @@ -69,7 +74,7 @@ namespace Umbraco.Web.Install.InstallSteps return false; } - var databaseSettings = Current.Configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; + var databaseSettings = _connectionStrings[Constants.System.UmbracoConnectionName]; if (databaseSettings.IsConnectionStringConfigured()) { diff --git a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs similarity index 74% rename from src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs index 3187953989..17043f18a5 100644 --- a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs @@ -1,41 +1,31 @@ using System.Threading.Tasks; -using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; -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 HttpContextBase _httpContext; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly InstallHelper _installHelper; private readonly IGlobalSettings _globalSettings; - private readonly IUserService _userService; private readonly IUmbracoVersion _umbracoVersion; - public SetUmbracoVersionStep(HttpContextBase httpContext, InstallHelper installHelper, IGlobalSettings globalSettings, IUserService userService, IUmbracoVersion umbracoVersion) + public SetUmbracoVersionStep(IUmbracoContextAccessor umbracoContextAccessor, InstallHelper installHelper, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) { - _httpContext = httpContext; + _umbracoContextAccessor = umbracoContextAccessor; _installHelper = installHelper; _globalSettings = globalSettings; - _userService = userService; _umbracoVersion = umbracoVersion; } public override Task ExecuteAsync(object model) { - var security = new WebSecurity(_httpContext, _userService, _globalSettings); - + 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 84% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs index 539b261cf3..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 HttpContextBase _httContext; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IPackagingService _packagingService; - public StarterKitInstallStep(HttpContextBase httContext, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) + public StarterKitInstallStep(IUmbracoApplicationLifetime umbracoApplicationLifetime, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) { - _httContext = httContext; + _umbracoApplicationLifetime = umbracoApplicationLifetime; _umbracoContextAccessor = umbracoContextAccessor; _packagingService = packagingService; } @@ -34,7 +33,9 @@ namespace Umbraco.Web.Install.InstallSteps InstallBusinessLogic(packageId); - UmbracoApplication.Restart(_httContext); + _umbracoApplicationLifetime.Restart(); + + return Task.FromResult(null); } diff --git a/src/Umbraco.Web/Macros/MacroTagParser.cs b/src/Umbraco.Infrastructure/Macros/MacroTagParser.cs similarity index 96% rename from src/Umbraco.Web/Macros/MacroTagParser.cs rename to src/Umbraco.Infrastructure/Macros/MacroTagParser.cs index 7dea3674b0..2cbd84e20a 100644 --- a/src/Umbraco.Web/Macros/MacroTagParser.cs +++ b/src/Umbraco.Infrastructure/Macros/MacroTagParser.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Macros /// /// Parses the macro syntax in a string and renders out it's contents /// - internal class MacroTagParser + public class MacroTagParser { private static readonly Regex MacroRteContent = new Regex(@"()", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline); @@ -35,7 +35,7 @@ namespace Umbraco.Web.Macros /// {/div} /// /// - internal static string FormatRichTextPersistedDataForEditor(string persistedContent, IDictionary htmlAttributes) + public static string FormatRichTextPersistedDataForEditor(string persistedContent, IDictionary htmlAttributes) { return MacroPersistedFormat.Replace(persistedContent, match => { @@ -92,7 +92,7 @@ namespace Umbraco.Web.Macros /// since this is exactly how we need to persist it to the db. /// /// - internal static string FormatRichTextContentForPersistence(string rteContent) + public static string FormatRichTextContentForPersistence(string rteContent) { if (string.IsNullOrEmpty(rteContent)) { @@ -145,7 +145,7 @@ namespace Umbraco.Web.Macros /// This method simply parses the macro contents, it does not create a string or result, /// this is up to the developer calling this method to implement this with the callbacks. /// - internal static void ParseMacros( + public static void ParseMacros( string text, Action textFoundCallback, Action> macroFoundCallback ) diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 2074409ec8..987d3a98fb 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -220,11 +220,5 @@ namespace Umbraco.Core.Manifest return manifest; } - - // purely for tests - public IEnumerable ParseGridEditors(string text) - { - return _jsonSerializer.Deserialize>(text); - } } } 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/Media/UploadAutoFillProperties.cs b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs index 4c02111ccd..665693d91f 100644 --- a/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs @@ -17,13 +17,13 @@ namespace Umbraco.Web.Media { private readonly IMediaFileSystem _mediaFileSystem; private readonly ILogger _logger; - private readonly IContentSection _contentSection; + private readonly IContentSettings _contentSettings; - public UploadAutoFillProperties(IMediaFileSystem mediaFileSystem, ILogger logger, IContentSection contentSection) + public UploadAutoFillProperties(IMediaFileSystem mediaFileSystem, ILogger logger, IContentSettings contentSettings) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _contentSection = contentSection ?? throw new ArgumentNullException(nameof(contentSection)); + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } /// @@ -68,7 +68,7 @@ namespace Umbraco.Web.Media using (var filestream = _mediaFileSystem.OpenFile(filepath)) { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); - var size = _contentSection.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null; + var size = _contentSettings.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null; SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } @@ -102,7 +102,7 @@ namespace Umbraco.Web.Media else { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); - var size = _contentSection.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null; + var size = _contentSettings.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null; SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/DeleteIndexBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/DeleteIndexBuilder.cs index b3cacb2f95..4aced4378c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/DeleteIndexBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/DeleteIndexBuilder.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Migrations.Expressions.Delete.Index { public class DeleteIndexBuilder : ExpressionBuilderBase, - IDeleteIndexForTableBuilder, IDeleteIndexOnColumnBuilder + IDeleteIndexForTableBuilder, IExecutableBuilder { public DeleteIndexBuilder(DeleteIndexExpression expression) : base(expression) @@ -14,28 +14,10 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.Index /// public void Do() => Expression.Execute(); - public IndexColumnDefinition CurrentColumn { get; set; } - - public IDeleteIndexOnColumnBuilder OnTable(string tableName) + public IExecutableBuilder OnTable(string tableName) { Expression.Index.TableName = tableName; return this; } - - /// - public IExecutableBuilder OnColumn(string columnName) - { - var column = new IndexColumnDefinition { Name = columnName }; - Expression.Index.Columns.Add(column); - return new ExecutableBuilder(Expression); - } - - /// - public IExecutableBuilder OnColumns(params string[] columnNames) - { - foreach (string columnName in columnNames) - Expression.Index.Columns.Add(new IndexColumnDefinition { Name = columnName }); - return new ExecutableBuilder(Expression); - } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexForTableBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexForTableBuilder.cs index 8501c6b02f..8251107cbb 100644 --- a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexForTableBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexForTableBuilder.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Migrations.Expressions.Delete.Index +using Umbraco.Core.Migrations.Expressions.Common; + +namespace Umbraco.Core.Migrations.Expressions.Delete.Index { /// /// Builds a Delete expression. @@ -8,6 +10,6 @@ /// /// Specifies the table of the index to delete. /// - IDeleteIndexOnColumnBuilder OnTable(string tableName); + IExecutableBuilder OnTable(string tableName); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexOnColumnBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexOnColumnBuilder.cs deleted file mode 100644 index 3aa877bf8e..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Expressions/Delete/Index/IDeleteIndexOnColumnBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Umbraco.Core.Migrations.Expressions.Common; - -namespace Umbraco.Core.Migrations.Expressions.Delete.Index -{ - /// - /// Builds a Delete expression. - /// - public interface IDeleteIndexOnColumnBuilder : IFluentBuilder, IExecutableBuilder - { - /// - /// Specifies the column of the index. - /// - [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")] - IExecutableBuilder OnColumn(string columnName); - - /// - /// Specifies the column of the index. - /// - [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")] - IExecutableBuilder OnColumns(params string[] columnNames); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs index 8b1e6d741b..b92a8499fa 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs @@ -28,6 +28,7 @@ namespace Umbraco.Core.Migrations.Install private readonly IIOHelper _ioHelper; private readonly IUmbracoVersion _umbracoVersion; private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator; + private readonly IConnectionStrings _connectionStrings; private DatabaseSchemaResult _databaseSchemaValidationResult; @@ -44,7 +45,8 @@ namespace Umbraco.Core.Migrations.Install IKeyValueService keyValueService, IIOHelper ioHelper, IUmbracoVersion umbracoVersion, - IDbProviderFactoryCreator dbProviderFactoryCreator) + IDbProviderFactoryCreator dbProviderFactoryCreator, + IConnectionStrings connectionStrings) { _scopeProvider = scopeProvider; _globalSettings = globalSettings; @@ -56,6 +58,7 @@ namespace Umbraco.Core.Migrations.Install _ioHelper = ioHelper; _umbracoVersion = umbracoVersion; _dbProviderFactoryCreator = dbProviderFactoryCreator; + _connectionStrings = connectionStrings; } #region Status @@ -138,12 +141,12 @@ namespace Umbraco.Core.Migrations.Install /// public void ConfigureEmbeddedDatabaseConnection() { - ConfigureEmbeddedDatabaseConnection(_databaseFactory, _ioHelper, _logger); + ConfigureEmbeddedDatabaseConnection(_databaseFactory, _ioHelper); } - private void ConfigureEmbeddedDatabaseConnection(IUmbracoDatabaseFactory factory, IIOHelper ioHelper, ILogger logger) + private void ConfigureEmbeddedDatabaseConnection(IUmbracoDatabaseFactory factory, IIOHelper ioHelper) { - SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe, ioHelper, logger); + _connectionStrings.SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe, ioHelper); var path = Path.Combine(ioHelper.GetRootDirectorySafe(), "App_Data", "Umbraco.sdf"); if (File.Exists(path) == false) @@ -166,7 +169,7 @@ namespace Umbraco.Core.Migrations.Install { const string providerName = Constants.DbProviderNames.SqlServer; - SaveConnectionString(connectionString, providerName, _ioHelper, _logger); + _connectionStrings.SaveConnectionString(connectionString, providerName, _ioHelper); _databaseFactory.Configure(connectionString, providerName); } @@ -182,7 +185,7 @@ namespace Umbraco.Core.Migrations.Install { var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out var providerName); - SaveConnectionString(connectionString, providerName, _ioHelper, _logger); + _connectionStrings.SaveConnectionString(connectionString, providerName, _ioHelper); _databaseFactory.Configure(connectionString, providerName); } @@ -213,7 +216,7 @@ namespace Umbraco.Core.Migrations.Install public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName) { var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName); - SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer, _ioHelper, _logger); + _connectionStrings.SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer, _ioHelper); _databaseFactory.Configure(connectionString, Constants.DbProviderNames.SqlServer); } @@ -282,75 +285,8 @@ namespace Umbraco.Core.Migrations.Install return server.ToLower().StartsWith("tcp:".ToLower()); } - /// - /// Saves the connection string as a proper .net connection string in web.config. - /// - /// Saves the ConnectionString in the very nasty 'medium trust'-supportive way. - /// The connection string. - /// The provider name. - /// A logger. - private static void SaveConnectionString(string connectionString, string providerName, IIOHelper ioHelper, ILogger logger) - { - if (connectionString == null) throw new ArgumentNullException(nameof(connectionString)); - if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionString)); - if (providerName == null) throw new ArgumentNullException(nameof(providerName)); - if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(providerName)); - var fileSource = "web.config"; - var fileName = ioHelper.MapPath(ioHelper.Root +"/" + fileSource); - var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); - if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root)."); - - var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault(); - if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings)."); - - // handle configSource - var configSourceAttribute = connectionStrings.Attribute("configSource"); - if (configSourceAttribute != null) - { - fileSource = configSourceAttribute.Value; - fileName = ioHelper.MapPath(ioHelper.Root + "/" + fileSource); - - if (!File.Exists(fileName)) - throw new Exception($"Invalid configSource \"{fileSource}\" (no such file)."); - - xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); - if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root)."); - - connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault(); - if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings)."); - } - - // create or update connection string - var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name")?.Value == Constants.System.UmbracoConnectionName); - if (setting == null) - { - connectionStrings.Add(new XElement("add", - new XAttribute("name", Constants.System.UmbracoConnectionName), - new XAttribute("connectionString", connectionString), - new XAttribute("providerName", providerName))); - } - else - { - AddOrUpdateAttribute(setting, "connectionString", connectionString); - AddOrUpdateAttribute(setting, "providerName", providerName); - } - - // save - logger.Info("Saving connection string to {ConfigFile}.", fileSource); - xml.Save(fileName, SaveOptions.DisableFormatting); - logger.Info("Saved connection string to {ConfigFile}.", fileSource); - } - - private static void AddOrUpdateAttribute(XElement element, string name, string value) - { - var attribute = element.Attribute(name); - if (attribute == null) - element.Add(new XAttribute(name, value)); - else - attribute.Value = value; - } #endregion diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index bd0b733623..da6574670b 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -155,6 +155,8 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Domains, Name = "Domains" }); _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.KeyValues, Name = "KeyValues" }); _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Languages, Name = "Languages" }); + + _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MainDom, Name = "MainDom" }); } private void CreateContentTypeData() diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs index bf07e4d08f..f4c6150073 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; @@ -84,10 +85,18 @@ namespace Umbraco.Core.Migrations protected void ReplaceColumn(string tableName, string currentName, string newName) { - AddColumn(tableName, newName, out var sqls); - Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do(); - foreach (var sql in sqls) Execute.Sql(sql).Do(); - Delete.Column(currentName).FromTable(tableName).Do(); + if (DatabaseType.IsSqlCe()) + { + AddColumn(tableName, newName, out var sqls); + Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do(); + foreach (var sql in sqls) Execute.Sql(sql).Do(); + Delete.Column(currentName).FromTable(tableName).Do(); + } + else + { + Execute.Sql(SqlSyntax.FormatColumnRename(tableName, currentName, newName)).Do(); + AlterColumn(tableName, newName); + } } protected bool TableExists(string tableName) diff --git a/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs b/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs new file mode 100644 index 0000000000..4f176c797f --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs @@ -0,0 +1,25 @@ +using Umbraco.Core; +using Umbraco.Core.Cookie; +using Umbraco.Core.Migrations; + +namespace Umbraco.Web.Migrations.PostMigrations +{ + /// + /// Clears Csrf tokens. + /// + public class ClearCsrfCookies : IMigration + { + private readonly ICookieManager _cookieManager; + + public ClearCsrfCookies(ICookieManager cookieManager) + { + _cookieManager = cookieManager; + } + + public void Migrate() + { + _cookieManager.ExpireCookie(Constants.Web.AngularCookieName); + _cookieManager.ExpireCookie(Constants.Web.CsrfValidationCookieName); + } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/PostMigrations/NoopPublishedSnapshotRebuilder.cs b/src/Umbraco.Infrastructure/Migrations/PostMigrations/NoopPublishedSnapshotRebuilder.cs new file mode 100644 index 0000000000..cf53f161a4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/PostMigrations/NoopPublishedSnapshotRebuilder.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core.Migrations.PostMigrations +{ + /// + /// Implements in Umbraco.Core (doing nothing). + /// + public class NoopPublishedSnapshotRebuilder : IPublishedSnapshotRebuilder + { + /// + public void Rebuild() + { } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/PostMigrations/PublishedSnapshotRebuilder.cs b/src/Umbraco.Infrastructure/Migrations/PostMigrations/PublishedSnapshotRebuilder.cs index acc943b297..764e46af5d 100644 --- a/src/Umbraco.Infrastructure/Migrations/PostMigrations/PublishedSnapshotRebuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/PostMigrations/PublishedSnapshotRebuilder.cs @@ -1,12 +1,31 @@ -namespace Umbraco.Core.Migrations.PostMigrations +using Umbraco.Core.Migrations.PostMigrations; +using Umbraco.Web.Cache; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web.Migrations.PostMigrations { /// - /// Implements in Umbraco.Core (doing nothing). + /// Implements in Umbraco.Web (rebuilding). /// public class PublishedSnapshotRebuilder : IPublishedSnapshotRebuilder { + private readonly IPublishedSnapshotService _publishedSnapshotService; + private readonly DistributedCache _distributedCache; + + /// + /// Initializes a new instance of the class. + /// + public PublishedSnapshotRebuilder(IPublishedSnapshotService publishedSnapshotService, DistributedCache distributedCache) + { + _publishedSnapshotService = publishedSnapshotService; + _distributedCache = distributedCache; + } + /// public void Rebuild() - { } + { + _publishedSnapshotService.Rebuild(); + _distributedCache.RefreshAllPublishedSnapshot(); + } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs index 29006c8811..4872e0019d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs @@ -12,6 +12,7 @@ namespace Umbraco.Core.Migrations.Upgrade.Common { // remove those that may already have keys Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.KeyValue).Do(); + Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.PropertyData).Do(); // re-create *all* keys and indexes foreach (var x in DatabaseSchemaCreator.OrderedTables) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 2bb9b404f4..b8a5755212 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -186,13 +186,15 @@ namespace Umbraco.Core.Migrations.Upgrade To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); - // to 8.5.0... + // to 8.6.0... To("{4759A294-9860-46BC-99F9-B4C975CAE580}"); To("{0BC866BC-0665-487A-9913-0290BD0169AD}"); - - // to 8.6.0 To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); To("{EE288A91-531B-4995-8179-1D62D9AA3E2E}"); + To("{2AB29964-02A1-474D-BD6B-72148D2A53A2}"); + + + To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); //FINAL } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs index 309f8acbc3..66f9114370 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs @@ -75,8 +75,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var labelPropertyTypes = Database.Fetch(Sql() .Select(x => x.Id, x => x.Alias) .From() - .Where(x => x.DataTypeId == Constants.DataTypes.LabelString) - ); + .Where(x => x.DataTypeId == Constants.DataTypes.LabelString)); var intPropertyAliases = new[] { Constants.Conventions.Media.Width, Constants.Conventions.Media.Height, Constants.Conventions.Member.FailedPasswordAttempts }; var bigintPropertyAliases = new[] { Constants.Conventions.Media.Bytes }; @@ -101,16 +100,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 foreach (var value in values) Database.Execute(Sql() .Update(u => u - .Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?) null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture)) - .Set(x => x.TextValue, null)) + .Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?)null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture)) + .Set(x => x.TextValue, null) + .Set(x => x.VarcharValue, null)) .Where(x => x.Id == value.Id)); values = Database.Fetch(Sql().Select(x => x.Id, x => x.VarcharValue).From().WhereIn(x => x.PropertyTypeId, dtPropertyTypes)); foreach (var value in values) Database.Execute(Sql() .Update(u => u - .Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?) null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None)) - .Set(x => x.TextValue, null)) + .Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?)null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None)) + .Set(x => x.TextValue, null) + .Set(x => x.VarcharValue, null)) .Where(x => x.Id == value.Id)); // anything that's custom... ppl will have to figure it out manually, there isn't much we can do about it diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs index 84dd393b0d..eabbd34b08 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs @@ -4,8 +4,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using NPoco; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 @@ -47,14 +49,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } } - var propertyTypes = Database.Fetch(Sql().Select().From()); + var propertyTypes = Database.Fetch(Sql().Select().From()); foreach (var dto in propertyTypes) { dto.Variations = GetNewValue(dto.Variations); Database.Update(dto); } - var contentTypes = Database.Fetch(Sql().Select().From()); + var contentTypes = Database.Fetch(Sql().Select().From()); foreach (var dto in contentTypes) { dto.Variations = GetNewValue(dto.Variations); @@ -62,57 +64,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } } - // we *need* to use this private DTO here, which does *not* have extra properties, which would kill the migration + // we *need* to use these private DTOs here, which does *not* have extra properties, which would kill the migration - [TableName(TableName)] - [PrimaryKey("pk")] - [ExplicitColumns] - private class ContentTypeDto - { - public const string TableName = Constants.DatabaseSchema.Tables.ContentType; + - [Column("pk")] - [PrimaryKeyColumn(IdentitySeed = 535)] - public int PrimaryKey { get; set; } - - [Column("nodeId")] - [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")] - public int NodeId { get; set; } - - [Column("alias")] - [NullSetting(NullSetting = NullSettings.Null)] - public string Alias { get; set; } - - [Column("icon")] - [Index(IndexTypes.NonClustered)] - [NullSetting(NullSetting = NullSettings.Null)] - public string Icon { get; set; } - - [Column("thumbnail")] - [Constraint(Default = "folder.png")] - public string Thumbnail { get; set; } - - [Column("description")] - [NullSetting(NullSetting = NullSettings.Null)] - [Length(1500)] - public string Description { get; set; } - - [Column("isContainer")] - [Constraint(Default = "0")] - public bool IsContainer { get; set; } - - [Column("allowAtRoot")] - [Constraint(Default = "0")] - public bool AllowAtRoot { get; set; } - - [Column("variations")] - [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] - public byte Variations { get; set; } - - [ResultColumn] - public NodeDto NodeDto { get; set; } - } + } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs index 343557e3f5..b82d1eab8b 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs @@ -2,8 +2,7 @@ using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; @@ -34,11 +33,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } var sqlPropertyTpes = Sql() - .Select() - .From() - .Where(x => dataTypeIds.Contains(x.DataTypeId)); + .Select() + .From() + .Where(x => dataTypeIds.Contains(x.DataTypeId)); - var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); + var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); if (propertyTypeIds.Count == 0) return; 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/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs new file mode 100644 index 0000000000..bc41e5e32a --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs @@ -0,0 +1,63 @@ +using NPoco; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors + /// + [TableName(TableName)] + [PrimaryKey("pk")] + [ExplicitColumns] + internal class ContentTypeDto80 + { + public const string TableName = Constants.DatabaseSchema.Tables.ContentType; + + [Column("pk")] + [PrimaryKeyColumn(IdentitySeed = 535)] + public int PrimaryKey { get; set; } + + [Column("nodeId")] + [ForeignKey(typeof(NodeDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")] + public int NodeId { get; set; } + + [Column("alias")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Alias { get; set; } + + [Column("icon")] + [Index(IndexTypes.NonClustered)] + [NullSetting(NullSetting = NullSettings.Null)] + public string Icon { get; set; } + + [Column("thumbnail")] + [Constraint(Default = "folder.png")] + public string Thumbnail { get; set; } + + [Column("description")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(1500)] + public string Description { get; set; } + + [Column("isContainer")] + [Constraint(Default = "0")] + public bool IsContainer { get; set; } + + [Column("allowAtRoot")] + [Constraint(Default = "0")] + public bool AllowAtRoot { get; set; } + + [Column("variations")] + [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] + public byte Variations { get; set; } + + [ResultColumn] + public NodeDto NodeDto { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs new file mode 100644 index 0000000000..f0a36b223d --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs @@ -0,0 +1,142 @@ +using NPoco; +using System; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors + /// + [TableName(TableName)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class PropertyDataDto80 + { + public const string TableName = Constants.DatabaseSchema.Tables.PropertyData; + public const int VarcharLength = 512; + public const int SegmentLength = 256; + + private decimal? _decimalValue; + + // pk, not used at the moment (never updating) + [Column("id")] + [PrimaryKeyColumn] + public int Id { get; set; } + + [Column("versionId")] + [ForeignKey(typeof(ContentVersionDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,propertyTypeId,languageId,segment")] + public int VersionId { get; set; } + + [Column("propertyTypeId")] + [ForeignKey(typeof(PropertyTypeDto80))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_PropertyTypeId")] + public int PropertyTypeId { get; set; } + + [Column("languageId")] + [ForeignKey(typeof(LanguageDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? LanguageId { get; set; } + + [Column("segment")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Segment")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(SegmentLength)] + public string Segment { get; set; } + + [Column("intValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? IntegerValue { get; set; } + + [Column("decimalValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public decimal? DecimalValue + { + get => _decimalValue; + set => _decimalValue = value?.Normalize(); + } + + [Column("dateValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public DateTime? DateValue { get; set; } + + [Column("varcharValue")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(VarcharLength)] + public string VarcharValue { get; set; } + + [Column("textValue")] + [NullSetting(NullSetting = NullSettings.Null)] + [SpecialDbType(SpecialDbTypes.NTEXT)] + public string TextValue { get; set; } + + [ResultColumn] + [Reference(ReferenceType.OneToOne, ColumnName = "PropertyTypeId")] + public PropertyTypeDto80 PropertyTypeDto { get; set; } + + [Ignore] + public object Value + { + get + { + if (IntegerValue.HasValue) + return IntegerValue.Value; + + if (DecimalValue.HasValue) + return DecimalValue.Value; + + if (DateValue.HasValue) + return DateValue.Value; + + if (!string.IsNullOrEmpty(VarcharValue)) + return VarcharValue; + + if (!string.IsNullOrEmpty(TextValue)) + return TextValue; + + return null; + } + } + + public PropertyDataDto80 Clone(int versionId) + { + return new PropertyDataDto80 + { + VersionId = versionId, + PropertyTypeId = PropertyTypeId, + LanguageId = LanguageId, + Segment = Segment, + IntegerValue = IntegerValue, + DecimalValue = DecimalValue, + DateValue = DateValue, + VarcharValue = VarcharValue, + TextValue = TextValue, + PropertyTypeDto = PropertyTypeDto + }; + } + + protected bool Equals(PropertyDataDto other) + { + return Id == other.Id; + } + + public override bool Equals(object other) + { + return + !ReferenceEquals(null, other) // other is not null + && (ReferenceEquals(this, other) // and either ref-equals, or same id + || other is PropertyDataDto pdata && pdata.Id == Id); + } + + public override int GetHashCode() + { + // ReSharper disable once NonReadonlyMemberInGetHashCode + return Id; + } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs new file mode 100644 index 0000000000..6e2bd7371a --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs @@ -0,0 +1,80 @@ +using NPoco; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations before 8.6 since the schema has changed and running SQL against the new table would result in errors + /// + [TableName(Constants.DatabaseSchema.Tables.PropertyType)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class PropertyTypeDto80 + { + [Column("id")] + [PrimaryKeyColumn(IdentitySeed = 50)] + public int Id { get; set; } + + [Column("dataTypeId")] + [ForeignKey(typeof(DataTypeDto), Column = "nodeId")] + public int DataTypeId { get; set; } + + [Column("contentTypeId")] + [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] + public int ContentTypeId { get; set; } + + [Column("propertyTypeGroupId")] + [NullSetting(NullSetting = NullSettings.Null)] + [ForeignKey(typeof(PropertyTypeGroupDto))] + public int? PropertyTypeGroupId { get; set; } + + [Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyTypeAlias")] + [Column("Alias")] + public string Alias { get; set; } + + [Column("Name")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Name { get; set; } + + [Column("sortOrder")] + [Constraint(Default = "0")] + public int SortOrder { get; set; } + + [Column("mandatory")] + [Constraint(Default = "0")] + public bool Mandatory { get; set; } + + [Column("validationRegExp")] + [NullSetting(NullSetting = NullSettings.Null)] + public string ValidationRegExp { get; set; } + + [Column("Description")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(2000)] + public string Description { get; set; } + + [Column("variations")] + [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] + public byte Variations { get; set; } + + [ResultColumn] + [Reference(ReferenceType.OneToOne, ColumnName = "DataTypeId")] + public DataTypeDto DataTypeDto { get; set; } + + [Column("UniqueID")] + [NullSetting(NullSetting = NullSettings.NotNull)] + [Constraint(Default = SystemMethods.NewGuid)] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")] + public Guid UniqueId { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs index 58ec0e30c2..bc3df6f584 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs @@ -18,11 +18,12 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 AddColumn("macroSource", out var sqls2); //populate the new columns with legacy data - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = '', macroType = {(int)MacroTypes.Unknown}").Do(); - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroXSLT, macroType = {(int)MacroTypes.Unknown} WHERE macroXSLT != '' AND macroXSLT IS NOT NULL").Do(); - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptAssembly, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptAssembly != '' AND macroScriptAssembly IS NOT NULL").Do(); - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptType, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptType != '' AND macroScriptType IS NOT NULL").Do(); - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroPython, macroType = {(int)MacroTypes.PartialView} WHERE macroPython != '' AND macroPython IS NOT NULL").Do(); + //when the macro type is PartialView, it corresponds to 7, else it is 4 for Unknown + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = '', macroType = 4").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroXSLT, macroType = 4 WHERE macroXSLT != '' AND macroXSLT IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptAssembly, macroType = 4 WHERE macroScriptAssembly != '' AND macroScriptAssembly IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptType, macroType = 4 WHERE macroScriptType != '' AND macroScriptType IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroPython, macroType = 7 WHERE macroPython != '' AND macroPython IS NOT NULL").Do(); //now apply constraints (NOT NULL) to new table foreach (var sql in sqls1) Execute.Sql(sql).Do(); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs index b60923fcba..2b27bdafe8 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { @@ -17,14 +18,24 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 AddColumn("id", out var sqls); - // SQLCE does not support UPDATE...FROM - var temp2 = Database.Fetch($@"SELECT v.versionId, v.id + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var versions = Database.Fetch($@"SELECT v.versionId, v.id FROM cmsContentVersion v JOIN umbracoNode n on v.contentId=n.id WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'"); - foreach (var t in temp2) - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do(); - + foreach (var t in versions) + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do(); + } + else + { + Database.Execute($@"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id=v.id +FROM {Constants.DatabaseSchema.Tables.MediaVersion} m +JOIN cmsContentVersion v on m.versionId = v.versionId +JOIN umbracoNode n on v.contentId=n.id +WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'"); + } foreach (var sql in sqls) Execute.Sql(sql).Do(); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..adc451ef6a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Umbraco.Core.Migrations.Install; @@ -19,6 +20,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 public override void Migrate() { MigratePropertyData(); + CreatePropertyDataIndexes(); MigrateContentAndPropertyTypes(); MigrateContent(); MigrateVersions(); @@ -74,10 +76,20 @@ HAVING COUNT(v2.id) <> 1").Any()) { Alter.Table(PreTables.PropertyData).AddColumn("versionId2").AsInt32().Nullable().Do(); - // SQLCE does not support UPDATE...FROM - var temp = Database.Fetch($"SELECT id, versionId FROM {PreTables.ContentVersion}"); - foreach (var t in temp) - Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id }); + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var versions = Database.Fetch($"SELECT id, versionId FROM {PreTables.ContentVersion}"); + foreach (var t in versions) + Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id }); + } + else + { + Database.Execute($@"UPDATE {PreTables.PropertyData} SET versionId2={PreTables.ContentVersion}.id +FROM {PreTables.ContentVersion} +INNER JOIN {PreTables.PropertyData} ON {PreTables.ContentVersion}.versionId = {PreTables.PropertyData}.versionId"); + } + Delete.Column("versionId").FromTable(PreTables.PropertyData).Do(); ReplaceColumn(PreTables.PropertyData, "versionId2", "versionId"); } @@ -90,6 +102,22 @@ HAVING COUNT(v2.id) <> 1").Any()) Rename.Table(PreTables.PropertyData).To(Constants.DatabaseSchema.Tables.PropertyData).Do(); } + private void CreatePropertyDataIndexes() + { + // Creates a temporary index on umbracoPropertyData to speed up other migrations which update property values. + // It will be removed in CreateKeysAndIndexes before the normal indexes for the table are created + var tableDefinition = Persistence.DatabaseModelDefinitions.DefinitionFactory.GetTableDefinition(typeof(PropertyDataDto), SqlSyntax); + Execute.Sql(SqlSyntax.FormatPrimaryKey(tableDefinition)).Do(); + Create.Index("IX_umbracoPropertyData_Temp").OnTable(PropertyDataDto.TableName) + .WithOptions().Unique() + .WithOptions().NonClustered() + .OnColumn("versionId").Ascending() + .OnColumn("propertyTypeId").Ascending() + .OnColumn("languageId").Ascending() + .OnColumn("segment").Ascending() + .Do(); + } + private void MigrateContentAndPropertyTypes() { if (!ColumnExists(PreTables.ContentType, "variations")) @@ -153,22 +181,40 @@ HAVING COUNT(v2.id) <> 1").Any()) ReplaceColumn(PreTables.ContentVersion, "ContentId", "nodeId"); // populate contentVersion text, current and userId columns for documents - // SQLCE does not support UPDATE...FROM - var temp1 = Database.Fetch($"SELECT versionId, text, newest, documentUser FROM {PreTables.Document}"); - foreach (var t in temp1) - Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId", - new { text = t.text, current = t.newest, userId=t.documentUser, versionId=t.versionId }); + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var documents = Database.Fetch($"SELECT versionId, text, published, newest, documentUser FROM {PreTables.Document}"); + foreach (var t in documents) + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId", + new { text = t.text, current = t.newest && !t.published, userId = t.documentUser, versionId = t.versionId }); + } + else + { + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=d.text, {SqlSyntax.GetQuotedColumnName("current")}=(d.newest & ~d.published), userId=d.documentUser +FROM {PreTables.ContentVersion} v INNER JOIN {PreTables.Document} d ON d.versionId = v.versionId"); + } // populate contentVersion text and current columns for non-documents, userId is default - // SQLCE does not support UPDATE...FROM - var temp2 = Database.Fetch($@"SELECT cver.versionId, n.text + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var otherContent = Database.Fetch($@"SELECT cver.versionId, n.text FROM {PreTables.ContentVersion} cver JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})"); - foreach (var t in temp2) - Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId", - new { text = t.text, versionId=t.versionId }); + foreach (var t in otherContent) + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId", + new { text = t.text, versionId = t.versionId }); + } + else + { + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=n.text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 +FROM {PreTables.ContentVersion} cver +JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id +WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})"); + } // create table Create.Table(withoutKeysAndIndexes: true).Do(); @@ -179,36 +225,42 @@ SELECT cver.id, doc.templateId, doc.published FROM {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId"); + // need to add extra rows for where published=newest // 'cos INSERT above has inserted the 'published' document version // and v8 always has a 'edited' document version too - var temp3 = Database.Fetch($@"SELECT doc.nodeId, doc.updateDate, doc.documentUser, doc.text, doc.templateId, cver.id versionId + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text) +SELECT doc.nodeId, NEWID(), doc.updateDate, doc.documentUser, 1, doc.text FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId WHERE doc.newest=1 AND doc.published=1"); - var getIdentity = "@@@@IDENTITY"; - foreach (var t in temp3) - { - Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text) -VALUES (@nodeId, @versionId, @versionDate, @userId, 1, @text)", new { nodeId=t.nodeId, versionId=Guid.NewGuid(), versionDate=t.updateDate, userId=t.documentUser, text=t.text }); - var id = Database.ExecuteScalar("SELECT " + getIdentity); - Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} SET {SqlSyntax.GetQuotedColumnName("current")}=0 WHERE nodeId=@0 AND id<>@1", (int) t.nodeId, id); - Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published) -VALUES (@id, @templateId, 0)", new { id=id, templateId=t.templateId }); - var versionId = (int) t.versionId; - var pdatas = Database.Fetch(Sql().Select().From().Where(x => x.VersionId == versionId)); - foreach (var pdata in pdatas) - { - pdata.VersionId = id; - Database.Insert(pdata); - } - } + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published) +SELECT cverNew.id, doc.templateId, 0 +FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId +WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); + + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} (propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,versionId) +SELECT propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,cverNew.id +FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId +JOIN {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} pd ON pd.versionId=cver.id +WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); + // reduce document to 1 row per content Database.Execute($@"DELETE FROM {PreTables.Document} WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE {SqlSyntax.GetQuotedColumnName("current")} = 1) AND (published<>1 OR newest<>1)"); + // ensure that documents with a published version are marked as published + Database.Execute($@"UPDATE {PreTables.Document} SET published=1 WHERE nodeId IN ( +SELECT nodeId FROM {PreTables.ContentVersion} cv INNER JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON dv.id = cv.id WHERE dv.published=1)"); + // drop some document columns Delete.Column("text").FromTable(PreTables.Document).Do(); Delete.Column("templateId").FromTable(PreTables.Document).Do(); @@ -223,7 +275,7 @@ WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE if (!ColumnExists(PreTables.Document, "edited")) { AddColumn(PreTables.Document, "edited", out var sqls); - Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=0"); + Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=~published"); foreach (var sql in sqls) Database.Execute(sql); } @@ -240,11 +292,15 @@ JOIN {Constants.DatabaseSchema.Tables.PropertyData} v1 ON cv1.id=v1.versionId JOIN {PreTables.ContentVersion} cv2 ON n.id=cv2.nodeId JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON cv2.id=dv.id AND dv.published=1 JOIN {Constants.DatabaseSchema.Tables.PropertyData} v2 ON cv2.id=v2.versionId -WHERE v1.propertyTypeId=v2.propertyTypeId AND v1.languageId=v2.languageId AND v1.segment=v2.segment"); +WHERE v1.propertyTypeId=v2.propertyTypeId +AND (v1.languageId=v2.languageId OR (v1.languageId IS NULL AND v2.languageId IS NULL)) +AND (v1.segment=v2.segment OR (v1.segment IS NULL AND v2.segment IS NULL))"); + var updatedIds = new HashSet(); foreach (var t in temp) if (t.intValue1 != t.intValue2 || t.decimalValue1 != t.decimalValue2 || t.dateValue1 != t.dateValue2 || t.varcharValue1 != t.varcharValue2 || t.textValue1 != t.textValue2) - Database.Execute("UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeIdd", new { t.id }); + if (updatedIds.Add((int)t.id)) + Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeId", new { nodeId = t.id }); // drop more columns Delete.Column("versionId").FromTable(PreTables.ContentVersion).Do(); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs index b68aa23d78..052b72ca26 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Migrations.PostMigrations; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Services; @@ -27,15 +28,15 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); var sqlPropertyData = Sql() - .Select(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto))) - .From() - .InnerJoin().On((left, right) => left.PropertyTypeId == right.Id) - .InnerJoin().On((left, right) => left.DataTypeId == right.NodeId) + .Select(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto))) + .From() + .InnerJoin().On((left, right) => left.PropertyTypeId == right.Id) + .InnerJoin().On((left, right) => left.DataTypeId == right.NodeId) .Where(x => x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce || x.EditorAlias == Constants.PropertyEditors.Aliases.Grid); - var properties = Database.Fetch(sqlPropertyData); + var properties = Database.Fetch(sqlPropertyData); var exceptions = new List(); foreach (var property in properties) @@ -43,6 +44,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 var value = property.TextValue; if (string.IsNullOrWhiteSpace(value)) continue; + + bool propertyChanged = false; if (property.PropertyTypeDto.DataTypeDto.EditorAlias == Constants.PropertyEditors.Aliases.Grid) { try @@ -55,7 +58,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 var controlValue = control["value"]; if (controlValue?.Type == JTokenType.String) { - control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value()); + control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value(), out var controlChanged); + propertyChanged |= controlChanged; } } @@ -76,10 +80,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 } else { - property.TextValue = UpdateMediaUrls(mediaLinkPattern, value); + property.TextValue = UpdateMediaUrls(mediaLinkPattern, value, out propertyChanged); } - Database.Update(property); + if (propertyChanged) + Database.Update(property); } @@ -91,10 +96,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 Context.AddPostMigration(); } - private string UpdateMediaUrls(Regex mediaLinkPattern, string value) + private string UpdateMediaUrls(Regex mediaLinkPattern, string value, out bool changed) { - return mediaLinkPattern.Replace(value, match => + bool matched = false; + + var result = mediaLinkPattern.Replace(value, match => { + matched = true; + // match groups: // - 1 = from the beginning of the a tag until href attribute value begins // - 2 = the href attribute value excluding the querystring (if present) @@ -106,6 +115,10 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 ? match.Value : $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}"; }); + + changed = matched; + + return result; } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs new file mode 100644 index 0000000000..6ca493ac7e --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 +{ + public class AddMainDomLock : MigrationBase + { + public AddMainDomLock(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MainDom, Name = "MainDom" }); + } + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs index 30eb30109e..f44695da69 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { + public class AddPropertyTypeValidationMessageColumns : MigrationBase { public AddPropertyTypeValidationMessageColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs index 75de01dd7f..c744409c2f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs @@ -4,21 +4,30 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { public class MissingContentVersionsIndexes : MigrationBase { + private const string IndexName = "IX_" + ContentVersionDto.TableName + "_NodeId"; + public MissingContentVersionsIndexes(IMigrationContext context) : base(context) { } public override void Migrate() { - Create - .Index("IX_" + ContentVersionDto.TableName + "_NodeId") - .OnTable(ContentVersionDto.TableName) - .OnColumn("nodeId") - .Ascending() - .OnColumn("current") - .Ascending() - .WithOptions().NonClustered() - .Do(); + // We must check before we create an index because if we are upgrading from v7 we force re-create all + // indexes in the whole DB and then this would throw + + if (!IndexExists(IndexName)) + { + Create + .Index(IndexName) + .OnTable(ContentVersionDto.TableName) + .OnColumn("nodeId") + .Ascending() + .OnColumn("current") + .Ascending() + .WithOptions().NonClustered() + .Do(); + } + } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs new file mode 100644 index 0000000000..fa5116c990 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs @@ -0,0 +1,33 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 +{ + public class MissingDictionaryIndex : MigrationBase + { + public MissingDictionaryIndex(IMigrationContext context) + : base(context) + { + + } + + /// + /// Adds an index to the foreign key column parent on DictionaryDto's table + /// if it doesn't already exist + /// + public override void Migrate() + { + var indexName = "IX_" + DictionaryDto.TableName + "_Parent"; + + if (!IndexExists(indexName)) + { + Create + .Index(indexName) + .OnTable(DictionaryDto.TableName) + .OnColumn("parent") + .Ascending() + .WithOptions().NonClustered() + .Do(); + } + } + } +} diff --git a/src/Umbraco.Web/Models/BackOfficeTour.cs b/src/Umbraco.Infrastructure/Models/BackOfficeTour.cs similarity index 85% rename from src/Umbraco.Web/Models/BackOfficeTour.cs rename to src/Umbraco.Infrastructure/Models/BackOfficeTour.cs index d5987ec5bc..7396d3d00d 100644 --- a/src/Umbraco.Web/Models/BackOfficeTour.cs +++ b/src/Umbraco.Infrastructure/Models/BackOfficeTour.cs @@ -16,20 +16,32 @@ namespace Umbraco.Web.Models [DataMember(Name = "name")] public string Name { get; set; } + [DataMember(Name = "alias")] public string Alias { get; set; } + [DataMember(Name = "group")] public string Group { get; set; } + [DataMember(Name = "groupOrder")] public int GroupOrder { get; set; } + + [DataMember(Name = "hidden")] + public bool Hidden { get; set; } + [DataMember(Name = "allowDisable")] public bool AllowDisable { get; set; } + [DataMember(Name = "requiredSections")] public List RequiredSections { get; set; } + [DataMember(Name = "steps")] public BackOfficeTourStep[] Steps { get; set; } [DataMember(Name = "culture")] public string Culture { get; set; } + + [DataMember(Name = "contentType")] + public string ContentType { get; set; } } } diff --git a/src/Umbraco.Web/Models/BackOfficeTourFile.cs b/src/Umbraco.Infrastructure/Models/BackOfficeTourFile.cs similarity index 100% rename from src/Umbraco.Web/Models/BackOfficeTourFile.cs rename to src/Umbraco.Infrastructure/Models/BackOfficeTourFile.cs diff --git a/src/Umbraco.Web/Models/BackOfficeTourStep.cs b/src/Umbraco.Infrastructure/Models/BackOfficeTourStep.cs similarity index 100% rename from src/Umbraco.Web/Models/BackOfficeTourStep.cs rename to src/Umbraco.Infrastructure/Models/BackOfficeTourStep.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/AssignedContentPermissions.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/AssignedContentPermissions.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/AssignedContentPermissions.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/AssignedContentPermissions.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/AssignedUserGroupPermissions.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/AssignedUserGroupPermissions.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/Notification.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/BackOfficeNotification.cs similarity index 75% rename from src/Umbraco.Web/Models/ContentEditing/Notification.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/BackOfficeNotification.cs index 8852b2ca02..0dd132a503 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Notification.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/BackOfficeNotification.cs @@ -3,14 +3,14 @@ namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "notification", Namespace = "")] - public class Notification + public class BackOfficeNotification { - public Notification() + public BackOfficeNotification() { } - public Notification(string header, string message, NotificationStyle notificationType) + public BackOfficeNotification(string header, string message, NotificationStyle notificationType) { Header = header; Message = message; @@ -25,6 +25,6 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "type")] public NotificationStyle NotificationType { get; set; } - + } } diff --git a/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/CodeFileDisplay.cs similarity index 94% rename from src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/CodeFileDisplay.cs index 8c89bf263e..7e186932ac 100644 --- a/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/CodeFileDisplay.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models.ContentEditing { public CodeFileDisplay() { - Notifications = new List(); + Notifications = new List(); } /// @@ -53,7 +53,7 @@ namespace Umbraco.Web.Models.ContentEditing [ReadOnly(true)] public string Id { get; set; } - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// Some custom validation is required for valid file names diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentBaseSave.cs similarity index 93% rename from src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentBaseSave.cs index 8a840a60c3..47eb1852e7 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentBaseSave.cs @@ -31,10 +31,10 @@ namespace Umbraco.Web.Models.ContentEditing /// [IgnoreDataMember] TPersisted IContentSave.PersistedContent { get; set; } - + //Non explicit internal getter so we don't need to explicitly cast in our own code [IgnoreDataMember] - internal TPersisted PersistedContent + public TPersisted PersistedContent { get => ((IContentSave)this).PersistedContent; set => ((IContentSave) this).PersistedContent = value; @@ -49,7 +49,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is not used for outgoing model information. /// [IgnoreDataMember] - internal ContentPropertyCollectionDto PropertyCollectionDto { get; set; } + public ContentPropertyCollectionDto PropertyCollectionDto { get; set; } #endregion diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplay.cs similarity index 96% rename from src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplay.cs index b6a90a93c3..73740a14de 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplay.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Models.ContentEditing public ContentItemDisplay() { AllowPreview = true; - Notifications = new List(); + Notifications = new List(); Errors = new Dictionary(); Variants = new List(); ContentApps = new List(); @@ -173,7 +173,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is not used for outgoing model information. /// [IgnoreDataMember] - internal IContent PersistedContent { get; set; } + public IContent PersistedContent { get; set; } /// /// The DTO object used to gather all required content data including data type information etc... for use with validation - used during inbound model binding @@ -184,14 +184,14 @@ namespace Umbraco.Web.Models.ContentEditing /// This is not used for outgoing model information. /// [IgnoreDataMember] - internal ContentPropertyCollectionDto ContentDto { get; set; } + public ContentPropertyCollectionDto ContentDto { get; set; } /// /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] [ReadOnly(true)] - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// This is used for validation of a content item. diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplayBase.cs similarity index 92% rename from src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplayBase.cs index 7e2ba8e6cd..e0beae7fce 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentItemDisplayBase.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Models.ContentEditing { protected ContentItemDisplayBase() { - Notifications = new List(); + Notifications = new List(); Errors = new Dictionary(); } @@ -31,7 +31,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "notifications")] [ReadOnly(true)] - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// This is used for validation of a content item. diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeBasic.cs similarity index 82% rename from src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeBasic.cs index 9918b9c3a5..33749a451c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeBasic.cs @@ -4,10 +4,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Models.Validation; + namespace Umbraco.Web.Models.ContentEditing { @@ -70,15 +67,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "iconFilePath")] [ReadOnly(true)] - public string IconFilePath - { - get - { - return IconIsClass - ? string.Empty - : string.Format("{0}images/umbraco/{1}", Current.Configs.Global().Path.EnsureEndsWith("/"), Icon); - } - } + public string IconFilePath { get; set; } /// /// Returns true if the icon represents a CSS class instead of a file path @@ -103,15 +92,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "thumbnailFilePath")] [ReadOnly(true)] - public string ThumbnailFilePath - { - get - { - return ThumbnailIsClass - ? string.Empty - : Current.IOHelper.ResolveUrl("~/umbraco/images/thumbnails/" + Thumbnail); - } - } + public string ThumbnailFilePath { get; set; } [DataMember(Name = "blueprints")] [ReadOnly(true)] diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeCompositionDisplay.cs similarity index 95% rename from src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeCompositionDisplay.cs index 96b540d3ba..77b5f53b24 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeCompositionDisplay.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.ContentEditing //initialize collections so at least their never null AllowedContentTypes = new List(); CompositeContentTypes = new List(); - Notifications = new List(); + Notifications = new List(); } //name, alias, icon, thumb, desc, inherited from basic @@ -40,7 +40,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "notifications")] [ReadOnly(true)] - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// This is used for validation of a content item. diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentTypeSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentVariationDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentVariationDisplay.cs similarity index 95% rename from src/Umbraco.Web/Models/ContentEditing/ContentVariationDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ContentVariationDisplay.cs index aff79d7b9d..cf871142a6 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentVariationDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ContentVariationDisplay.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Models.ContentEditing public ContentVariantDisplay() { Tabs = new List>(); - Notifications = new List(); + Notifications = new List(); } [DataMember(Name = "name", IsRequired = true)] @@ -76,7 +76,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "notifications")] [ReadOnly(true)] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeConfigurationFieldDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeConfigurationFieldDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeConfigurationFieldDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeConfigurationFieldDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeConfigurationFieldSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeConfigurationFieldSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeConfigurationFieldSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeConfigurationFieldSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeDisplay.cs similarity index 88% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeDisplay.cs index a234d1e76f..9bead4bf5d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DataTypeDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeDisplay.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Models.ContentEditing { public DataTypeDisplay() { - Notifications = new List(); + Notifications = new List(); } /// @@ -33,7 +33,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeReferences.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeReferences.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeSave.cs similarity index 92% rename from src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeSave.cs index ad74261543..db4acd4c4e 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/DataTypeSave.cs @@ -39,13 +39,13 @@ namespace Umbraco.Web.Models.ContentEditing /// Gets or sets the persisted data type. /// [IgnoreDataMember] - internal IDataType PersistedDataType { get; set; } + public IDataType PersistedDataType { get; set; } /// /// Gets or sets the property editor. /// [IgnoreDataMember] - internal IDataEditor PropertyEditor { get; set; } + public IDataEditor PropertyEditor { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionaryDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DictionaryDisplay.cs similarity index 89% rename from src/Umbraco.Web/Models/ContentEditing/DictionaryDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DictionaryDisplay.cs index bb93c72ac7..9e0dee36f6 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DictionaryDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/DictionaryDisplay.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models.ContentEditing /// public DictionaryDisplay() { - this.Notifications = new List(); + this.Notifications = new List(); this.Translations = new List(); } @@ -24,7 +24,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// Gets or sets the parent id. diff --git a/src/Umbraco.Web/Models/ContentEditing/DictionarySave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DictionarySave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DictionarySave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DictionarySave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DocumentTypeDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DocumentTypeDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DocumentTypeSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DocumentTypeSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/DomainSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/DomainSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/DomainSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/DomainSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/EditorNavigation.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/EditorNavigation.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/EditorNavigation.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/EditorNavigation.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/EntityBasic.cs similarity index 97% rename from src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/EntityBasic.cs index 6cb2ccffaf..512bc1517d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/EntityBasic.cs @@ -3,12 +3,10 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; -using System.Web.Http.ModelBinding; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Models.Validation; using Umbraco.Core.Serialization; -using Umbraco.Web.WebApi; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/GetAvailableCompositionsFilter.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/GetAvailableCompositionsFilter.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/IErrorModel.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/IErrorModel.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/IErrorModel.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/IErrorModel.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/INotificationModel.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/INotificationModel.cs similarity index 86% rename from src/Umbraco.Web/Models/ContentEditing/INotificationModel.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/INotificationModel.cs index d718ae65f5..910d47a221 100644 --- a/src/Umbraco.Web/Models/ContentEditing/INotificationModel.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/INotificationModel.cs @@ -9,6 +9,6 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - List Notifications { get; } + List Notifications { get; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/ListViewAwareContentItemDisplayBase.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ListViewAwareContentItemDisplayBase.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/ListViewAwareContentItemDisplayBase.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ListViewAwareContentItemDisplayBase.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MacroDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MacroDisplay.cs similarity index 93% rename from src/Umbraco.Web/Models/ContentEditing/MacroDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MacroDisplay.cs index e84887b88b..33d2a959b6 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MacroDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MacroDisplay.cs @@ -14,13 +14,13 @@ namespace Umbraco.Web.Models.ContentEditing /// public MacroDisplay() { - this.Notifications = new List(); + this.Notifications = new List(); this.Parameters = new List(); } /// [DataMember(Name = "notifications")] - public List Notifications { get; } + public List Notifications { get; } /// /// Gets or sets a value indicating whether the macro can be used in a rich text editor. diff --git a/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MacroParameter.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MacroParameter.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MacroParameterDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MacroParameterDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MacroParameterDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MacroParameterDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaItemDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MediaItemDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaItemSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaItemSave.cs similarity index 90% rename from src/Umbraco.Web/Models/ContentEditing/MediaItemSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MediaItemSave.cs index b31f1d1782..d983077fa2 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MediaItemSave.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaItemSave.cs @@ -1,6 +1,5 @@ using System.Runtime.Serialization; using Umbraco.Core.Models; -using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaTypeDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaTypeDisplay.cs similarity index 70% rename from src/Umbraco.Web/Models/ContentEditing/MediaTypeDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MediaTypeDisplay.cs index 40227184db..ea0393336c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MediaTypeDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaTypeDisplay.cs @@ -5,6 +5,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentType", Namespace = "")] public class MediaTypeDisplay : ContentTypeCompositionDisplay { - + [DataMember(Name = "isSystemMediaType")] + public bool IsSystemMediaType { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MediaTypeSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MediaTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MediaTypeSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberBasic.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberBasic.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberGroupDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberGroupDisplay.cs similarity index 78% rename from src/Umbraco.Web/Models/ContentEditing/MemberGroupDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberGroupDisplay.cs index 6453fdb7a4..7706514711 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberGroupDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberGroupDisplay.cs @@ -9,13 +9,13 @@ namespace Umbraco.Web.Models.ContentEditing { public MemberGroupDisplay() { - Notifications = new List(); + Notifications = new List(); } /// /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberGroupSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberGroupSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberGroupSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberGroupSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberListDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberListDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberSave.cs similarity index 92% rename from src/Umbraco.Web/Models/ContentEditing/MemberSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberSave.cs index 10774de1a4..b9ec6fad67 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberSave.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; -using Newtonsoft.Json.Linq; using Umbraco.Core.Models; -using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Validation; -using Umbraco.Web.WebApi.Filters; using Umbraco.Core; namespace Umbraco.Web.Models.ContentEditing diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberTypeDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberTypeDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberTypeDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberTypeDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MemberTypeSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/MemberTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MemberTypeSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/MessagesExtensions.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/MessagesExtensions.cs similarity index 87% rename from src/Umbraco.Web/Models/ContentEditing/MessagesExtensions.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/MessagesExtensions.cs index 1f526a50f3..c27fa39d44 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MessagesExtensions.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/MessagesExtensions.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Models.ContentEditing { if (model.Exists(header, msg, type)) return; - model.Notifications.Add(new Notification() + model.Notifications.Add(new BackOfficeNotification() { Header = header, Message = msg, @@ -22,7 +22,7 @@ namespace Umbraco.Web.Models.ContentEditing { if (model.Exists(header, msg, NotificationStyle.Success)) return; - model.Notifications.Add(new Notification() + model.Notifications.Add(new BackOfficeNotification() { Header = header, Message = msg, @@ -34,7 +34,7 @@ namespace Umbraco.Web.Models.ContentEditing { if (model.Exists(header, msg, NotificationStyle.Error)) return; - model.Notifications.Add(new Notification() + model.Notifications.Add(new BackOfficeNotification() { Header = header, Message = msg, @@ -46,7 +46,7 @@ namespace Umbraco.Web.Models.ContentEditing { if (model.Exists(header, msg, NotificationStyle.Warning)) return; - model.Notifications.Add(new Notification() + model.Notifications.Add(new BackOfficeNotification() { Header = header, Message = msg, @@ -58,7 +58,7 @@ namespace Umbraco.Web.Models.ContentEditing { if (model.Exists(header, msg, NotificationStyle.Info)) return; - model.Notifications.Add(new Notification() + model.Notifications.Add(new BackOfficeNotification() { Header = header, Message = msg, diff --git a/src/Umbraco.Web/Models/ContentEditing/ModelWithNotifications.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/ModelWithNotifications.cs similarity index 85% rename from src/Umbraco.Web/Models/ContentEditing/ModelWithNotifications.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/ModelWithNotifications.cs index d4e979eb85..2cf34e029d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ModelWithNotifications.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/ModelWithNotifications.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Models.ContentEditing public ModelWithNotifications(T value) { Value = value; - Notifications = new List(); + Notifications = new List(); } /// @@ -26,6 +26,6 @@ namespace Umbraco.Web.Models.ContentEditing /// The notifications /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PostedFiles.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/PostedFiles.cs similarity index 74% rename from src/Umbraco.Web/Models/ContentEditing/PostedFiles.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/PostedFiles.cs index 4f3e5dbb18..e0ec347b45 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PostedFiles.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/PostedFiles.cs @@ -9,16 +9,16 @@ namespace Umbraco.Web.Models.ContentEditing /// temporary files that were created. /// [DataContract] - internal class PostedFiles : IHaveUploadedFiles, INotificationModel + public class PostedFiles : IHaveUploadedFiles, INotificationModel { public PostedFiles() { UploadedFiles = new List(); - Notifications = new List(); + Notifications = new List(); } public List UploadedFiles { get; private set; } [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/PropertyGroupDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/PropertyGroupDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/PublicAccess.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/PublicAccess.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/RelationTypeDisplay.cs similarity index 93% rename from src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/RelationTypeDisplay.cs index b7f209cd20..49b0e15e6b 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/RelationTypeDisplay.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Models.ContentEditing { public RelationTypeDisplay() { - Notifications = new List(); + Notifications = new List(); } /// @@ -51,6 +51,6 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/RelationTypeSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/RelationTypeSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/RichTextEditorCommand.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/RichTextEditorCommand.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/RichTextEditorConfiguration.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/RichTextEditorConfiguration.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/SearchResultEntity.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/SearchResultEntity.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/SearchResults.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/SearchResults.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/SearchResults.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/SimpleNotificationModel.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/SimpleNotificationModel.cs similarity index 70% rename from src/Umbraco.Web/Models/ContentEditing/SimpleNotificationModel.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/SimpleNotificationModel.cs index af35412a49..6b74ba5e0a 100644 --- a/src/Umbraco.Web/Models/ContentEditing/SimpleNotificationModel.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/SimpleNotificationModel.cs @@ -8,19 +8,19 @@ namespace Umbraco.Web.Models.ContentEditing { public SimpleNotificationModel() { - Notifications = new List(); + Notifications = new List(); } - public SimpleNotificationModel(params Notification[] notifications) + public SimpleNotificationModel(params BackOfficeNotification[] notifications) { - Notifications = new List(notifications); + Notifications = new List(notifications); } /// /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } /// /// A default message diff --git a/src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/TabbedContentItem.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/TabbedContentItem.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/TemplateDisplay.cs similarity index 94% rename from src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/TemplateDisplay.cs index ffa4e4e100..b752aa1af2 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/TemplateDisplay.cs @@ -42,6 +42,6 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/TreeSearchResult.cs similarity index 98% rename from src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/TreeSearchResult.cs index ce4c053099..820f3bae70 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/TreeSearchResult.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Runtime.Serialization; -using Examine; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserBasic.cs similarity index 93% rename from src/Umbraco.Web/Models/ContentEditing/UserBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserBasic.cs index 3f1b879705..9a48a8243e 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/UserBasic.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models.ContentEditing { public UserBasic() { - Notifications = new List(); + Notifications = new List(); UserGroups = new List(); } @@ -63,6 +63,6 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserDetail.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserDetail.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserDetail.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupBasic.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupBasic.cs similarity index 90% rename from src/Umbraco.Web/Models/ContentEditing/UserGroupBasic.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupBasic.cs index 0446ed6d34..3d959e0d32 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserGroupBasic.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupBasic.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Models.ContentEditing { public UserGroupBasic() { - Notifications = new List(); + Notifications = new List(); Sections = Enumerable.Empty
(); } @@ -17,7 +17,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] - public List Notifications { get; private set; } + public List Notifications { get; private set; } [DataMember(Name = "sections")] public IEnumerable
Sections { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupDisplay.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupDisplay.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserGroupDisplay.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupDisplay.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupPermissionsSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupPermissionsSave.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupSave.cs similarity index 97% rename from src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupSave.cs index 4172033188..4b76ec1bec 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/UserGroupSave.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.Models.ContentEditing /// The real persisted user group /// [IgnoreDataMember] - internal IUserGroup PersistedUserGroup { get; set; } + public IUserGroup PersistedUserGroup { get; set; } public IEnumerable Validate(ValidationContext validationContext) { diff --git a/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs similarity index 80% rename from src/Umbraco.Web/Models/ContentEditing/UserInvite.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs index b18b1539be..06e4d0748c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs @@ -2,9 +2,8 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; +using Umbraco.Composing; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; namespace Umbraco.Web.Models.ContentEditing { @@ -32,10 +31,10 @@ namespace Umbraco.Web.Models.ContentEditing public IEnumerable Validate(ValidationContext validationContext) { if (UserGroups.Any() == false) - yield return new ValidationResult("A user must be assigned to at least one group", new[] { "UserGroups" }); + yield return new ValidationResult("A user must be assigned to at least one group", new[] { nameof(UserGroups) }); - if (Current.Configs.Settings().Security.UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) - yield return new ValidationResult("A username cannot be empty", new[] { "Username" }); + if (Current.Configs.Security().UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) + yield return new ValidationResult("A username cannot be empty", new[] { nameof(Username) }); } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserProfile.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserProfile.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserProfile.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserProfile.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserSave.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentEditing/UserSave.cs rename to src/Umbraco.Infrastructure/Models/ContentEditing/UserSave.cs diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs similarity index 87% rename from src/Umbraco.Web/Models/ContentTypeImportModel.cs rename to src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs index f6f9a5926d..0880c5d1a3 100644 --- a/src/Umbraco.Web/Models/ContentTypeImportModel.cs +++ b/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models public string Name { get; set; } [DataMember(Name = "notifications")] - public List Notifications { get; } = new List(); + public List Notifications { get; } = new List(); [DataMember(Name = "tempFileName")] public string TempFileName { get; set; } diff --git a/src/Umbraco.Infrastructure/Models/DataType.cs b/src/Umbraco.Infrastructure/Models/DataType.cs index c237f6381c..299795f865 100644 --- a/src/Umbraco.Infrastructure/Models/DataType.cs +++ b/src/Umbraco.Infrastructure/Models/DataType.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.Serialization; using Newtonsoft.Json; using Umbraco.Core.Models.Entities; diff --git a/src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs new file mode 100644 index 0000000000..f88309d7e3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs @@ -0,0 +1,64 @@ +using System.Globalization; +using System.Text; +using Umbraco.Core; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models +{ + public class ImageProcessorImageUrlGenerator : IImageUrlGenerator + { + public string GetImageUrl(ImageUrlGenerationOptions options) + { + if (options == null) return null; + + var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty); + + if (options.FocalPoint != null) AppendFocalPoint(imageProcessorUrl, options); + else if (options.Crop != null) AppendCrop(imageProcessorUrl, options); + else if (options.DefaultCrop) imageProcessorUrl.Append("?anchor=center&mode=crop"); + else + { + imageProcessorUrl.Append("?mode=").Append((options.ImageCropMode ?? "crop").ToLower()); + + if (options.ImageCropAnchor != null) imageProcessorUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToLower()); + } + + var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&format="); + + //Only put quality here, if we don't have a format specified. + //Otherwise we need to put quality at the end to avoid it being overridden by the format. + if (options.Quality != null && hasFormat == false) imageProcessorUrl.Append("&quality=").Append(options.Quality); + if (options.HeightRatio != null) imageProcessorUrl.Append("&heightratio=").Append(options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture)); + if (options.WidthRatio != null) imageProcessorUrl.Append("&widthratio=").Append(options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture)); + if (options.Width != null) imageProcessorUrl.Append("&width=").Append(options.Width); + if (options.Height != null) imageProcessorUrl.Append("&height=").Append(options.Height); + if (options.UpScale == false) imageProcessorUrl.Append("&upscale=false"); + if (options.AnimationProcessMode != null) imageProcessorUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode); + if (options.FurtherOptions != null) imageProcessorUrl.Append(options.FurtherOptions); + + //If furtherOptions contains a format, we need to put the quality after the format. + if (options.Quality != null && hasFormat) imageProcessorUrl.Append("&quality=").Append(options.Quality); + if (options.CacheBusterValue != null) imageProcessorUrl.Append("&rnd=").Append(options.CacheBusterValue); + + return imageProcessorUrl.ToString(); + } + + private void AppendFocalPoint(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) + { + imageProcessorUrl.Append("?center="); + imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); + imageProcessorUrl.Append("&mode=crop"); + } + + private void AppendCrop(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) + { + imageProcessorUrl.Append("?crop="); + imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); + imageProcessorUrl.Append("&cropmode=percentage"); + } + } +} diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Infrastructure/Models/LocalPackageInstallModel.cs similarity index 97% rename from src/Umbraco.Web/Models/LocalPackageInstallModel.cs rename to src/Umbraco.Infrastructure/Models/LocalPackageInstallModel.cs index 9d017148a2..4eab8ed2c2 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Infrastructure/Models/LocalPackageInstallModel.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Models public List UploadedFiles { get; } = new List(); [DataMember(Name = "notifications")] - public List Notifications { get; } = new List(); + public List Notifications { get; } = new List(); /// /// A flag to determine if this package is compatible to be installed diff --git a/src/Umbraco.Infrastructure/Models/Macro.cs b/src/Umbraco.Infrastructure/Models/Macro.cs index 95e3477212..083c288e09 100644 --- a/src/Umbraco.Infrastructure/Models/Macro.cs +++ b/src/Umbraco.Infrastructure/Models/Macro.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Models /// /// /// - public Macro(IShortStringHelper shortStringHelper, int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, bool cacheByPage, bool cacheByMember, bool dontRender, string macroSource, MacroTypes macroType) + public Macro(IShortStringHelper shortStringHelper, int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, bool cacheByPage, bool cacheByMember, bool dontRender, string macroSource) : this(shortStringHelper) { Id = id; @@ -53,7 +53,6 @@ namespace Umbraco.Core.Models CacheByMember = cacheByMember; DontRender = dontRender; MacroSource = macroSource; - MacroType = macroType; } /// @@ -69,7 +68,6 @@ namespace Umbraco.Core.Models /// public Macro(IShortStringHelper shortStringHelper, string @alias, string name, string macroSource, - MacroTypes macroType, bool cacheByPage = false, bool cacheByMember = false, bool dontRender = true, @@ -85,7 +83,6 @@ namespace Umbraco.Core.Models CacheByMember = cacheByMember; DontRender = dontRender; MacroSource = macroSource; - MacroType = macroType; } private string _alias; @@ -96,7 +93,6 @@ namespace Umbraco.Core.Models private bool _cacheByMember; private bool _dontRender; private string _macroSource; - private MacroTypes _macroType = MacroTypes.Unknown; private MacroPropertyCollection _properties; private List _addedProperties; private List _removedProperties; @@ -247,16 +243,6 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _macroSource, nameof(MacroSource)); } - /// - /// Gets or set the path to the Partial View to render - /// - [DataMember] - public MacroTypes MacroType - { - get => _macroType; - set => SetPropertyValueAndDetectChanges(value, ref _macroType, nameof(MacroType)); - } - /// /// Gets or sets a list of Macro Properties /// diff --git a/src/Umbraco.Web/Models/Mapping/AuditMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/AuditMapDefinition.cs similarity index 93% rename from src/Umbraco.Web/Models/Mapping/AuditMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/AuditMapDefinition.cs index 9bc0229550..51a0fed704 100644 --- a/src/Umbraco.Web/Models/Mapping/AuditMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/AuditMapDefinition.cs @@ -4,7 +4,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class AuditMapDefinition : IMapDefinition + public class AuditMapDefinition : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) { diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/CodeFileMapDefinition.cs similarity index 100% rename from src/Umbraco.Web/Models/Mapping/CodeFileMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/CodeFileMapDefinition.cs diff --git a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/CommonMapper.cs similarity index 66% rename from src/Umbraco.Web/Models/Mapping/CommonMapper.cs rename to src/Umbraco.Infrastructure/Models/Mapping/CommonMapper.cs index 7bf4a94b1c..2d7ee84675 100644 --- a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/CommonMapper.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; -using System.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -11,27 +9,27 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Trees; + using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile; namespace Umbraco.Web.Models.Mapping { - internal class CommonMapper + public class CommonMapper { private readonly IUserService _userService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ContentAppFactoryCollection _contentAppDefinitions; private readonly ILocalizedTextService _localizedTextService; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; public CommonMapper(IUserService userService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ContentAppFactoryCollection contentAppDefinitions, ILocalizedTextService localizedTextService) { _userService = userService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _umbracoContextAccessor = umbracoContextAccessor; _contentAppDefinitions = contentAppDefinitions; _localizedTextService = localizedTextService; + _umbracoContextAccessor = umbracoContextAccessor; } public UserProfile GetOwner(IContentBase source, MapperContext context) @@ -48,10 +46,9 @@ namespace Umbraco.Web.Models.Mapping public ContentTypeBasic GetContentType(IContentBase source, MapperContext context) { - // TODO: We can resolve the UmbracoContext from the IValueResolver options! - // OMG - if (HttpContext.Current != null && Composing.Current.UmbracoContext != null && Composing.Current.UmbracoContext.Security.CurrentUser != null - && Composing.Current.UmbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + + var user = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser; + if (user?.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)) ?? false) { var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source); var contentTypeBasic = context.Map(contentType); @@ -62,25 +59,6 @@ namespace Umbraco.Web.Models.Mapping return null; } - public string GetTreeNodeUrl(IContentBase source) - where TController : ContentTreeControllerBase - { - var umbracoContext = _umbracoContextAccessor.UmbracoContext; - if (umbracoContext == null) return null; - - var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext); - return urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(source.Key.ToString("N"), null)); - } - - public string GetMemberTreeNodeUrl(IContentBase source) - { - var umbracoContext = _umbracoContextAccessor.UmbracoContext; - if (umbracoContext == null) return null; - - var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext); - return urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(source.Key.ToString("N"), null)); - } - public IEnumerable GetContentApps(IContentBase source) { var apps = _contentAppDefinitions.GetContentAppsFor(source).ToArray(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/ContentSavedStateMapper.cs similarity index 96% rename from src/Umbraco.Web/Models/Mapping/ContentSavedStateMapper.cs rename to src/Umbraco.Infrastructure/Models/Mapping/ContentSavedStateMapper.cs index dfb334621d..82f1d4e9bb 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateMapper.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/ContentSavedStateMapper.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Models.Mapping /// Returns the for an item /// /// - internal class ContentBasicSavedStateMapper + public class ContentBasicSavedStateMapper where T : ContentPropertyBasic { private readonly ContentSavedStateMapper _inner = new ContentSavedStateMapper(); @@ -25,7 +25,7 @@ namespace Umbraco.Web.Models.Mapping /// Returns the for an item /// /// - internal class ContentSavedStateMapper + public class ContentSavedStateMapper where T : ContentPropertyBasic { public ContentSavedState Map(IContent source, MapperContext context) diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/ContentTypeMapDefinition.cs similarity index 92% rename from src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/ContentTypeMapDefinition.cs index c39d90682d..59f63f4f15 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/ContentTypeMapDefinition.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -10,6 +10,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Services; using Umbraco.Core.Exceptions; +using Umbraco.Core.IO; using Umbraco.Core.Strings; namespace Umbraco.Web.Models.Mapping @@ -17,7 +18,7 @@ namespace Umbraco.Web.Models.Mapping /// /// Defines mappings for content/media/members type mappings /// - internal class ContentTypeMapDefinition : IMapDefinition + public class ContentTypeMapDefinition : IMapDefinition { private readonly PropertyEditorCollection _propertyEditors; private readonly IDataTypeService _dataTypeService; @@ -27,11 +28,13 @@ namespace Umbraco.Web.Models.Mapping private readonly IMemberTypeService _memberTypeService; private readonly ILogger _logger; private readonly IShortStringHelper _shortStringHelper; + private readonly IIOHelper _ioHelper; + private readonly IGlobalSettings _globalSettings; public ContentTypeMapDefinition(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, - ILogger logger, IShortStringHelper shortStringHelper) + ILogger logger, IShortStringHelper shortStringHelper, IIOHelper ioHelper, IGlobalSettings globalSettings) { _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; @@ -41,7 +44,8 @@ namespace Umbraco.Web.Models.Mapping _memberTypeService = memberTypeService; _logger = logger; _shortStringHelper = shortStringHelper; - + _ioHelper = ioHelper; + _globalSettings = globalSettings; } public void DefineMaps(UmbracoMapper mapper) @@ -151,6 +155,7 @@ namespace Umbraco.Web.Models.Mapping //default listview target.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media"; + target.IsSystemMediaType = source.IsSystemMediaType(); if (string.IsNullOrEmpty(source.Name)) return; @@ -177,13 +182,18 @@ namespace Umbraco.Web.Models.Mapping } // Umbraco.Code.MapAll -Blueprints - private static void Map(IContentTypeBase source, ContentTypeBasic target, string entityType) + private void Map(IContentTypeBase source, ContentTypeBasic target, string entityType) { target.Udi = Udi.Create(entityType, source.Key); target.Alias = source.Alias; target.CreateDate = source.CreateDate; target.Description = source.Description; target.Icon = source.Icon; + target.IconFilePath = target.IconIsClass + ? string.Empty + : $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}"; + + target.Trashed = source.Trashed; target.Id = source.Id; target.IsContainer = source.IsContainer; target.IsElement = source.IsElement; @@ -192,36 +202,38 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = source.ParentId; target.Path = source.Path; target.Thumbnail = source.Thumbnail; - target.Trashed = source.Trashed; + target.ThumbnailFilePath = target.ThumbnailIsClass + ? string.Empty + : _ioHelper.ResolveUrl("~/umbraco/images/thumbnails/" + source.Thumbnail); target.UpdateDate = source.UpdateDate; } // no MapAll - uses the IContentTypeBase map method, which has MapAll - private static void Map(IContentTypeComposition source, ContentTypeBasic target, MapperContext context) + private void Map(IContentTypeComposition source, ContentTypeBasic target, MapperContext context) { Map(source, target, Constants.UdiEntityType.MemberType); } // no MapAll - uses the IContentTypeBase map method, which has MapAll - private static void Map(IContentType source, ContentTypeBasic target, MapperContext context) + private void Map(IContentType source, ContentTypeBasic target, MapperContext context) { Map(source, target, Constants.UdiEntityType.DocumentType); } // no MapAll - uses the IContentTypeBase map method, which has MapAll - private static void Map(IMediaType source, ContentTypeBasic target, MapperContext context) + private void Map(IMediaType source, ContentTypeBasic target, MapperContext context) { Map(source, target, Constants.UdiEntityType.MediaType); } // no MapAll - uses the IContentTypeBase map method, which has MapAll - private static void Map(IMemberType source, ContentTypeBasic target, MapperContext context) + private void Map(IMemberType source, ContentTypeBasic target, MapperContext context) { Map(source, target, Constants.UdiEntityType.MemberType); } // Umbraco.Code.MapAll -CreateDate -DeleteDate -UpdateDate - // Umbraco.Code.MapAll -SupportsPublishing -Key -PropertyEditorAlias -ValueStorageType + // Umbraco.Code.MapAll -SupportsPublishing -Key -PropertyEditorAlias -ValueStorageType -Variations private static void Map(PropertyTypeBasic source, IPropertyType target, MapperContext context) { target.Name = source.Label; @@ -231,9 +243,7 @@ namespace Umbraco.Web.Models.Mapping target.MandatoryMessage = source.Validation.MandatoryMessage; target.ValidationRegExp = source.Validation.Pattern; target.ValidationRegExpMessage = source.Validation.PatternMessage; - target.Variations = source.AllowCultureVariant - ? target.Variations.SetFlag(ContentVariation.Culture) - : target.Variations.UnsetFlag(ContentVariation.Culture); + target.SetVariesBy(ContentVariation.Culture, source.AllowCultureVariant); if (source.Id > 0) target.Id = source.Id; @@ -375,7 +385,7 @@ namespace Umbraco.Web.Models.Mapping target.Validation = source.Validation; } - // Umbraco.Code.MapAll -CreatorId -Level -SortOrder + // Umbraco.Code.MapAll -CreatorId -Level -SortOrder -Variations // Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate // Umbraco.Code.MapAll -ContentTypeComposition (done by AfterMapSaveToType) private static void MapSaveToTypeBase(TSource source, IContentTypeComposition target, MapperContext context) @@ -405,9 +415,7 @@ namespace Umbraco.Web.Models.Mapping if (!(target is IMemberType)) { - target.Variations = source.AllowCultureVariant - ? target.Variations.SetFlag(ContentVariation.Culture) - : target.Variations.UnsetFlag(ContentVariation.Culture); + target.SetVariesBy(ContentVariation.Culture, source.AllowCultureVariant); } // handle property groups and property types @@ -487,6 +495,9 @@ namespace Umbraco.Web.Models.Mapping target.CreateDate = source.CreateDate; target.Description = source.Description; target.Icon = source.Icon; + target.IconFilePath = target.IconIsClass + ? string.Empty + : $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}"; target.Id = source.Id; target.IsContainer = source.IsContainer; target.IsElement = source.IsElement; @@ -495,10 +506,13 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = source.ParentId; target.Path = source.Path; target.Thumbnail = source.Thumbnail; + target.ThumbnailFilePath = target.ThumbnailIsClass + ? string.Empty + : _ioHelper.ResolveUrl("~/umbraco/images/thumbnails/" + source.Thumbnail); target.Udi = MapContentTypeUdi(source); target.UpdateDate = source.UpdateDate; - target.AllowedContentTypes = source.AllowedContentTypes.Select(x => x.Id.Value); + target.AllowedContentTypes = source.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value); target.CompositeContentTypes = source.ContentTypeComposition.Select(x => x.Alias); target.LockedCompositeContentTypes = MapLockedCompositions(source); } @@ -524,6 +538,9 @@ namespace Umbraco.Web.Models.Mapping target.CompositeContentTypes = source.CompositeContentTypes; target.Description = source.Description; target.Icon = source.Icon; + target.IconFilePath = target.IconIsClass + ? string.Empty + : $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}"; target.Id = source.Id; target.IsContainer = source.IsContainer; target.IsElement = source.IsElement; @@ -532,6 +549,9 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = source.ParentId; target.Path = source.Path; target.Thumbnail = source.Thumbnail; + target.ThumbnailFilePath = target.ThumbnailIsClass + ? string.Empty + : _ioHelper.ResolveUrl("~/umbraco/images/thumbnails/" + source.Thumbnail); target.Trashed = source.Trashed; target.Udi = source.Udi; } @@ -571,7 +591,7 @@ namespace Umbraco.Web.Models.Mapping return aliases.OrderBy(x => x); } - internal static Udi MapContentTypeUdi(IContentTypeComposition source) + public static Udi MapContentTypeUdi(IContentTypeComposition source) { if (source == null) return null; diff --git a/src/Umbraco.Infrastructure/Models/Mapping/ContentVariantMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/ContentVariantMapper.cs new file mode 100644 index 0000000000..10a19287e7 --- /dev/null +++ b/src/Umbraco.Infrastructure/Models/Mapping/ContentVariantMapper.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Mapping; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using Language = Umbraco.Web.Models.ContentEditing.Language; + +namespace Umbraco.Web.Models.Mapping +{ + public class ContentVariantMapper + { + private readonly ILocalizationService _localizationService; + + public ContentVariantMapper(ILocalizationService localizationService) + { + _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); + } + + public IEnumerable Map(IContent source, MapperContext context) + { + var variesByCulture = source.ContentType.VariesByCulture(); + var variesBySegment = source.ContentType.VariesBySegment(); + + IList variants = new List(); + + if (!variesByCulture && !variesBySegment) + { + // this is invariant so just map the IContent instance to ContentVariationDisplay + var variantDisplay = context.Map(source); + variants.Add(variantDisplay); + } + else if (variesByCulture && !variesBySegment) + { + var languages = GetLanguages(context); + variants = languages + .Select(language => CreateVariantDisplay(context, source, language, null)) + .ToList(); + } + else if (variesBySegment && !variesByCulture) + { + // Segment only + var segments = GetSegments(source); + variants = segments + .Select(segment => CreateVariantDisplay(context, source, null, segment)) + .ToList(); + } + else + { + // Culture and segment + var languages = GetLanguages(context).ToList(); + var segments = GetSegments(source).ToList(); + + if (languages.Count == 0 || segments.Count == 0) + { + // This should not happen + throw new InvalidOperationException("No languages or segments available"); + } + + variants = languages + .SelectMany(language => segments + .Select(segment => CreateVariantDisplay(context, source, language, segment))) + .ToList(); + } + + return SortVariants(variants); + } + + private IList SortVariants(IList variants) + { + if (variants == null || variants.Count <= 1) + { + return variants; + } + + // Default variant first, then order by language, segment. + return variants + .OrderBy(v => IsDefaultLanguage(v) ? 0 : 1) + .ThenBy(v => IsDefaultSegment(v) ? 0 : 1) + .ThenBy(v => v?.Language?.Name) + .ThenBy(v => v.Segment) + .ToList(); + } + + private static bool IsDefaultSegment(ContentVariantDisplay variant) + { + return variant.Segment == null; + } + + private static bool IsDefaultLanguage(ContentVariantDisplay variant) + { + return variant.Language == null || variant.Language.IsDefault; + } + + private IEnumerable GetLanguages(MapperContext context) + { + var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList(); + if (allLanguages.Count == 0) + { + // This should never happen + return Enumerable.Empty(); + } + else + { + return context.MapEnumerable(allLanguages).ToList(); + } + } + + /// + /// Returns all segments assigned to the content + /// + /// + /// + /// Returns all segments assigned to the content including 'null' values + /// + private IEnumerable GetSegments(IContent content) + { + return content.Properties.SelectMany(p => p.Values.Select(v => v.Segment)).Distinct(); + } + + private ContentVariantDisplay CreateVariantDisplay(MapperContext context, IContent content, Language language, string segment) + { + context.SetCulture(language?.IsoCode); + context.SetSegment(segment); + + var variantDisplay = context.Map(content); + + variantDisplay.Segment = segment; + variantDisplay.Language = language; + variantDisplay.Name = content.GetCultureName(language?.IsoCode); + + return variantDisplay; + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/DataTypeMapDefinition.cs similarity index 94% rename from src/Umbraco.Web/Models/Mapping/DataTypeMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/DataTypeMapDefinition.cs index 3074d0a2c8..0bd6d54c08 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/DataTypeMapDefinition.cs @@ -2,24 +2,26 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; -using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class DataTypeMapDefinition : IMapDefinition + public class DataTypeMapDefinition : IMapDefinition { private readonly PropertyEditorCollection _propertyEditors; private readonly ILogger _logger; + private readonly IContentSettings _contentSettings; - public DataTypeMapDefinition(PropertyEditorCollection propertyEditors, ILogger logger) + public DataTypeMapDefinition(PropertyEditorCollection propertyEditors, ILogger logger, IContentSettings contentSettings) { _propertyEditors = propertyEditors; _logger = logger; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } private static readonly int[] SystemIds = @@ -126,9 +128,8 @@ namespace Umbraco.Web.Models.Mapping private IEnumerable MapAvailableEditors(IDataType source, MapperContext context) { - var contentSection = Current.Configs.Settings().Content; var properties = _propertyEditors - .Where(x => !x.IsDeprecated || contentSection.ShowDeprecatedPropertyEditors || source.EditorAlias == x.Alias) + .Where(x => !x.IsDeprecated || _contentSettings.ShowDeprecatedPropertyEditors || source.EditorAlias == x.Alias) .OrderBy(x => x.Name); return context.MapEnumerable(properties); } @@ -139,7 +140,7 @@ namespace Umbraco.Web.Models.Mapping // an empty fields list, which made no sense since there would be nothing to map to - and besides, // a datatype without an editor alias is a serious issue - v8 wants an editor here - if (string.IsNullOrWhiteSpace(dataType.EditorAlias) || !Current.PropertyEditors.TryGet(dataType.EditorAlias, out var editor)) + if (string.IsNullOrWhiteSpace(dataType.EditorAlias) || !_propertyEditors.TryGet(dataType.EditorAlias, out var editor)) throw new InvalidOperationException($"Could not find a property editor with alias \"{dataType.EditorAlias}\"."); var configurationEditor = editor.GetConfigurationEditor(); diff --git a/src/Umbraco.Web/Models/Mapping/DictionaryMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/DictionaryMapDefinition.cs similarity index 98% rename from src/Umbraco.Web/Models/Mapping/DictionaryMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/DictionaryMapDefinition.cs index c7ae0cb717..278adc56e0 100644 --- a/src/Umbraco.Web/Models/Mapping/DictionaryMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/DictionaryMapDefinition.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Models.Mapping /// /// The dictionary model mapper. /// - internal class DictionaryMapDefinition : IMapDefinition + public class DictionaryMapDefinition : IMapDefinition { private readonly ILocalizationService _localizationService; diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/EntityMapDefinition.cs similarity index 88% rename from src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/EntityMapDefinition.cs index 34b8f664f3..ba7b5cddf2 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/EntityMapDefinition.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Examine; -using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -13,7 +11,7 @@ using Umbraco.Examine; namespace Umbraco.Web.Models.Mapping { - internal class EntityMapDefinition : IMapDefinition + public class EntityMapDefinition : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) { @@ -171,13 +169,19 @@ namespace Umbraco.Web.Models.Mapping // TODO: Properly map this (not aftermap) //get the icon if there is one - target.Icon = source.Values.ContainsKey(UmbracoExamineIndex.IconFieldName) - ? source.Values[UmbracoExamineIndex.IconFieldName] + target.Icon = source.Values.ContainsKey(UmbracoExamineFieldNames.IconFieldName) + ? source.Values[UmbracoExamineFieldNames.IconFieldName] : Constants.Icons.DefaultIcon; - target.Name = source.Values.ContainsKey("nodeName") ? source.Values["nodeName"] : "[no name]"; + target.Name = source.Values.ContainsKey(UmbracoExamineFieldNames.NodeNameFieldName) ? source.Values[UmbracoExamineFieldNames.NodeNameFieldName] : "[no name]"; - if (source.Values.TryGetValue(UmbracoExamineIndex.UmbracoFileFieldName, out var umbracoFile)) + var culture = context.GetCulture(); + if(culture.IsNullOrWhiteSpace() == false) + { + target.Name = source.Values.ContainsKey($"nodeName_{culture}") ? source.Values[$"nodeName_{culture}"] : target.Name; + } + + if (source.Values.TryGetValue(UmbracoExamineFieldNames.UmbracoFileFieldName, out var umbracoFile)) { if (umbracoFile != null) { @@ -185,16 +189,16 @@ namespace Umbraco.Web.Models.Mapping } } - if (source.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName)) + if (source.Values.ContainsKey(UmbracoExamineFieldNames.NodeKeyFieldName)) { - if (Guid.TryParse(source.Values[UmbracoExamineIndex.NodeKeyFieldName], out var key)) + if (Guid.TryParse(source.Values[UmbracoExamineFieldNames.NodeKeyFieldName], out var key)) { target.Key = key; //need to set the UDI - if (source.Values.ContainsKey(LuceneIndex.CategoryFieldName)) + if (source.Values.ContainsKey(ExamineFieldNames.CategoryFieldName)) { - switch (source.Values[LuceneIndex.CategoryFieldName]) + switch (source.Values[ExamineFieldNames.CategoryFieldName]) { case IndexTypes.Member: target.Udi = new GuidUdi(Constants.UdiEntityType.Member, target.Key); @@ -222,11 +226,11 @@ namespace Umbraco.Web.Models.Mapping } } - target.Path = source.Values.ContainsKey(UmbracoExamineIndex.IndexPathFieldName) ? source.Values[UmbracoExamineIndex.IndexPathFieldName] : ""; + target.Path = source.Values.ContainsKey(UmbracoExamineFieldNames.IndexPathFieldName) ? source.Values[UmbracoExamineFieldNames.IndexPathFieldName] : ""; - if (source.Values.ContainsKey(LuceneIndex.ItemTypeFieldName)) + if (source.Values.ContainsKey(ExamineFieldNames.ItemTypeFieldName)) { - target.AdditionalData.Add("contentType", source.Values[LuceneIndex.ItemTypeFieldName]); + target.AdditionalData.Add("contentType", source.Values[ExamineFieldNames.ItemTypeFieldName]); } } @@ -238,7 +242,7 @@ namespace Umbraco.Web.Models.Mapping return memberEntity.ContentTypeIcon.IfNullOrWhiteSpace(Constants.Icons.Member); case IContentEntitySlim contentEntity: // NOTE: this case covers both content and media entities - return contentEntity.ContentTypeIcon; + return contentEntity.ContentTypeIcon; } return null; diff --git a/src/Umbraco.Web/Models/Mapping/LanguageMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/LanguageMapDefinition.cs similarity index 97% rename from src/Umbraco.Web/Models/Mapping/LanguageMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/LanguageMapDefinition.cs index 28074d4817..6fcbe1a88b 100644 --- a/src/Umbraco.Web/Models/Mapping/LanguageMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/LanguageMapDefinition.cs @@ -8,7 +8,7 @@ using Language = Umbraco.Web.Models.ContentEditing.Language; namespace Umbraco.Web.Models.Mapping { - internal class LanguageMapDefinition : IMapDefinition + public class LanguageMapDefinition : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) { diff --git a/src/Umbraco.Web/Models/Mapping/MacroMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/MacroMapDefinition.cs similarity index 76% rename from src/Umbraco.Web/Models/Mapping/MacroMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/MacroMapDefinition.cs index e5bca22287..d980958a91 100644 --- a/src/Umbraco.Web/Models/Mapping/MacroMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/MacroMapDefinition.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Logging; @@ -9,7 +10,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class MacroMapDefinition : IMapDefinition + public class MacroMapDefinition : IMapDefinition { private readonly ParameterEditorCollection _parameterEditors; private readonly ILogger _logger; @@ -23,6 +24,7 @@ namespace Umbraco.Web.Models.Mapping public void DefineMaps(UmbracoMapper mapper) { mapper.Define((source, context) => new EntityBasic(), Map); + mapper.Define((source, context) => new MacroDisplay(), Map); mapper.Define>((source, context) => context.MapEnumerable(source.Properties.Values)); mapper.Define((source, context) => new MacroParameter(), Map); } @@ -40,6 +42,23 @@ namespace Umbraco.Web.Models.Mapping target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key); } + private void Map(IMacro source, MacroDisplay target, MapperContext context) + { + target.Alias = source.Alias; + target.Icon = Constants.Icons.Macro; + target.Id = source.Id; + target.Key = source.Key; + target.Name = source.Name; + target.ParentId = -1; + target.Path = "-1," + source.Id; + target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key); + target.CacheByPage = source.CacheByPage; + target.CacheByUser = source.CacheByMember; + target.CachePeriod = source.CacheDuration; + target.UseInEditor = source.UseInEditor; + target.RenderInEditor = !source.DontRender; + target.View = source.MacroSource; + } // Umbraco.Code.MapAll -Value private void Map(IMacroProperty source, MacroParameter target, MapperContext context) { diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/MemberTabsAndPropertiesMapper.cs similarity index 92% rename from src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs rename to src/Umbraco.Infrastructure/Models/Mapping/MemberTabsAndPropertiesMapper.cs index 616ad81fa4..2cb226bec2 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/MemberTabsAndPropertiesMapper.cs @@ -3,14 +3,12 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Mapping; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Dictionary; -using Umbraco.Web.Security; -using Umbraco.Web.Security.Providers; using Umbraco.Core.Configuration; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Models.Mapping { @@ -22,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping /// This also ensures that the IsLocked out property is readonly when the member is not locked out - this is because /// an admin cannot actually set isLockedOut = true, they can only unlock. /// - internal class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper + public class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILocalizedTextService _localizedTextService; @@ -30,9 +28,18 @@ namespace Umbraco.Web.Models.Mapping private readonly IMemberService _memberService; private readonly IMemberGroupService _memberGroupService; private readonly IMemberPasswordConfiguration _memberPasswordConfiguration; + private readonly PropertyEditorCollection _propertyEditorCollection; - public MemberTabsAndPropertiesMapper(ICultureDictionary cultureDictionary, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IMemberTypeService memberTypeService, IMemberService memberService, IMemberGroupService memberGroupService, IMemberPasswordConfiguration memberPasswordConfiguration) - : base(cultureDictionary, localizedTextService) + public MemberTabsAndPropertiesMapper(ICultureDictionary cultureDictionary, + IUmbracoContextAccessor umbracoContextAccessor, + ILocalizedTextService localizedTextService, + IMemberTypeService memberTypeService, + IMemberService memberService, + IMemberGroupService memberGroupService, + IMemberPasswordConfiguration memberPasswordConfiguration, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + PropertyEditorCollection propertyEditorCollection) + : base(cultureDictionary, localizedTextService, contentTypeBaseServiceProvider) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); @@ -40,6 +47,7 @@ namespace Umbraco.Web.Models.Mapping _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); _memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService)); _memberPasswordConfiguration = memberPasswordConfiguration; + _propertyEditorCollection = propertyEditorCollection; } /// @@ -110,7 +118,7 @@ namespace Umbraco.Web.Models.Mapping Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}doctype", Label = _localizedTextService.Localize("content/membertype"), Value = _localizedTextService.UmbracoDictionaryTranslate(CultureDictionary, member.ContentType.Name), - View = Current.PropertyEditors[Constants.PropertyEditors.Aliases.Label].GetValueEditor().View + View = _propertyEditorCollection[Constants.PropertyEditors.Aliases.Label].GetValueEditor().View }, GetLoginProperty(_memberTypeService, member, _localizedTextService), new ContentPropertyDisplay @@ -125,10 +133,10 @@ namespace Umbraco.Web.Models.Mapping { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password", Label = _localizedTextService.Localize("password"), - + Value = new Dictionary { - // TODO: why ignoreCase, what are we doing here?! + // TODO: why ignoreCase, what are we doing here?! {"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)}, }, // TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/PropertyTypeGroupMapper.cs similarity index 99% rename from src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs rename to src/Umbraco.Infrastructure/Models/Mapping/PropertyTypeGroupMapper.cs index c41065a967..5cdc9c5fa4 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/PropertyTypeGroupMapper.cs @@ -8,7 +8,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; -using Current = Umbraco.Core.Composing.Current; namespace Umbraco.Web.Models.Mapping { diff --git a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/RedirectUrlMapDefinition.cs similarity index 52% rename from src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/RedirectUrlMapDefinition.cs index e773fcfee5..08c89b34f8 100644 --- a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/RedirectUrlMapDefinition.cs @@ -1,20 +1,19 @@ using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Routing; namespace Umbraco.Web.Models.Mapping { - internal class RedirectUrlMapDefinition : IMapDefinition + public class RedirectUrlMapDefinition : IMapDefinition { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; - public RedirectUrlMapDefinition(IUmbracoContextAccessor umbracoContextAccessor) + public RedirectUrlMapDefinition(IPublishedUrlProvider publishedUrlProvider) { - _umbracoContextAccessor = umbracoContextAccessor; + _publishedUrlProvider = publishedUrlProvider; } - private UmbracoContext UmbracoContext => _umbracoContextAccessor.UmbracoContext; - public void DefineMaps(UmbracoMapper mapper) { mapper.Define((source, context) => new ContentRedirectUrl(), Map); @@ -26,8 +25,8 @@ namespace Umbraco.Web.Models.Mapping target.ContentId = source.ContentId; target.CreateDateUtc = source.CreateDateUtc; target.Culture = source.Culture; - target.DestinationUrl = source.ContentId > 0 ? UmbracoContext?.UrlProvider?.GetUrl(source.ContentId, culture: source.Culture) : "#"; - target.OriginalUrl = UmbracoContext?.UrlProvider?.GetUrlFromRoute(source.ContentId, source.Url, source.Culture); + target.DestinationUrl = source.ContentId > 0 ? _publishedUrlProvider?.GetUrl(source.ContentId, culture: source.Culture) : "#"; + target.OriginalUrl = _publishedUrlProvider?.GetUrlFromRoute(source.ContentId, source.Url, source.Culture); target.RedirectId = source.Key; } } diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/RelationMapDefinition.cs similarity index 98% rename from src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/RelationMapDefinition.cs index d26a867858..d6ec4fd969 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/RelationMapDefinition.cs @@ -7,7 +7,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class RelationMapDefinition : IMapDefinition + public class RelationMapDefinition : IMapDefinition { private readonly IEntityService _entityService; private readonly IRelationService _relationService; diff --git a/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/SectionMapDefinition.cs similarity index 96% rename from src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/SectionMapDefinition.cs index e05e6e5c84..20b1d2e05f 100644 --- a/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/SectionMapDefinition.cs @@ -7,7 +7,7 @@ using Umbraco.Web.Sections; namespace Umbraco.Web.Models.Mapping { - internal class SectionMapDefinition : IMapDefinition + public class SectionMapDefinition : IMapDefinition { private readonly ILocalizedTextService _textService; public SectionMapDefinition(ILocalizedTextService textService) diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs b/src/Umbraco.Infrastructure/Models/Mapping/TabsAndPropertiesMapper.cs similarity index 92% rename from src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs rename to src/Umbraco.Infrastructure/Models/Mapping/TabsAndPropertiesMapper.cs index 32432d74b1..d9c76b5ebc 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/TabsAndPropertiesMapper.cs @@ -6,12 +6,11 @@ using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Composing; using Umbraco.Core.Dictionary; namespace Umbraco.Web.Models.Mapping { - internal abstract class TabsAndPropertiesMapper + public abstract class TabsAndPropertiesMapper { protected ICultureDictionary CultureDictionary { get; } protected ILocalizedTextService LocalizedTextService { get; } @@ -115,18 +114,22 @@ namespace Umbraco.Web.Models.Mapping /// /// Creates the tabs collection with properties assigned for display models /// - internal class TabsAndPropertiesMapper : TabsAndPropertiesMapper + public class TabsAndPropertiesMapper : TabsAndPropertiesMapper where TSource : IContentBase { - public TabsAndPropertiesMapper(ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService) + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + + public TabsAndPropertiesMapper(ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) : base(cultureDictionary, localizedTextService) - { } + { + _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); + } public virtual IEnumerable> Map(TSource source, MapperContext context) { var tabs = new List>(); - var contentType = Current.Services.ContentTypeBaseServices.GetContentTypeOf(source); + var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source); // add the tabs, for properties that belong to a tab // need to aggregate the tabs, as content.PropertyGroups contains all the composition tabs, diff --git a/src/Umbraco.Web/Models/Mapping/TagMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/TagMapDefinition.cs similarity index 91% rename from src/Umbraco.Web/Models/Mapping/TagMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/TagMapDefinition.cs index 2161183504..32912489c2 100644 --- a/src/Umbraco.Web/Models/Mapping/TagMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/TagMapDefinition.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models; namespace Umbraco.Web.Models.Mapping { - internal class TagMapDefinition : IMapDefinition + public class TagMapDefinition : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) { diff --git a/src/Umbraco.Web/Models/Mapping/TemplateMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/TemplateMapDefinition.cs similarity index 97% rename from src/Umbraco.Web/Models/Mapping/TemplateMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/TemplateMapDefinition.cs index 6868040586..99b69c1fb3 100644 --- a/src/Umbraco.Web/Models/Mapping/TemplateMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/TemplateMapDefinition.cs @@ -5,7 +5,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class TemplateMapDefinition : IMapDefinition + public class TemplateMapDefinition : IMapDefinition { private readonly IShortStringHelper _shortStringHelper; diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Infrastructure/Models/Mapping/UserMapDefinition.cs similarity index 98% rename from src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs rename to src/Umbraco.Infrastructure/Models/Mapping/UserMapDefinition.cs index 3b824f9051..19a78bcbe4 100644 --- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Infrastructure/Models/Mapping/UserMapDefinition.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Mapping; @@ -20,7 +19,7 @@ using Umbraco.Web.Services; namespace Umbraco.Web.Models.Mapping { - internal class UserMapDefinition : IMapDefinition + public class UserMapDefinition : IMapDefinition { private readonly ISectionService _sectionService; private readonly IEntityService _entityService; @@ -31,9 +30,11 @@ namespace Umbraco.Web.Models.Mapping private readonly IGlobalSettings _globalSettings; private readonly IMediaFileSystem _mediaFileSystem; private readonly IShortStringHelper _shortStringHelper; + private readonly IImageUrlGenerator _imageUrlGenerator; public UserMapDefinition(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService, - AppCaches appCaches, ActionCollection actions, IGlobalSettings globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper) + AppCaches appCaches, ActionCollection actions, IGlobalSettings globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, + IImageUrlGenerator imageUrlGenerator) { _sectionService = sectionService; _entityService = entityService; @@ -44,6 +45,7 @@ namespace Umbraco.Web.Models.Mapping _globalSettings = globalSettings; _mediaFileSystem = mediaFileSystem; _shortStringHelper = shortStringHelper; + _imageUrlGenerator = imageUrlGenerator; } public void DefineMaps(UmbracoMapper mapper) @@ -279,7 +281,7 @@ namespace Umbraco.Web.Models.Mapping private void Map(IUser source, UserDisplay target, MapperContext context) { target.AvailableCultures = _textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName); - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService), UmbracoObjectTypes.Document, "content/contentRoot", context); target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService), UmbracoObjectTypes.Media, "media/mediaRoot", context); target.CreateDate = source.CreateDate; @@ -310,7 +312,7 @@ namespace Umbraco.Web.Models.Mapping //Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost //Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look //like the load time is waiting. - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); @@ -329,7 +331,7 @@ namespace Umbraco.Web.Models.Mapping private void Map(IUser source, UserDetail target, MapperContext context) { target.AllowedSections = source.AllowedSections; - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); diff --git a/src/Umbraco.Infrastructure/Models/MediaExtensions.cs b/src/Umbraco.Infrastructure/Models/MediaExtensions.cs index 08612d2810..ffcc2c2a92 100644 --- a/src/Umbraco.Infrastructure/Models/MediaExtensions.cs +++ b/src/Umbraco.Infrastructure/Models/MediaExtensions.cs @@ -1,7 +1,5 @@ using System.Linq; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Models @@ -11,17 +9,15 @@ namespace Umbraco.Core.Models /// /// Gets the url of a media item. /// - public static string GetUrl(this IMedia media, string propertyAlias, ILogger logger, PropertyEditorCollection propertyEditors) + public static string GetUrl(this IMedia media, string propertyAlias, MediaUrlGeneratorCollection mediaUrlGenerators) { if (!media.Properties.TryGetValue(propertyAlias, out var property)) return string.Empty; - if (propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) - && editor is IDataEditorWithMediaPath dataEditor) - { - // TODO: would need to be adjusted to variations, when media become variants - var value = property.GetValue(); - return dataEditor.GetMediaPath(value); + // TODO: would need to be adjusted to variations, when media become variants + if (mediaUrlGenerators.TryGetMediaPath(property.PropertyType.PropertyEditorAlias, property.GetValue(), out var mediaUrl)) + { + return mediaUrl; } // Without knowing what it is, just adding a string here might not be very nice @@ -31,10 +27,10 @@ namespace Umbraco.Core.Models /// /// Gets the urls of a media item. /// - public static string[] GetUrls(this IMedia media, IContentSection contentSection, ILogger logger, PropertyEditorCollection propertyEditors) + public static string[] GetUrls(this IMedia media, IContentSettings contentSettings, MediaUrlGeneratorCollection mediaUrlGenerators) { - return contentSection.ImageAutoFillProperties - .Select(field => media.GetUrl(field.Alias, logger, propertyEditors)) + return contentSettings.ImageAutoFillProperties + .Select(field => media.GetUrl(field.Alias, mediaUrlGenerators)) .Where(link => string.IsNullOrWhiteSpace(link) == false) .ToArray(); } diff --git a/src/Umbraco.Infrastructure/Models/Membership/User.cs b/src/Umbraco.Infrastructure/Models/Membership/User.cs index ed35aea8f3..c3c26825df 100644 --- a/src/Umbraco.Infrastructure/Models/Membership/User.cs +++ b/src/Umbraco.Infrastructure/Models/Membership/User.cs @@ -113,7 +113,6 @@ namespace Umbraco.Core.Models.Membership private DateTime _lastPasswordChangedDate; private DateTime _lastLoginDate; private DateTime _lastLockoutDate; - private bool _defaultToLiveEditing; private IDictionary _additionalData; private object _additionalDataLock = new object(); @@ -321,13 +320,6 @@ namespace Umbraco.Core.Models.Membership set => SetPropertyValueAndDetectChanges(value, ref _language, nameof(Language)); } - [IgnoreDataMember] - internal bool DefaultToLiveEditing - { - get => _defaultToLiveEditing; - set => SetPropertyValueAndDetectChanges(value, ref _defaultToLiveEditing, nameof(DefaultToLiveEditing)); - } - /// /// Gets the groups that user is part of /// diff --git a/src/Umbraco.Infrastructure/Models/PropertyType.cs b/src/Umbraco.Infrastructure/Models/PropertyType.cs index a7ac63f70f..8b15fdfac4 100644 --- a/src/Umbraco.Infrastructure/Models/PropertyType.cs +++ b/src/Umbraco.Infrastructure/Models/PropertyType.cs @@ -168,6 +168,7 @@ namespace Umbraco.Core.Models /// [DataMember] + [DoNotClone] public Lazy PropertyGroupId { get => _propertyGroupId; diff --git a/src/Umbraco.Infrastructure/Models/UserExtensions.cs b/src/Umbraco.Infrastructure/Models/UserExtensions.cs index ebc5fab793..0c0e4fbc48 100644 --- a/src/Umbraco.Infrastructure/Models/UserExtensions.cs +++ b/src/Umbraco.Infrastructure/Models/UserExtensions.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net; using System.Security.Cryptography; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; @@ -26,7 +23,7 @@ namespace Umbraco.Core.Models /// /// A list of 5 different sized avatar URLs /// - public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, IMediaFileSystem mediaFileSystem) + public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, IMediaFileSystem mediaFileSystem, IImageUrlGenerator imageUrlGenerator) { // If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. // Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception @@ -80,11 +77,11 @@ namespace Umbraco.Core.Models var avatarUrl = mediaFileSystem.GetUrl(user.Avatar); return new[] { - avatarUrl + "?width=30&height=30&mode=crop", - avatarUrl + "?width=60&height=60&mode=crop", - avatarUrl + "?width=90&height=90&mode=crop", - avatarUrl + "?width=150&height=150&mode=crop", - avatarUrl + "?width=300&height=300&mode=crop" + imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 30, Height = 30 }), + imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 60, Height = 60 }), + imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 90, Height = 90 }), + imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 150, Height = 150 }), + imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 300, Height = 300 }) }; } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 1699365dfd..6b1aa96e69 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -1133,7 +1133,6 @@ namespace Umbraco.Core.Packaging { var macroName = macroElement.Element("name").Value; var macroAlias = macroElement.Element("alias").Value; - var macroType = Enum.Parse(macroElement.Element("macroType").Value); var macroSource = macroElement.Element("macroSource").Value; //Following xml elements are treated as nullable properties @@ -1169,7 +1168,7 @@ namespace Umbraco.Core.Packaging } var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(_shortStringHelper, macroAlias, macroName, macroSource, macroType, + var macro = existingMacro ?? new Macro(_shortStringHelper, macroAlias, macroName, macroSource, cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); var properties = macroElement.Element("properties"); diff --git a/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs b/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs new file mode 100644 index 0000000000..5a3938d9cb --- /dev/null +++ b/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Persistence +{ + /// + /// A provider that just generates insert commands + /// + public class BasicBulkSqlInsertProvider : IBulkSqlInsertProvider + { + public int BulkInsertRecords(IUmbracoDatabase database, IEnumerable records) + { + var recordsA = records.ToArray(); + if (recordsA.Length == 0) return 0; + + return BulkInsertRecordsWithCommands(database, recordsA); + } + + /// + /// Bulk-insert records using commands. + /// + /// The type of the records. + /// The database. + /// The records. + /// The number of records that were inserted. + internal static int BulkInsertRecordsWithCommands(IUmbracoDatabase database, T[] records) + { + foreach (var command in database.GenerateBulkInsertCommands(records)) + command.ExecuteNonQuery(); + + return records.Length; // what else? + } + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs index 655474b217..d357e9adbc 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs @@ -5,11 +5,12 @@ using Umbraco.Core.Persistence.DatabaseAnnotations; namespace Umbraco.Core.Persistence.Dtos { - [TableName(Constants.DatabaseSchema.Tables.DictionaryEntry)] + [TableName(TableName)] [PrimaryKey("pk")] [ExplicitColumns] internal class DictionaryDto { + public const string TableName = Constants.DatabaseSchema.Tables.DictionaryEntry; [Column("pk")] [PrimaryKeyColumn] public int PrimaryKey { get; set; } @@ -21,6 +22,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("parent")] [NullSetting(NullSetting = NullSettings.Null)] [ForeignKey(typeof(DictionaryDto), Column = "id")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Parent")] public Guid? Parent { get; set; } [Column("key")] diff --git a/src/Umbraco.Infrastructure/Persistence/EntityNotFoundException.cs b/src/Umbraco.Infrastructure/Persistence/EntityNotFoundException.cs deleted file mode 100644 index ea6d5142f0..0000000000 --- a/src/Umbraco.Infrastructure/Persistence/EntityNotFoundException.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Persistence -{ - /// - /// An exception used to indicate that an Umbraco entity could not be found. - /// - /// - [Obsolete("Instead of throwing an exception, return null or an HTTP 404 status code instead.")] - [Serializable] - public class EntityNotFoundException : Exception - { - /// - /// Gets the identifier. - /// - /// - /// The identifier. - /// - /// - /// This object should be serializable to prevent a to be thrown. - /// - public object Id { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public EntityNotFoundException() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The identifier. - /// The message. - public EntityNotFoundException(object id, string message) - : base(message) - { - Id = id; - } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public EntityNotFoundException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public EntityNotFoundException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected EntityNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - Id = info.GetValue(nameof(Id), typeof(object)); - } - - /// - /// When overridden in a derived class, sets the with information about the exception. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - /// info - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - info.AddValue(nameof(Id), Id); - - base.GetObjectData(info, context); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - var result = base.ToString(); - - if (Id != null) - { - return "Umbraco entity (id: " + Id + ") not found. " + result; - } - - return result; - } - } -} diff --git a/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs index 4ae859ba80..13f9d3ee5c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs @@ -185,7 +185,7 @@ namespace Umbraco.Core.Persistence.Factories /// /// Builds a dto from an IMedia item. /// - public static MediaDto BuildDto(PropertyEditorCollection propertyEditors, IMedia entity) + public static MediaDto BuildDto(MediaUrlGeneratorCollection mediaUrlGenerators, IMedia entity) { var contentDto = BuildContentDto(entity, Constants.ObjectTypes.Media); @@ -193,7 +193,7 @@ namespace Umbraco.Core.Persistence.Factories { NodeId = entity.Id, ContentDto = contentDto, - MediaVersionDto = BuildMediaVersionDto(propertyEditors, entity, contentDto) + MediaVersionDto = BuildMediaVersionDto(mediaUrlGenerators, entity, contentDto) }; return dto; @@ -287,7 +287,7 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - private static MediaVersionDto BuildMediaVersionDto(PropertyEditorCollection propertyEditors, IMedia entity, ContentDto contentDto) + private static MediaVersionDto BuildMediaVersionDto(MediaUrlGeneratorCollection mediaUrlGenerators, IMedia entity, ContentDto contentDto) { // try to get a path from the string being stored for media // TODO: only considering umbracoFile @@ -295,11 +295,9 @@ namespace Umbraco.Core.Persistence.Factories string path = null; if (entity.Properties.TryGetValue(Constants.Conventions.Media.File, out var property) - && propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) - && editor is IDataEditorWithMediaPath dataEditor) + && mediaUrlGenerators.TryGetMediaPath(property.PropertyType.PropertyEditorAlias, property.GetValue(), out var mediaPath)) { - var value = property.GetValue(); - path = dataEditor.GetMediaPath(value); + path = mediaPath; } var dto = new MediaVersionDto diff --git a/src/Umbraco.Infrastructure/Persistence/Factories/DataTypeFactory.cs b/src/Umbraco.Infrastructure/Persistence/Factories/DataTypeFactory.cs index 04609b2821..fa991d6679 100644 --- a/src/Umbraco.Infrastructure/Persistence/Factories/DataTypeFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/Factories/DataTypeFactory.cs @@ -1,25 +1,24 @@ using System; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; - namespace Umbraco.Core.Persistence.Factories { internal static class DataTypeFactory { - public static IDataType BuildEntity(DataTypeDto dto, PropertyEditorCollection editors, ILogger logger, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizedTextService localizedTextService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) + public static IDataType BuildEntity(DataTypeDto dto, PropertyEditorCollection editors, ILogger logger) { + // Check we have an editor for the data type. if (!editors.TryGet(dto.EditorAlias, out var editor)) { - logger.Warn(typeof(DataType), "Could not find an editor with alias {EditorAlias}, treating as Label." - +" The site may fail to boot and / or load data types and run.", dto.EditorAlias); - //convert to label - editor = new LabelPropertyEditor(logger, ioHelper,dataTypeService , localizedTextService, localizationService, shortStringHelper); + logger.Warn(typeof(DataType), "Could not find an editor with alias {EditorAlias}, treating as Label. " + + "The site may fail to boot and/or load data types and run.", dto.EditorAlias); + + // Create as special type, which downstream can be handled by converting to a LabelPropertyEditor to make clear + // the situation to the user. + editor = new MissingPropertyEditor(); } var dataType = new DataType(editor); diff --git a/src/Umbraco.Infrastructure/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Infrastructure/Persistence/Factories/MacroFactory.cs index 25cde62c75..5d0cbc9c34 100644 --- a/src/Umbraco.Infrastructure/Persistence/Factories/MacroFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/Factories/MacroFactory.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Factories { public static IMacro BuildEntity(IShortStringHelper shortStringHelper, MacroDto dto) { - var model = new Macro(shortStringHelper, dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroSource, (MacroTypes)dto.MacroType); + var model = new Macro(shortStringHelper, dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroSource); try { @@ -45,7 +45,7 @@ namespace Umbraco.Core.Persistence.Factories RefreshRate = entity.CacheDuration, UseInEditor = entity.UseInEditor, MacroPropertyDtos = BuildPropertyDtos(entity), - MacroType = (int)entity.MacroType + MacroType = 7 //PartialView }; if (entity.HasIdentity) diff --git a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs index b91be4c1e6..23ef0bfda5 100644 --- a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs @@ -9,6 +9,7 @@ namespace Umbraco.Core.Persistence DbProviderFactory CreateFactory(); DbProviderFactory CreateFactory(string providerName); ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName); + IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName); void CreateDatabase(); } } diff --git a/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabaseFactory.cs index c2d65b824f..92e24a7e65 100644 --- a/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabaseFactory.cs @@ -48,13 +48,21 @@ namespace Umbraco.Core.Persistence void Configure(string connectionString, string providerName); /// - /// Gets the Sql context. + /// Gets the . /// /// - /// Getting the Sql context causes the factory to initialize if it is not already initialized. + /// Getting the causes the factory to initialize if it is not already initialized. /// ISqlContext SqlContext { get; } + /// + /// Gets the . + /// + /// + /// Getting the causes the factory to initialize if it is not already initialized. + /// + IBulkSqlInsertProvider BulkSqlInsertProvider { get; } + /// /// Configures the database factory for upgrades. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/MacroMapper.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/MacroMapper.cs index 6a0d803052..f6f7de8347 100644 --- a/src/Umbraco.Infrastructure/Persistence/Mappers/MacroMapper.cs +++ b/src/Umbraco.Infrastructure/Persistence/Mappers/MacroMapper.cs @@ -19,7 +19,6 @@ namespace Umbraco.Core.Persistence.Mappers DefineMap(nameof(Macro.Alias), nameof(MacroDto.Alias)); DefineMap(nameof(Macro.CacheByPage), nameof(MacroDto.CacheByPage)); DefineMap(nameof(Macro.CacheByMember), nameof(MacroDto.CachePersonalized)); - DefineMap(nameof(Macro.MacroType), nameof(MacroDto.MacroType)); DefineMap(nameof(Macro.DontRender), nameof(MacroDto.DontRender)); DefineMap(nameof(Macro.Name), nameof(MacroDto.Name)); DefineMap(nameof(Macro.CacheDuration), nameof(MacroDto.RefreshRate)); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs index 69b0698a96..4020244733 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs @@ -26,5 +26,17 @@ namespace Umbraco.Core.Persistence.Repositories /// /// bool HasContainerInPath(string contentPath); + + /// + /// Gets a value indicating whether there is a list view content item in the path. + /// + /// + /// + bool HasContainerInPath(params int[] ids); + + /// + /// Returns true or false depending on whether content nodes have been created based on the provided content type id. + /// + bool HasContentNodes(int id); } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs index 0788594e3a..4cb533e86f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs @@ -77,7 +77,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement public IEnumerable Get(AuditType type, IQuery query) { var sqlClause = GetBaseQuery(false) - .Where(x => x.Header == type.ToString()); + .Where("(logHeader=@0)", type.ToString()); + var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 8890a859a8..16b9d852fd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -1312,20 +1312,33 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", return test; } - /// - /// Given the path of a content item, this will return true if the content item exists underneath a list view content item - /// - /// - /// + /// public bool HasContainerInPath(string contentPath) { - var ids = contentPath.Split(',').Select(int.Parse); + var ids = contentPath.Split(',').Select(int.Parse).ToArray(); + return HasContainerInPath(ids); + } + + /// + public bool HasContainerInPath(params int[] ids) + { var sql = new Sql($@"SELECT COUNT(*) FROM cmsContentType INNER JOIN {Constants.DatabaseSchema.Tables.Content} ON cmsContentType.nodeId={Constants.DatabaseSchema.Tables.Content}.contentTypeId WHERE {Constants.DatabaseSchema.Tables.Content}.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true }); return Database.ExecuteScalar(sql) > 0; } + /// + /// Returns true or false depending on whether content nodes have been created based on the provided content type id. + /// + public bool HasContentNodes(int id) + { + var sql = new Sql( + $"SELECT CASE WHEN EXISTS (SELECT * FROM {Constants.DatabaseSchema.Tables.Content} WHERE contentTypeId = @id) THEN 1 ELSE 0 END", + new { id }); + return Database.ExecuteScalar(sql) == 1; + } + protected override IEnumerable GetDeleteClauses() { // in theory, services should have ensured that content items of the given content type diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs index 1f0d944c7e..42a89384d7 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -7,7 +7,6 @@ using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Events; using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; @@ -17,7 +16,6 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; -using Umbraco.Core.Strings; using static Umbraco.Core.Persistence.SqlExtensionsStatics; namespace Umbraco.Core.Persistence.Repositories.Implement @@ -28,22 +26,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement internal class DataTypeRepository : NPocoRepositoryBase, IDataTypeRepository { private readonly Lazy _editors; - private readonly IIOHelper _ioHelper; - private readonly Lazy _dataTypeService; - private readonly ILocalizedTextService _localizedTextService; - private readonly ILocalizationService _localizationService; - private readonly IShortStringHelper _shortStringHelper; - // TODO: https://github.com/umbraco/Umbraco-CMS/issues/4237 - get rid of Lazy injection and fix circular dependencies - public DataTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, Lazy editors, ILogger logger, IIOHelper ioHelper, Lazy dataTypeService, ILocalizedTextService localizedTextService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) + public DataTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, Lazy editors, ILogger logger) : base(scopeAccessor, cache, logger) { _editors = editors; - _ioHelper = ioHelper; - _dataTypeService = dataTypeService; - _localizedTextService = localizedTextService; - _localizationService = localizationService; - _shortStringHelper = shortStringHelper; } #region Overrides of RepositoryBase @@ -67,7 +54,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } var dtos = Database.Fetch(dataTypeSql); - return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, Logger,_ioHelper, _dataTypeService.Value, _localizedTextService, _localizationService, _shortStringHelper)).ToArray(); + return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, Logger)).ToArray(); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -78,7 +65,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var dtos = Database.Fetch(sql); - return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, Logger, _ioHelper, _dataTypeService.Value, _localizedTextService, _localizationService, _shortStringHelper)).ToArray(); + return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, Logger)).ToArray(); } #endregion diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs index c3f9f03ec0..3d006914a2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs @@ -653,10 +653,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var entity = new MediaEntitySlim(); BuildContentEntity(entity, dto); - if (dto is MediaEntityDto contentDto) + // fill in the media info + if (dto is MediaEntityDto mediaEntityDto) { - // fill in the media info - entity.MediaPath = contentDto.MediaPath; + entity.MediaPath = mediaEntityDto.MediaPath; + } + else if (dto is GenericContentEntityDto genericContentEntityDto) + { + entity.MediaPath = genericContentEntityDto.MediaPath; } return entity; 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/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index b0afc3b5f8..11f8c4d696 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -25,6 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { private readonly IMediaTypeRepository _mediaTypeRepository; private readonly ITagRepository _tagRepository; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly MediaByGuidReadRepository _mediaByGuidReadRepository; public MediaRepository( @@ -37,12 +38,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditorCollection, + MediaUrlGeneratorCollection mediaUrlGenerators, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeService dataTypeService) : base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditorCollection, dataValueReferenceFactories, dataTypeService) { _mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); + _mediaUrlGenerators = mediaUrlGenerators; _mediaByGuidReadRepository = new MediaByGuidReadRepository(this, scopeAccessor, cache, logger); } @@ -239,7 +242,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.SanitizeEntityPropertiesForXmlStorage(); // create the dto - var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); + var dto = ContentBaseFactory.BuildDto(_mediaUrlGenerators, entity); // derive path and level from parent var parent = GetParentNodeDto(entity.ParentId); @@ -330,7 +333,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // create the dto - var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); + var dto = ContentBaseFactory.BuildDto(_mediaUrlGenerators, entity); // update the node dto var nodeDto = dto.ContentDto.NodeDto; 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/Repositories/Implement/UserGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs index 1f0ab0cbe7..07cfbb05a8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs @@ -32,10 +32,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement _permissionRepository = new PermissionRepository(scopeAccessor, appCaches, logger); } - public const string GetByAliasCacheKeyPrefix = "UserGroupRepository_GetByAlias_"; + public static string GetByAliasCacheKey(string alias) { - return GetByAliasCacheKeyPrefix + alias; + return CacheKeys.UserGroupGetByAliasCacheKeyPrefix + alias; } public IUserGroup Get(string alias) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs index f3db5c5906..2f1b6c5814 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs @@ -543,6 +543,16 @@ ORDER BY colName"; } } + // If userlogin or the email has changed then need to reset security stamp + if (changedCols.Contains("userLogin") || changedCols.Contains("userEmail")) + { + userDto.EmailConfirmedDate = null; + userDto.SecurityStampToken = entity.SecurityStamp = Guid.NewGuid().ToString(); + + changedCols.Add("emailConfirmedDate"); + changedCols.Add("securityStampToken"); + } + //only update the changed cols if (changedCols.Count > 0) { diff --git a/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs index 68e7f7acdc..4631aca889 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs @@ -8,6 +8,10 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence { + + /// + /// A bulk sql insert provider for Sql Server + /// public class SqlServerBulkSqlInsertProvider : IBulkSqlInsertProvider { public int BulkInsertRecords(IUmbracoDatabase database, IEnumerable records) @@ -20,22 +24,7 @@ namespace Umbraco.Core.Persistence return database.DatabaseType.IsSqlServer2008OrLater() ? BulkInsertRecordsSqlServer(database, pocoData, recordsA) - : BulkInsertRecordsWithCommands(database, recordsA); - } - - /// - /// Bulk-insert records using commands. - /// - /// The type of the records. - /// The database. - /// The records. - /// The number of records that were inserted. - private static int BulkInsertRecordsWithCommands(IUmbracoDatabase database, T[] records) - { - foreach (var command in database.GenerateBulkInsertCommands(records)) - command.ExecuteNonQuery(); - - return records.Length; // what else? + : BasicBulkSqlInsertProvider.BulkInsertRecordsWithCommands(database, recordsA); } /// 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 37375ef25d..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())"); @@ -251,6 +253,11 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName) } public override void WriteLock(IDatabase db, params int[] lockIds) + { + WriteLock(db, TimeSpan.FromMilliseconds(1800), lockIds); + } + + public void WriteLock(IDatabase db, TimeSpan timeout, params int[] lockIds) { // soon as we get Database, a transaction is started @@ -261,7 +268,7 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName) // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks foreach (var lockId in lockIds) { - db.Execute(@"SET LOCK_TIMEOUT 1800;"); + db.Execute($"SET LOCK_TIMEOUT {timeout.TotalMilliseconds};"); var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId }); if (i == 0) // ensure we are actually locking! throw new ArgumentException($"LockObject with id={lockId} does not exist."); 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/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs index 99c4c4cf2d..a93c8db409 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs @@ -27,9 +27,9 @@ namespace Umbraco.Core.Persistence // TODO: this class needs not be disposable! internal class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFactory { - private readonly Configs _configs; private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator; - private readonly IBulkSqlInsertProvider _bulkSqlInsertProvider; + private readonly IGlobalSettings _globalSettings; + private readonly IConnectionStrings _connectionStrings; private readonly Lazy _mappers; private readonly ILogger _logger; @@ -41,6 +41,7 @@ namespace Umbraco.Core.Persistence private string _providerName; private DatabaseType _databaseType; private ISqlSyntaxProvider _sqlSyntax; + private IBulkSqlInsertProvider _bulkSqlInsertProvider; private RetryPolicy _connectionRetryPolicy; private RetryPolicy _commandRetryPolicy; private NPoco.MapperCollection _pocoMappers; @@ -71,28 +72,28 @@ namespace Umbraco.Core.Persistence /// Initializes a new instance of the . /// /// Used by core runtime. - public UmbracoDatabaseFactory(ILogger logger, Lazy mappers, Configs configs, IDbProviderFactoryCreator dbProviderFactoryCreator, IBulkSqlInsertProvider bulkSqlInsertProvider) - : this(Constants.System.UmbracoConnectionName, logger, mappers, configs, dbProviderFactoryCreator, bulkSqlInsertProvider) + public UmbracoDatabaseFactory(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, Lazy mappers,IDbProviderFactoryCreator dbProviderFactoryCreator) + : this(Constants.System.UmbracoConnectionName, globalSettings, connectionStrings, logger, mappers, dbProviderFactoryCreator) { - _configs = configs; + } /// /// Initializes a new instance of the . /// /// Used by the other ctor and in tests. - public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy mappers, Configs configs, IDbProviderFactoryCreator dbProviderFactoryCreator, IBulkSqlInsertProvider bulkSqlInsertProvider) + public UmbracoDatabaseFactory(string connectionStringName, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, ILogger logger, Lazy mappers, IDbProviderFactoryCreator dbProviderFactoryCreator) { if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName)); if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionStringName)); + _globalSettings = globalSettings; + _connectionStrings = connectionStrings; _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); - _configs = configs; _dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator)); - _bulkSqlInsertProvider = bulkSqlInsertProvider ?? throw new ArgumentNullException(nameof(bulkSqlInsertProvider)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - var settings = configs.ConnectionStrings()[connectionStringName]; + var settings = connectionStrings[connectionStringName]; if (settings == null) { @@ -117,12 +118,11 @@ namespace Umbraco.Core.Persistence /// Initializes a new instance of the . /// /// Used in tests. - public UmbracoDatabaseFactory(string connectionString, string providerName, ILogger logger, Lazy mappers, IDbProviderFactoryCreator dbProviderFactoryCreator, IBulkSqlInsertProvider bulkSqlInsertProvider) + public UmbracoDatabaseFactory(string connectionString, string providerName, ILogger logger, Lazy mappers, IDbProviderFactoryCreator dbProviderFactoryCreator) { _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator)); - _bulkSqlInsertProvider = bulkSqlInsertProvider ?? throw new ArgumentNullException(nameof(bulkSqlInsertProvider)); if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName)) { @@ -163,7 +163,7 @@ namespace Umbraco.Core.Persistence { // replace NPoco database type by a more efficient one - var setting = _configs.Global().DatabaseFactoryServerVersion; + var setting = _globalSettings.DatabaseFactoryServerVersion; var fromSettings = false; if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.") @@ -205,6 +205,17 @@ namespace Umbraco.Core.Persistence } } + /// + public IBulkSqlInsertProvider BulkSqlInsertProvider + { + get + { + // must be initialized to have a bulk insert provider + EnsureInitialized(); + return _bulkSqlInsertProvider; + } + } + /// public void ConfigureForUpgrade() { @@ -260,6 +271,8 @@ namespace Umbraco.Core.Persistence if (_sqlSyntax == null) throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\"."); + _bulkSqlInsertProvider = _dbProviderFactoryCreator.CreateBulkSqlInsertProvider(_providerName); + if (_databaseType.IsSqlServer()) UpdateSqlServerDatabaseType(); diff --git a/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs index 5791b3d05a..67f3dd61cd 100644 --- a/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ColorPickerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs index 0b7da0054f..0f6996ddd4 100644 --- a/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ContentPickerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ContentPickerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs similarity index 79% rename from src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs index 0a32a2eb7c..4c996fd4fc 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; @@ -24,18 +23,21 @@ namespace Umbraco.Web.PropertyEditors { private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; + private readonly ILocalizedTextService _localizedTextService; private readonly IIOHelper _ioHelper; public ContentPickerPropertyEditor( IDataTypeService dataTypeService, ILocalizationService localizationService, + ILocalizedTextService localizedTextService, ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + : base(logger, dataTypeService,localizationService,localizedTextService, shortStringHelper) { _dataTypeService = dataTypeService; _localizationService = localizationService; + _localizedTextService = localizedTextService; _ioHelper = ioHelper; } @@ -44,11 +46,11 @@ namespace Umbraco.Web.PropertyEditors return new ContentPickerConfigurationEditor(_ioHelper); } - protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Attribute); internal class ContentPickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public ContentPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) + public ContentPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs index 8ccb1680cc..95bc898ea6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs @@ -22,9 +22,6 @@ namespace Umbraco.Core.PropertyEditors [DataContract] public class DataEditor : IDataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; - private readonly ILocalizedTextService _localizedTextService; private IDictionary _defaultConfiguration; private IDataValueEditor _dataValueEditor; @@ -33,11 +30,12 @@ namespace Umbraco.Core.PropertyEditors /// public DataEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, EditorType type = EditorType.PropertyValue) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; - _localizedTextService = localizedTextService; - ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + DataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); + LocalizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); + LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); + ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + // defaults Type = type; @@ -62,6 +60,9 @@ namespace Umbraco.Core.PropertyEditors protected DataEditorAttribute Attribute { get; } protected IShortStringHelper ShortStringHelper { get; } + protected ILocalizedTextService LocalizedTextService { get; } + protected ILocalizationService LocalizationService { get; } + protected IDataTypeService DataTypeService { get; } /// /// Gets a logger. @@ -118,7 +119,7 @@ namespace Umbraco.Core.PropertyEditors /// Technically, it could be cached by datatype but let's keep things /// simple enough for now. /// - public IDataValueEditor GetValueEditor(object configuration) + public virtual IDataValueEditor GetValueEditor(object configuration) { // if an explicit value editor has been set (by the manifest parser) // then return it, and ignore the configuration, which is going to be @@ -179,7 +180,7 @@ namespace Umbraco.Core.PropertyEditors if (Attribute == null) throw new InvalidOperationException($"The editor is not attributed with {nameof(DataEditorAttribute)}"); - return new DataValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Attribute); + return new DataValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute); } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs index 51dfe6c5c4..2737dcfef1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -11,37 +11,51 @@ namespace Umbraco.Core.PropertyEditors : base(items) { } + // TODO: We could further reduce circular dependencies with PropertyEditorCollection by not having IDataValueReference implemented + // by property editors and instead just use the already built in IDataValueReferenceFactory and/or refactor that into a more normal collection + public IEnumerable GetAllReferences(IPropertyCollection properties, PropertyEditorCollection propertyEditors) { - var trackedRelations = new List(); + var trackedRelations = new HashSet(); foreach (var p in properties) { if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue; - //TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here - if (!p.PropertyType.VariesByNothing()) continue; - var val = p.GetValue(); // get the invariant value + //TODO: We will need to change this once we support tracking via variants/segments + // for now, we are tracking values from ALL variants - var valueEditor = editor.GetValueEditor(); - if (valueEditor is IDataValueReference reference) + foreach(var propertyVal in p.Values) { - var refs = reference.GetReferences(val); - trackedRelations.AddRange(refs); + var val = propertyVal.EditedValue; + + var valueEditor = editor.GetValueEditor(); + if (valueEditor is IDataValueReference reference) + { + var refs = reference.GetReferences(val); + foreach(var r in refs) + trackedRelations.Add(r); } - // Loop over collection that may be add to existing property editors - // implementation of GetReferences in IDataValueReference. - // Allows developers to add support for references by a - // package /property editor that did not implement IDataValueReference themselves - foreach (var item in this) - { - // Check if this value reference is for this datatype/editor - // Then call it's GetReferences method - to see if the value stored - // in the dataeditor/property has referecnes to media/content items - if (item.IsForEditor(editor)) - trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val)); + // Loop over collection that may be add to existing property editors + // implementation of GetReferences in IDataValueReference. + // Allows developers to add support for references by a + // package /property editor that did not implement IDataValueReference themselves + foreach (var item in this) + { + // Check if this value reference is for this datatype/editor + // Then call it's GetReferences method - to see if the value stored + // in the dataeditor/property has referecnes to media/content items + if (item.IsForEditor(editor)) + { + foreach(var r in item.GetDataValueReference().GetReferences(val)) + trackedRelations.Add(r); + } + + } } + + } return trackedRelations; diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DateTimeConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DateTimeConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs similarity index 80% rename from src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs index 35d4e29737..d2a08c3a2b 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs @@ -1,8 +1,8 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -24,8 +24,8 @@ namespace Umbraco.Web.PropertyEditors /// Initializes a new instance of the class. /// /// - public DateTimePropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public DateTimePropertyEditor(ILogger logger, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService,localizedTextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DateValueEditor.cs similarity index 79% rename from src/Umbraco.Web/PropertyEditors/DateValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DateValueEditor.cs index 4e242fc380..8973dd812b 100644 --- a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DateValueEditor.cs @@ -1,6 +1,5 @@ using System; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; @@ -14,8 +13,8 @@ namespace Umbraco.Web.PropertyEditors /// internal class DateValueEditor : DataValueEditor { - public DateValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public DateValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { Validators.Add(new DateTimeValidator()); } diff --git a/src/Umbraco.Web/PropertyEditors/DecimalConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DecimalConfigurationEditor.cs similarity index 99% rename from src/Umbraco.Web/PropertyEditors/DecimalConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DecimalConfigurationEditor.cs index 22b1123b7c..952e3557dd 100644 --- a/src/Umbraco.Web/PropertyEditors/DecimalConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DecimalConfigurationEditor.cs @@ -35,4 +35,4 @@ namespace Umbraco.Web.PropertyEditors }); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DecimalPropertyEditor.cs similarity index 73% rename from src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DecimalPropertyEditor.cs index 894c8a3a43..e5ca56878f 100644 --- a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DecimalPropertyEditor.cs @@ -1,8 +1,8 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -21,8 +21,12 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public DecimalPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public DecimalPropertyEditor(ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { } /// diff --git a/src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexibleConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexibleConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs index 897ca63afc..73ce726257 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/EmailAddressConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/EmailAddressConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/EmailAddressConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs similarity index 72% rename from src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs index 619419085d..c5bcaf0f86 100644 --- a/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs @@ -1,9 +1,9 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -21,8 +21,14 @@ namespace Umbraco.Web.PropertyEditors /// /// The constructor will setup the property editor based on the attribute if one is found /// - public EmailAddressPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public EmailAddressPropertyEditor( + ILogger logger, + IIOHelper ioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs similarity index 86% rename from src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index a1b0b99d8d..698fcb10b3 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -20,22 +19,24 @@ namespace Umbraco.Web.PropertyEditors "fileupload", Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] - public class FileUploadPropertyEditor : DataEditor, IDataEditorWithMediaPath + public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator { private readonly IMediaFileSystem _mediaFileSystem; - private readonly IContentSection _contentSection; + private readonly IContentSettings _contentSettings; private readonly UploadAutoFillProperties _uploadAutoFillProperties; private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; + private readonly ILocalizedTextService _localizedTextService; - public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSection, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) + public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSettings contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); - _contentSection = contentSection; + _contentSettings = contentSettings; _dataTypeService = dataTypeService; _localizationService = localizationService; - _uploadAutoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, contentSection); + _localizedTextService = localizedTextService; + _uploadAutoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, contentSettings); } /// @@ -44,12 +45,21 @@ namespace Umbraco.Web.PropertyEditors /// The corresponding property value editor. protected override IDataValueEditor CreateValueEditor() { - var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, ShortStringHelper); - editor.Validators.Add(new UploadFileTypeValidator()); + var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, _contentSettings); + editor.Validators.Add(new UploadFileTypeValidator(_localizedTextService, _contentSettings)); return editor; } - public string GetMediaPath(object value) => value?.ToString(); + public bool TryGetMediaPath(string alias, object value, out string mediaPath) + { + if (alias == Alias) + { + mediaPath = value?.ToString(); + return true; + } + mediaPath = null; + return false; + } /// /// Gets a value indicating whether a property is an upload field. @@ -167,7 +177,7 @@ namespace Umbraco.Web.PropertyEditors foreach (var property in properties) { - var autoFillConfig = _contentSection.GetConfig(property.Alias); + var autoFillConfig = _contentSettings.GetConfig(property.Alias); if (autoFillConfig == null) continue; foreach (var pvalue in property.Values) diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs similarity index 90% rename from src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs index a5e12165af..e45896551c 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; @@ -16,11 +16,13 @@ namespace Umbraco.Web.PropertyEditors internal class FileUploadPropertyValueEditor : DataValueEditor { private readonly IMediaFileSystem _mediaFileSystem; + private readonly IContentSettings _contentSettings; - public FileUploadPropertyValueEditor(DataEditorAttribute attribute, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public FileUploadPropertyValueEditor(DataEditorAttribute attribute, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IContentSettings contentSettings) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } /// @@ -99,7 +101,7 @@ namespace Umbraco.Web.PropertyEditors { // process the file // no file, invalid file, reject change - if (UploadFileTypeValidator.IsValidFileExtension(file.FileName) == false) + if (UploadFileTypeValidator.IsValidFileExtension(file.FileName, _contentSettings) == false) return null; // get the filepath diff --git a/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/GridConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/GridConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/GridConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs index 8a275b69cd..f9ff1ad0c4 100644 --- a/src/Umbraco.Web/PropertyEditors/GridConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs @@ -4,7 +4,6 @@ using System.Linq; using Newtonsoft.Json; using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.Validators; namespace Umbraco.Web.PropertyEditors { diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs similarity index 80% rename from src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index 51f1197245..72e0843240 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -1,9 +1,8 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -29,40 +28,32 @@ namespace Umbraco.Web.PropertyEditors public class GridPropertyEditor : DataEditor { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private readonly ILogger _logger; - private readonly IMediaService _mediaService; - private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly HtmlImageSourceParser _imageSourceParser; private readonly RichTextEditorPastedImages _pastedImages; private readonly HtmlLocalLinkParser _localLinkParser; + private readonly IImageUrlGenerator _imageUrlGenerator; public GridPropertyEditor( ILogger logger, - IMediaService mediaService, - IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, + ILocalizedTextService localizedTextService, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, HtmlLocalLinkParser localLinkParser, IIOHelper ioHelper, - IShortStringHelper shortStringHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) + IShortStringHelper shortStringHelper, + IImageUrlGenerator imageUrlGenerator) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _umbracoContextAccessor = umbracoContextAccessor; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; - _logger = logger; - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _imageSourceParser = imageSourceParser; _pastedImages = pastedImages; _localLinkParser = localLinkParser; + _imageUrlGenerator = imageUrlGenerator; } public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory(); @@ -71,7 +62,7 @@ namespace Umbraco.Web.PropertyEditors /// Overridden to ensure that the value is validated /// /// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _imageSourceParser, _pastedImages, _localLinkParser, ShortStringHelper); + protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _umbracoContextAccessor, DataTypeService, LocalizationService, LocalizedTextService, _imageSourceParser, _pastedImages, _localLinkParser, ShortStringHelper, _imageUrlGenerator); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(_ioHelper); @@ -82,26 +73,27 @@ namespace Umbraco.Web.PropertyEditors private readonly RichTextEditorPastedImages _pastedImages; private readonly RichTextPropertyEditor.RichTextPropertyValueEditor _richTextPropertyValueEditor; private readonly MediaPickerPropertyEditor.MediaPickerPropertyValueEditor _mediaPickerPropertyValueEditor; + private readonly IImageUrlGenerator _imageUrlGenerator; public GridPropertyValueEditor( DataEditorAttribute attribute, - IMediaService mediaService, - IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, - ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, + ILocalizedTextService localizedTextService, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, HtmlLocalLinkParser localLinkParser, - IShortStringHelper shortStringHelper) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + IShortStringHelper shortStringHelper, + IImageUrlGenerator imageUrlGenerator) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; _pastedImages = pastedImages; - _richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor,logger, dataTypeService, localizationService, shortStringHelper, imageSourceParser, localLinkParser, pastedImages); - _mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(dataTypeService, localizationService, shortStringHelper, attribute); + _richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, umbracoContextAccessor, dataTypeService, localizationService, localizedTextService, shortStringHelper, imageSourceParser, localLinkParser, pastedImages, imageUrlGenerator); + _mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute); + _imageUrlGenerator = imageUrlGenerator; } /// @@ -136,7 +128,7 @@ namespace Umbraco.Web.PropertyEditors // Parse the HTML var html = rte.Value?.ToString(); - var parseAndSavedTempImages = _pastedImages.FindAndPersistPastedTempImages(html, mediaParentId, userId); + var parseAndSavedTempImages = _pastedImages.FindAndPersistPastedTempImages(html, mediaParentId, userId, _imageUrlGenerator); var editorValueWithMediaUrlsRemoved = _imageSourceParser.RemoveImageSources(parseAndSavedTempImages); rte.Value = editorValueWithMediaUrlsRemoved; @@ -156,10 +148,10 @@ namespace Umbraco.Web.PropertyEditors /// public override object ToEditor(IProperty property, string culture = null, string segment = null) { - var val = property.GetValue(culture, segment); - if (val == null) return string.Empty; + var val = property.GetValue(culture, segment)?.ToString(); + if (val.IsNullOrWhiteSpace()) return string.Empty; - var grid = DeserializeGridValue(val.ToString(), out var rtes, out _); + var grid = DeserializeGridValue(val, out var rtes, out _); //process the rte values foreach (var rte in rtes.ToList()) diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs similarity index 95% rename from src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs rename to src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs index af72f0b819..195764fbbf 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs @@ -1,18 +1,17 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Xml; using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { - using System.Collections.Generic; - using System.Linq; - using Umbraco.Core.Models; - /// /// Parses the grid value into indexable values /// @@ -63,7 +62,7 @@ namespace Umbraco.Web.PropertyEditors } //First save the raw value to a raw field - result.Add(new KeyValuePair>($"{UmbracoExamineIndex.RawFieldPrefix}{property.Alias}", new[] { rawVal })); + result.Add(new KeyValuePair>($"{UmbracoExamineFieldNames.RawFieldPrefix}{property.Alias}", new[] { rawVal })); if (sb.Length > 0) { diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ImageCropperConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ImageCropperConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs similarity index 93% rename from src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 7901f2d862..586a120609 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -1,17 +1,14 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using Umbraco.Core.Media; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Media; @@ -29,10 +26,10 @@ namespace Umbraco.Web.PropertyEditors HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] - public class ImageCropperPropertyEditor : DataEditor, IDataEditorWithMediaPath + public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator { private readonly IMediaFileSystem _mediaFileSystem; - private readonly IContentSection _contentSettings; + private readonly IContentSettings _contentSettings; private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; @@ -41,7 +38,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public ImageCropperPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper, ILocalizedTextService localizedTextService) + public ImageCropperPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSettings contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper, ILocalizedTextService localizedTextService) : base(logger, dataTypeService, localizationService, localizedTextService,shortStringHelper) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); @@ -54,13 +51,22 @@ namespace Umbraco.Web.PropertyEditors _autoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, _contentSettings); } - public string GetMediaPath(object value) => GetFileSrcFromPropertyValue(value, out _, false); + public bool TryGetMediaPath(string alias, object value, out string mediaPath) + { + if (alias == Alias) + { + mediaPath = GetFileSrcFromPropertyValue(value, out _, false); + return true; + } + mediaPath = null; + return false; + } /// /// Creates the corresponding property value editor. /// /// The corresponding property value editor. - protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, Logger, _mediaFileSystem, _dataTypeService, _localizationService, ShortStringHelper); + protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, Logger, _mediaFileSystem, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings); /// /// Creates the corresponding preValue editor. diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs similarity index 93% rename from src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index f8b6688ded..1c7c8b922a 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json.Linq; -using System; +using System; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -22,12 +22,14 @@ namespace Umbraco.Web.PropertyEditors { private readonly ILogger _logger; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IContentSettings _contentSettings; - public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IContentSettings contentSettings) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } /// @@ -144,7 +146,7 @@ namespace Umbraco.Web.PropertyEditors { // process the file // no file, invalid file, reject change - if (UploadFileTypeValidator.IsValidFileExtension(file.FileName) == false) + if (UploadFileTypeValidator.IsValidFileExtension(file.FileName, _contentSettings) == false) return null; // get the filepath diff --git a/src/Umbraco.Web/PropertyEditors/IntegerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/IntegerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/IntegerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/IntegerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/IntegerPropertyEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/IntegerPropertyEditor.cs index 4e54200d5e..49e49e005a 100644 --- a/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/IntegerPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs index ef4a24d53e..f91c8efdd1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs @@ -18,10 +18,7 @@ namespace Umbraco.Core.PropertyEditors public class LabelPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizedTextService _localizedTextService; - private readonly ILocalizationService _localizationService; - private readonly IShortStringHelper _shortStringHelper; + /// /// Initializes a new instance of the class. @@ -30,14 +27,10 @@ namespace Umbraco.Core.PropertyEditors : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _ioHelper = ioHelper; - _dataTypeService = dataTypeService; - _localizedTextService = localizedTextService; - _localizationService = localizationService; - _shortStringHelper = shortStringHelper; } /// - protected override IDataValueEditor CreateValueEditor() => new LabelPropertyValueEditor(_dataTypeService, _localizationService,_localizedTextService, _shortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new LabelPropertyValueEditor(DataTypeService, LocalizationService,LocalizedTextService, ShortStringHelper, Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new LabelConfigurationEditor(_ioHelper); diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/ListViewConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ListViewConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ListViewConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ListViewConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs similarity index 69% rename from src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs index 705506abd5..f46743f0cc 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs @@ -1,8 +1,8 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -25,8 +25,14 @@ namespace Umbraco.Web.PropertyEditors /// Initializes a new instance of the class. /// /// - public ListViewPropertyEditor(ILogger logger, IIOHelper iioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public ListViewPropertyEditor( + ILogger logger, + IIOHelper iioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _iioHelper = iioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MarkdownConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MarkdownConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MarkdownConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs similarity index 68% rename from src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs index f08452294d..167b05df53 100644 --- a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs @@ -1,8 +1,8 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -24,8 +24,14 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public MarkdownPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MarkdownPropertyEditor( + ILogger logger, + IIOHelper ioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MediaPickerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MediaPickerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs similarity index 72% rename from src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs index 735cd1ac79..d2fcf4e4cc 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs @@ -1,14 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core; using Umbraco.Core.IO; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { @@ -25,8 +22,6 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.MediaImage)] public class MediaPickerPropertyEditor : DataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; /// @@ -41,20 +36,18 @@ namespace Umbraco.Web.PropertyEditors ILocalizedTextService localizedTextService) : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; } /// protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute); - internal class MediaPickerPropertyValueEditor : DataValueEditor, IDataValueReference + public class MediaPickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public MediaPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService,localizationService, Current.Services.TextService, shortStringHelper,attribute) + public MediaPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService,localizationService, localizedTextService, shortStringHelper,attribute) { } @@ -70,6 +63,4 @@ namespace Umbraco.Web.PropertyEditors } } } - - } diff --git a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MemberGroupPickerPropertyEditor.cs similarity index 55% rename from src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MemberGroupPickerPropertyEditor.cs index f88e08d84d..fd1bd0d102 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -1,7 +1,7 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -15,8 +15,13 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.MemberGroup)] public class MemberGroupPickerPropertyEditor : DataEditor { - public MemberGroupPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MemberGroupPickerPropertyEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { } } } diff --git a/src/Umbraco.Web/PropertyEditors/MemberPickerConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/MemberPickerConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MemberPickerConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MemberPickerConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MemberPickerPropertyEditor.cs similarity index 59% rename from src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MemberPickerPropertyEditor.cs index b7ee696940..092e790e51 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MemberPickerPropertyEditor.cs @@ -1,7 +1,7 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -15,8 +15,13 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.Member)] public class MemberPickerPropertyEditor : DataEditor { - public MemberPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MemberPickerPropertyEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { } protected override IConfigurationEditor CreateConfigurationEditor() => new MemberPickerConfiguration(); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MissingPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MissingPropertyEditor.cs new file mode 100644 index 0000000000..902aab7cc7 --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/MissingPropertyEditor.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Represents a temporary representation of an editor for cases where a data type is created but not editor is available. + /// + public class MissingPropertyEditor : IDataEditor + { + public string Alias => "Umbraco.Missing"; + + public EditorType Type => EditorType.Nothing; + + public string Name => "Missing property editor"; + + public string Icon => string.Empty; + + public string Group => string.Empty; + + public bool IsDeprecated => false; + + public IDictionary DefaultConfiguration => throw new NotImplementedException(); + + public IPropertyIndexValueFactory PropertyIndexValueFactory => throw new NotImplementedException(); + + public IConfigurationEditor GetConfigurationEditor() + { + return new ConfigurationEditor(); + } + + public IDataValueEditor GetValueEditor() + { + throw new NotImplementedException(); + } + + public IDataValueEditor GetValueEditor(object configuration) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfiguration.cs similarity index 95% rename from src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfiguration.cs index 81a96b5ad7..0725971e95 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfiguration.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json.Linq; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs similarity index 67% rename from src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index 946b11f365..f1ed4e6c44 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; @@ -19,26 +18,22 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-page-add")] public class MultiNodeTreePickerPropertyEditor : DataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - public MultiNodeTreePickerPropertyEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public MultiNodeTreePickerPropertyEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; } protected override IConfigurationEditor CreateConfigurationEditor() => new MultiNodePickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute); public class MultiNodeTreePickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public MultiNodeTreePickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public MultiNodeTreePickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { } diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs similarity index 54% rename from src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs index a56a0a8c63..1be4067763 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs @@ -1,12 +1,12 @@ using System; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Logging; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Web.PropertyEditors { @@ -20,24 +20,24 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-link")] public class MultiUrlPickerPropertyEditor : DataEditor { - private readonly IEntityService _entityService; + private readonly Lazy _entityService; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; - public MultiUrlPickerPropertyEditor(ILogger logger, IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, EditorType.PropertyValue) + public MultiUrlPickerPropertyEditor(ILogger logger, Lazy entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IIOHelper ioHelper, IShortStringHelper shortStringHelper, IUmbracoContextAccessor umbracoContextAccessor, IPublishedUrlProvider publishedUrlProvider) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper, EditorType.PropertyValue) { _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; + _umbracoContextAccessor = umbracoContextAccessor; + _publishedUrlProvider = publishedUrlProvider; } protected override IConfigurationEditor CreateConfigurationEditor() => new MultiUrlPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerValueEditor(_entityService, _publishedSnapshotAccessor, Logger, _dataTypeService, _localizationService, ShortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerValueEditor(_entityService.Value, _publishedSnapshotAccessor, Logger, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute, _umbracoContextAccessor, _publishedUrlProvider); } } diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs similarity index 89% rename from src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs index 5564d44e4a..f3d2255b6b 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -11,9 +11,10 @@ using Umbraco.Core.Models.Entities; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.PublishedCache; +using Umbraco.Core; +using Umbraco.Web.Routing; namespace Umbraco.Web.PropertyEditors { @@ -21,14 +22,18 @@ namespace Umbraco.Web.PropertyEditors { private readonly IEntityService _entityService; private readonly ILogger _logger; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - public MultiUrlPickerValueEditor(IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public MultiUrlPickerValueEditor(IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, IPublishedUrlProvider publishedUrlProvider) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _publishedUrlProvider = publishedUrlProvider; } public override object ToEditor(IProperty property, string culture = null, string segment = null) @@ -84,7 +89,7 @@ namespace Umbraco.Web.PropertyEditors icon = documentEntity.ContentTypeIcon; published = culture == null ? documentEntity.Published : documentEntity.PublishedCultures.Contains(culture); udi = new GuidUdi(Constants.UdiEntityType.Document, documentEntity.Key); - url = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(entity.Key)?.Url() ?? "#"; + url = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; trashed = documentEntity.Trashed; } else if(entity is IContentEntitySlim contentEntity) @@ -92,7 +97,7 @@ namespace Umbraco.Web.PropertyEditors icon = contentEntity.ContentTypeIcon; published = !contentEntity.Trashed; udi = new GuidUdi(Constants.UdiEntityType.Media, contentEntity.Key); - url = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(entity.Key)?.Url() ?? "#"; + url = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; trashed = contentEntity.Trashed; } else @@ -160,7 +165,7 @@ namespace Umbraco.Web.PropertyEditors } [DataContract] - internal class LinkDto + public class LinkDto { [DataMember(Name = "name")] public string Name { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/MultipleTextStringConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs index 7db6c6f0d9..8aba505aed 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -38,7 +37,7 @@ namespace Umbraco.Web.PropertyEditors /// Initializes a new instance of the class. /// public MultipleTextStringPropertyEditor(ILogger logger, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _ioHelper = ioHelper; _dataTypeService = dataTypeService; @@ -60,7 +59,7 @@ namespace Umbraco.Web.PropertyEditors private readonly ILocalizedTextService _localizedTextService; public MultipleTextStringPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _localizedTextService = localizedTextService; } diff --git a/src/Umbraco.Web/PropertyEditors/MultipleValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs similarity index 85% rename from src/Umbraco.Web/PropertyEditors/MultipleValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs index 392b4e890a..40525bff79 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs @@ -2,7 +2,6 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -17,12 +16,12 @@ namespace Umbraco.Web.PropertyEditors /// /// This is re-used by editors such as the multiple drop down list or check box list /// - internal class MultipleValueEditor : DataValueEditor + public class MultipleValueEditor : DataValueEditor { private readonly ILogger _logger; - internal MultipleValueEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, localizedTextService,shortStringHelper, attribute) + public MultipleValueEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _logger = logger; } diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/NestedContentConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentConfigurationEditor.cs similarity index 87% rename from src/Umbraco.Web/PropertyEditors/NestedContentConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/NestedContentConfigurationEditor.cs index 566c800b3d..7eeee68b07 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentConfigurationEditor.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Umbraco.Core.IO; +using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs similarity index 89% rename from src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index 995f7ae509..70490af1a3 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -7,7 +6,6 @@ using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -31,9 +29,7 @@ namespace Umbraco.Web.PropertyEditors public class NestedContentPropertyEditor : DataEditor { private readonly Lazy _propertyEditors; - private readonly IDataTypeService _dataTypeService; private readonly IContentTypeService _contentTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; internal const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; @@ -50,9 +46,7 @@ namespace Umbraco.Web.PropertyEditors : base (logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _propertyEditors = propertyEditors; - _dataTypeService = dataTypeService; _contentTypeService = contentTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; } @@ -67,25 +61,29 @@ namespace Umbraco.Web.PropertyEditors #region Value Editor - protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(_dataTypeService, _localizationService, _contentTypeService, ShortStringHelper, Attribute, PropertyEditors); + protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, _contentTypeService, ShortStringHelper, Attribute, PropertyEditors); internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference { private readonly PropertyEditorCollection _propertyEditors; + private readonly IContentTypeService _contentTypeService; private readonly IDataTypeService _dataTypeService; private readonly NestedContentValues _nestedContentValues; - private readonly Lazy> _contentTypes = new Lazy>(() => - Current.Services.ContentTypeService.GetAll().ToDictionary(c => c.Alias) - ); + private readonly Lazy> _contentTypes; - public NestedContentPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IContentTypeService contentTypeService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, PropertyEditorCollection propertyEditors) - : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) + public NestedContentPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IContentTypeService contentTypeService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, PropertyEditorCollection propertyEditors) + : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, attribute) { _propertyEditors = propertyEditors; + _contentTypeService = contentTypeService; _dataTypeService = dataTypeService; _nestedContentValues = new NestedContentValues(contentTypeService); Validators.Add(new NestedContentValidator(propertyEditors, dataTypeService, _nestedContentValues)); + + _contentTypes = new Lazy>(() => + _contentTypeService.GetAll().ToDictionary(c => c.Alias) + ); } /// @@ -312,9 +310,19 @@ namespace Umbraco.Web.PropertyEditors if (row.PropType.Mandatory) { if (row.JsonRowValue[row.PropKey] == null) - validationResults.Add(new ValidationResult("Item " + (row.RowIndex + 1) + " '" + row.PropType.Name + "' cannot be null", new[] { row.PropKey })); + { + var message = string.IsNullOrWhiteSpace(row.PropType.MandatoryMessage) + ? $"'{row.PropType.Name}' cannot be null" + : row.PropType.MandatoryMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); + } else if (row.JsonRowValue[row.PropKey].ToString().IsNullOrWhiteSpace() || (row.JsonRowValue[row.PropKey].Type == JTokenType.Array && !row.JsonRowValue[row.PropKey].HasValues)) - validationResults.Add(new ValidationResult("Item " + (row.RowIndex + 1) + " '" + row.PropType.Name + "' cannot be empty", new[] { row.PropKey })); + { + var message = string.IsNullOrWhiteSpace(row.PropType.MandatoryMessage) + ? $"'{row.PropType.Name}' cannot be empty" + : row.PropType.MandatoryMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); + } } // Check regex @@ -324,7 +332,10 @@ namespace Umbraco.Web.PropertyEditors var regex = new Regex(row.PropType.ValidationRegExp); if (!regex.IsMatch(row.JsonRowValue[row.PropKey].ToString())) { - validationResults.Add(new ValidationResult("Item " + (row.RowIndex + 1) + " '" + row.PropType.Name + "' is invalid, it does not match the correct pattern", new[] { row.PropKey })); + var message = string.IsNullOrWhiteSpace(row.PropType.ValidationRegExpMessage) + ? $"'{row.PropType.Name}' is invalid, it does not match the correct pattern" + : row.PropType.ValidationRegExpMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs similarity index 61% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs index 2771b24bd6..726963b9c5 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -18,8 +18,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public ContentTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public ContentTypeParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", false); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs similarity index 67% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs index baa38b492e..a514a47a01 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs @@ -1,7 +1,7 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -19,8 +19,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public MultipleContentPickerParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MultipleContentPickerParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiPicker", "1"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs similarity index 53% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs index 7fa3679eb1..7097f951c6 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -12,8 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultipleContentTypeParameterEditor : DataEditor { - public MultipleContentTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public MultipleContentTypeParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs similarity index 71% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs index 22a3f9784f..7b6a965706 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs @@ -1,7 +1,7 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -20,8 +20,12 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public MultipleMediaPickerParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MultipleMediaPickerParameterEditor(ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { DefaultConfiguration.Add("multiPicker", "1"); } diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs similarity index 60% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs index 4db3226f39..da98492442 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -12,8 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultiplePropertyGroupParameterEditor : DataEditor { - public MultiplePropertyGroupParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MultiplePropertyGroupParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs similarity index 65% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs index 9978513810..46a5652455 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -12,8 +12,12 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultiplePropertyTypeParameterEditor : DataEditor { - public MultiplePropertyTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public MultiplePropertyTypeParameterEditor(ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "1"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs similarity index 59% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs index eff4546645..d331fee6ee 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -12,8 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class PropertyGroupParameterEditor : DataEditor { - public PropertyGroupParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public PropertyGroupParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs similarity index 58% rename from src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs index e9cf57b7b6..affcd4738d 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors @@ -12,8 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class PropertyTypeParameterEditor : DataEditor { - public PropertyTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public PropertyTypeParameterEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs rename to src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs index df603d9e88..de071d1a1e 100644 --- a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Web.PropertyEditors { - internal sealed class PropertyEditorsComponent : IComponent + public sealed class PropertyEditorsComponent : IComponent { private readonly PropertyEditorCollection _propertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComposer.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs rename to src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComposer.cs diff --git a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs similarity index 68% rename from src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs index 005a684c0d..f6b24e5859 100644 --- a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; @@ -20,16 +19,20 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-target")] public class RadioButtonsPropertyEditor : DataEditor { - private readonly ILocalizedTextService _textService; private readonly IIOHelper _ioHelper; /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RadioButtonsPropertyEditor(ILogger logger, ILocalizedTextService textService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) + public RadioButtonsPropertyEditor( + ILogger logger, + IIOHelper ioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService,localizedTextService, shortStringHelper) { - _textService = textService; _ioHelper = ioHelper; } @@ -37,6 +40,6 @@ namespace Umbraco.Web.PropertyEditors /// Return a custom pre-value editor /// /// - protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_textService, _ioHelper); + protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(LocalizedTextService, _ioHelper); } } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextConfiguration.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/RichTextConfiguration.cs diff --git a/src/Umbraco.Web/PropertyEditors/RichTextConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextConfigurationEditor.cs similarity index 78% rename from src/Umbraco.Web/PropertyEditors/RichTextConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/RichTextConfigurationEditor.cs index c6d0b95483..752ae97334 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextConfigurationEditor.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents the configuration editor for the rich text value editor. /// - internal class RichTextConfigurationEditor : ConfigurationEditor + public class RichTextConfigurationEditor : ConfigurationEditor { public RichTextConfigurationEditor(IIOHelper ioHelper) : base(ioHelper) { diff --git a/src/Umbraco.Web/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs similarity index 91% rename from src/Umbraco.Web/PropertyEditors/RichTextEditorPastedImages.cs rename to src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 80c55e37d4..eee45a636c 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -1,7 +1,7 @@ -using HtmlAgilityPack; -using System; +using System; using System.Collections.Generic; using System.IO; +using HtmlAgilityPack; using Umbraco.Core; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; @@ -9,6 +9,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Web.Routing; using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors @@ -22,10 +23,11 @@ namespace Umbraco.Web.PropertyEditors private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly IMediaFileSystem _mediaFileSystem; private readonly IShortStringHelper _shortStringHelper; + private readonly IPublishedUrlProvider _publishedUrlProvider; const string TemporaryImageDataAttribute = "data-tmpimg"; - public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IIOHelper ioHelper, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper) + public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IIOHelper ioHelper, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -34,6 +36,7 @@ namespace Umbraco.Web.PropertyEditors _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); _mediaFileSystem = mediaFileSystem; _shortStringHelper = shortStringHelper; + _publishedUrlProvider = publishedUrlProvider; } /// @@ -43,7 +46,7 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId) + public string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IImageUrlGenerator imageUrlGenerator) { // Find all img's that has data-tmpimg attribute // Use HTML Agility Pack - https://html-agility-pack.net @@ -108,7 +111,7 @@ namespace Umbraco.Web.PropertyEditors if (mediaTyped == null) throw new PanicException($"Could not find media by id {udi.Guid} or there was no UmbracoContext available."); - var location = mediaTyped.Url; + var location = mediaTyped.Url(_publishedUrlProvider); // Find the width & height attributes as we need to set the imageprocessor QueryString var width = img.GetAttributeValue("width", int.MinValue); @@ -116,7 +119,7 @@ namespace Umbraco.Web.PropertyEditors if (width != int.MinValue && height != int.MinValue) { - location = $"{location}?width={width}&height={height}&mode=max"; + location = imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(location) { ImageCropMode = "max", Width = width, Height = height }); } img.SetAttributeValue("src", location); diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs similarity index 80% rename from src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index f80ee2fa6f..dea9a5f002 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -33,21 +31,14 @@ namespace Umbraco.Web.PropertyEditors private readonly HtmlImageSourceParser _imageSourceParser; private readonly HtmlLocalLinkParser _localLinkParser; private readonly RichTextEditorPastedImages _pastedImages; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private readonly ILogger _logger; - private readonly IMediaService _mediaService; - private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IShortStringHelper _shortStringHelper; + private readonly IImageUrlGenerator _imageUrlGenerator; /// /// The constructor will setup the property editor based on the attribute if one is found /// public RichTextPropertyEditor( ILogger logger, - IMediaService mediaService, - IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, @@ -56,27 +47,23 @@ namespace Umbraco.Web.PropertyEditors RichTextEditorPastedImages pastedImages, IShortStringHelper shortStringHelper, IIOHelper ioHelper, - ILocalizedTextService localizedTextService) + ILocalizedTextService localizedTextService, + IImageUrlGenerator imageUrlGenerator) : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; _localLinkParser = localLinkParser; _pastedImages = pastedImages; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; - _logger = logger; - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _shortStringHelper = shortStringHelper; + _imageUrlGenerator = imageUrlGenerator; } /// /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _shortStringHelper, _imageSourceParser, _localLinkParser, _pastedImages); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _imageSourceParser, _localLinkParser, _pastedImages, _imageUrlGenerator); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(_ioHelper); @@ -91,14 +78,26 @@ namespace Umbraco.Web.PropertyEditors private readonly HtmlImageSourceParser _imageSourceParser; private readonly HtmlLocalLinkParser _localLinkParser; private readonly RichTextEditorPastedImages _pastedImages; + private readonly IImageUrlGenerator _imageUrlGenerator; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages) - : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) + public RichTextPropertyValueEditor( + DataEditorAttribute attribute, + IUmbracoContextAccessor umbracoContextAccessor, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + HtmlImageSourceParser imageSourceParser, + HtmlLocalLinkParser localLinkParser, + RichTextEditorPastedImages pastedImages, + IImageUrlGenerator imageUrlGenerator) + : base(dataTypeService, localizationService,localizedTextService, shortStringHelper, attribute) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; _localLinkParser = localLinkParser; _pastedImages = pastedImages; + _imageUrlGenerator = imageUrlGenerator; } /// @@ -152,7 +151,7 @@ namespace Umbraco.Web.PropertyEditors var mediaParent = config?.MediaParentId; var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid; - var parseAndSavedTempImages = _pastedImages.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId); + var parseAndSavedTempImages = _pastedImages.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId, _imageUrlGenerator); var editorValueWithMediaUrlsRemoved = _imageSourceParser.RemoveImageSources(parseAndSavedTempImages); var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); @@ -189,7 +188,7 @@ namespace Umbraco.Web.PropertyEditors //index the stripped HTML values yield return new KeyValuePair>(property.Alias, new object[] { strVal.StripHtml() }); //store the raw value - yield return new KeyValuePair>($"{UmbracoExamineIndex.RawFieldPrefix}{property.Alias}", new object[] { strVal }); + yield return new KeyValuePair>($"{UmbracoExamineFieldNames.RawFieldPrefix}{property.Alias}", new object[] { strVal }); } } } diff --git a/src/Umbraco.Web/PropertyEditors/SliderConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/SliderConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/SliderConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/SliderConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs similarity index 66% rename from src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs index f282b7b682..88bfc3f4e3 100644 --- a/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs @@ -1,8 +1,8 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -22,8 +22,14 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public SliderPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public SliderPropertyEditor( + ILogger logger, + IIOHelper ioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/TagConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TagConfigurationEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/TagConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TagConfigurationEditor.cs index aed32f10a1..3877611a68 100644 --- a/src/Umbraco.Web/PropertyEditors/TagConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TagConfigurationEditor.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs similarity index 78% rename from src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs index 5682e512ca..7fccd3a15f 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs @@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; @@ -27,24 +26,29 @@ namespace Umbraco.Web.PropertyEditors { private readonly ManifestValueValidatorCollection _validators; private readonly IIOHelper _ioHelper; - private readonly ILocalizedTextService _localizedTextService; - public TagsPropertyEditor(ManifestValueValidatorCollection validators, ILogger logger, IIOHelper ioHelper, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public TagsPropertyEditor( + ManifestValueValidatorCollection validators, + ILogger logger, + IIOHelper ioHelper, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _validators = validators; _ioHelper = ioHelper; - _localizedTextService = localizedTextService; } - protected override IDataValueEditor CreateValueEditor() => new TagPropertyValueEditor(Current.Services.DataTypeService, Current.Services.LocalizationService, ShortStringHelper, Attribute); + protected override IDataValueEditor CreateValueEditor() => new TagPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute); - protected override IConfigurationEditor CreateConfigurationEditor() => new TagConfigurationEditor(_validators, _ioHelper, _localizedTextService); + protected override IConfigurationEditor CreateConfigurationEditor() => new TagConfigurationEditor(_validators, _ioHelper, LocalizedTextService); internal class TagPropertyValueEditor : DataValueEditor { - public TagPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) + public TagPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService,localizedTextService, shortStringHelper, attribute) { } /// diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TextAreaConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TextAreaConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs index c91788b99b..dd3c1fd350 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextOnlyValueEditor.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TextOnlyValueEditor.cs index e9389779fd..e53c673986 100644 --- a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextOnlyValueEditor.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; diff --git a/src/Umbraco.Web/PropertyEditors/TextboxConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextboxConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TextboxConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TextboxConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/TextboxPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/TextboxPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs index 79d42f19d4..b89f5e228a 100644 --- a/src/Umbraco.Web/PropertyEditors/TextboxPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalseConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalseConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/TrueFalseConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TrueFalseConfigurationEditor.cs diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs index 3c98b61292..3d82dc2809 100644 --- a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs @@ -1,5 +1,4 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; diff --git a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs similarity index 69% rename from src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs rename to src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs index 6855ab3bb8..b833a60c2d 100644 --- a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs @@ -4,16 +4,26 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using Newtonsoft.Json.Linq; +using Umbraco.Composing; using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Web.Composing; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { internal class UploadFileTypeValidator : IValueValidator { + private readonly ILocalizedTextService _localizedTextService; + private readonly IContentSettings _contentSettings; + + public UploadFileTypeValidator(ILocalizedTextService localizedTextService, IContentSettings contentSettings) + { + _localizedTextService = localizedTextService; + _contentSettings = contentSettings; + } + public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { string selectedFiles = null; @@ -36,20 +46,20 @@ namespace Umbraco.Web.PropertyEditors foreach (string filename in fileNames) { - if (IsValidFileExtension(filename) == false) + if (IsValidFileExtension(filename, _contentSettings) == false) { //we only store a single value for this editor so the 'member' or 'field' // we'll associate this error with will simply be called 'value' - yield return new ValidationResult(Current.Services.TextService.Localize("errors/dissallowedMediaType"), new[] { "value" }); + yield return new ValidationResult(_localizedTextService.Localize("errors/dissallowedMediaType"), new[] { "value" }); } } } - - internal static bool IsValidFileExtension(string fileName) + + internal static bool IsValidFileExtension(string fileName, IContentSettings contentSettings) { if (fileName.IndexOf('.') <= 0) return false; var extension = new FileInfo(fileName).Extension.TrimStart("."); - return Current.Configs.Settings().Content.IsFileAllowedForUpload(extension); + return contentSettings.IsFileAllowedForUpload(extension); } } } diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/UserPickerConfiguration.cs similarity index 99% rename from src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs rename to src/Umbraco.Infrastructure/PropertyEditors/UserPickerConfiguration.cs index f7bd0ceb7d..4d497867a1 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/UserPickerConfiguration.cs @@ -10,4 +10,4 @@ namespace Umbraco.Web.PropertyEditors {"entityType", "User"} }; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/UserPickerPropertyEditor.cs similarity index 59% rename from src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/UserPickerPropertyEditor.cs index 607042f143..429527f3eb 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/UserPickerPropertyEditor.cs @@ -1,7 +1,7 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors @@ -15,8 +15,13 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.User)] public class UserPickerPropertyEditor : DataEditor { - public UserPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) + public UserPickerPropertyEditor( + ILogger logger, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { } protected override IConfigurationEditor CreateConfigurationEditor() => new UserPickerConfiguration(); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs similarity index 99% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs index 0d6089f0f4..164ce185ae 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 89cd7b49c3..1367c04cdb 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Globalization; using System.Linq; using System.Runtime.Serialization; -using System.Text; using Newtonsoft.Json; +using Umbraco.Core.Models; using Umbraco.Core.Serialization; using Umbraco.Core.Strings; @@ -59,38 +58,28 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters : Crops.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } - public void AppendCropBaseUrl(StringBuilder url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint) + public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint) { if (preferFocalPoint && HasFocalPoint() || crop != null && crop.Coordinates == null && HasFocalPoint() || defaultCrop && HasFocalPoint()) { - url.Append("?center="); - url.Append(FocalPoint.Top.ToString(CultureInfo.InvariantCulture)); - url.Append(","); - url.Append(FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); - url.Append("&mode=crop"); + return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) }; } else if (crop != null && crop.Coordinates != null && preferFocalPoint == false) { - url.Append("?crop="); - url.Append(crop.Coordinates.X1.ToString(CultureInfo.InvariantCulture)).Append(","); - url.Append(crop.Coordinates.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); - url.Append(crop.Coordinates.X2.ToString(CultureInfo.InvariantCulture)).Append(","); - url.Append(crop.Coordinates.Y2.ToString(CultureInfo.InvariantCulture)); - url.Append("&cropmode=percentage"); + return new ImageUrlGenerationOptions(url) { Crop = new ImageUrlGenerationOptions.CropCoordinates(crop.Coordinates.X1, crop.Coordinates.Y1, crop.Coordinates.X2, crop.Coordinates.Y2) }; } else { - url.Append("?anchor=center"); - url.Append("&mode=crop"); + return new ImageUrlGenerationOptions(url) { DefaultCrop = true }; } } /// /// Gets the value image url for a specified crop. /// - public string GetCropUrl(string alias, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null) + public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null) { var crop = GetCrop(alias); @@ -98,38 +87,31 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters if (crop == null && !string.IsNullOrWhiteSpace(alias)) return null; - var url = new StringBuilder(); - - AppendCropBaseUrl(url, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint); + var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint); if (crop != null && useCropDimensions) { - url.Append("&width=").Append(crop.Width); - url.Append("&height=").Append(crop.Height); + options.Width = crop.Width; + options.Height = crop.Height; } - if (cacheBusterValue != null) - url.Append("&rnd=").Append(cacheBusterValue); + options.CacheBusterValue = cacheBusterValue; - return url.ToString(); + return imageUrlGenerator.GetImageUrl(options); } /// /// Gets the value image url for a specific width and height. /// - public string GetCropUrl(int width, int height, bool useFocalPoint = false, string cacheBusterValue = null) + public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null) { - var url = new StringBuilder(); + var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint); - AppendCropBaseUrl(url, null, true, useFocalPoint); + options.Width = width; + options.Height = height; + options.CacheBusterValue = cacheBusterValue; - url.Append("&width=").Append(width); - url.Append("&height=").Append(height); - - if (cacheBusterValue != null) - url.Append("&rnd=").Append(cacheBusterValue); - - return url.ToString(); + return imageUrlGenerator.GetImageUrl(options); } /// 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.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 4608b5c5da..4fdc13baa9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -1,12 +1,23 @@ using System; +using HeyRed.MarkdownSharp; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; +using Umbraco.Web.Templates; namespace Umbraco.Core.PropertyEditors.ValueConverters { [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { + private readonly HtmlLocalLinkParser _localLinkParser; + private readonly HtmlUrlParser _urlParser; + + public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser) + { + _localLinkParser = localLinkParser; + _urlParser = urlParser; + } + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MarkdownEditor.Equals(propertyType.EditorAlias); @@ -15,20 +26,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // PropertyCacheLevel.Content is ok here because that converter does not parse {locallink} nor executes macros public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) - => PropertyCacheLevel.Element; + => PropertyCacheLevel.Snapshot; public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { - // in xml a string is: string - // in the database a string is: string - // default value is: null - return source; + if (source == null) return null; + var sourceString = source.ToString(); + + // ensures string is parsed for {localLink} and urls are resolved correctly + sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlParser.EnsureUrls(sourceString); + + return sourceString; } public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { + // convert markup to HTML for frontend rendering. // source should come from ConvertSource and be a string (or null) already - return new HtmlEncodedString(inter == null ? string.Empty : (string) inter); + var mark = new Markdown(); + return new HtmlEncodedString(inter == null ? string.Empty : mark.Transform((string)inter)); } public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs similarity index 90% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs index cd69fd9de6..19cca866df 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs @@ -2,9 +2,7 @@ using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; -using Umbraco.Web.Security; namespace Umbraco.Web.PropertyEditors.ValueConverters { @@ -12,10 +10,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class MemberPickerValueConverter : PropertyValueConverterBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; - public MemberPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor) + public MemberPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IUmbracoContextAccessor umbracoContextAccessor) { _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + _umbracoContextAccessor = umbracoContextAccessor; } public override bool IsConverter(IPublishedPropertyType propertyType) @@ -45,7 +45,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (source == null) return null; - if (Current.UmbracoContext != null) + if (_umbracoContextAccessor.UmbracoContext != null) { IPublishedContent member; if (source is int id) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs similarity index 96% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index 46ebbd0abb..e35205b704 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -3,13 +3,10 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -22,6 +19,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class MultiNodeTreePickerValueConverter : PropertyValueConverterBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private static readonly List PropertiesToExclude = new List { @@ -29,9 +27,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters Constants.Conventions.Content.Redirect.ToLower(CultureInfo.InvariantCulture) }; - public MultiNodeTreePickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor) + public MultiNodeTreePickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IUmbracoContextAccessor umbracoContextAccessor) { _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + _umbracoContextAccessor = umbracoContextAccessor; } public override bool IsConverter(IPublishedPropertyType propertyType) @@ -70,7 +69,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } // TODO: Inject an UmbracoHelper and create a GetUmbracoHelper method based on either injected or singleton - if (Current.UmbracoContext != null) + if (_umbracoContextAccessor.UmbracoContext != null) { if (propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.MultiNodeTreePicker)) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs similarity index 82% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index 2ceac6cfa8..72beb0106a 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -1,5 +1,3 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; @@ -7,8 +5,10 @@ using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Serialization; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Web.PropertyEditors.ValueConverters { @@ -16,11 +16,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IProfilingLogger _proflog; + private readonly IJsonSerializer _jsonSerializer; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; - public MultiUrlPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IProfilingLogger proflog) + public MultiUrlPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IProfilingLogger proflog, IJsonSerializer jsonSerializer, IUmbracoContextAccessor umbracoContextAccessor, IPublishedUrlProvider publishedUrlProvider) { _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); _proflog = proflog ?? throw new ArgumentNullException(nameof(proflog)); + _jsonSerializer = jsonSerializer; + _umbracoContextAccessor = umbracoContextAccessor; + _publishedUrlProvider = publishedUrlProvider; } public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MultiUrlPicker.Equals(propertyType.EditorAlias); @@ -48,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } var links = new List(); - var dtos = JsonConvert.DeserializeObject>(inter.ToString()); + var dtos = _jsonSerializer.Deserialize>(inter.ToString()); foreach (var dto in dtos) { @@ -69,7 +75,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { continue; } - url = content.Url(); + url = content.Url(_publishedUrlProvider); } links.Add( diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs similarity index 96% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 559777786f..4a25049695 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -6,6 +6,7 @@ using Newtonsoft.Json.Linq; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -14,6 +15,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// /// Provides an implementation for for nested content. /// + [DefaultPropertyValueConverter(typeof(JsonValueConverter))] public class NestedContentManyValueConverter : NestedContentValueConverterBase { private readonly IProfilingLogger _proflog; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs similarity index 96% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index 06aa0b42fb..c9c99615f6 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json.Linq; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -13,6 +14,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// /// Provides an implementation for for nested content. /// + [DefaultPropertyValueConverter(typeof(JsonValueConverter))] public class NestedContentSingleValueConverter : NestedContentValueConverterBase { private readonly IProfilingLogger _proflog; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs similarity index 96% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 67031faaa0..a55f73980b 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -1,18 +1,13 @@ -using System.Text; -using Umbraco.Core; -using Umbraco.Core.Macros; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors.ValueConverters; -using Umbraco.Web.Templates; -using System.Linq; +using System.Linq; +using System.Text; using HtmlAgilityPack; -using Umbraco.Core.Cache; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Macros; -using System.Web; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Strings; +using Umbraco.Web.Macros; +using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors.ValueConverters { @@ -66,7 +61,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters macroAlias, umbracoContext.PublishedRequest?.PublishedContent, //needs to be explicitly casted to Dictionary - macroAttributes.ConvertTo(x => (string)x, x => x)).GetAsText())); + macroAttributes.ConvertTo(x => (string)x, x => x)).Text)); return sb.ToString(); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 939a658407..93d670601e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -3,7 +3,6 @@ using System.Linq; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -using Umbraco.Web.Composing; using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors.ValueConverters diff --git a/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueListConfigurationEditor.cs similarity index 100% rename from src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueListConfigurationEditor.cs index 6facc69f40..7802767ad6 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueListConfigurationEditor.cs @@ -2,8 +2,8 @@ using System.Linq; using Newtonsoft.Json.Linq; using Umbraco.Core.IO; -using Umbraco.Core.Services; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { diff --git a/src/Umbraco.Web/PropertyEditors/ValueListUniqueValueValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueListUniqueValueValidator.cs similarity index 94% rename from src/Umbraco.Web/PropertyEditors/ValueListUniqueValueValidator.cs rename to src/Umbraco.Infrastructure/PropertyEditors/ValueListUniqueValueValidator.cs index 38dc6fb3ae..c4f105fd02 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueListUniqueValueValidator.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueListUniqueValueValidator.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a validator which ensures that all values in the list are unique. /// - internal class ValueListUniqueValueValidator : IValueValidator + public class ValueListUniqueValueValidator : IValueValidator { public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { @@ -24,7 +24,7 @@ namespace Umbraco.Web.PropertyEditors var groupedValues = json.OfType() .Where(x => x["value"] != null) - .Select((x, index) => new { value = x["value"].ToString(), index}) + .Select((x, index) => new { value = x["value"].ToString(), index }) .Where(x => x.value.IsNullOrWhiteSpace() == false) .GroupBy(x => x.value); diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs similarity index 98% rename from src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs rename to src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs index e453471bb8..ba4f76dca6 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.PublishedCache private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); // default ctor - internal PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) + public PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) { _contentTypeService = contentTypeService; _mediaTypeService = mediaTypeService; diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs new file mode 100644 index 0000000000..e995850a1f --- /dev/null +++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Xml.XPath; +using Examine; +using Examine.Search; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Xml; +using Umbraco.Examine; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web +{ + /// + /// A class used to query for published content, media items + /// + public class PublishedContentQuery : IPublishedContentQuery + { + private readonly IExamineManager _examineManager; + private readonly IPublishedSnapshot _publishedSnapshot; + private readonly IVariationContextAccessor _variationContextAccessor; + + /// + /// Initializes a new instance of the class. + /// + public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, + IVariationContextAccessor variationContextAccessor, IExamineManager examineManager) + { + _publishedSnapshot = publishedSnapshot ?? throw new ArgumentNullException(nameof(publishedSnapshot)); + _variationContextAccessor = variationContextAccessor ?? + throw new ArgumentNullException(nameof(variationContextAccessor)); + _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); + } + + #region Convert Helpers + + private static bool ConvertIdObjectToInt(object id, out int intId) + { + switch (id) + { + case string s: + return int.TryParse(s, out intId); + + case int i: + intId = i; + return true; + + default: + intId = default; + return false; + } + } + + private static bool ConvertIdObjectToGuid(object id, out Guid guidId) + { + switch (id) + { + case string s: + return Guid.TryParse(s, out guidId); + + case Guid g: + guidId = g; + return true; + + default: + guidId = default; + return false; + } + } + private static bool ConvertIdObjectToUdi(object id, out Udi guidId) + { + switch (id) + { + case string s: + return UdiParser.TryParse(s, out guidId); + + case Udi u: + guidId = u; + return true; + + default: + guidId = default; + return false; + } + } + + #endregion + + #region Content + + public IPublishedContent Content(int id) => ItemById(id, _publishedSnapshot.Content); + + public IPublishedContent Content(Guid id) => ItemById(id, _publishedSnapshot.Content); + + public IPublishedContent Content(Udi id) + { + if (!(id is GuidUdi udi)) return null; + return ItemById(udi.Guid, _publishedSnapshot.Content); + } + + public IPublishedContent Content(object id) + { + if (ConvertIdObjectToInt(id, out var intId)) + return Content(intId); + if (ConvertIdObjectToGuid(id, out var guidId)) + return Content(guidId); + if (ConvertIdObjectToUdi(id, out var udiId)) + return Content(udiId); + return null; + } + + public IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars) => + ItemByXPath(xpath, vars, _publishedSnapshot.Content); + + public IEnumerable Content(IEnumerable ids) => + ItemsByIds(_publishedSnapshot.Content, ids); + + public IEnumerable Content(IEnumerable ids) => + ItemsByIds(_publishedSnapshot.Content, ids); + + public IEnumerable Content(IEnumerable ids) + { + return ids.Select(Content).WhereNotNull(); + } + public IEnumerable ContentAtXPath(string xpath, params XPathVariable[] vars) => + ItemsByXPath(xpath, vars, _publishedSnapshot.Content); + + public IEnumerable ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) => + ItemsByXPath(xpath, vars, _publishedSnapshot.Content); + + public IEnumerable ContentAtRoot() => ItemsAtRoot(_publishedSnapshot.Content); + + #endregion + + #region Media + + public IPublishedContent Media(int id) => ItemById(id, _publishedSnapshot.Media); + + public IPublishedContent Media(Guid id) => ItemById(id, _publishedSnapshot.Media); + + public IPublishedContent Media(Udi id) + { + if (!(id is GuidUdi udi)) return null; + return ItemById(udi.Guid, _publishedSnapshot.Media); + } + + public IPublishedContent Media(object id) + { + if (ConvertIdObjectToInt(id, out var intId)) + return Media(intId); + if (ConvertIdObjectToGuid(id, out var guidId)) + return Media(guidId); + if (ConvertIdObjectToUdi(id, out var udiId)) + return Media(udiId); + return null; + } + + public IEnumerable Media(IEnumerable ids) => ItemsByIds(_publishedSnapshot.Media, ids); + public IEnumerable Media(IEnumerable ids) + { + return ids.Select(Media).WhereNotNull(); + } + + public IEnumerable Media(IEnumerable ids) => ItemsByIds(_publishedSnapshot.Media, ids); + + public IEnumerable MediaAtRoot() => ItemsAtRoot(_publishedSnapshot.Media); + + #endregion + + #region Used by Content/Media + + private static IPublishedContent ItemById(int id, IPublishedCache cache) + { + var doc = cache.GetById(id); + return doc; + } + + private static IPublishedContent ItemById(Guid id, IPublishedCache cache) + { + var doc = cache.GetById(id); + return doc; + } + + private static IPublishedContent ItemByXPath(string xpath, XPathVariable[] vars, IPublishedCache cache) + { + var doc = cache.GetSingleByXPath(xpath, vars); + return doc; + } + + //NOTE: Not used? + //private IPublishedContent ItemByXPath(XPathExpression xpath, XPathVariable[] vars, IPublishedCache cache) + //{ + // var doc = cache.GetSingleByXPath(xpath, vars); + // return doc; + //} + + private static IEnumerable ItemsByIds(IPublishedCache cache, IEnumerable ids) + { + return ids.Select(eachId => ItemById(eachId, cache)).WhereNotNull(); + } + + private IEnumerable ItemsByIds(IPublishedCache cache, IEnumerable ids) + { + return ids.Select(eachId => ItemById(eachId, cache)).WhereNotNull(); + } + + private static IEnumerable ItemsByXPath(string xpath, XPathVariable[] vars, + IPublishedCache cache) + { + var doc = cache.GetByXPath(xpath, vars); + return doc; + } + + private static IEnumerable ItemsByXPath(XPathExpression xpath, XPathVariable[] vars, + IPublishedCache cache) + { + var doc = cache.GetByXPath(xpath, vars); + return doc; + } + + private static IEnumerable ItemsAtRoot(IPublishedCache cache) => cache.GetAtRoot(); + + #endregion + + #region Search + + /// + public IEnumerable Search(string term, string culture = "*", + string indexName = Constants.UmbracoIndexes.ExternalIndexName) => + Search(term, 0, 0, out _, culture, indexName); + + /// + public IEnumerable Search(string term, int skip, int take, out long totalRecords, + string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName) + { + if (skip < 0) + { + throw new ArgumentOutOfRangeException(nameof(skip), skip, + "The value must be greater than or equal to zero."); + } + + if (take < 0) + { + throw new ArgumentOutOfRangeException(nameof(take), take, + "The value must be greater than or equal to zero."); + } + + if (string.IsNullOrEmpty(indexName)) + { + indexName = Constants.UmbracoIndexes.ExternalIndexName; + } + + if (!_examineManager.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex)) + { + throw new InvalidOperationException( + $"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}"); + } + + var query = umbIndex.GetSearcher().CreateQuery(IndexTypes.Content); + + IQueryExecutor queryExecutor; + if (culture == "*") + { + // Search everything + queryExecutor = query.ManagedQuery(term); + } + else if (string.IsNullOrWhiteSpace(culture)) + { + // Only search invariant + queryExecutor = query + .Field(UmbracoExamineFieldNames.VariesByCultureFieldName, "n") // Must not vary by culture + .And().ManagedQuery(term); + } + else + { + // Only search the specified culture + var fields = + umbIndex.GetCultureAndInvariantFields(culture) + .ToArray(); // Get all index fields suffixed with the culture name supplied + queryExecutor = query.ManagedQuery(term, fields); + } + + var results = skip == 0 && take == 0 + ? queryExecutor.Execute() + : queryExecutor.Execute(skip + take); + + totalRecords = results.TotalItemCount; + + return new CultureContextualSearchResults( + results.Skip(skip).ToPublishedSearchResults(_publishedSnapshot.Content), _variationContextAccessor, + culture); + } + + /// + public IEnumerable Search(IQueryExecutor query) => Search(query, 0, 0, out _); + + /// + public IEnumerable Search(IQueryExecutor query, int skip, int take, + out long totalRecords) + { + if (skip < 0) + { + throw new ArgumentOutOfRangeException(nameof(skip), skip, + "The value must be greater than or equal to zero."); + } + + if (take < 0) + { + throw new ArgumentOutOfRangeException(nameof(take), take, + "The value must be greater than or equal to zero."); + } + + var results = skip == 0 && take == 0 + ? query.Execute() + : query.Execute(skip + take); + + totalRecords = results.TotalItemCount; + + return results.Skip(skip).ToPublishedSearchResults(_publishedSnapshot); + } + + /// + /// This is used to contextualize the values in the search results when enumerating over them so that the correct + /// culture values are used + /// + private class CultureContextualSearchResults : IEnumerable + { + private readonly string _culture; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IEnumerable _wrapped; + + public CultureContextualSearchResults(IEnumerable wrapped, + IVariationContextAccessor variationContextAccessor, string culture) + { + _wrapped = wrapped; + _variationContextAccessor = variationContextAccessor; + _culture = culture; + } + + public IEnumerator GetEnumerator() + { + //We need to change the current culture to what is requested and then change it back + var originalContext = _variationContextAccessor.VariationContext; + if (!_culture.IsNullOrWhiteSpace() && !_culture.InvariantEquals(originalContext.Culture)) + _variationContextAccessor.VariationContext = new VariationContext(_culture); + + //now the IPublishedContent returned will be contextualized to the culture specified and will be reset when the enumerator is disposed + return new CultureContextualSearchResultsEnumerator(_wrapped.GetEnumerator(), _variationContextAccessor, + originalContext); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Resets the variation context when this is disposed + /// + private class CultureContextualSearchResultsEnumerator : IEnumerator + { + private readonly VariationContext _originalContext; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IEnumerator _wrapped; + + public CultureContextualSearchResultsEnumerator(IEnumerator wrapped, + IVariationContextAccessor variationContextAccessor, VariationContext originalContext) + { + _wrapped = wrapped; + _variationContextAccessor = variationContextAccessor; + _originalContext = originalContext; + } + + public void Dispose() + { + _wrapped.Dispose(); + //reset + _variationContextAccessor.VariationContext = _originalContext; + } + + public bool MoveNext() => _wrapped.MoveNext(); + + public void Reset() + { + _wrapped.Reset(); + } + + public PublishedSearchResult Current => _wrapped.Current; + object IEnumerator.Current => Current; + } + } + + #endregion + } +} diff --git a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs similarity index 86% rename from src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs rename to src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs index eae198bb59..ac8d0980c4 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs +++ b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs @@ -1,3 +1,4 @@ +using Examine; using System.Globalization; using System.Linq; using Umbraco.Core; @@ -15,13 +16,15 @@ namespace Umbraco.Web.Routing { private readonly ILogger _logger; private readonly IEntityService _entityService; - private readonly IContentSection _contentConfigSection; + private readonly IContentSettings _contentConfigSettings; + private readonly IExamineManager _examineManager; - public ContentFinderByConfigured404(ILogger logger, IEntityService entityService, IContentSection contentConfigSection) + public ContentFinderByConfigured404(ILogger logger, IEntityService entityService, IContentSettings contentConfigSettings, IExamineManager examineManager) { _logger = logger; _entityService = entityService; - _contentConfigSection = contentConfigSection; + _contentConfigSettings = contentConfigSettings; + _examineManager = examineManager; } /// @@ -29,7 +32,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindContent(PublishedRequest frequest) + public bool TryFindContent(IPublishedRequest frequest) { _logger.Debug("Looking for a page to handle 404."); @@ -60,9 +63,9 @@ namespace Umbraco.Web.Routing } var error404 = NotFoundHandlerHelper.GetCurrentNotFoundPageId( - _contentConfigSection.Error404Collection.ToArray(), + _contentConfigSettings.Error404Collection.ToArray(), _entityService, - new PublishedContentQuery(frequest.UmbracoContext.PublishedSnapshot, frequest.UmbracoContext.VariationContextAccessor), + new PublishedContentQuery(frequest.UmbracoContext.PublishedSnapshot, frequest.UmbracoContext.VariationContextAccessor, _examineManager), errorCulture); IPublishedContent content = null; diff --git a/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs similarity index 99% rename from src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs rename to src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs index 38ecb09b2b..335e1f868a 100644 --- a/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs +++ b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs @@ -1,13 +1,13 @@ using System; using System.Globalization; using System.Linq; +using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Xml; -using Umbraco.Web.Composing; namespace Umbraco.Web.Routing { diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs similarity index 88% rename from src/Umbraco.Web/Routing/RedirectTrackingComponent.cs rename to src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs index fcf6f55f44..f9256b3692 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.PublishedCache; @@ -22,21 +24,24 @@ namespace Umbraco.Web.Routing { private const string _eventStateKey = "Umbraco.Web.Redirects.RedirectTrackingEventHandler"; - private readonly IUmbracoSettingsSection _umbracoSettings; + + private readonly IWebRoutingSettings _webRoutingSettings; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IRedirectUrlService _redirectUrlService; + private readonly IVariationContextAccessor _variationContextAccessor; - public RedirectTrackingComponent(IUmbracoSettingsSection umbracoSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService) + public RedirectTrackingComponent(IWebRoutingSettings webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) { - _umbracoSettings = umbracoSettings; + _webRoutingSettings = webRoutingSettings; _publishedSnapshotAccessor = publishedSnapshotAccessor; _redirectUrlService = redirectUrlService; + _variationContextAccessor = variationContextAccessor; } public void Initialize() { // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_umbracoSettings.WebRouting.DisableRedirectUrlTracking) return; + if (_webRoutingSettings.DisableRedirectUrlTracking) return; ContentService.Publishing += ContentService_Publishing; ContentService.Published += ContentService_Published; @@ -98,12 +103,12 @@ namespace Umbraco.Web.Routing { var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content; var entityContent = contentCache.GetById(entity.Id); - if (entityContent == null) return; + if (entityContent == null) return; - // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) + // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? new[] { (string)null }; - foreach (var x in entityContent.DescendantsOrSelf()) + foreach (var x in entityContent.DescendantsOrSelf(_variationContextAccessor)) { // if this entity defines specific cultures, use those instead of the default ones var cultures = x.Cultures.Any() ? x.Cultures.Keys : defaultCultures; diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs similarity index 100% rename from src/Umbraco.Web/Routing/RedirectTrackingComposer.cs rename to src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs diff --git a/src/Umbraco.Core/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs similarity index 76% rename from src/Umbraco.Core/Runtime/CoreInitialComposer.cs rename to src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index 159eb8bca9..5f4e9aec1d 100644 --- a/src/Umbraco.Core/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -1,9 +1,12 @@ using System; +using System.IO; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Dashboards; using Umbraco.Core.Hosting; using Umbraco.Core.Dictionary; using Umbraco.Core.IO; @@ -13,17 +16,23 @@ using Umbraco.Core.Migrations; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Migrations.PostMigrations; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PackageActions; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; using Umbraco.Core.Scoping; -using Umbraco.Core.Security; using Umbraco.Core.Serialization; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Web.Models.PublishedContent; +using Umbraco.Web.PublishedCache; +using Umbraco.Web; +using Umbraco.Web.Migrations.PostMigrations; +using Umbraco.Web.Install; +using Umbraco.Web.Trees; +using Umbraco.Web.PropertyEditors; +using Umbraco.Web.Services; using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; namespace Umbraco.Core.Runtime @@ -38,7 +47,6 @@ namespace Umbraco.Core.Runtime // composers composition - .ComposeConfiguration() .ComposeRepositories() .ComposeServices() .ComposeCoreMappingProfiles() @@ -55,6 +63,8 @@ namespace Umbraco.Core.Runtime composition.RegisterUnique(f => f.GetInstance()); composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); // register database builder // *not* a singleton, don't want to keep it around @@ -78,6 +88,11 @@ namespace Umbraco.Core.Runtime // properties and parameters derive from data editors composition.DataEditors() .Add(() => composition.TypeLoader.GetDataEditors()); + + composition.MediaUrlGenerators() + .Add() + .Add(); + composition.RegisterUnique(); composition.RegisterUnique(); @@ -126,7 +141,7 @@ namespace Umbraco.Core.Runtime composition.RegisterUnique(); composition.RegisterUnique(factory - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); composition.UrlSegmentProviders() .Append(); @@ -136,11 +151,30 @@ namespace Umbraco.Core.Runtime // by default, register a noop factory composition.RegisterUnique(); - // by default, register a noop rebuilder + // by default composition.RegisterUnique(); composition.SetCultureDictionaryFactory(); composition.Register(f => f.GetInstance().CreateDictionary(), Lifetime.Singleton); + composition.RegisterUnique(); + + // register the published snapshot accessor - the "current" published snapshot is in the umbraco context + composition.RegisterUnique(); + + composition.RegisterUnique(); + + composition.RegisterUnique(); + + // register core CMS dashboards and 3rd party types - will be ordered by weight attribute & merged with package.manifest dashboards + composition.Dashboards() + .Add(composition.TypeLoader.GetTypes()); + + // will be injected in controllers when needed to invoke rest endpoints on Our + composition.RegisterUnique(); + composition.RegisterUnique(); + + // Grid config is not a real config file as we know them + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs similarity index 81% rename from src/Umbraco.Core/Runtime/CoreRuntime.cs rename to src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 6dbb41093c..74a1c06eec 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Reflection; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; @@ -26,6 +27,8 @@ namespace Umbraco.Core.Runtime private IFactory _factory; private RuntimeState _state; private readonly IUmbracoBootPermissionChecker _umbracoBootPermissionChecker; + private readonly IGlobalSettings _globalSettings; + private readonly IConnectionStrings _connectionStrings; public CoreRuntime( @@ -38,7 +41,6 @@ namespace Umbraco.Core.Runtime IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, IDbProviderFactoryCreator dbProviderFactoryCreator, - IBulkSqlInsertProvider bulkSqlInsertProvider, IMainDom mainDom) { IOHelper = ioHelper; @@ -48,18 +50,21 @@ namespace Umbraco.Core.Runtime HostingEnvironment = hostingEnvironment; BackOfficeInfo = backOfficeInfo; DbProviderFactoryCreator = dbProviderFactoryCreator; - BulkSqlInsertProvider = bulkSqlInsertProvider; _umbracoBootPermissionChecker = umbracoBootPermissionChecker; Logger = logger; MainDom = mainDom; + _globalSettings = Configs.Global(); + _connectionStrings = configs.ConnectionStrings(); + + // runtime state // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' // as the second one captures the current value (null) and therefore fails _state = new RuntimeState(Logger, - Configs.Settings(), Configs.Global(), + Configs.Global(), new Lazy(() => mainDom), new Lazy(() => _factory.GetInstance()), UmbracoVersion,HostingEnvironment, BackOfficeInfo) @@ -75,7 +80,7 @@ namespace Umbraco.Core.Runtime protected IBackOfficeInfo BackOfficeInfo { get; } public IDbProviderFactoryCreator DbProviderFactoryCreator { get; } - public IBulkSqlInsertProvider BulkSqlInsertProvider { get; } + //public IBulkSqlInsertProvider BulkSqlInsertProvider { get; } /// /// Gets the profiler. @@ -169,12 +174,21 @@ namespace Umbraco.Core.Runtime // type finder/loader var typeLoader = new TypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, new DirectoryInfo(HostingEnvironment.LocalTempPath), ProfilingLogger); - // main dom - var mainDom = new MainDom(Logger, HostingEnvironment); + // runtime state + // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' + // as the second one captures the current value (null) and therefore fails + _state = new RuntimeState(Logger, + Configs.Global(), + new Lazy(() => _factory.GetInstance()), + new Lazy(() => _factory.GetInstance()), + UmbracoVersion, HostingEnvironment, BackOfficeInfo) + { + Level = RuntimeLevel.Boot + }; // create the composition composition = new Composition(register, typeLoader, ProfilingLogger, _state, Configs, IOHelper, appCaches); - composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper, UmbracoVersion, DbProviderFactoryCreator, BulkSqlInsertProvider); + composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, appCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper, UmbracoVersion, DbProviderFactoryCreator); // run handlers RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory); @@ -202,7 +216,7 @@ namespace Umbraco.Core.Runtime composers.Compose(); // create the factory - _factory = Current.Factory = composition.CreateFactory(); + _factory = composition.CreateFactory(); // create & initialize the components _components = _factory.GetInstance(); @@ -229,7 +243,7 @@ namespace Umbraco.Core.Runtime { try { - _factory = Current.Factory = composition?.CreateFactory(); + _factory = composition?.CreateFactory(); } catch { /* yea */ } } @@ -362,7 +376,21 @@ namespace Umbraco.Core.Runtime /// /// protected virtual ITypeFinder GetTypeFinder() - => new TypeFinder(Logger); + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( + // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website + // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since + // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() + // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. + // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own + // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting + // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance + // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point + // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. + // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are + // getting rid of netframework. + Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())); /// @@ -390,7 +418,7 @@ namespace Umbraco.Core.Runtime /// /// This is strictly internal, for tests only. protected internal virtual IUmbracoDatabaseFactory GetDatabaseFactory() - => new UmbracoDatabaseFactory(Logger, new Lazy(() => _factory.GetInstance()), Configs, DbProviderFactoryCreator, BulkSqlInsertProvider); + => new UmbracoDatabaseFactory(Logger, _globalSettings, _connectionStrings, new Lazy(() => _factory.GetInstance()), DbProviderFactoryCreator); #endregion diff --git a/src/Umbraco.Infrastructure/Runtime/MainDomSemaphoreLock.cs b/src/Umbraco.Infrastructure/Runtime/MainDomSemaphoreLock.cs new file mode 100644 index 0000000000..419a1781a2 --- /dev/null +++ b/src/Umbraco.Infrastructure/Runtime/MainDomSemaphoreLock.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Umbraco.Core.Hosting; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Runtime +{ + /// + /// Uses a system-wide Semaphore and EventWaitHandle to synchronize the current AppDomain + /// + public class MainDomSemaphoreLock : IMainDomLock + { + private readonly SystemLock _systemLock; + + // event wait handle used to notify current main domain that it should + // release the lock because a new domain wants to be the main domain + private readonly EventWaitHandle _signal; + private readonly ILogger _logger; + private IDisposable _lockRelease; + + public MainDomSemaphoreLock(ILogger logger, IHostingEnvironment hostingEnvironment) + { + var lockName = "UMBRACO-" + MainDom.GetMainDomId(hostingEnvironment) + "-MAINDOM-LCK"; + _systemLock = new SystemLock(lockName); + + var eventName = "UMBRACO-" + MainDom.GetMainDomId(hostingEnvironment) + "-MAINDOM-EVT"; + _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + _logger = logger; + } + + //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread + public Task ListenAsync() => _signal.WaitOneAsync(); + + public Task AcquireLockAsync(int millisecondsTimeout) + { + // signal other instances that we want the lock, then wait on the lock, + // which may timeout, and this is accepted - see comments below + + // signal, then wait for the lock, then make sure the event is + // reset (maybe there was noone listening..) + _signal.Set(); + + // if more than 1 instance reach that point, one will get the lock + // and the other one will timeout, which is accepted + + //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. + try + { + _lockRelease = _systemLock.Lock(millisecondsTimeout); + return Task.FromResult(true); + } + catch (TimeoutException ex) + { + _logger.Error(ex); + return Task.FromResult(false); + } + finally + { + // we need to reset the event, because otherwise we would end up + // signaling ourselves and committing suicide immediately. + // only 1 instance can reach that point, but other instances may + // have started and be trying to get the lock - they will timeout, + // which is accepted + + _signal.Reset(); + } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _lockRelease?.Dispose(); + _signal.Close(); + _signal.Dispose(); + } + + disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + } +} diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs new file mode 100644 index 0000000000..6d73934387 --- /dev/null +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -0,0 +1,418 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Runtime +{ + public class SqlMainDomLock : IMainDomLock + { + private string _lockId; + private const string MainDomKey = "Umbraco.Core.Runtime.SqlMainDom"; + private const string UpdatedSuffix = "_updated"; + private readonly ILogger _logger; + private IUmbracoDatabase _db; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private SqlServerSyntaxProvider _sqlServerSyntax = new SqlServerSyntaxProvider(); + private bool _mainDomChanging = false; + private readonly UmbracoDatabaseFactory _dbFactory; + private bool _hasError; + private object _locker = new object(); + + public SqlMainDomLock(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, IDbProviderFactoryCreator dbProviderFactoryCreator) + { + // unique id for our appdomain, this is more unique than the appdomain id which is just an INT counter to its safer + _lockId = Guid.NewGuid().ToString(); + _logger = logger; + _dbFactory = new UmbracoDatabaseFactory( + Constants.System.UmbracoConnectionName, + globalSettings, + connectionStrings, + _logger, + new Lazy(() => new MapperCollection(Enumerable.Empty())), + dbProviderFactoryCreator + ); + } + + public async Task AcquireLockAsync(int millisecondsTimeout) + { + if (!(_dbFactory.SqlContext.SqlSyntax is SqlServerSyntaxProvider sqlServerSyntaxProvider)) + throw new NotSupportedException("SqlMainDomLock is only supported for Sql Server"); + + _sqlServerSyntax = sqlServerSyntaxProvider; + + _logger.Debug("Acquiring lock..."); + + var db = GetDatabase(); + + var tempId = Guid.NewGuid().ToString(); + + try + { + db.BeginTransaction(IsolationLevel.ReadCommitted); + + try + { + // wait to get a write lock + _sqlServerSyntax.WriteLock(db, TimeSpan.FromMilliseconds(millisecondsTimeout), Constants.Locks.MainDom); + } + catch (Exception ex) + { + if (IsLockTimeoutException(ex)) + { + _logger.Error(ex, "Sql timeout occurred, could not acquire MainDom."); + _hasError = true; + return false; + } + + // unexpected (will be caught below) + throw; + } + + var result = InsertLockRecord(tempId); //we change the row to a random Id to signal other MainDom to shutdown + if (result == RecordPersistenceType.Insert) + { + // if we've inserted, then there was no MainDom so we can instantly acquire + + // TODO: see the other TODO, could we just delete the row and that would indicate that we + // are MainDom? then we don't leave any orphan rows behind. + + InsertLockRecord(_lockId); // so update with our appdomain id + _logger.Debug("Acquired with ID {LockId}", _lockId); + return true; + } + + // if we've updated, this means there is an active MainDom, now we need to wait to + // for the current MainDom to shutdown which also requires releasing our write lock + } + catch (Exception ex) + { + ResetDatabase(); + // unexpected + _logger.Error(ex, "Unexpected error, cannot acquire MainDom"); + _hasError = true; + return false; + } + finally + { + db?.CompleteTransaction(); + } + + return await WaitForExistingAsync(tempId, millisecondsTimeout); + } + + public Task ListenAsync() + { + if (_hasError) + { + _logger.Warn("Could not acquire MainDom, listening is canceled."); + return Task.CompletedTask; + } + + // Create a long running task (dedicated thread) + // to poll to check if we are still the MainDom registered in the DB + return Task.Factory.StartNew(ListeningLoop, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + } + + private void ListeningLoop() + { + while (true) + { + // poll every 1 second + Thread.Sleep(1000); + + lock (_locker) + { + // If cancellation has been requested we will just exit. Depending on timing of the shutdown, + // we will have already flagged _mainDomChanging = true, or we're shutting down faster than + // the other MainDom is taking to startup. In this case the db row will just be deleted and the + // new MainDom will just take over. + if (_cancellationTokenSource.IsCancellationRequested) + return; + + var db = GetDatabase(); + + try + { + db.BeginTransaction(IsolationLevel.ReadCommitted); + + // get a read lock + _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); + + // TODO: We could in theory just check if the main dom row doesn't exist, that could indicate that + // we are still the maindom. An empty value might be better because then we won't have any orphan rows + // if the app is terminated. Could that work? + + if (!IsMainDomValue(_lockId)) + { + // we are no longer main dom, another one has come online, exit + _mainDomChanging = true; + _logger.Debug("Detected new booting application, releasing MainDom lock."); + return; + } + } + catch (Exception ex) + { + ResetDatabase(); + // unexpected + _logger.Error(ex, "Unexpected error, listening is canceled."); + _hasError = true; + return; + } + finally + { + db?.CompleteTransaction(); + } + } + + } + } + + private void ResetDatabase() + { + if (_db.InTransaction) + _db.AbortTransaction(); + _db.Dispose(); + _db = null; + } + + private IUmbracoDatabase GetDatabase() + { + if (_db != null) + return _db; + + _db = _dbFactory.CreateDatabase(); + return _db; + } + + /// + /// Wait for any existing MainDom to release so we can continue booting + /// + /// + /// + /// + private Task WaitForExistingAsync(string tempId, int millisecondsTimeout) + { + var updatedTempId = tempId + UpdatedSuffix; + + return Task.Run(() => + { + var db = GetDatabase(); + var watch = new Stopwatch(); + watch.Start(); + while(true) + { + // poll very often, we need to take over as fast as we can + Thread.Sleep(100); + + try + { + db.BeginTransaction(IsolationLevel.ReadCommitted); + + // get a read lock + _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); + + // the row + var mainDomRows = db.Fetch("SELECT * FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey }); + + if (mainDomRows.Count == 0 || mainDomRows[0].Value == updatedTempId) + { + // the other main dom has updated our record + // Or the other maindom shutdown super fast and just deleted the record + // which indicates that we + // can acquire it and it has shutdown. + + _sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom); + + // so now we update the row with our appdomain id + InsertLockRecord(_lockId); + _logger.Debug("Acquired with ID {LockId}", _lockId); + return true; + } + else if (mainDomRows.Count == 1 && !mainDomRows[0].Value.StartsWith(tempId)) + { + // in this case, the prefixed ID is different which means + // another new AppDomain has come online and is wanting to take over. In that case, we will not + // acquire. + + _logger.Debug("Cannot acquire, another booting application detected."); + + return false; + } + } + catch (Exception ex) + { + ResetDatabase(); + + if (IsLockTimeoutException(ex)) + { + _logger.Error(ex, "Sql timeout occurred, waiting for existing MainDom is canceled."); + _hasError = true; + return false; + } + // unexpected + _logger.Error(ex, "Unexpected error, waiting for existing MainDom is canceled."); + _hasError = true; + return false; + } + finally + { + db?.CompleteTransaction(); + } + + if (watch.ElapsedMilliseconds >= millisecondsTimeout) + { + // if the timeout has elapsed, it either means that the other main dom is taking too long to shutdown, + // or it could mean that the previous appdomain was terminated and didn't clear out the main dom SQL row + // and it's just been left as an orphan row. + // There's really know way of knowing unless we are constantly updating the row for the current maindom + // which isn't ideal. + // So... we're going to 'just' take over, if the writelock works then we'll assume we're ok + + _logger.Debug("Timeout elapsed, assuming orphan row, acquiring MainDom."); + + try + { + db.BeginTransaction(IsolationLevel.ReadCommitted); + + _sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom); + + // so now we update the row with our appdomain id + InsertLockRecord(_lockId); + _logger.Debug("Acquired with ID {LockId}", _lockId); + return true; + } + catch (Exception ex) + { + ResetDatabase(); + + if (IsLockTimeoutException(ex)) + { + // something is wrong, we cannot acquire, not much we can do + _logger.Error(ex, "Sql timeout occurred, could not forcibly acquire MainDom."); + _hasError = true; + return false; + } + _logger.Error(ex, "Unexpected error, could not forcibly acquire MainDom."); + _hasError = true; + return false; + } + finally + { + db?.CompleteTransaction(); + } + } + } + }, _cancellationTokenSource.Token); + } + + /// + /// Inserts or updates the key/value row + /// + private RecordPersistenceType InsertLockRecord(string id) + { + var db = GetDatabase(); + return db.InsertOrUpdate(new KeyValueDto + { + Key = MainDomKey, + Value = id, + Updated = DateTime.Now + }); + } + + /// + /// Checks if the DB row value is equals the value + /// + /// + private bool IsMainDomValue(string val) + { + var db = GetDatabase(); + return db.ExecuteScalar("SELECT COUNT(*) FROM umbracoKeyValue WHERE [key] = @key AND [value] = @val", + new { key = MainDomKey, val = val }) == 1; + } + + /// + /// Checks if the exception is an SQL timeout + /// + /// + /// + private bool IsLockTimeoutException(Exception exception) => exception is SqlException sqlException && sqlException.Number == 1222; + + #region IDisposable Support + private bool _disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + lock (_locker) + { + // immediately cancel all sub-tasks, we don't want them to keep querying + _cancellationTokenSource.Cancel(); + _cancellationTokenSource.Dispose(); + + var db = GetDatabase(); + try + { + db.BeginTransaction(IsolationLevel.ReadCommitted); + + // get a write lock + _sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom); + + // When we are disposed, it means we have released the MainDom lock + // and called all MainDom release callbacks, in this case + // if another maindom is actually coming online we need + // to signal to the MainDom coming online that we have shutdown. + // To do that, we update the existing main dom DB record with a suffixed "_updated" string. + // Otherwise, if we are just shutting down, we want to just delete the row. + if (_mainDomChanging) + { + _logger.Debug("Releasing MainDom, updating row, new application is booting."); + db.Execute($"UPDATE umbracoKeyValue SET [value] = [value] + '{UpdatedSuffix}' WHERE [key] = @key", new { key = MainDomKey }); + } + else + { + _logger.Debug("Releasing MainDom, deleting row, application is shutting down."); + db.Execute("DELETE FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey }); + } + } + catch (Exception ex) + { + ResetDatabase(); + _logger.Error(ex, "Unexpected error during dipsose."); + _hasError = true; + } + finally + { + db?.CompleteTransaction(); + ResetDatabase(); + } + } + } + + _disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + + } +} diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs index 5952e73e62..eadb9fed21 100644 --- a/src/Umbraco.Infrastructure/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/RuntimeState.cs @@ -20,7 +20,6 @@ namespace Umbraco.Core public class RuntimeState : IRuntimeState { private readonly ILogger _logger; - private readonly IUmbracoSettingsSection _settings; private readonly IGlobalSettings _globalSettings; private readonly ConcurrentHashSet _applicationUrls = new ConcurrentHashSet(); private readonly Lazy _mainDom; @@ -32,13 +31,12 @@ namespace Umbraco.Core /// /// Initializes a new instance of the class. /// - public RuntimeState(ILogger logger, IUmbracoSettingsSection settings, IGlobalSettings globalSettings, + public RuntimeState(ILogger logger, IGlobalSettings globalSettings, Lazy mainDom, Lazy serverRegistrar, IUmbracoVersion umbracoVersion, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) { _logger = logger; - _settings = settings; _globalSettings = globalSettings; _mainDom = mainDom; _serverRegistrar = serverRegistrar; diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs similarity index 96% rename from src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs rename to src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs index c0475b1f79..0b5b81319f 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs @@ -2,9 +2,9 @@ using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; -using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Events; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; namespace Umbraco.Web.Scheduling @@ -81,6 +81,7 @@ namespace Umbraco.Web.Scheduling private readonly string _logPrefix; private readonly BackgroundTaskRunnerOptions _options; private readonly ILogger _logger; + private readonly IHostingEnvironment _hostingEnvironment; private readonly object _locker = new object(); private readonly BufferBlock _tasks = new BufferBlock(new DataflowBlockOptions()); @@ -102,9 +103,10 @@ namespace Umbraco.Web.Scheduling /// Initializes a new instance of the class. /// /// A logger. + /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(ILogger logger, MainDomHook hook = null) - : this(typeof(T).FullName, new BackgroundTaskRunnerOptions(), logger, hook) + public BackgroundTaskRunner(ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + : this(typeof(T).FullName, new BackgroundTaskRunnerOptions(), logger, hostingEnvironment, hook) { } /// @@ -112,9 +114,10 @@ namespace Umbraco.Web.Scheduling /// /// The name of the runner. /// A logger. + /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(string name, ILogger logger, MainDomHook hook = null) - : this(name, new BackgroundTaskRunnerOptions(), logger, hook) + public BackgroundTaskRunner(string name, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + : this(name, new BackgroundTaskRunnerOptions(), logger, hostingEnvironment, hook) { } /// @@ -122,9 +125,10 @@ namespace Umbraco.Web.Scheduling /// /// The set of options. /// A logger. + /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(BackgroundTaskRunnerOptions options, ILogger logger, MainDomHook hook = null) - : this(typeof(T).FullName, options, logger, hook) + public BackgroundTaskRunner(BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + : this(typeof(T).FullName, options, logger, hostingEnvironment, hook) { } /// @@ -133,15 +137,17 @@ namespace Umbraco.Web.Scheduling /// The name of the runner. /// The set of options. /// A logger. + /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger logger, MainDomHook hook = null) + public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _hostingEnvironment = hostingEnvironment; _logPrefix = "[" + name + "] "; if (options.Hosted) - HostingEnvironment.RegisterObject(this); + _hostingEnvironment.RegisterObject(this); if (hook != null) _completed = _terminated = hook.Register() == false; @@ -212,7 +218,7 @@ namespace Umbraco.Web.Scheduling /// /// Used to wait until the runner has terminated. /// - /// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown. + /// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown. /// /// internal ThreadingTaskImmutable TerminatedAwaitable @@ -350,14 +356,14 @@ namespace Umbraco.Web.Scheduling if (force) { - // we must bring everything down, now + // we must bring everything down, now lock (_locker) { // was Complete() enough? // if _tasks.Complete() ended up triggering code to stop the runner and reset // the _isRunning flag, then there's no need to initiate a cancel on the cancelation token. if (_isRunning == false) - return; + return; } // try to cancel running async tasks (cannot do much about sync tasks) @@ -805,7 +811,7 @@ namespace Umbraco.Web.Scheduling if (immediate) { //only unregister when it's the final call, else we won't be notified of the final call - HostingEnvironment.UnregisterObject(this); + _hostingEnvironment.UnregisterObject(this); } if (_terminated) return; // already taken care of diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs similarity index 89% rename from src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs rename to src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs index 746bc61e34..00bbba8bb8 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs @@ -2,31 +2,31 @@ using System.Threading; using System.Threading.Tasks; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Logging; using Umbraco.Core.Sync; using Umbraco.Web.HealthCheck; namespace Umbraco.Web.Scheduling { - internal class HealthCheckNotifier : RecurringTaskBase + public class HealthCheckNotifier : RecurringTaskBase { private readonly IRuntimeState _runtimeState; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; private readonly IProfilingLogger _logger; + private readonly IHealthChecks _healthChecksConfig; public HealthCheckNotifier(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IRuntimeState runtimeState, - IProfilingLogger logger) + IRuntimeState runtimeState, IProfilingLogger logger, IHealthChecks healthChecksConfig) : base(runner, delayMilliseconds, periodMilliseconds) { _healthChecks = healthChecks; _notifications = notifications; _runtimeState = runtimeState; _logger = logger; + _healthChecksConfig = healthChecksConfig; } public override async Task PerformRunAsync(CancellationToken token) @@ -53,7 +53,7 @@ namespace Umbraco.Web.Scheduling using (_logger.DebugDuration("Health checks executing", "Health checks complete")) { - var healthCheckConfig = Current.Configs.HealthChecks(); + var healthCheckConfig = _healthChecksConfig; // Don't notify for any checks that are disabled, nor for any disabled // just for notifications diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs similarity index 85% rename from src/Umbraco.Web/Scheduling/LogScrubber.cs rename to src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs index db13a80f9b..563d79a193 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs @@ -9,16 +9,16 @@ using Umbraco.Core.Sync; namespace Umbraco.Web.Scheduling { - internal class LogScrubber : RecurringTaskBase + public class LogScrubber : RecurringTaskBase { private readonly IRuntimeState _runtime; private readonly IAuditService _auditService; - private readonly IUmbracoSettingsSection _settings; + private readonly ILoggingSettings _settings; private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; public LogScrubber(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, IProfilingLogger logger) + IRuntimeState runtime, IAuditService auditService, ILoggingSettings settings, IScopeProvider scopeProvider, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; @@ -29,13 +29,13 @@ namespace Umbraco.Web.Scheduling } // maximum age, in minutes - private int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings) + private int GetLogScrubbingMaximumAge(ILoggingSettings settings) { var maximumAge = 24 * 60; // 24 hours, in minutes try { - if (settings.Logging.MaxLogAge > -1) - maximumAge = settings.Logging.MaxLogAge; + if (settings.MaxLogAge > -1) + maximumAge = settings.MaxLogAge; } catch (Exception ex) { @@ -45,7 +45,7 @@ namespace Umbraco.Web.Scheduling } - public static int GetLogScrubbingInterval(IUmbracoSettingsSection settings, ILogger logger) + public static int GetLogScrubbingInterval() { const int interval = 4 * 60 * 60 * 1000; // 4 hours, in milliseconds return interval; diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs similarity index 91% rename from src/Umbraco.Web/Scheduling/ScheduledPublishing.cs rename to src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs index 2e79e40d7a..b074704033 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs @@ -7,21 +7,23 @@ using Umbraco.Core.Sync; namespace Umbraco.Web.Scheduling { - internal class ScheduledPublishing : RecurringTaskBase + public class ScheduledPublishing : RecurringTaskBase { private readonly IRuntimeState _runtime; private readonly IContentService _contentService; private readonly IUmbracoContextFactory _umbracoContextFactory; private readonly ILogger _logger; + private readonly IServerMessenger _serverMessenger; public ScheduledPublishing(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILogger logger) + IRuntimeState runtime, IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILogger logger, IServerMessenger serverMessenger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _contentService = contentService; _umbracoContextFactory = umbracoContextFactory; _logger = logger; + _serverMessenger = serverMessenger; } public override bool PerformRun() @@ -76,7 +78,7 @@ namespace Umbraco.Web.Scheduling finally { // if running on a temp context, we have to flush the messenger - if (contextReference.IsRoot && Composing.Current.ServerMessenger is BatchedDatabaseServerMessenger m) + if (contextReference.IsRoot && _serverMessenger is IBatchedDatabaseServerMessenger m) m.FlushBatch(); } } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs similarity index 73% rename from src/Umbraco.Web/Scheduling/SchedulerComponent.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index b537146874..1d4a654830 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -4,13 +4,15 @@ using System.IO; using System.Threading; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Scoping; using Umbraco.Core.Services; +using Umbraco.Core.Sync; using Umbraco.Web.HealthCheck; using Umbraco.Web.Routing; @@ -27,10 +29,17 @@ namespace Umbraco.Web.Scheduling private readonly IContentService _contentService; private readonly IAuditService _auditService; private readonly IProfilingLogger _logger; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IScopeProvider _scopeProvider; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; private readonly IUmbracoContextFactory _umbracoContextFactory; + private readonly IHealthChecks _healthChecksConfig; + private readonly IIOHelper _ioHelper; + private readonly IServerMessenger _serverMessenger; + private readonly IRequestAccessor _requestAccessor; + private readonly ILoggingSettings _loggingSettings; + private readonly IKeepAliveSettings _keepAliveSettings; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -46,31 +55,41 @@ namespace Umbraco.Web.Scheduling public SchedulerComponent(IRuntimeState runtime, IContentService contentService, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger) + IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, + IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig, + IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor, + ILoggingSettings loggingSettings, IKeepAliveSettings keepAliveSettings) { _runtime = runtime; _contentService = contentService; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; + _hostingEnvironment = hostingEnvironment; _umbracoContextFactory = umbracoContextFactory; _healthChecks = healthChecks; _notifications = notifications; + _healthChecksConfig = healthChecksConfig ?? throw new ArgumentNullException(nameof(healthChecksConfig)); + _ioHelper = ioHelper; + _serverMessenger = serverMessenger; + _requestAccessor = requestAccessor; + _loggingSettings = loggingSettings; + _keepAliveSettings = keepAliveSettings; } public void Initialize() { // backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly - _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", _logger); - _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", _logger); - _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", _logger); - _scrubberRunner = new BackgroundTaskRunner("LogScrubber", _logger); - _fileCleanupRunner = new BackgroundTaskRunner("TempFileCleanup", _logger); - _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger); + _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", _logger, _hostingEnvironment); + _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", _logger, _hostingEnvironment); + _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", _logger, _hostingEnvironment); + _scrubberRunner = new BackgroundTaskRunner("LogScrubber", _logger, _hostingEnvironment); + _fileCleanupRunner = new BackgroundTaskRunner("TempFileCleanup", _logger, _hostingEnvironment); + _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger, _hostingEnvironment); // we will start the whole process when a successful request is made - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; } public void Terminate() @@ -84,7 +103,7 @@ namespace Umbraco.Web.Scheduling { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } @@ -95,20 +114,19 @@ namespace Umbraco.Web.Scheduling LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () => { _logger.Debug("Initializing the scheduler"); - var settings = Current.Configs.Settings(); var tasks = new List(); - if (settings.KeepAlive.DisableKeepAliveTask == false) + if (_keepAliveSettings.DisableKeepAliveTask == false) { - tasks.Add(RegisterKeepAlive(settings.KeepAlive)); + tasks.Add(RegisterKeepAlive(_keepAliveSettings)); } tasks.Add(RegisterScheduledPublishing()); - tasks.Add(RegisterLogScrubber(settings)); + tasks.Add(RegisterLogScrubber(_loggingSettings)); tasks.Add(RegisterTempFileCleanup()); - var healthCheckConfig = Current.Configs.HealthChecks(); + var healthCheckConfig = _healthChecksConfig; if (healthCheckConfig.NotificationSettings.Enabled) tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger)); @@ -116,11 +134,11 @@ namespace Umbraco.Web.Scheduling }); } - private IBackgroundTask RegisterKeepAlive(IKeepAliveSection keepAliveSection) + private IBackgroundTask RegisterKeepAlive(IKeepAliveSettings keepAliveSettings) { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSection, _logger); + var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSettings, _logger); _keepAliveRunner.TryAdd(task); return task; } @@ -129,7 +147,7 @@ namespace Umbraco.Web.Scheduling { // scheduled publishing/unpublishing // install on all, will only run on non-replica servers - var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _contentService, _umbracoContextFactory, _logger); + var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _contentService, _umbracoContextFactory, _logger, _serverMessenger); _publishingRunner.TryAdd(task); return task; } @@ -155,16 +173,16 @@ namespace Umbraco.Web.Scheduling } var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000; - var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger); + var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, _healthChecksConfig); _healthCheckRunner.TryAdd(task); return task; } - private IBackgroundTask RegisterLogScrubber(IUmbracoSettingsSection settings) + private IBackgroundTask RegisterLogScrubber(ILoggingSettings settings) { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); + var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(), _runtime, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } @@ -174,7 +192,7 @@ namespace Umbraco.Web.Scheduling // temp file cleanup, will run on all servers - even though file upload should only be handled on the master, this will // ensure that in the case it happes on replicas that they are cleaned up. var task = new TempFileCleanup(_fileCleanupRunner, DefaultDelayMilliseconds, OneHourMilliseconds, - new[] { new DirectoryInfo(Current.IOHelper.MapPath(Constants.SystemDirectories.TempFileUploads)) }, + new[] { new DirectoryInfo(_ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads)) }, TimeSpan.FromDays(1), //files that are over a day old _runtime, _logger); _scrubberRunner.TryAdd(task); diff --git a/src/Umbraco.Web/Scheduling/SchedulerComposer.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/SchedulerComposer.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs diff --git a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs index 4a7ccae481..dce6658f16 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Scoping /// Provides scopes. /// public interface IScopeProvider - { + { /// /// Creates an ambient scope. /// diff --git a/src/Umbraco.Web/Search/BackgroundIndexRebuilder.cs b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs similarity index 93% rename from src/Umbraco.Web/Search/BackgroundIndexRebuilder.cs rename to src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs index 0ae5ceade9..2c964a2723 100644 --- a/src/Umbraco.Web/Search/BackgroundIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Logging; using Umbraco.Examine; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Hosting; using Umbraco.Web.Scheduling; namespace Umbraco.Web.Search @@ -17,12 +18,14 @@ namespace Umbraco.Web.Search private readonly IndexRebuilder _indexRebuilder; private readonly IMainDom _mainDom; private readonly IProfilingLogger _logger; + private readonly IHostingEnvironment _hostingEnvironment; private static BackgroundTaskRunner _rebuildOnStartupRunner; - public BackgroundIndexRebuilder(IMainDom mainDom, IProfilingLogger logger, IndexRebuilder indexRebuilder) + public BackgroundIndexRebuilder(IMainDom mainDom, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IndexRebuilder indexRebuilder) { _mainDom = mainDom; _logger = logger; + _hostingEnvironment = hostingEnvironment; _indexRebuilder = indexRebuilder; } @@ -51,7 +54,7 @@ namespace Umbraco.Web.Search _rebuildOnStartupRunner = new BackgroundTaskRunner( "RebuildIndexesOnStartup", - _logger); + _logger, _hostingEnvironment); _rebuildOnStartupRunner.TryAdd(task); } @@ -115,7 +118,6 @@ namespace Umbraco.Web.Search if (_waitMilliseconds > 0) Thread.Sleep(_waitMilliseconds); - _indexRebuilder.ExamineManager.ConfigureIndexes(_mainDom, _logger); _indexRebuilder.RebuildIndexes(_onlyEmptyIndexes); } } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineComponent.cs similarity index 95% rename from src/Umbraco.Web/Search/ExamineComponent.cs rename to src/Umbraco.Infrastructure/Search/ExamineComponent.cs index 149b4d1436..037981d8b4 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineComponent.cs @@ -13,10 +13,6 @@ using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; using Umbraco.Web.Cache; using Umbraco.Examine; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Examine.LuceneEngine.Directories; -using Umbraco.Core.Composing; -using System.ComponentModel; namespace Umbraco.Web.Search { @@ -30,17 +26,17 @@ namespace Umbraco.Web.Search private readonly BackgroundIndexRebuilder _backgroundIndexRebuilder; private static object _isConfiguredLocker = new object(); private readonly IScopeProvider _scopeProvider; - private readonly ServiceContext _services; + private readonly ServiceContext _services; private readonly IMainDom _mainDom; private readonly IProfilingLogger _logger; private readonly IUmbracoIndexesCreator _indexCreator; - + // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us // but greater that SafeXmlReaderWriter priority which is 60 private const int EnlistPriority = 80; - + public ExamineComponent(IMainDom mainDom, IExamineManager examineManager, IProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, @@ -66,15 +62,6 @@ namespace Umbraco.Web.Search public void Initialize() { - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the AppDomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - DirectoryFactory.DefaultLockFactory = d => - { - var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); - return simpleFsLockFactory; - }; - //let's deal with shutting down Examine with MainDom var examineShutdownRegistered = _mainDom.Register(() => { @@ -119,10 +106,6 @@ namespace Umbraco.Web.Search public void Terminate() { } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("This method should not be used and will be removed in future versions, rebuilding indexes can be done with the IndexRebuilder or the BackgroundIndexRebuilder")] - public static void RebuildIndexes(IndexRebuilder indexRebuilder, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) => Current.Factory.GetInstance().RebuildIndexes(onlyEmptyIndexes, waitMilliseconds); - #region Cache refresher updated event handlers /// @@ -706,6 +689,6 @@ namespace Umbraco.Web.Search } #endregion - + } } diff --git a/src/Umbraco.Web/Search/ExamineComposer.cs b/src/Umbraco.Infrastructure/Search/ExamineComposer.cs similarity index 82% rename from src/Umbraco.Web/Search/ExamineComposer.cs rename to src/Umbraco.Infrastructure/Search/ExamineComposer.cs index d62fc97efb..fe3c604f8b 100644 --- a/src/Umbraco.Web/Search/ExamineComposer.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineComposer.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.Search composition.Register(Lifetime.Singleton); composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(factory => new ContentValueSetBuilder( factory.GetInstance(), @@ -48,11 +48,6 @@ namespace Umbraco.Web.Search composition.RegisterUnique, MediaValueSetBuilder>(); composition.RegisterUnique, MemberValueSetBuilder>(); composition.RegisterUnique(); - - //We want to manage Examine's AppDomain shutdown sequence ourselves so first we'll disable Examine's default behavior - //and then we'll use MainDom to control Examine's shutdown - this MUST be done in Compose ie before ExamineManager - //is instantiated, as the value is used during instantiation - ExamineManager.DisableDefaultHostingEnvironmentRegistration(); } } } diff --git a/src/Umbraco.Web/Search/ExamineFinalComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineFinalComponent.cs similarity index 68% rename from src/Umbraco.Web/Search/ExamineFinalComponent.cs rename to src/Umbraco.Infrastructure/Search/ExamineFinalComponent.cs index 95000b2b46..ffcd96af7e 100644 --- a/src/Umbraco.Web/Search/ExamineFinalComponent.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineFinalComponent.cs @@ -12,15 +12,11 @@ namespace Umbraco.Web.Search /// public sealed class ExamineFinalComponent : IComponent { - private readonly IProfilingLogger _logger; - private readonly IExamineManager _examineManager; BackgroundIndexRebuilder _indexRebuilder; private readonly IMainDom _mainDom; - public ExamineFinalComponent(IProfilingLogger logger, IExamineManager examineManager, BackgroundIndexRebuilder indexRebuilder, IMainDom mainDom) + public ExamineFinalComponent(BackgroundIndexRebuilder indexRebuilder, IMainDom mainDom) { - _logger = logger; - _examineManager = examineManager; _indexRebuilder = indexRebuilder; _mainDom = mainDom; } @@ -29,8 +25,6 @@ namespace Umbraco.Web.Search { if (!_mainDom.IsMainDom) return; - _examineManager.ConfigureIndexes(_mainDom, _logger); - // TODO: Instead of waiting 5000 ms, we could add an event handler on to fulfilling the first request, then start? _indexRebuilder.RebuildIndexes(true, 5000); } diff --git a/src/Umbraco.Web/Search/ExamineFinalComposer.cs b/src/Umbraco.Infrastructure/Search/ExamineFinalComposer.cs similarity index 100% rename from src/Umbraco.Web/Search/ExamineFinalComposer.cs rename to src/Umbraco.Infrastructure/Search/ExamineFinalComposer.cs diff --git a/src/Umbraco.Web/Search/ExamineIndexModel.cs b/src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs similarity index 100% rename from src/Umbraco.Web/Search/ExamineIndexModel.cs rename to src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs diff --git a/src/Umbraco.Web/Search/ExamineSearcherModel.cs b/src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs similarity index 100% rename from src/Umbraco.Web/Search/ExamineSearcherModel.cs rename to src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs diff --git a/src/Umbraco.Web/Search/ExamineUserComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs similarity index 100% rename from src/Umbraco.Web/Search/ExamineUserComponent.cs rename to src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs diff --git a/src/Umbraco.Web/Search/SearchableApplicationTree.cs b/src/Umbraco.Infrastructure/Search/SearchableApplicationTree.cs similarity index 100% rename from src/Umbraco.Web/Search/SearchableApplicationTree.cs rename to src/Umbraco.Infrastructure/Search/SearchableApplicationTree.cs diff --git a/src/Umbraco.Web/Search/SearchableTreeCollection.cs b/src/Umbraco.Infrastructure/Search/SearchableTreeCollection.cs similarity index 96% rename from src/Umbraco.Web/Search/SearchableTreeCollection.cs rename to src/Umbraco.Infrastructure/Search/SearchableTreeCollection.cs index 0e7b1cd268..5c251a857d 100644 --- a/src/Umbraco.Web/Search/SearchableTreeCollection.cs +++ b/src/Umbraco.Infrastructure/Search/SearchableTreeCollection.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Services; using Umbraco.Web.Services; using Umbraco.Web.Trees; diff --git a/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs b/src/Umbraco.Infrastructure/Search/SearchableTreeCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs rename to src/Umbraco.Infrastructure/Search/SearchableTreeCollectionBuilder.cs diff --git a/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcher.cs new file mode 100644 index 0000000000..ecaf7354ca --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcher.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Examine; +using Umbraco.Core; +using Umbraco.Core.Mapping; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Examine; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.Routing; +using Umbraco.Web.Trees; + +namespace Umbraco.Web.Search +{ + + /// + /// Used for internal Umbraco implementations of + /// + public class UmbracoTreeSearcher + { + private readonly ILocalizationService _languageService; + private readonly IEntityService _entityService; + private readonly UmbracoMapper _mapper; + private readonly ISqlContext _sqlContext; + private readonly IBackOfficeExamineSearcher _backOfficeExamineSearcher; + private readonly IPublishedUrlProvider _publishedUrlProvider; + + + public UmbracoTreeSearcher( + ILocalizationService languageService, + IEntityService entityService, + UmbracoMapper mapper, + ISqlContext sqlContext, + IBackOfficeExamineSearcher backOfficeExamineSearcher, + IPublishedUrlProvider publishedUrlProvider) + { + _languageService = languageService; + _entityService = entityService; + _mapper = mapper; + _sqlContext = sqlContext; + _backOfficeExamineSearcher = backOfficeExamineSearcher; + _publishedUrlProvider = publishedUrlProvider; + } + + /// + /// Searches Examine for results based on the entity type + /// + /// + /// + /// + /// + /// + /// A starting point for the search, generally a node id, but for members this is a member type alias + /// + /// + /// + /// If set to true, user and group start node permissions will be ignored. + /// + public IEnumerable ExamineSearch( + string query, + UmbracoEntityTypes entityType, + int pageSize, + long pageIndex, out long totalFound, string culture = null, string searchFrom = null, bool ignoreUserStartNodes = false) + { + var pagedResult = _backOfficeExamineSearcher.Search(query, entityType, pageSize, pageIndex, out totalFound, searchFrom, ignoreUserStartNodes); + + switch (entityType) + { + case UmbracoEntityTypes.Member: + return MemberFromSearchResults(pagedResult.ToArray()); + case UmbracoEntityTypes.Media: + return MediaFromSearchResults(pagedResult); + case UmbracoEntityTypes.Document: + return ContentFromSearchResults(pagedResult, culture); + default: + throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); + } + } + + /// + /// Searches with the for results based on the entity type + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable EntitySearch(UmbracoObjectTypes objectType, string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + { + //if it's a GUID, match it + Guid.TryParse(query, out var g); + + var results = _entityService.GetPagedDescendants(objectType, pageIndex, pageSize, out totalFound, + filter: _sqlContext.Query().Where(x => x.Name.Contains(query) || x.Key == g)); + return _mapper.MapEnumerable(results); + } + + /// + /// Returns a collection of entities for media based on search results + /// + /// + /// + private IEnumerable MemberFromSearchResults(IEnumerable results) + { + //add additional data + foreach (var result in results) + { + var m = _mapper.Map(result); + + //if no icon could be mapped, it will be set to document, so change it to picture + if (m.Icon == Constants.Icons.DefaultIcon) + { + m.Icon = Constants.Icons.Member; + } + + if (result.Values.ContainsKey("email") && result.Values["email"] != null) + { + m.AdditionalData["Email"] = result.Values["email"]; + } + if (result.Values.ContainsKey(UmbracoExamineFieldNames.NodeKeyFieldName) && result.Values[UmbracoExamineFieldNames.NodeKeyFieldName] != null) + { + if (Guid.TryParse(result.Values[UmbracoExamineFieldNames.NodeKeyFieldName], out var key)) + { + m.Key = key; + } + } + + yield return m; + } + } + + /// + /// Returns a collection of entities for media based on search results + /// + /// + /// + private IEnumerable MediaFromSearchResults(IEnumerable results) + => _mapper.Map>(results); + + /// + /// Returns a collection of entities for content based on search results + /// + /// + /// + /// + private IEnumerable ContentFromSearchResults(IEnumerable results, string culture = null) + { + var defaultLang = _languageService.GetDefaultLanguageIsoCode(); + + foreach (var result in results) + { + var entity = _mapper.Map(result, context => { + if(culture != null) { + context.SetCulture(culture); + } + } + ); + + var intId = entity.Id.TryConvertTo(); + if (intId.Success) + { + //if it varies by culture, return the default language URL + if (result.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out var varies) && varies == "y") + { + entity.AdditionalData["Url"] = _publishedUrlProvider.GetUrl(intId.Result, culture: culture ?? defaultLang); + } + else + { + entity.AdditionalData["Url"] = _publishedUrlProvider.GetUrl(intId.Result); + } + } + + yield return entity; + } + } + + } +} diff --git a/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcherFields.cs b/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcherFields.cs new file mode 100644 index 0000000000..a1c9936542 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/UmbracoTreeSearcherFields.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Examine; + +namespace Umbraco.Web.Search +{ + public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields + { + private IReadOnlyList _backOfficeFields = new List {"id", "__NodeId", "__Key"}; + public IEnumerable GetBackOfficeFields() + { + return _backOfficeFields; + } + + + private IReadOnlyList _backOfficeMembersFields = new List {"email", "loginName"}; + public IEnumerable GetBackOfficeMembersFields() + { + return _backOfficeMembersFields; + } + private IReadOnlyList _backOfficeMediaFields = new List { UmbracoExamineFieldNames.UmbracoFileFieldName }; + public IEnumerable GetBackOfficeMediaFields() + { + return _backOfficeMediaFields; + } + public IEnumerable GetBackOfficeDocumentFields() + { + return Enumerable.Empty(); + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/IdkMap.cs b/src/Umbraco.Infrastructure/Services/IdKeyMap.cs similarity index 99% rename from src/Umbraco.Infrastructure/Services/IdkMap.cs rename to src/Umbraco.Infrastructure/Services/IdKeyMap.cs index f7790ba76b..62c135e717 100644 --- a/src/Umbraco.Infrastructure/Services/IdkMap.cs +++ b/src/Umbraco.Infrastructure/Services/IdKeyMap.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services { - public class IdkMap + public class IdKeyMap : IIdKeyMap { private readonly IScopeProvider _scopeProvider; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); @@ -15,7 +15,7 @@ namespace Umbraco.Core.Services private readonly Dictionary> _id2Key = new Dictionary>(); private readonly Dictionary> _key2Id = new Dictionary>(); - public IdkMap(IScopeProvider scopeProvider) + public IdKeyMap(IScopeProvider scopeProvider) { _scopeProvider = scopeProvider; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/AuditService.cs b/src/Umbraco.Infrastructure/Services/Implement/AuditService.cs index 5eb08f2dea..7d3be1d52b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/AuditService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/AuditService.cs @@ -142,7 +142,7 @@ namespace Umbraco.Core.Services.Implement if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - if (userId < 0) + if (userId < Constants.Security.SuperUserId) { totalRecords = 0; return Enumerable.Empty(); diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index f386f22f39..d02f84d294 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -2030,13 +2030,6 @@ namespace Umbraco.Core.Services.Implement _documentRepository.Save(content); } - /// - /// Empties the Recycle Bin by deleting all that resides in the bin - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] - public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId); - /// /// Empties the Recycle Bin by deleting all that resides in the bin /// diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 45baf9720f..7e39894aa3 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -318,6 +318,15 @@ namespace Umbraco.Core.Services.Implement } } + public bool HasContainerInPath(params int[] ids) + { + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + // can use same repo for both content and media + return Repository.HasContainerInPath(ids); + } + } + public IEnumerable GetDescendants(int id, bool andSelf) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) @@ -369,6 +378,15 @@ namespace Umbraco.Core.Services.Implement } } + public bool HasContentNodes(int id) + { + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(ReadLockIds); + return Repository.HasContentNodes(id); + } + } + #endregion #region Save diff --git a/src/Umbraco.Web/Services/DashboardService.cs b/src/Umbraco.Infrastructure/Services/Implement/DashboardService.cs similarity index 98% rename from src/Umbraco.Web/Services/DashboardService.cs rename to src/Umbraco.Infrastructure/Services/Implement/DashboardService.cs index 9080a92893..918ab57485 100644 --- a/src/Umbraco.Web/Services/DashboardService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DashboardService.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Dashboards; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; using Umbraco.Web.Dashboards; using Umbraco.Web.Models.ContentEditing; @@ -15,7 +13,7 @@ namespace Umbraco.Web.Services /// /// A utility class for determine dashboard security /// - internal class DashboardService : IDashboardService + public class DashboardService : IDashboardService { // TODO: Unit test all this!!! :/ diff --git a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs index f9d0256568..08bed264ae 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Exceptions; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; @@ -10,6 +11,7 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; +using Umbraco.Core.Strings; namespace Umbraco.Core.Services.Implement { @@ -23,10 +25,15 @@ namespace Umbraco.Core.Services.Implement private readonly IContentTypeRepository _contentTypeRepository; private readonly IAuditRepository _auditRepository; private readonly IEntityRepository _entityRepository; + private readonly IIOHelper _ioHelper; + private readonly ILocalizedTextService _localizedTextService; + private readonly ILocalizationService _localizationService; + private readonly IShortStringHelper _shortStringHelper; public DataTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IDataTypeRepository dataTypeRepository, IDataTypeContainerRepository dataTypeContainerRepository, - IAuditRepository auditRepository, IEntityRepository entityRepository, IContentTypeRepository contentTypeRepository) + IAuditRepository auditRepository, IEntityRepository entityRepository, IContentTypeRepository contentTypeRepository, + IIOHelper ioHelper, ILocalizedTextService localizedTextService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) : base(provider, logger, eventMessagesFactory) { _dataTypeRepository = dataTypeRepository; @@ -34,6 +41,10 @@ namespace Umbraco.Core.Services.Implement _auditRepository = auditRepository; _entityRepository = entityRepository; _contentTypeRepository = contentTypeRepository; + _ioHelper = ioHelper; + _localizedTextService = localizedTextService; + _localizationService = localizationService; + _shortStringHelper = shortStringHelper; } #region Containers @@ -227,7 +238,9 @@ namespace Umbraco.Core.Services.Implement { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - return _dataTypeRepository.Get(Query().Where(x => x.Name == name)).FirstOrDefault(); + var dataType = _dataTypeRepository.Get(Query().Where(x => x.Name == name)).FirstOrDefault(); + ConvertMissingEditorOfDataTypeToLabel(dataType); + return dataType; } } @@ -240,7 +253,9 @@ namespace Umbraco.Core.Services.Implement { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - return _dataTypeRepository.Get(id); + var dataType = _dataTypeRepository.Get(id); + ConvertMissingEditorOfDataTypeToLabel(dataType); + return dataType; } } @@ -254,7 +269,9 @@ namespace Umbraco.Core.Services.Implement using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.Key == id); - return _dataTypeRepository.Get(query).FirstOrDefault(); + var dataType = _dataTypeRepository.Get(query).FirstOrDefault(); + ConvertMissingEditorOfDataTypeToLabel(dataType); + return dataType; } } @@ -268,7 +285,9 @@ namespace Umbraco.Core.Services.Implement using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.EditorAlias == propertyEditorAlias); - return _dataTypeRepository.Get(query); + var dataType = _dataTypeRepository.Get(query); + ConvertMissingEditorsOfDataTypesToLabels(dataType); + return dataType; } } @@ -281,7 +300,31 @@ namespace Umbraco.Core.Services.Implement { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - return _dataTypeRepository.GetMany(ids); + var dataTypes = _dataTypeRepository.GetMany(ids); + ConvertMissingEditorsOfDataTypesToLabels(dataTypes); + return dataTypes; + } + } + + private void ConvertMissingEditorOfDataTypeToLabel(IDataType dataType) + { + if (dataType == null) + { + return; + } + + ConvertMissingEditorsOfDataTypesToLabels(new[] { dataType }); + } + + private void ConvertMissingEditorsOfDataTypesToLabels(IEnumerable dataTypes) + { + // Any data types that don't have an associated editor are created of a specific type. + // We convert them to labels to make clear to the user why the data type cannot be used. + var dataTypesWithMissingEditors = dataTypes + .Where(x => x.Editor is MissingPropertyEditor); + foreach (var dataType in dataTypesWithMissingEditors) + { + dataType.Editor = new LabelPropertyEditor(Logger, _ioHelper, this, _localizedTextService, _localizationService, _shortStringHelper); } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityService.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityService.cs index 04e2624592..8436f56457 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityService.cs @@ -19,12 +19,12 @@ namespace Umbraco.Core.Services.Implement private readonly IEntityRepository _entityRepository; private readonly Dictionary _objectTypes; private IQuery _queryRootEntity; - private readonly IdkMap _idkMap; + private readonly IIdKeyMap _idKeyMap; - public EntityService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IdkMap idkMap, IEntityRepository entityRepository) + public EntityService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IIdKeyMap idKeyMap, IEntityRepository entityRepository) : base(provider, logger, eventMessagesFactory) { - _idkMap = idkMap; + _idKeyMap = idKeyMap; _entityRepository = entityRepository; _objectTypes = new Dictionary @@ -440,19 +440,19 @@ namespace Umbraco.Core.Services.Implement /// public Attempt GetId(Guid key, UmbracoObjectTypes objectType) { - return _idkMap.GetIdForKey(key, objectType); + return _idKeyMap.GetIdForKey(key, objectType); } /// public Attempt GetId(Udi udi) { - return _idkMap.GetIdForUdi(udi); + return _idKeyMap.GetIdForUdi(udi); } /// public Attempt GetKey(int id, UmbracoObjectTypes umbracoObjectType) { - return _idkMap.GetKeyForId(id, umbracoObjectType); + return _idKeyMap.GetKeyForId(id, umbracoObjectType); } /// diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index e8fea4c0e1..d69297eb57 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -415,7 +415,6 @@ namespace Umbraco.Core.Services.Implement var xml = new XElement("macro"); xml.Add(new XElement("name", macro.Name)); xml.Add(new XElement("alias", macro.Alias)); - xml.Add(new XElement("macroType", macro.MacroType)); xml.Add(new XElement("macroSource", macro.MacroSource)); xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString())); xml.Add(new XElement("dontRender", macro.DontRender.ToString())); diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 25f2c619d2..2a7a743273 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -87,8 +87,8 @@ namespace Umbraco.Core.Services.Implement _stylesheetRepository.Save(stylesheet); saveEventArgs.CanCancel = false; scope.Events.Dispatch(SavedStylesheet, this, saveEventArgs); + Audit(AuditType.Save, userId, -1, "Stylesheet"); - Audit(AuditType.Save, userId, -1, UmbracoObjectTypes.Stylesheet.GetName()); scope.Complete(); } } @@ -115,8 +115,8 @@ namespace Umbraco.Core.Services.Implement _stylesheetRepository.Delete(stylesheet); deleteEventArgs.CanCancel = false; scope.Events.Dispatch(DeletedStylesheet, this, deleteEventArgs); + Audit(AuditType.Delete, userId, -1, "Stylesheet"); - Audit(AuditType.Delete, userId, -1, UmbracoObjectTypes.Stylesheet.GetName()); scope.Complete(); } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/IInstallationService.cs b/src/Umbraco.Infrastructure/Services/Implement/IInstallationService.cs new file mode 100644 index 0000000000..334088f8ae --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Implement/IInstallationService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IInstallationService + { + Task LogInstall(InstallLog installLog); + } +} diff --git a/src/Umbraco.Infrastructure/Services/Implement/Implement/InstallationService.cs b/src/Umbraco.Infrastructure/Services/Implement/Implement/InstallationService.cs new file mode 100644 index 0000000000..a1f74e0862 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Implement/Implement/InstallationService.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; + +namespace Umbraco.Core.Services.Implement +{ + public class InstallationService : IInstallationService + { + private readonly IInstallationRepository _installationRepository; + + public InstallationService(IInstallationRepository installationRepository) + { + _installationRepository = installationRepository; + } + + public async Task LogInstall(InstallLog installLog) + { + await _installationRepository.SaveInstallLogAsync(installLog); + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 27e428d1d6..14bebc4eb8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -1029,13 +1029,6 @@ namespace Umbraco.Core.Services.Implement _mediaRepository.Save(media); } - /// - /// Empties the Recycle Bin by deleting all that resides in the bin - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] - public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId); - /// /// Empties the Recycle Bin by deleting all that resides in the bin /// diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index e551017295..4876772c86 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -546,7 +546,7 @@ namespace Umbraco.Core.Services.Implement return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, null, Ordering.By("Name")); } } - + /// /// Finds a list of objects by a partial email string /// @@ -1114,7 +1114,7 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs after members have been exported. /// - internal static event TypedEventHandler Exported; + public static event TypedEventHandler Exported; #endregion diff --git a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs index c8b8e617c9..d6c30b24c6 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs @@ -28,16 +28,16 @@ namespace Umbraco.Core.Services.Implement private readonly ILocalizationService _localizationService; private readonly INotificationsRepository _notificationsRepository; private readonly IGlobalSettings _globalSettings; - private readonly IContentSection _contentSection; + private readonly IContentSettings _contentSettings; private readonly ILogger _logger; private readonly IIOHelper _ioHelper; public NotificationService(IScopeProvider provider, IUserService userService, IContentService contentService, ILocalizationService localizationService, - ILogger logger, IIOHelper ioHelper, INotificationsRepository notificationsRepository, IGlobalSettings globalSettings, IContentSection contentSection) + ILogger logger, IIOHelper ioHelper, INotificationsRepository notificationsRepository, IGlobalSettings globalSettings, IContentSettings contentSettings) { _notificationsRepository = notificationsRepository; _globalSettings = globalSettings; - _contentSection = contentSection; + _contentSettings = contentSettings; _uowProvider = provider ?? throw new ArgumentNullException(nameof(provider)); _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); @@ -302,7 +302,7 @@ namespace Umbraco.Core.Services.Implement if (content.ContentType.VariesByNothing()) { - if (!_contentSection.DisableHtmlEmail) + if (!_contentSettings.DisableHtmlEmail) { //create the HTML summary for invariant content @@ -344,7 +344,7 @@ namespace Umbraco.Core.Services.Implement { //it's variant, so detect what cultures have changed - if (!_contentSection.DisableHtmlEmail) + if (!_contentSettings.DisableHtmlEmail) { //Create the HTML based summary (ul of culture names) @@ -406,13 +406,13 @@ namespace Umbraco.Core.Services.Implement summary.ToString()); // create the mail message - var mail = new MailMessage(_contentSection.NotificationEmailAddress, mailingUser.Email); + var mail = new MailMessage(_contentSettings.NotificationEmailAddress, mailingUser.Email); // populate the message mail.Subject = createSubject((mailingUser, subjectVars)); - if (_contentSection.DisableHtmlEmail) + if (_contentSettings.DisableHtmlEmail) { mail.IsBodyHtml = false; mail.Body = createBody((user: mailingUser, body: bodyVars, false)); diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index 1d79cfb88b..f8982fc538 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -115,7 +115,6 @@ namespace Umbraco.Core.Services.Implement user = new User(_globalSettings) { - DefaultToLiveEditing = false, Email = email, Language = _globalSettings.DefaultUILanguage, Name = username, @@ -1180,7 +1179,7 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs before Save /// - internal static event TypedEventHandler> SavingUserGroup; + public static event TypedEventHandler> SavingUserGroup; /// /// Occurs after Save diff --git a/src/Umbraco.Web/Suspendable.cs b/src/Umbraco.Infrastructure/Suspendable.cs similarity index 87% rename from src/Umbraco.Web/Suspendable.cs rename to src/Umbraco.Infrastructure/Suspendable.cs index cfe4d28e8b..7287d1c364 100644 --- a/src/Umbraco.Web/Suspendable.cs +++ b/src/Umbraco.Infrastructure/Suspendable.cs @@ -1,8 +1,6 @@ -using System; -using Examine; -using Examine.Providers; +using Umbraco.Composing; +using Umbraco.Core.Cache; using Umbraco.Core.Logging; -using Umbraco.Core.Composing; using Umbraco.Examine; using Umbraco.Web.Cache; using Umbraco.Web.Search; @@ -36,7 +34,7 @@ namespace Umbraco.Web _suspended = true; } - public static void ResumeDocumentCache() + public static void ResumeDocumentCache(CacheRefresherCollection cacheRefresherCollection) { _suspended = false; @@ -45,7 +43,7 @@ namespace Umbraco.Web if (_tried == false) return; _tried = false; - var pageRefresher = Current.CacheRefreshers[ContentCacheRefresher.UniqueId]; + var pageRefresher = cacheRefresherCollection[ContentCacheRefresher.UniqueId]; pageRefresher.RefreshAll(); } } @@ -72,7 +70,7 @@ namespace Umbraco.Web _suspended = true; } - public static void ResumeIndexers(IndexRebuilder indexRebuilder, ILogger logger) + public static void ResumeIndexers(IndexRebuilder indexRebuilder, ILogger logger, BackgroundIndexRebuilder backgroundIndexRebuilder) { _suspended = false; @@ -81,8 +79,7 @@ namespace Umbraco.Web if (_tried == false) return; _tried = false; - // TODO: when resuming do we always want a full rebuild of all indexes? - ExamineComponent.RebuildIndexes(indexRebuilder, logger, false); + backgroundIndexRebuilder.RebuildIndexes(false); } } diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 948304e4e4..5a46a37d43 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Sync // but only processes instructions coming from remote servers, // thus ensuring that instructions run only once // - public class DatabaseServerMessenger : ServerMessengerBase + public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger { private readonly IRuntimeState _runtime; private readonly ManualResetEvent _syncIdle; @@ -126,10 +126,6 @@ namespace Umbraco.Core.Sync const int weight = 10; - //TODO Why do we have interface if we expect to be exact type!!!? - // if (!(_runtime is RuntimeState runtime)) - // throw new NotSupportedException($"Unsupported IRuntimeState implementation {_runtime.GetType().FullName}, expecting {typeof(RuntimeState).FullName}."); - var registered = _runtime.MainDom.Register( () => { diff --git a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs index 3b47cabbaf..b65254b181 100644 --- a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs @@ -4,7 +4,6 @@ using System.Linq; using Newtonsoft.Json; using Umbraco.Composing; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; namespace Umbraco.Core.Sync diff --git a/src/Umbraco.Web/TagQuery.cs b/src/Umbraco.Infrastructure/TagQuery.cs similarity index 100% rename from src/Umbraco.Web/TagQuery.cs rename to src/Umbraco.Infrastructure/TagQuery.cs diff --git a/src/Umbraco.Web/Trees/ISearchableTree.cs b/src/Umbraco.Infrastructure/Trees/ISearchableTree.cs similarity index 100% rename from src/Umbraco.Web/Trees/ISearchableTree.cs rename to src/Umbraco.Infrastructure/Trees/ISearchableTree.cs diff --git a/src/Umbraco.Web/Models/Trees/TreeNode.cs b/src/Umbraco.Infrastructure/Trees/TreeNode.cs similarity index 95% rename from src/Umbraco.Web/Models/Trees/TreeNode.cs rename to src/Umbraco.Infrastructure/Trees/TreeNode.cs index 52691bf081..7d3c657207 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNode.cs +++ b/src/Umbraco.Infrastructure/Trees/TreeNode.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; +using Umbraco.Composing; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Trees @@ -24,7 +23,7 @@ namespace Umbraco.Web.Models.Trees /// The parent id for the current node /// /// - internal TreeNode(string nodeId, string parentId, string getChildNodesUrl, string menuUrl) + public TreeNode(string nodeId, string parentId, string getChildNodesUrl, string menuUrl) { if (nodeId == null) throw new ArgumentNullException(nameof(nodeId)); if (string.IsNullOrWhiteSpace(nodeId)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(nodeId)); @@ -113,7 +112,7 @@ namespace Umbraco.Web.Models.Trees return Current.IOHelper.ResolveUrl("~" + Icon.TrimStart('~')); //legacy icon path - return string.Format("{0}images/umbraco/{1}", Current.Configs.Global().Path.EnsureEndsWith("/"), Icon); + return string.Format("{0}images/umbraco/{1}", Current.IOHelper.BackOfficePath.EnsureEndsWith("/"), Icon); } } diff --git a/src/Umbraco.Web/Models/Trees/TreeNodeCollection.cs b/src/Umbraco.Infrastructure/Trees/TreeNodeCollection.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/TreeNodeCollection.cs rename to src/Umbraco.Infrastructure/Trees/TreeNodeCollection.cs diff --git a/src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs b/src/Umbraco.Infrastructure/Trees/TreeNodeExtensions.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs rename to src/Umbraco.Infrastructure/Trees/TreeNodeExtensions.cs diff --git a/src/Umbraco.Web/Models/Trees/TreeRootNode.cs b/src/Umbraco.Infrastructure/Trees/TreeRootNode.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/TreeRootNode.cs rename to src/Umbraco.Infrastructure/Trees/TreeRootNode.cs diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 795ce65381..5e0088cc2e 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -2,16 +2,16 @@ netstandard2.0 - 7.3 + 8 - + + - + - - + @@ -27,12 +27,14 @@ + + <_UnmanagedRegistrationCache Remove="obj\Umbraco.Infrastructure.csproj.UnmanagedRegistration.cache" /> - + @@ -47,13 +49,6 @@ - - - - - - <_Parameter1>Umbraco.Core - <_Parameter1>Umbraco.Tests @@ -62,12 +57,12 @@ - - - - + + + + diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidator.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidator.cs index 1fdb64c62a..75affe09e7 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidator.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidator.cs @@ -1,4 +1,4 @@ -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.ModelsBuilder.Embedded.BackOffice diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs index 15ca2cca24..1e96e64df8 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using Umbraco.Core.Configuration; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; -using Umbraco.ModelsBuilder.Embedded.Configuration; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/DashboardReport.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/DashboardReport.cs index 25ddc838e8..6e22313474 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/DashboardReport.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/DashboardReport.cs @@ -1,5 +1,6 @@ using System.Text; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Configuration; +using Umbraco.Core.Configuration; namespace Umbraco.ModelsBuilder.Embedded.BackOffice { diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MediaTypeModelValidator.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MediaTypeModelValidator.cs index 9dc1ea6c20..fcd42908e7 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MediaTypeModelValidator.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MediaTypeModelValidator.cs @@ -1,4 +1,4 @@ -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.ModelsBuilder.Embedded.BackOffice diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MemberTypeModelValidator.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MemberTypeModelValidator.cs index 8d0a98eeab..2e249eed4d 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MemberTypeModelValidator.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/MemberTypeModelValidator.cs @@ -1,4 +1,4 @@ -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.ModelsBuilder.Embedded.BackOffice diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs index 1d9de265e9..17b694de56 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs @@ -3,9 +3,10 @@ using System.Net; using System.Net.Http; using System.Runtime.Serialization; using System.Web.Hosting; +using Umbraco.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Core.Exceptions; using Umbraco.ModelsBuilder.Embedded.Building; -using Umbraco.ModelsBuilder.Embedded.Configuration; using Umbraco.Web.Editors; using Umbraco.Web.WebApi.Filters; diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/Builder.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/Builder.cs index ffd56d4312..f64e5ed1ce 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/Builder.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/Builder.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core; +using Umbraco.Core.Configuration; namespace Umbraco.ModelsBuilder.Embedded.Building { @@ -18,6 +19,8 @@ namespace Umbraco.ModelsBuilder.Embedded.Building internal abstract class Builder { + + private readonly IList _typeModels; protected Dictionary ModelsMap { get; } = new Dictionary(); @@ -209,7 +212,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building // use configured else fallback to default return string.IsNullOrWhiteSpace(Config.ModelsNamespace) - ? ModelsBuilderConfig.DefaultModelsNamespace + ? Constants.ModelsBuilder.DefaultModelsNamespace : Config.ModelsNamespace; } diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/ModelsGenerator.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/ModelsGenerator.cs index 8a3bc5a5b5..648a2e76fa 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/ModelsGenerator.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/ModelsGenerator.cs @@ -1,6 +1,7 @@ using System.IO; using System.Text; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; namespace Umbraco.ModelsBuilder.Embedded.Building { @@ -9,20 +10,23 @@ namespace Umbraco.ModelsBuilder.Embedded.Building private readonly UmbracoServices _umbracoService; private readonly IModelsBuilderConfig _config; private readonly OutOfDateModelsStatus _outOfDateModels; + private readonly IIOHelper _ioHelper; - public ModelsGenerator(UmbracoServices umbracoService, IModelsBuilderConfig config, OutOfDateModelsStatus outOfDateModels) + public ModelsGenerator(UmbracoServices umbracoService, IModelsBuilderConfig config, OutOfDateModelsStatus outOfDateModels, IIOHelper ioHelper) { _umbracoService = umbracoService; _config = config; _outOfDateModels = outOfDateModels; + _ioHelper = ioHelper; } internal void GenerateModels() { - if (!Directory.Exists(_config.ModelsDirectory)) - Directory.CreateDirectory(_config.ModelsDirectory); + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); + if (!Directory.Exists(modelsDirectory)) + Directory.CreateDirectory(modelsDirectory); - foreach (var file in Directory.GetFiles(_config.ModelsDirectory, "*.generated.cs")) + foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) File.Delete(file); var typeModels = _umbracoService.GetAllTypes(); @@ -33,7 +37,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building { var sb = new StringBuilder(); builder.Generate(sb, typeModel); - var filename = Path.Combine(_config.ModelsDirectory, typeModel.ClrName + ".generated.cs"); + var filename = Path.Combine(modelsDirectory, typeModel.ClrName + ".generated.cs"); File.WriteAllText(filename, sb.ToString()); } diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs index d1190a0374..723ee10f35 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; namespace Umbraco.ModelsBuilder.Embedded.Building { @@ -94,7 +94,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building // private static void WriteGeneratedCodeAttribute(StringBuilder sb, string tabs) { - sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder\", \"{1}\")]\n", tabs, ApiVersion.Current.Version); + sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder.Embedded\", \"{1}\")]\n", tabs, ApiVersion.Current.Version); } private void WriteContentType(StringBuilder sb, TypeModel type) diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs index a93df97806..0ffad1c5bc 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs @@ -14,7 +14,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building sb.Append("// \n"); sb.Append("// This code was generated by a tool.\n"); sb.Append("//\n"); - sb.AppendFormat("// Umbraco.ModelsBuilder v{0}\n", ApiVersion.Current.Version); + sb.AppendFormat("// Umbraco.ModelsBuilder.Embedded v{0}\n", ApiVersion.Current.Version); sb.Append("//\n"); sb.Append("// Changes to this file will be lost if the code is regenerated.\n"); sb.Append("// \n"); diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs index d856cae1e7..769cca7317 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs @@ -4,13 +4,14 @@ using System.Reflection; using System.Web; using System.Web.Mvc; using System.Web.Routing; +using Umbraco.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.ModelsBuilder.Embedded.BackOffice; -using Umbraco.ModelsBuilder.Embedded.Configuration; using Umbraco.Web; using Umbraco.Web.JavaScript; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs index 126a6dbc40..e582301740 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs @@ -1,21 +1,17 @@ using System.Linq; using System.Reflection; +using Umbraco.Core.Configuration; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Composing; -using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.ModelsBuilder.Embedded.Building; -using Umbraco.ModelsBuilder.Embedded.Configuration; -using Umbraco.Web; -using Umbraco.Web.PublishedCache.NuCache; -using Umbraco.Web.Features; namespace Umbraco.ModelsBuilder.Embedded.Compose { - [ComposeBefore(typeof(NuCacheComposer))] + [ComposeBefore(typeof(IPublishedCacheComposer))] [RuntimeLevel(MinLevel = RuntimeLevel.Run)] public sealed class ModelsBuilderComposer : ICoreComposer { @@ -29,15 +25,13 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose return; } - composition.Components().Append(); composition.Register(Lifetime.Singleton); - composition.Configs.Add(() => new ModelsBuilderConfig(composition.IOHelper)); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); - + if (composition.Configs.ModelsBuilder().ModelsMode == ModelsMode.PureLive) ComposeForLiveModels(composition); else if (composition.Configs.ModelsBuilder().EnableFactory) diff --git a/src/Umbraco.ModelsBuilder.Embedded/ConfigsExtensions.cs b/src/Umbraco.ModelsBuilder.Embedded/ConfigsExtensions.cs index d634547a49..d625c754c5 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/ConfigsExtensions.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/ConfigsExtensions.cs @@ -1,5 +1,4 @@ using Umbraco.Core.Configuration; -using Umbraco.ModelsBuilder.Embedded.Configuration; namespace Umbraco.ModelsBuilder.Embedded { diff --git a/src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs b/src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs index 0359c49654..b7b2695a08 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs @@ -7,7 +7,7 @@ namespace Umbraco.ModelsBuilder.Embedded /// /// And therefore it should not be generated. [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public sealed class ImplementPropertyTypeAttribute : Attribute + public class ImplementPropertyTypeAttribute : Attribute { public ImplementPropertyTypeAttribute(string alias) { diff --git a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs index d4b4636563..61d39cd373 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs @@ -1,10 +1,10 @@ using System; using System.Threading; -using System.Web.Hosting; +using Umbraco.Core.Configuration; +using Umbraco.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.ModelsBuilder.Embedded.Building; -using Umbraco.ModelsBuilder.Embedded.Configuration; using Umbraco.Web.Cache; namespace Umbraco.ModelsBuilder.Embedded diff --git a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProviderModule.cs b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProviderModule.cs index 678ff241b0..a04723a05e 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProviderModule.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProviderModule.cs @@ -1,7 +1,7 @@ using System; using System.Web; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.ModelsBuilder.Embedded; // will install only if configuration says it needs to be installed diff --git a/src/Umbraco.ModelsBuilder.Embedded/ModelsGenerationError.cs b/src/Umbraco.ModelsBuilder.Embedded/ModelsGenerationError.cs index a692f633a5..f8f6e8c7bc 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/ModelsGenerationError.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/ModelsGenerationError.cs @@ -1,17 +1,20 @@ using System; using System.IO; using System.Text; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; namespace Umbraco.ModelsBuilder.Embedded { public sealed class ModelsGenerationError { private readonly IModelsBuilderConfig _config; + private readonly IIOHelper _ioHelper; - public ModelsGenerationError(IModelsBuilderConfig config) + public ModelsGenerationError(IModelsBuilderConfig config, IIOHelper ioHelper) { _config = config; + _ioHelper = ioHelper; } public void Clear() @@ -56,7 +59,7 @@ namespace Umbraco.ModelsBuilder.Embedded private string GetErrFile() { - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) return null; diff --git a/src/Umbraco.ModelsBuilder.Embedded/OutOfDateModelsStatus.cs b/src/Umbraco.ModelsBuilder.Embedded/OutOfDateModelsStatus.cs index 5425c31c77..b8105eeef2 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/OutOfDateModelsStatus.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/OutOfDateModelsStatus.cs @@ -1,5 +1,6 @@ using System.IO; -using Umbraco.ModelsBuilder.Embedded.Configuration; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Web.Cache; namespace Umbraco.ModelsBuilder.Embedded @@ -7,10 +8,12 @@ namespace Umbraco.ModelsBuilder.Embedded public sealed class OutOfDateModelsStatus { private readonly IModelsBuilderConfig _config; + private readonly IIOHelper _ioHelper; - public OutOfDateModelsStatus(IModelsBuilderConfig config) + public OutOfDateModelsStatus(IModelsBuilderConfig config, IIOHelper ioHelper) { _config = config; + _ioHelper = ioHelper; } internal void Install() @@ -25,7 +28,7 @@ namespace Umbraco.ModelsBuilder.Embedded private string GetFlagPath() { - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); return Path.Combine(modelsDirectory, "ood.flag"); diff --git a/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs b/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs index 68c149adde..5fa17d3c77 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs @@ -2,7 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("Umbraco.ModelsBuilder")] +[assembly: AssemblyTitle("Umbraco.ModelsBuilder.Embedded")] [assembly: AssemblyDescription("Umbraco ModelsBuilder")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyProduct("Umbraco CMS")] diff --git a/src/Umbraco.ModelsBuilder.Embedded/PublishedElementExtensions.cs b/src/Umbraco.ModelsBuilder.Embedded/PublishedElementExtensions.cs index 29429ba74f..8a0a688942 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/PublishedElementExtensions.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/PublishedElementExtensions.cs @@ -17,7 +17,7 @@ namespace Umbraco.Web /// /// Gets the value of a property. /// - public static TValue Value(this TModel model, Expression> property, string culture = null, string segment = null, Fallback fallback = default, TValue defaultValue = default) + public static TValue ValueFor(this TModel model, Expression> property, string culture = null, string segment = null, Fallback fallback = default, TValue defaultValue = default) where TModel : IPublishedElement { var alias = GetAlias(model, property); @@ -45,7 +45,7 @@ namespace Umbraco.Web var attribute = member.GetCustomAttribute(); if (attribute == null) throw new InvalidOperationException("Property is not marked with ImplementPropertyType attribute."); - + return attribute.Alias; } } diff --git a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs index 8e8a19c729..5db79de977 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs @@ -10,13 +10,14 @@ using System.Text.RegularExpressions; using System.Threading; using System.Web; using System.Web.Compilation; -using System.Web.Hosting; using System.Web.WebPages.Razor; +using Umbraco.Core.Configuration; using Umbraco.Core; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.ModelsBuilder.Embedded.Building; -using Umbraco.ModelsBuilder.Embedded.Configuration; using File = System.IO.File; namespace Umbraco.ModelsBuilder.Embedded @@ -41,29 +42,38 @@ namespace Umbraco.ModelsBuilder.Embedded private static readonly string[] OurFiles = { "models.hash", "models.generated.cs", "all.generated.cs", "all.dll.path", "models.err" }; private readonly IModelsBuilderConfig _config; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IIOHelper _ioHelper; private readonly ModelsGenerationError _errors; - public PureLiveModelFactory(Lazy umbracoServices, IProfilingLogger logger, IModelsBuilderConfig config) + public PureLiveModelFactory( + Lazy umbracoServices, + IProfilingLogger logger, + IModelsBuilderConfig config, + IHostingEnvironment hostingEnvironment, + IIOHelper ioHelper) { _umbracoServices = umbracoServices; _logger = logger; _config = config; - _errors = new ModelsGenerationError(config); + _hostingEnvironment = hostingEnvironment; + _ioHelper = ioHelper; + _errors = new ModelsGenerationError(config, ioHelper); _ver = 1; // zero is for when we had no version _skipver = -1; // nothing to skip RazorBuildProvider.CodeGenerationStarted += RazorBuildProvider_CodeGenerationStarted; - if (!HostingEnvironment.IsHosted) return; + if (!_hostingEnvironment.IsHosted) return; - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); // BEWARE! if the watcher is not properly released then for some reason the // BuildManager will start confusing types - using a 'registered object' here // though we should probably plug into Umbraco's MainDom - which is internal - HostingEnvironment.RegisterObject(this); + _hostingEnvironment.RegisterObject(this); _watcher = new FileSystemWatcher(modelsDirectory); _watcher.Changed += WatcherOnChanged; _watcher.EnableRaisingEvents = true; @@ -206,7 +216,7 @@ namespace Umbraco.ModelsBuilder.Embedded _hasModels = false; _pendingRebuild = true; - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); @@ -328,7 +338,7 @@ namespace Umbraco.ModelsBuilder.Embedded private Assembly GetModelsAssembly(bool forceRebuild) { - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); @@ -550,7 +560,7 @@ namespace Umbraco.ModelsBuilder.Embedded private string GenerateModelsCode(IList typeModels) { - var modelsDirectory = _config.ModelsDirectory; + var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); @@ -667,7 +677,7 @@ namespace Umbraco.ModelsBuilder.Embedded { _watcher.EnableRaisingEvents = false; _watcher.Dispose(); - HostingEnvironment.UnregisterObject(this); + _hostingEnvironment.UnregisterObject(this); } #endregion diff --git a/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj b/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj index f4a1780ad2..a03862326d 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj +++ b/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj @@ -12,7 +12,7 @@ v4.7.2 512 true - 7.3 + 8 true @@ -59,10 +59,6 @@ - - - - @@ -92,7 +88,7 @@ 2.10.0 - 1.0.0-beta2-19324-01 + 1.0.0 runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -101,12 +97,8 @@ - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - - {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + {29aa69d9-b597-4395-8d42-43b1263c240a} Umbraco.Core @@ -123,6 +115,5 @@ 5.2.7 - \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlCeBulkSqlInsertProvider.cs b/src/Umbraco.Persistance.SqlCe/SqlCeBulkSqlInsertProvider.cs similarity index 100% rename from src/Umbraco.Core/Persistence/SqlCeBulkSqlInsertProvider.cs rename to src/Umbraco.Persistance.SqlCe/SqlCeBulkSqlInsertProvider.cs diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs similarity index 99% rename from src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs rename to src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs index 2ed0fb878c..7abcc60c86 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/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.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj b/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj new file mode 100644 index 0000000000..3174d4a7e2 --- /dev/null +++ b/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj @@ -0,0 +1,28 @@ + + + + net472 + 8 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.PublishedCache.NuCache/CacheKeys.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs rename to src/Umbraco.PublishedCache.NuCache/CacheKeys.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs similarity index 96% rename from src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs rename to src/Umbraco.PublishedCache.NuCache/ContentCache.cs index 84edb9113c..dd5a76837e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs @@ -104,8 +104,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children(culture)).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]) - : GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]); + ? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]) + : GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -114,7 +114,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { - content = GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]); + content = GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); } return content; @@ -144,7 +144,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // or we reach the content root, collecting urls in the way var pathParts = new List(); var n = node; - var urlSegment = n.UrlSegment(culture); + var urlSegment = n.UrlSegment(_variationContextAccessor, culture); var hasDomains = _domainCache.HasAssigned(n.Id); while (hasDomains == false && n != null) // n is null at root { @@ -156,7 +156,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // move to parent node n = n.Parent; if (n != null) - urlSegment = n.UrlSegment(culture); + urlSegment = n.UrlSegment(_variationContextAccessor, culture); hasDomains = n != null && _domainCache.HasAssigned(n.Id); } @@ -184,9 +184,9 @@ namespace Umbraco.Web.PublishedCache.NuCache while (content != null && i < parts.Count) { var part = parts[i++]; - content = content.Children(culture).FirstOrDefault(x => + content = content.Children(_variationContextAccessor, culture).FirstOrDefault(x => { - var urlSegment = x.UrlSegment(culture); + var urlSegment = x.UrlSegment(_variationContextAccessor, culture); return urlSegment == part; }); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs similarity index 95% rename from src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs rename to src/Umbraco.PublishedCache.NuCache/ContentNode.cs index de750fb800..38ba78caae 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // represents a content "node" ie a pair of draft + published versions // internal, never exposed, to be accessed from ContentStore (only!) [DebuggerDisplay("Id: {Id}, Path: {Path}")] - internal class ContentNode + public class ContentNode { // special ctor for root pseudo node public ContentNode() @@ -112,6 +112,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // everything that is common to both draft and published versions // keep this as small as possible + + public readonly int Id; public readonly Guid Uid; public IPublishedContentType ContentType; @@ -119,10 +121,14 @@ namespace Umbraco.Web.PublishedCache.NuCache public readonly string Path; public readonly int SortOrder; public readonly int ParentContentId; + + // TODO: Can we make everything readonly?? This would make it easier to debug and be less error prone especially for new developers. + // Once a Node is created and exists in the cache it is readonly so we should be able to make that happen at the API level too. public int FirstChildContentId; public int LastChildContentId; public int NextSiblingContentId; public int PreviousSiblingContentId; + public readonly DateTime CreateDate; public readonly int CreatorId; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs similarity index 98% rename from src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs rename to src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs index 842ca19388..8e24afd620 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs @@ -4,7 +4,7 @@ using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Web.PublishedCache.NuCache { // what's needed to actually build a content node - internal struct ContentNodeKit + public struct ContentNodeKit { public ContentNode Node; public int ContentTypeId; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs similarity index 66% rename from src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs rename to src/Umbraco.PublishedCache.NuCache/ContentStore.cs index 19be13b762..2927bd6cc0 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -14,12 +14,24 @@ using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Web.PublishedCache.NuCache { - // stores content - internal class ContentStore + /// + /// Stores content in memory and persists it back to disk + /// + /// + /// + /// Methods in this class suffixed with the term "Locked" means that those methods can only be called within a WriteLock. A WriteLock + /// is acquired by the GetScopedWriteLock method. Locks are not allowed to be recursive. + /// + /// + /// This class's logic is based on the class but has been slightly modified to suit these purposes. + /// + /// + public class ContentStore { // this class is an extended version of SnapDictionary // most of the snapshots management code, etc is an exact copy // SnapDictionary has unit tests to ensure it all works correctly + // For locking information, see SnapDictionary private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IVariationContextAccessor _variationContextAccessor; @@ -39,7 +51,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private long _liveGen, _floorGen; private bool _nextGen, _collectAuto; private Task _collectTask; - private volatile int _wlocked; private List> _wchanges; // TODO: collection trigger (ok for now) @@ -82,15 +93,9 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly string _instanceId = Guid.NewGuid().ToString("N"); - private class ReadLockInfo - { - public bool Taken; - } - private class WriteLockInfo { public bool Taken; - public bool Count; } // a scope contextual that represents a locked writer to the dictionary @@ -121,22 +126,26 @@ namespace Umbraco.Web.PublishedCache.NuCache return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } + private void EnsureLocked() + { + if (!Monitor.IsEntered(_wlocko)) + throw new InvalidOperationException("Write lock must be acquried."); + } + private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { + if (Monitor.IsEntered(_wlocko)) + throw new InvalidOperationException("Recursive locks not allowed"); + Monitor.Enter(_wlocko, ref lockInfo.Taken); - var rtaken = false; - try + lock(_rlocko) { - Monitor.Enter(_rlocko, ref rtaken); - // see SnapDictionary try { } finally { - _wlocked++; - lockInfo.Count = true; - if (_nextGen == false || (forceGen && _wlocked == 1)) + if (_nextGen == false || (forceGen)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot @@ -147,62 +156,48 @@ namespace Umbraco.Web.PublishedCache.NuCache } } } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - } - - private void Lock(ReadLockInfo lockInfo) - { - Monitor.Enter(_rlocko, ref lockInfo.Taken); } private void Release(WriteLockInfo lockInfo, bool commit = true) { - if (commit == false) + try { - var rtaken = false; - try + if (commit == false) { - Monitor.Enter(_rlocko, ref rtaken); - try { } - finally + lock (_rlocko) { - _nextGen = false; - _liveGen -= 1; + // see SnapDictionary + try { } + finally + { + _nextGen = false; + _liveGen -= 1; + } } - } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - Rollback(_contentNodes); - RollbackRoot(); - Rollback(_contentTypesById); - Rollback(_contentTypesByAlias); + Rollback(_contentNodes); + RollbackRoot(); + Rollback(_contentTypesById); + Rollback(_contentTypesByAlias); + } + else if (_localDb != null && _wchanges != null) + { + foreach (var change in _wchanges) + { + if (change.Value.IsNull) + _localDb.TryRemove(change.Key, out ContentNodeKit unused); + else + _localDb[change.Key] = change.Value; + } + _wchanges = null; + _localDb.Commit(); + } } - else if (_localDb != null && _wchanges != null) + finally { - foreach (var change in _wchanges) - { - if (change.Value.IsNull) - _localDb.TryRemove(change.Key, out ContentNodeKit unused); - else - _localDb[change.Key] = change.Value; - } - _wchanges = null; - _localDb.Commit(); + if (lockInfo.Taken) + Monitor.Exit(_wlocko); } - - if (lockInfo.Count) _wlocked--; - if (lockInfo.Taken) Monitor.Exit(_wlocko); - } - - private void Release(ReadLockInfo lockInfo) - { - if (lockInfo.Taken) Monitor.Exit(_rlocko); } private void RollbackRoot() @@ -249,7 +244,11 @@ namespace Umbraco.Web.PublishedCache.NuCache { _localDb?.Dispose(); } - catch { /* TBD: May already be throwing so don't throw again */} + catch (Exception ex) + { + /* TBD: May already be throwing so don't throw again */ + _logger.Error(ex, "Error trying to release DB"); + } finally { _localDb = null; @@ -257,6 +256,11 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + catch (Exception ex) + { + _logger.Error(ex, "Error trying to lock"); + throw; + } finally { Release(lockInfo); @@ -273,87 +277,111 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public void NewContentTypes(IEnumerable types) + /// + /// Sets data for new content types + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public void NewContentTypesLocked(IEnumerable types) { - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); + EnsureLocked(); - foreach (var type in types) - { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); - } - } - finally + foreach (var type in types) { - Release(lockInfo); + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); } } - public void UpdateContentTypes(IEnumerable types) + /// + /// Sets data for updated content types + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public void UpdateContentTypesLocked(IEnumerable types) { //nothing to do if this is empty, no need to lock/allocate/iterate/etc... if (!types.Any()) return; - var lockInfo = new WriteLockInfo(); - try + EnsureLocked(); + + var index = types.ToDictionary(x => x.Id, x => x); + + foreach (var type in index.Values) { - Lock(lockInfo); + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + } - var index = types.ToDictionary(x => x.Id, x => x); - - foreach (var type in index.Values) - { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); - } - - foreach (var link in _contentNodes.Values) - { - var node = link.Value; - if (node == null) continue; - var contentTypeId = node.ContentType.Id; - if (index.TryGetValue(contentTypeId, out var contentType) == false) continue; + foreach (var link in _contentNodes.Values) + { + var node = link.Value; + if (node == null) continue; + var contentTypeId = node.ContentType.Id; + if (index.TryGetValue(contentTypeId, out var contentType) == false) continue; SetValueLocked(_contentNodes, node.Id, new ContentNode(node, _publishedModelFactory, contentType)); - } - } - finally - { - Release(lockInfo); } } - public void SetAllContentTypes(IEnumerable types) + /// + /// Updates/sets data for all content types + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public void SetAllContentTypesLocked(IEnumerable types) { - var lockInfo = new WriteLockInfo(); - try + EnsureLocked(); + + // clear all existing content types + ClearLocked(_contentTypesById); + ClearLocked(_contentTypesByAlias); + + // set all new content types + foreach (var type in types) { - Lock(lockInfo); - - // clear all existing content types - ClearLocked(_contentTypesById); - ClearLocked(_contentTypesByAlias); - - // set all new content types - foreach (var type in types) - { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); - } - - // beware! at that point the cache is inconsistent, - // assuming we are going to SetAll content items! - } - finally - { - Release(lockInfo); + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); } + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! } - public void UpdateContentTypes(IReadOnlyCollection removedIds, IReadOnlyCollection refreshedTypes, IReadOnlyCollection kits) + /// + /// Updates/sets/removes data for content types + /// + /// + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public void UpdateContentTypesLocked(IReadOnlyCollection removedIds, IReadOnlyCollection refreshedTypes, IReadOnlyCollection kits) { + EnsureLocked(); + var removedIdsA = removedIds ?? Array.Empty(); var refreshedTypesA = refreshedTypes ?? Array.Empty(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToList(); @@ -362,85 +390,84 @@ namespace Umbraco.Web.PublishedCache.NuCache if (kits.Count == 0 && refreshedIdsA.Count == 0 && removedIdsA.Count == 0) return; //exit - there is nothing to do here - var lockInfo = new WriteLockInfo(); - try + var removedContentTypeNodes = new List(); + var refreshedContentTypeNodes = new List(); + + // find all the nodes that are either refreshed or removed, + // because of their content type being either refreshed or removed + foreach (var link in _contentNodes.Values) { - Lock(lockInfo); - - var removedContentTypeNodes = new List(); - var refreshedContentTypeNodes = new List(); - - // find all the nodes that are either refreshed or removed, - // because of their content type being either refreshed or removed - foreach (var link in _contentNodes.Values) - { - var node = link.Value; - if (node == null) continue; - var contentTypeId = node.ContentType.Id; - if (removedIdsA.Contains(contentTypeId)) removedContentTypeNodes.Add(node.Id); - if (refreshedIdsA.Contains(contentTypeId)) refreshedContentTypeNodes.Add(node.Id); - } - - // perform deletion of content with removed content type - // removing content types should have removed their content already - // but just to be 100% sure, clear again here - foreach (var node in removedContentTypeNodes) - ClearBranchLocked(node); - - // perform deletion of removed content types - foreach (var id in removedIdsA) - { - if (_contentTypesById.TryGetValue(id, out var link) == false || link.Value == null) - continue; - SetValueLocked(_contentTypesById, id, null); - SetValueLocked(_contentTypesByAlias, link.Value.Alias, null); - } - - // perform update of refreshed content types - foreach (var type in refreshedTypesA) - { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); - } - - // perform update of content with refreshed content type - from the kits - // skip missing type, skip missing parents & un-buildable kits - what else could we do? - // kits are ordered by level, so ParentExists is ok here - var visited = new List(); - foreach (var kit in kits.Where(x => - refreshedIdsA.Contains(x.ContentTypeId) && - BuildKit(x, out _))) - { - // replacing the node: must preserve the parents - var node = GetHead(_contentNodes, kit.Node.Id)?.Value; - if (node != null) - kit.Node.FirstChildContentId = node.FirstChildContentId; - - SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - - visited.Add(kit.Node.Id); - if (_localDb != null) RegisterChange(kit.Node.Id, kit); - } - - // all content should have been refreshed - but... - var orphans = refreshedContentTypeNodes.Except(visited); - foreach (var id in orphans) - ClearBranchLocked(id); + var node = link.Value; + if (node == null) continue; + var contentTypeId = node.ContentType.Id; + if (removedIdsA.Contains(contentTypeId)) removedContentTypeNodes.Add(node.Id); + if (refreshedIdsA.Contains(contentTypeId)) refreshedContentTypeNodes.Add(node.Id); } - finally + + // perform deletion of content with removed content type + // removing content types should have removed their content already + // but just to be 100% sure, clear again here + foreach (var node in removedContentTypeNodes) + ClearBranchLocked(node); + + // perform deletion of removed content types + foreach (var id in removedIdsA) { - Release(lockInfo); + if (_contentTypesById.TryGetValue(id, out var link) == false || link.Value == null) + continue; + SetValueLocked(_contentTypesById, id, null); + SetValueLocked(_contentTypesByAlias, link.Value.Alias, null); } + + // perform update of refreshed content types + foreach (var type in refreshedTypesA) + { + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + } + + // perform update of content with refreshed content type - from the kits + // skip missing type, skip missing parents & un-buildable kits - what else could we do? + // kits are ordered by level, so ParentExists is ok here + var visited = new List(); + foreach (var kit in kits.Where(x => + refreshedIdsA.Contains(x.ContentTypeId) && + BuildKit(x, out _))) + { + // replacing the node: must preserve the parents + var node = GetHead(_contentNodes, kit.Node.Id)?.Value; + if (node != null) + kit.Node.FirstChildContentId = node.FirstChildContentId; + + SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); + + visited.Add(kit.Node.Id); + if (_localDb != null) RegisterChange(kit.Node.Id, kit); + } + + // all content should have been refreshed - but... + var orphans = refreshedContentTypeNodes.Except(visited); + foreach (var id in orphans) + ClearBranchLocked(id); } - public void UpdateDataTypes(IEnumerable dataTypeIds, Func getContentType) + /// + /// Updates data types + /// + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public void UpdateDataTypesLocked(IEnumerable dataTypeIds, Func getContentType) { - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); + EnsureLocked(); - var contentTypes = _contentTypesById + var contentTypes = _contentTypesById .Where(kvp => kvp.Value.Value != null && kvp.Value.Value.PropertyTypes.Any(p => dataTypeIds.Contains(p.DataType.Id))) @@ -449,37 +476,32 @@ namespace Umbraco.Web.PublishedCache.NuCache .Where(x => x != null) // poof, gone, very unlikely and probably an anomaly .ToArray(); - var contentTypeIdsA = contentTypes.Select(x => x.Id).ToArray(); - var contentTypeNodes = new Dictionary>(); - foreach (var id in contentTypeIdsA) - contentTypeNodes[id] = new List(); - foreach (var link in _contentNodes.Values) - { - var node = link.Value; - if (node != null && contentTypeIdsA.Contains(node.ContentType.Id)) - contentTypeNodes[node.ContentType.Id].Add(node.Id); - } - - foreach (var contentType in contentTypes) - { - // again, weird situation - if (contentTypeNodes.ContainsKey(contentType.Id) == false) - continue; - - foreach (var id in contentTypeNodes[contentType.Id]) - { - _contentNodes.TryGetValue(id, out var link); - if (link?.Value == null) - continue; - var node = new ContentNode(link.Value, _publishedModelFactory, contentType); - SetValueLocked(_contentNodes, id, node); - if (_localDb != null) RegisterChange(id, node.ToKit()); - } - } - } - finally + var contentTypeIdsA = contentTypes.Select(x => x.Id).ToArray(); + var contentTypeNodes = new Dictionary>(); + foreach (var id in contentTypeIdsA) + contentTypeNodes[id] = new List(); + foreach (var link in _contentNodes.Values) { - Release(lockInfo); + var node = link.Value; + if (node != null && contentTypeIdsA.Contains(node.ContentType.Id)) + contentTypeNodes[node.ContentType.Id].Add(node.Id); + } + + foreach (var contentType in contentTypes) + { + // again, weird situation + if (contentTypeNodes.ContainsKey(contentType.Id) == false) + continue; + + foreach (var id in contentTypeNodes[contentType.Id]) + { + _contentNodes.TryGetValue(id, out var link); + if (link?.Value == null) + continue; + var node = new ContentNode(link.Value, _publishedModelFactory, contentType); + SetValueLocked(_contentNodes, id, node); + if (_localDb != null) RegisterChange(id, node.ToKit()); + } } } @@ -537,8 +559,22 @@ namespace Umbraco.Web.PublishedCache.NuCache return link; } - public bool Set(ContentNodeKit kit) + /// + /// Sets the data for a + /// + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public bool SetLocked(ContentNodeKit kit) { + EnsureLocked(); + // ReSharper disable LocalizableElement if (kit.IsEmpty) throw new ArgumentException("Kit is empty.", nameof(kit)); @@ -548,20 +584,15 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Set content ID: {KitNodeId}", kit.Node.Id); - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); + // get existing + _contentNodes.TryGetValue(kit.Node.Id, out var link); + var existing = link?.Value; - // get existing - _contentNodes.TryGetValue(kit.Node.Id, out var link); - var existing = link?.Value; + if (!BuildKit(kit, out var parent)) + return false; - if (!BuildKit(kit, out var parent)) - return false; - - // moving? - var moving = existing != null && existing.ParentContentId != kit.Node.ParentContentId; + // moving? + var moving = existing != null && existing.ParentContentId != kit.Node.ParentContentId; // manage children if (existing != null) @@ -570,42 +601,37 @@ namespace Umbraco.Web.PublishedCache.NuCache kit.Node.LastChildContentId = existing.LastChildContentId; } - // set - SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) RegisterChange(kit.Node.Id, kit); + // set + SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); + if (_localDb != null) RegisterChange(kit.Node.Id, kit); - // manage the tree - if (existing == null) - { - // new, add to parent - AddTreeNodeLocked(kit.Node, parent); - } - else if (moving || existing.SortOrder != kit.Node.SortOrder) - { - // moved, remove existing from its parent, add content to its parent - RemoveTreeNodeLocked(existing); - AddTreeNodeLocked(kit.Node); - } - else - { - // replacing existing, handle siblings - kit.Node.NextSiblingContentId = existing.NextSiblingContentId; - kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; - } - - _xmap[kit.Node.Uid] = kit.Node.Id; - } - finally + // manage the tree + if (existing == null) { - Release(lockInfo); + // new, add to parent + AddTreeNodeLocked(kit.Node, parent); } + else if (moving || existing.SortOrder != kit.Node.SortOrder) + { + // moved, remove existing from its parent, add content to its parent + RemoveTreeNodeLocked(existing); + AddTreeNodeLocked(kit.Node); + } + else + { + // replacing existing, handle siblings + kit.Node.NextSiblingContentId = existing.NextSiblingContentId; + kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; + } + + _xmap[kit.Node.Uid] = kit.Node.Id; return true; } private void ClearRootLocked() { - if (_root.Gen < _liveGen) + if (_root.Gen != _liveGen) _root = new LinkedNode(new ContentNode(), _liveGen, _root); else _root.Value.FirstChildContentId = -1; @@ -620,195 +646,216 @@ namespace Umbraco.Web.PublishedCache.NuCache /// True if the data is coming from the database (not the local cache db) /// /// + /// /// This requires that the collection is sorted by Level + ParentId + Sort Order. /// This should be used only on a site startup as the first generations. /// This CANNOT be used after startup since it bypasses all checks for Generations. + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// /// - internal bool SetAllFastSorted(IEnumerable kits, bool fromDb) + /// + /// Thrown if this method is not called within a write lock + /// + public bool SetAllFastSortedLocked(IEnumerable kits, bool fromDb) { - var lockInfo = new WriteLockInfo(); + EnsureLocked(); + var ok = true; - try + + ClearLocked(_contentNodes); + ClearRootLocked(); + + // The name of the game here is to populate each kit's + // FirstChildContentId + // LastChildContentId + // NextSiblingContentId + // PreviousSiblingContentId + + ContentNode previousNode = null; + ContentNode parent = null; + + foreach (var kit in kits) { - Lock(lockInfo); - - ClearLocked(_contentNodes); - ClearRootLocked(); - - // The name of the game here is to populate each kit's - // FirstChildContentId - // LastChildContentId - // NextSiblingContentId - // PreviousSiblingContentId - - ContentNode previousNode = null; - ContentNode parent = null; - - foreach (var kit in kits) + if (!BuildKit(kit, out var parentLink)) { - if (!BuildKit(kit, out var parentLink)) - { - ok = false; - continue; // skip that one - } - - var thisNode = kit.Node; - - if (parent == null) - { - // first parent - parent = parentLink.Value; - parent.FirstChildContentId = thisNode.Id; // this node is the first node - } - else if (parent.Id != parentLink.Value.Id) - { - // new parent - parent = parentLink.Value; - parent.FirstChildContentId = thisNode.Id; // this node is the first node - previousNode = null; // there is no previous sibling - } - - _logger.Debug($"Set {thisNode.Id} with parent {thisNode.ParentContentId}"); - SetValueLocked(_contentNodes, thisNode.Id, thisNode); - - // if we are initializing from the database source ensure the local db is updated - if (fromDb && _localDb != null) RegisterChange(thisNode.Id, kit); - - // this node is always the last child - parent.LastChildContentId = thisNode.Id; - - // wire previous node as previous sibling - if (previousNode != null) - { - previousNode.NextSiblingContentId = thisNode.Id; - thisNode.PreviousSiblingContentId = previousNode.Id; - } - - // this node becomes the previous node - previousNode = thisNode; - - _xmap[kit.Node.Uid] = kit.Node.Id; + ok = false; + continue; // skip that one } - } - finally - { - Release(lockInfo); + + var thisNode = kit.Node; + + if (parent == null) + { + // first parent + parent = parentLink.Value; + parent.FirstChildContentId = thisNode.Id; // this node is the first node + } + else if (parent.Id != parentLink.Value.Id) + { + // new parent + parent = parentLink.Value; + parent.FirstChildContentId = thisNode.Id; // this node is the first node + previousNode = null; // there is no previous sibling + } + + _logger.Debug($"Set {thisNode.Id} with parent {thisNode.ParentContentId}"); + SetValueLocked(_contentNodes, thisNode.Id, thisNode); + + // if we are initializing from the database source ensure the local db is updated + if (fromDb && _localDb != null) RegisterChange(thisNode.Id, kit); + + // this node is always the last child + parent.LastChildContentId = thisNode.Id; + + // wire previous node as previous sibling + if (previousNode != null) + { + previousNode.NextSiblingContentId = thisNode.Id; + thisNode.PreviousSiblingContentId = previousNode.Id; + } + + // this node becomes the previous node + previousNode = thisNode; + + _xmap[kit.Node.Uid] = kit.Node.Id; } return ok; } - public bool SetAll(IEnumerable kits) + /// + /// Set all data for a collection of + /// + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public bool SetAllLocked(IEnumerable kits) { - var lockInfo = new WriteLockInfo(); + EnsureLocked(); + var ok = true; - try + + ClearLocked(_contentNodes); + ClearRootLocked(); + + // do NOT clear types else they are gone! + //ClearLocked(_contentTypesById); + //ClearLocked(_contentTypesByAlias); + + foreach (var kit in kits) { - Lock(lockInfo); - - ClearLocked(_contentNodes); - ClearRootLocked(); - - // do NOT clear types else they are gone! - //ClearLocked(_contentTypesById); - //ClearLocked(_contentTypesByAlias); - - foreach (var kit in kits) + if (!BuildKit(kit, out var parent)) { - if (!BuildKit(kit, out var parent)) - { - ok = false; - continue; // skip that one - } - _logger.Debug($"Set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); - SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - - if (_localDb != null) RegisterChange(kit.Node.Id, kit); - AddTreeNodeLocked(kit.Node, parent); - - _xmap[kit.Node.Uid] = kit.Node.Id; + ok = false; + continue; // skip that one } - } - finally - { - Release(lockInfo); + _logger.Debug($"Set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); + SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); + + if (_localDb != null) RegisterChange(kit.Node.Id, kit); + AddTreeNodeLocked(kit.Node, parent); + + _xmap[kit.Node.Uid] = kit.Node.Id; } return ok; } - // IMPORTANT kits must be sorted out by LEVEL and by SORT ORDER - public bool SetBranch(int rootContentId, IEnumerable kits) + /// + /// Sets data for a branch of + /// + /// + /// + /// + /// + /// + /// IMPORTANT kits must be sorted out by LEVEL and by SORT ORDER + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// + /// Thrown if this method is not called within a write lock + /// + public bool SetBranchLocked(int rootContentId, IEnumerable kits) { - var lockInfo = new WriteLockInfo(); + EnsureLocked(); + var ok = true; - try + + // get existing + _contentNodes.TryGetValue(rootContentId, out var link); + var existing = link?.Value; + + // clear + if (existing != null) { - Lock(lockInfo); - - // get existing - _contentNodes.TryGetValue(rootContentId, out var link); - var existing = link?.Value; - - // clear - if (existing != null) - { - //this zero's out the branch (recursively), if we're in a new gen this will add a NULL placeholder for the gen - ClearBranchLocked(existing); - //TODO: This removes the current GEN from the tree - do we really want to do that? - RemoveTreeNodeLocked(existing); - } - - // now add them all back - foreach (var kit in kits) - { - if (!BuildKit(kit, out var parent)) - { - ok = false; - continue; // skip that one - } - SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) RegisterChange(kit.Node.Id, kit); - AddTreeNodeLocked(kit.Node, parent); - - _xmap[kit.Node.Uid] = kit.Node.Id; - } + //this zero's out the branch (recursively), if we're in a new gen this will add a NULL placeholder for the gen + ClearBranchLocked(existing); + //TODO: This removes the current GEN from the tree - do we really want to do that? + RemoveTreeNodeLocked(existing); } - finally + + // now add them all back + foreach (var kit in kits) { - Release(lockInfo); + if (!BuildKit(kit, out var parent)) + { + ok = false; + continue; // skip that one + } + SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); + if (_localDb != null) RegisterChange(kit.Node.Id, kit); + AddTreeNodeLocked(kit.Node, parent); + + _xmap[kit.Node.Uid] = kit.Node.Id; } return ok; } - public bool Clear(int id) + /// + /// Clears data for a given node id + /// + /// + /// + /// + /// This methods MUST be called from within a write lock, normally wrapped within GetScopedWriteLock + /// otherwise an exception will occur. + /// + /// + /// Thrown if this method is not called within a write lock + /// + public bool ClearLocked(int id) { - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); + EnsureLocked(); - // try to find the content - // if it is not there, nothing to do - _contentNodes.TryGetValue(id, out var link); // else null - if (link?.Value == null) return false; + // try to find the content + // if it is not there, nothing to do + _contentNodes.TryGetValue(id, out var link); // else null + if (link?.Value == null) return false; - var content = link.Value; - _logger.Debug("Clear content ID: {ContentId}", content.Id); + var content = link.Value; + _logger.Debug("Clear content ID: {ContentId}", content.Id); - // clear the entire branch - ClearBranchLocked(content); + // clear the entire branch + ClearBranchLocked(content); - // manage the tree - RemoveTreeNodeLocked(content); + // manage the tree + RemoveTreeNodeLocked(content); - return true; - } - finally - { - Release(lockInfo); - } + return true; } private void ClearBranchLocked(int id) @@ -902,8 +949,18 @@ namespace Umbraco.Web.PublishedCache.NuCache return link; } + /// + /// This removes this current node from the tree hiearchy by removing it from it's parent's linked list + /// + /// + /// + /// This is called within a lock which means a new Gen is being created therefore this will not modify any existing content in a Gen. + /// private void RemoveTreeNodeLocked(ContentNode content) { + // NOTE: DO NOT modify `content` here, this would modify data for an existing Gen, all modifications are done to clones + // which would be targeting the new Gen. + var parentLink = content.ParentContentId < 0 ? _root : GetRequiredLinkedNode(content.ParentContentId, "parent", null); @@ -940,9 +997,6 @@ namespace Umbraco.Web.PublishedCache.NuCache var prev = GenCloneLocked(prevLink); prev.NextSiblingContentId = content.NextSiblingContentId; } - - content.NextSiblingContentId = -1; - content.PreviousSiblingContentId = -1; } private bool ParentPublishedLocked(ContentNodeKit kit) @@ -958,7 +1012,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var node = link.Value; - if (node != null && link.Gen < _liveGen) + if (node != null && link.Gen != _liveGen) { node = new ContentNode(link.Value, _publishedModelFactory); if (link == _root) @@ -1112,7 +1166,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // this is safe only because we're write-locked foreach (var kvp in dict.Where(x => x.Value != null)) { - if (kvp.Value.Gen < _liveGen) + if (kvp.Value.Gen != _liveGen) { var link = new LinkedNode(null, _liveGen, kvp.Value); dict.TryUpdate(kvp.Key, link, kvp.Value); @@ -1200,11 +1254,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public Snapshot CreateSnapshot() { - var lockInfo = new ReadLockInfo(); - try + lock(_rlocko) { - Lock(lockInfo); - // if no next generation is required, and we already have one, // use it and create a new snapshot if (_nextGen == false && _genObj != null) @@ -1217,7 +1268,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // else we need to try to create a new gen ref // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe - if (_wlocked > 0) // volatile, cannot ++ but could -- + if (Monitor.IsEntered(_wlocko)) { // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; @@ -1256,10 +1307,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return snapshot; } - finally - { - Release(lockInfo); - } } public Snapshot LiveSnapshot => new Snapshot(this, _liveGen @@ -1282,7 +1329,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return _collectTask; // ReSharper disable InconsistentlySynchronizedField - var task = _collectTask = Task.Run(Collect); + var task = _collectTask = Task.Run((Action)Collect); _collectTask.ContinueWith(_ => { lock (_rlocko) @@ -1377,16 +1424,17 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public async Task WaitForPendingCollect() - { - Task task; - lock (_rlocko) - { - task = _collectTask; - } - if (task != null) - await task; - } + // TODO: This is never used? Should it be? Maybe move to TestHelper below? + //public async Task WaitForPendingCollect() + //{ + // Task task; + // lock (_rlocko) + // { + // task = _collectTask; + // } + // if (task != null) + // await task; + //} public long GenCount => _genObjs.Count; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs similarity index 90% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs index 910c0ca737..ad7cc72eec 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs @@ -1,12 +1,13 @@ using System.Configuration; using CSharpTest.Net.Collections; using CSharpTest.Net.Serialization; +using Umbraco.Core.Configuration; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { internal class BTree { - public static BPlusTree GetTree(string filepath, bool exists) + public static BPlusTree GetTree(string filepath, bool exists, INuCacheSettings settings) { var keySerializer = new PrimitiveSerializer(); var valueSerializer = new ContentNodeKitSerializer(); @@ -19,7 +20,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource CachePolicy = CachePolicy.None, // default is 4096, min 2^9 = 512, max 2^16 = 64K - FileBlockSize = GetBlockSize(), + FileBlockSize = GetBlockSize(settings), // other options? }; @@ -32,11 +33,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return tree; } - private static int GetBlockSize() + private static int GetBlockSize(INuCacheSettings settings) { var blockSize = 4096; - var appSetting = ConfigurationManager.AppSettings["Umbraco.Web.PublishedCache.NuCache.BTree.BlockSize"]; + var appSetting = settings.BTreeBlockSize; if (appSetting == null) return blockSize; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs similarity index 96% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs index 36586acd12..42e7e340cd 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { // represents everything that is specific to edited or published version - internal class ContentData + public class ContentData { public string Name { get; set; } public string UrlSegment { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentNestedData.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/ContentNestedData.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs similarity index 93% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs index a98a96f424..2521d42744 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// Represents the culture variation information on a content item /// - internal class CultureVariation + public class CultureVariation { [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/DatabaseDataSource.cs similarity index 96% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/DatabaseDataSource.cs index 4515e235ad..fcab4bb5df 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/DatabaseDataSource.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Scoping; using Umbraco.Core.Serialization; -using Umbraco.Web.Composing; using static Umbraco.Core.Persistence.SqlExtensionsStatics; namespace Umbraco.Web.PublishedCache.NuCache.DataSource @@ -20,6 +19,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // provides efficient database access for NuCache internal class DatabaseDataSource : IDataSource { + private readonly ILogger _logger; + + public DatabaseDataSource(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + // we want arrays, we want them all loaded, not an enumerable private Sql ContentSourcesSelect(IScope scope, Func, Sql> joins = null) @@ -181,7 +187,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return scope.Database.Query(sql).Select(CreateMediaNodeKit); } - private static ContentNodeKit CreateContentNodeKit(ContentSourceDto dto) + private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto) { ContentData d = null; ContentData p = null; @@ -192,7 +198,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { if (Debugger.IsAttached) throw new Exception("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding."); - Current.Logger.Warn("Missing cmsContentNu edited content for node {NodeId}, consider rebuilding.", dto.Id); + _logger.Warn("Missing cmsContentNu edited content for node {NodeId}, consider rebuilding.", dto.Id); } else { @@ -219,7 +225,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { if (Debugger.IsAttached) throw new Exception("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding."); - Current.Logger.Warn("Missing cmsContentNu published content for node {NodeId}, consider rebuilding.", dto.Id); + _logger.Warn("Missing cmsContentNu published content for node {NodeId}, consider rebuilding.", dto.Id); } else { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/IDataSource.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/IDataSource.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs similarity index 95% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs index e14426a2e2..9ab9401d71 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { - internal class PropertyData + public class PropertyData { private string _culture; private string _segment; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs rename to src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs b/src/Umbraco.PublishedCache.NuCache/DomainCache.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs rename to src/Umbraco.PublishedCache.NuCache/DomainCache.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.PublishedCache.NuCache/MediaCache.cs similarity index 98% rename from src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs rename to src/Umbraco.PublishedCache.NuCache/MediaCache.cs index 182086ed7f..376e1d2e48 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/MediaCache.cs @@ -11,7 +11,7 @@ using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable + public class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IVariationContextAccessor _variationContextAccessor; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.PublishedCache.NuCache/MemberCache.cs similarity index 99% rename from src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs rename to src/Umbraco.PublishedCache.NuCache/MemberCache.cs index 9e2d1f4582..68d82731d8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/MemberCache.cs @@ -6,11 +6,9 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; -using Umbraco.Web.Security; namespace Umbraco.Web.PublishedCache.NuCache { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/INavigableData.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/INavigableData.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigablePropertyType.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigablePropertyType.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/RootContent.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/RootContent.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/Source.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Navigable/Source.cs rename to src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.PublishedCache.NuCache/NuCacheComponent.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs rename to src/Umbraco.PublishedCache.NuCache/NuCacheComponent.cs diff --git a/src/Umbraco.PublishedCache.NuCache/NuCacheComposer.cs b/src/Umbraco.PublishedCache.NuCache/NuCacheComposer.cs new file mode 100644 index 0000000000..d3b1777163 --- /dev/null +++ b/src/Umbraco.PublishedCache.NuCache/NuCacheComposer.cs @@ -0,0 +1,44 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Infrastructure.PublishedCache; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Web.PublishedCache.NuCache +{ + public class NuCacheComposer : ComponentComposer, IPublishedCacheComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + // register the NuCache database data source + composition.Register(); + + // register the NuCache published snapshot service + // must register default options, required in the service ctor + composition.Register(factory => new PublishedSnapshotServiceOptions()); + composition.SetPublishedSnapshotService(); + + // replace this service since we want to improve the content/media + // mapping lookups if we are using nucache. + composition.RegisterUnique(factory => + { + var idkSvc = new IdKeyMap(factory.GetInstance()); + var publishedSnapshotService = factory.GetInstance() as PublishedSnapshotService; + if (publishedSnapshotService != null) + { + idkSvc.SetMapper(UmbracoObjectTypes.Document, id => publishedSnapshotService.GetDocumentUid(id), uid => publishedSnapshotService.GetDocumentId(uid)); + idkSvc.SetMapper(UmbracoObjectTypes.Media, id => publishedSnapshotService.GetMediaUid(id), uid => publishedSnapshotService.GetMediaId(uid)); + } + return idkSvc; + }); + + // add the NuCache health check (hidden from type finder) + // TODO: no NuCache health check yet + //composition.HealthChecks().Add(); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Property.cs rename to src/Umbraco.PublishedCache.NuCache/Property.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs similarity index 90% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs rename to src/Umbraco.PublishedCache.NuCache/PublishedContent.cs index 02cf88e74c..f60e2e1e99 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs @@ -4,9 +4,8 @@ using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Exceptions; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; +using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -26,7 +25,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - IPublishedModelFactory publishedModelFactory) + IPublishedModelFactory publishedModelFactory) : base(variationContextAccessor) { _contentNode = contentNode ?? throw new ArgumentNullException(nameof(contentNode)); ContentData = contentData ?? throw new ArgumentNullException(nameof(contentData)); @@ -48,30 +47,12 @@ namespace Umbraco.Web.PublishedCache.NuCache PropertiesArray = properties.ToArray(); } - private string GetProfileNameById(int id) - { - var cache = GetCurrentSnapshotCache(); - return cache == null - ? GetProfileNameByIdNoCache(id) - : (string)cache.Get(CacheKeys.ProfileName(id), () => GetProfileNameByIdNoCache(id)); - } - - private static string GetProfileNameByIdNoCache(int id) - { -#if DEBUG - var userService = Current.Services?.UserService; - if (userService == null) return "[null]"; // for tests -#else - // we don't want each published content to hold a reference to the service - // so where should they get the service from really? from the locator... - var userService = Current.Services.UserService; -#endif - var user = userService.GetProfileById(id); - return user?.Name; - } - // used when cloning in ContentNode - public PublishedContent(ContentNode contentNode, PublishedContent origin) + public PublishedContent( + ContentNode contentNode, + PublishedContent origin, + IVariationContextAccessor variationContextAccessor) + : base(variationContextAccessor) { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; @@ -88,7 +69,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone for previewing as draft a published content that is published and has no draft - private PublishedContent(PublishedContent origin) + private PublishedContent(PublishedContent origin) : base(origin.VariationContextAccessor) { _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; VariationContextAccessor = origin.VariationContextAccessor; @@ -166,18 +147,12 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override int CreatorId => _contentNode.CreatorId; - /// - public override string CreatorName => GetProfileNameById(_contentNode.CreatorId); - /// public override DateTime CreateDate => _contentNode.CreateDate; /// public override int WriterId => ContentData.WriterId; - /// - public override string WriterName => GetProfileNameById(ContentData.WriterId); - /// public override DateTime UpdateDate => ContentData.VersionDate; @@ -330,7 +305,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // beware what you use that one for - you don't want to cache its result private IAppCache GetAppropriateCache() { - var publishedSnapshot = (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; var cache = publishedSnapshot == null ? null : ((IsPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (ContentType.ItemType != PublishedItemType.Member) @@ -341,7 +316,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private IAppCache GetCurrentSnapshotCache() { - var publishedSnapshot = (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; return publishedSnapshot?.SnapshotCache; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs rename to src/Umbraco.PublishedCache.NuCache/PublishedMember.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs rename to src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs old mode 100755 new mode 100644 similarity index 93% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs rename to src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 5daa6de684..74295b7182 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Threading; using CSharpTest.Net.Collections; using Newtonsoft.Json; using Umbraco.Core; @@ -10,6 +11,8 @@ using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; +using Umbraco.Core.Install; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -49,6 +52,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ITypeFinder _typeFinder; private readonly IHostingEnvironment _hostingEnvironment; private readonly IShortStringHelper _shortStringHelper; + private readonly IIOHelper _ioHelper; + private readonly INuCacheSettings _config; // volatile because we read it with no lock private volatile bool _isReady; @@ -57,6 +62,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ContentStore _mediaStore; private readonly SnapDictionary _domainStore; private readonly object _storesLock = new object(); + private readonly object _elementsLock = new object(); private BPlusTree _localContentDb; private BPlusTree _localMediaDb; @@ -74,7 +80,7 @@ namespace Umbraco.Web.PublishedCache.NuCache //private static int _singletonCheck; public PublishedSnapshotService(PublishedSnapshotServiceOptions options, IMainDom mainDom, IRuntimeState runtime, - ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, + ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IProfilingLogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, @@ -84,7 +90,9 @@ namespace Umbraco.Web.PublishedCache.NuCache UrlSegmentProviderCollection urlSegmentProviders, ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, - IShortStringHelper shortStringHelper) + IShortStringHelper shortStringHelper, + IIOHelper ioHelper, + INuCacheSettings config) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -104,6 +112,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _typeFinder = typeFinder; _hostingEnvironment = hostingEnvironment; _shortStringHelper = shortStringHelper; + _ioHelper = ioHelper; + _config = config; // we need an Xml serializer here so that the member cache can support XPath, // for members this is done by navigating the serialized-to-xml member @@ -138,33 +148,38 @@ namespace Umbraco.Web.PublishedCache.NuCache // figure out whether it can read the databases or it should populate them from sql _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory, _localContentDb); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, publishedModelFactory, _localContentDb); _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory, _localMediaDb); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, publishedModelFactory, _localMediaDb); } else { _logger.Info("Creating the content store (local db ignored)"); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, publishedModelFactory); _logger.Info("Creating the media store (local db ignored)"); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, publishedModelFactory); } _domainStore = new SnapDictionary(); LoadCachesOnStartup(); } - - Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default; - int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? default; - - if (idkMap != null) - { - idkMap.SetMapper(UmbracoObjectTypes.Document, id => GetUid(_contentStore, id), uid => GetId(_contentStore, uid)); - idkMap.SetMapper(UmbracoObjectTypes.Media, id => GetUid(_mediaStore, id), uid => GetId(_mediaStore, uid)); - } } + #region Id <-> Key methods + + // NOTE: These aren't used within this object but are made available internally to improve the IdKey lookup performance + // when nucache is enabled. + + internal int GetDocumentId(Guid udi) => GetId(_contentStore, udi); + internal int GetMediaId(Guid udi) => GetId(_mediaStore, udi); + internal Guid GetDocumentUid(int id) => GetUid(_contentStore, id); + internal Guid GetMediaUid(int id) => GetUid(_mediaStore, id); + private int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? 0; + private Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? Guid.Empty; + + #endregion + /// /// Install phase of /// @@ -185,8 +200,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _localMediaDbExists = File.Exists(localMediaDbPath); // if both local databases exist then GetTree will open them, else new databases will be created - _localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists); - _localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists); + _localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists, _config); + _localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists, _config); _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists); } @@ -199,10 +214,15 @@ namespace Umbraco.Web.PublishedCache.NuCache /// private void MainDomRelease() { + _logger.Debug("Releasing from MainDom..."); + lock (_storesLock) { + _logger.Debug("Releasing content store..."); _contentStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned _localContentDb = null; + + _logger.Debug("Releasing media store..."); _mediaStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned _localMediaDb = null; @@ -347,7 +367,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override bool EnsureEnvironment(out IEnumerable errors) { // must have app_data and be able to write files into it - var ok = FilePermissionHelper.TryCreateDirectory(GetLocalFilesPath()); + var ok = _ioHelper.TryCreateDirectory(GetLocalFilesPath()); errors = ok ? Enumerable.Empty() : new[] { "NuCache local files." }; return ok; } @@ -386,7 +406,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var contentTypes = _serviceContext.ContentTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _contentStore.SetAllContentTypes(contentTypes); + _contentStore.SetAllContentTypesLocked(contentTypes); using (_logger.TraceDuration("Loading content from database")) { @@ -397,7 +417,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // IMPORTANT GetAllContentSources sorts kits by level + parentId + sortOrder var kits = _dataSource.GetAllContentSources(scope); - return onStartup ? _contentStore.SetAllFastSorted(kits, true) : _contentStore.SetAll(kits); + return onStartup ? _contentStore.SetAllFastSortedLocked(kits, true) : _contentStore.SetAllLocked(kits); } } @@ -405,7 +425,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var contentTypes = _serviceContext.ContentTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _contentStore.SetAllContentTypes(contentTypes); + _contentStore.SetAllContentTypesLocked(contentTypes); using (_logger.TraceDuration("Loading content from local cache file")) { @@ -457,7 +477,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var mediaTypes = _serviceContext.MediaTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _mediaStore.SetAllContentTypes(mediaTypes); + _mediaStore.SetAllContentTypesLocked(mediaTypes); using (_logger.TraceDuration("Loading media from database")) { @@ -469,7 +489,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Loading media from database..."); // IMPORTANT GetAllMediaSources sorts kits by level + parentId + sortOrder var kits = _dataSource.GetAllMediaSources(scope); - return onStartup ? _mediaStore.SetAllFastSorted(kits, true) : _mediaStore.SetAll(kits); + return onStartup ? _mediaStore.SetAllFastSortedLocked(kits, true) : _mediaStore.SetAllLocked(kits); } } @@ -477,7 +497,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var mediaTypes = _serviceContext.MediaTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _mediaStore.SetAllContentTypes(mediaTypes); + _mediaStore.SetAllContentTypesLocked(mediaTypes); using (_logger.TraceDuration("Loading media from local cache file")) { @@ -518,7 +538,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return false; } - return onStartup ? store.SetAllFastSorted(kits, false) : store.SetAll(kits); + return onStartup ? store.SetAllFastSortedLocked(kits, false) : store.SetAllLocked(kits); } // keep these around - might be useful @@ -624,7 +644,7 @@ namespace Umbraco.Web.PublishedCache.NuCache .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard))) { - _domainStore.Set(domain.Id, domain); + _domainStore.SetLocked(domain.Id, domain); } } @@ -672,10 +692,12 @@ namespace Umbraco.Web.PublishedCache.NuCache publishedChanged = publishedChanged2; } + if (draftChanged || publishedChanged) ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); } + // Calling this method means we have a lock on the contentStore (i.e. GetScopedWriteLock) private void NotifyLocked(IEnumerable payloads, out bool draftChanged, out bool publishedChanged) { publishedChanged = false; @@ -685,7 +707,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // content (and content types) are read-locked while reading content // contentStore is wlocked (so readable, only no new views) // and it can be wlocked by 1 thread only at a time - // contentStore is write-locked during changes + // contentStore is write-locked during changes - see note above, calls to this method are wrapped in contentStore.GetScopedWriteLock foreach (var payload in payloads) { @@ -705,7 +727,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { - if (_contentStore.Clear(payload.Id)) + if (_contentStore.ClearLocked(payload.Id)) draftChanged = publishedChanged = true; continue; } @@ -728,7 +750,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // ?? should we do some RV check here? // IMPORTANT GetbranchContentSources sorts kits by level and by sort order var kits = _dataSource.GetBranchContentSources(scope, capture.Id); - _contentStore.SetBranch(capture.Id, kits); + _contentStore.SetBranchLocked(capture.Id, kits); } else { @@ -736,11 +758,11 @@ namespace Umbraco.Web.PublishedCache.NuCache var kit = _dataSource.GetContentSource(scope, capture.Id); if (kit.IsEmpty) { - _contentStore.Clear(capture.Id); + _contentStore.ClearLocked(capture.Id); } else { - _contentStore.Set(kit); + _contentStore.SetLocked(kit); } } @@ -798,7 +820,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { - if (_mediaStore.Clear(payload.Id)) + if (_mediaStore.ClearLocked(payload.Id)) anythingChanged = true; continue; } @@ -821,7 +843,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // ?? should we do some RV check here? // IMPORTANT GetbranchContentSources sorts kits by level and by sort order var kits = _dataSource.GetBranchMediaSources(scope, capture.Id); - _mediaStore.SetBranch(capture.Id, kits); + _mediaStore.SetBranchLocked(capture.Id, kits); } else { @@ -829,11 +851,11 @@ namespace Umbraco.Web.PublishedCache.NuCache var kit = _dataSource.GetMediaSource(scope, capture.Id); if (kit.IsEmpty) { - _mediaStore.Clear(capture.Id); + _mediaStore.ClearLocked(capture.Id); } else { - _mediaStore.Set(kit); + _mediaStore.SetLocked(kit); } } @@ -935,14 +957,14 @@ namespace Umbraco.Web.PublishedCache.NuCache using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.ContentTree); - _contentStore.UpdateDataTypes(idsA, id => CreateContentType(PublishedItemType.Content, id)); + _contentStore.UpdateDataTypesLocked(idsA, id => CreateContentType(PublishedItemType.Content, id)); scope.Complete(); } using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.MediaTree); - _mediaStore.UpdateDataTypes(idsA, id => CreateContentType(PublishedItemType.Media, id)); + _mediaStore.UpdateDataTypesLocked(idsA, id => CreateContentType(PublishedItemType.Media, id)); scope.Complete(); } } @@ -972,7 +994,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } break; case DomainChangeTypes.Remove: - _domainStore.Clear(payload.Id); + _domainStore.ClearLocked(payload.Id); break; case DomainChangeTypes.Refresh: var domain = _serviceContext.DomainService.GetById(payload.Id); @@ -980,7 +1002,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (domain.RootContentId.HasValue == false) continue; // anomaly if (domain.LanguageIsoCode.IsNullOrWhiteSpace()) continue; // anomaly var culture = CultureInfo.GetCultureInfo(domain.LanguageIsoCode); - _domainStore.Set(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard)); + _domainStore.SetLocked(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard)); break; } } @@ -1065,11 +1087,11 @@ namespace Umbraco.Web.PublishedCache.NuCache ? Array.Empty() : _dataSource.GetTypeContentSources(scope, refreshedIds).ToArray(); - _contentStore.UpdateContentTypes(removedIds, typesA, kits); + _contentStore.UpdateContentTypesLocked(removedIds, typesA, kits); if (!otherIds.IsCollectionEmpty()) - _contentStore.UpdateContentTypes(CreateContentTypes(PublishedItemType.Content, otherIds.ToArray())); + _contentStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Content, otherIds.ToArray())); if (!newIds.IsCollectionEmpty()) - _contentStore.NewContentTypes(CreateContentTypes(PublishedItemType.Content, newIds.ToArray())); + _contentStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Content, newIds.ToArray())); scope.Complete(); } } @@ -1096,11 +1118,11 @@ namespace Umbraco.Web.PublishedCache.NuCache ? Array.Empty() : _dataSource.GetTypeMediaSources(scope, refreshedIds).ToArray(); - _mediaStore.UpdateContentTypes(removedIds, typesA, kits); + _mediaStore.UpdateContentTypesLocked(removedIds, typesA, kits); if (!otherIds.IsCollectionEmpty()) - _mediaStore.UpdateContentTypes(CreateContentTypes(PublishedItemType.Media, otherIds.ToArray()).ToArray()); + _mediaStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Media, otherIds.ToArray()).ToArray()); if (!newIds.IsCollectionEmpty()) - _mediaStore.NewContentTypes(CreateContentTypes(PublishedItemType.Media, newIds.ToArray()).ToArray()); + _mediaStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Media, newIds.ToArray()).ToArray()); scope.Complete(); } } @@ -1140,7 +1162,13 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentStore.Snapshot contentSnap, mediaSnap; SnapDictionary.Snapshot domainSnap; IAppCache elementsCache; - lock (_storesLock) + + // Here we are reading/writing to shared objects so we need to lock (can't be _storesLock which manages the actual nucache files + // and would result in a deadlock). Even though we are locking around underlying readlocks (within CreateSnapshot) it's because + // we need to ensure that the result of contentSnap.Gen (etc) and the re-assignment of these values and _elements cache + // are done atomically. + + lock (_elementsLock) { var scopeContext = _scopeProvider.Context; @@ -1165,12 +1193,14 @@ namespace Umbraco.Web.PublishedCache.NuCache // elements // just need to make sure nothing gets elements in another enlisted action... so using // a MaxValue to make sure this one runs last, and it should be ok + scopeContext.Enlist("Umbraco.Web.PublishedCache.NuCache.PublishedSnapshotService.Resync", () => this, (completed, svc) => { ((PublishedSnapshot)svc.CurrentPublishedSnapshot)?.Resync(); }, int.MaxValue); } + // create a new snapshot cache if snapshots are different gens if (contentSnap.Gen != _contentGen || mediaSnap.Gen != _mediaGen || domainSnap.Gen != _domainGen || _elementsCache == null) { @@ -1282,7 +1312,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) @@ -1337,7 +1367,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { //culture changed on an existing language var cultureChanged = e.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode))); - if(cultureChanged) + if (cultureChanged) { RebuildContentDbCache(); } @@ -1740,7 +1770,7 @@ AND cmsContentNu.nodeId IS NULL #region Instrument - public string GetStatus() + public override string GetStatus() { var dbCacheIsOk = VerifyContentDbCache() && VerifyMediaDbCache() @@ -1763,7 +1793,7 @@ AND cmsContentNu.nodeId IS NULL " and " + ms + " snapshot" + (ms > 1 ? "s" : "") + "."; } - public void Collect() + public override void Collect() { var contentCollect = _contentStore.CollectAsync(); var mediaCollect = _mediaStore.CollectAsync(); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotServiceOptions.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceOptions.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotServiceOptions.cs rename to src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceOptions.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs b/src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs rename to src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs b/src/Umbraco.PublishedCache.NuCache/Snap/GenRef.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs rename to src/Umbraco.PublishedCache.NuCache/Snap/GenRef.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs b/src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs similarity index 100% rename from src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs rename to src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs similarity index 81% rename from src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs rename to src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs index 9671949ff0..c38940da25 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs @@ -22,6 +22,11 @@ namespace Umbraco.Web.PublishedCache.NuCache // This class is optimized for many readers, few writers // Readers are lock-free + // NOTE - we used to lock _rlocko the long hand way with Monitor.Enter(_rlocko, ref lockTaken) but this has + // been replaced with a normal c# lock because that's exactly how the normal c# lock works, + // see https://blogs.msdn.microsoft.com/ericlippert/2009/03/06/locks-and-exceptions-do-not-mix/ + // for the readlock, there's no reason here to use the long hand way. + private readonly ConcurrentDictionary> _items; private readonly ConcurrentQueue _genObjs; private GenObj _genObj; @@ -30,7 +35,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private long _liveGen, _floorGen; private bool _nextGen, _collectAuto; private Task _collectTask; - private volatile int _wlocked; // minGenDelta to be adjusted // we may want to throttle collects even if delta is reached @@ -71,20 +75,14 @@ namespace Umbraco.Web.PublishedCache.NuCache // are all ignored - Release is private and meant to be invoked with 'commit' being false only // only on the outermost lock (by SnapDictionaryWriter) - // using (...) {} for locking is prone to nasty leaks in case of weird exceptions - // such as thread-abort or out-of-memory, but let's not worry about it now + // side note - using (...) {} for locking is prone to nasty leaks in case of weird exceptions + // such as thread-abort or out-of-memory, which is why we've moved away from the old using wrapper we had on locking. private readonly string _instanceId = Guid.NewGuid().ToString("N"); - private class ReadLockInfo - { - public bool Taken; - } - private class WriteLockInfo { public bool Taken; - public bool Count; } // a scope contextual that represents a locked writer to the dictionary @@ -92,8 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); private readonly SnapDictionary _dictionary; - private int _released; - + public ScopedWriteLock(SnapDictionary dictionary, bool scoped) { _dictionary = dictionary; @@ -102,8 +99,6 @@ namespace Umbraco.Web.PublishedCache.NuCache public override void Release(bool completed) { - if (Interlocked.CompareExchange(ref _released, 1, 0) != 0) - return; _dictionary.Release(_lockinfo, completed); } } @@ -117,28 +112,31 @@ namespace Umbraco.Web.PublishedCache.NuCache return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } + private void EnsureLocked() + { + if (!Monitor.IsEntered(_wlocko)) + throw new InvalidOperationException("Write lock must be acquried."); + } + private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { + if (Monitor.IsEntered(_wlocko)) + throw new InvalidOperationException("Recursive locks not allowed"); + Monitor.Enter(_wlocko, ref lockInfo.Taken); - var rtaken = false; - try + lock(_rlocko) { - Monitor.Enter(_rlocko, ref rtaken); - // assume everything in finally runs atomically // http://stackoverflow.com/questions/18501678/can-this-unexpected-behavior-of-prepareconstrainedregions-and-thread-abort-be-ex // http://joeduffyblog.com/2005/03/18/atomicity-and-asynchronous-exception-failures/ // http://joeduffyblog.com/2007/02/07/introducing-the-new-readerwriterlockslim-in-orcas/ // http://chabster.blogspot.fr/2013/12/readerwriterlockslim-fails-on-dual.html //RuntimeHelpers.PrepareConstrainedRegions(); - try { } finally + try { } + finally { - // increment the lock count, and register that this lock is counting - _wlocked++; - lockInfo.Count = true; - - if (_nextGen == false || (forceGen && _wlocked == 1)) + if (_nextGen == false || (forceGen)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot @@ -149,15 +147,6 @@ namespace Umbraco.Web.PublishedCache.NuCache } } } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - } - - private void Lock(ReadLockInfo lockInfo) - { - Monitor.Enter(_rlocko, ref lockInfo.Taken); } private void Release(WriteLockInfo lockInfo, bool commit = true) @@ -168,22 +157,17 @@ namespace Umbraco.Web.PublishedCache.NuCache if (commit == false) { - var rtaken = false; - try + lock(_rlocko) { - Monitor.Enter(_rlocko, ref rtaken); - try { } finally + try { } + finally { // forget about the temp. liveGen _nextGen = false; _liveGen -= 1; } } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - + foreach (var item in _items) { var link = item.Value; @@ -197,16 +181,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - // decrement the lock count, if counting, then exit the lock - if (lockInfo.Count) _wlocked--; + // TODO: Shouldn't this be in a finally block? Monitor.Exit(_wlocko); } - private void Release(ReadLockInfo lockInfo) - { - if (lockInfo.Taken) Monitor.Exit(_rlocko); - } - #endregion #region Set, Clear, Get, Has @@ -219,75 +197,59 @@ namespace Umbraco.Web.PublishedCache.NuCache return link; } - public void Set(TKey key, TValue value) + public void SetLocked(TKey key, TValue value) { - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); + EnsureLocked(); - // this is safe only because we're write-locked - var link = GetHead(key); - if (link != null) + // this is safe only because we're write-locked + var link = GetHead(key); + if (link != null) + { + // already in the dict + if (link.Gen != _liveGen) { - // already in the dict - if (link.Gen != _liveGen) - { - // for an older gen - if value is different then insert a new - // link for the new gen, with the new value - if (link.Value != value) - _items.TryUpdate(key, new LinkedNode(value, _liveGen, link), link); - } - else - { - // for the live gen - we can fix the live gen - and remove it - // if value is null and there's no next gen - if (value == null && link.Next == null) - _items.TryRemove(key, out link); - else - link.Value = value; - } + // for an older gen - if value is different then insert a new + // link for the new gen, with the new value + if (link.Value != value) + _items.TryUpdate(key, new LinkedNode(value, _liveGen, link), link); } else { - _items.TryAdd(key, new LinkedNode(value, _liveGen)); - } - } - finally - { - Release(lockInfo); - } - } - - public void Clear(TKey key) - { - Set(key, null); - } - - public void Clear() - { - var lockInfo = new WriteLockInfo(); - try - { - Lock(lockInfo); - - // this is safe only because we're write-locked - foreach (var kvp in _items.Where(x => x.Value != null)) - { - if (kvp.Value.Gen < _liveGen) - { - var link = new LinkedNode(null, _liveGen, kvp.Value); - _items.TryUpdate(kvp.Key, link, kvp.Value); - } + // for the live gen - we can fix the live gen - and remove it + // if value is null and there's no next gen + if (value == null && link.Next == null) + _items.TryRemove(key, out link); else - { - kvp.Value.Value = null; - } + link.Value = value; } } - finally + else { - Release(lockInfo); + _items.TryAdd(key, new LinkedNode(value, _liveGen)); + } + } + + public void ClearLocked(TKey key) + { + SetLocked(key, null); + } + + public void ClearLocked() + { + EnsureLocked(); + + // this is safe only because we're write-locked + foreach (var kvp in _items.Where(x => x.Value != null)) + { + if (kvp.Value.Gen < _liveGen) + { + var link = new LinkedNode(null, _liveGen, kvp.Value); + _items.TryUpdate(kvp.Key, link, kvp.Value); + } + else + { + kvp.Value.Value = null; + } } } @@ -347,11 +309,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public Snapshot CreateSnapshot() { - var lockInfo = new ReadLockInfo(); - try + lock(_rlocko) { - Lock(lockInfo); - // if no next generation is required, and we already have a gen object, // use it to create a new snapshot if (_nextGen == false && _genObj != null) @@ -360,7 +319,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // else we need to try to create a new gen object // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe - if (_wlocked > 0) // volatile, cannot ++ but could -- + if (Monitor.IsEntered(_wlocko)) { // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; @@ -398,10 +357,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return snapshot; } - finally - { - Release(lockInfo); - } } public Task CollectAsync() @@ -496,17 +451,18 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public /*async*/ Task PendingCollect() - { - Task task; - lock (_rlocko) - { - task = _collectTask; - } - return task ?? Task.CompletedTask; - //if (task != null) - // await task; - } + // TODO: This is never used? Should it be? Maybe move to TestHelper below? + //public /*async*/ Task PendingCollect() + //{ + // Task task; + // lock (_rlocko) + // { + // task = _collectTask; + // } + // return task ?? Task.CompletedTask; + // //if (task != null) + // // await task; + //} public long GenCount => _genObjs.Count; @@ -531,7 +487,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public long LiveGen => _dict._liveGen; public long FloorGen => _dict._floorGen; public bool NextGen => _dict._nextGen; - public int WLocked => _dict._wlocked; + public bool IsLocked => Monitor.IsEntered(_dict._wlocko); public bool CollectAuto { diff --git a/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj similarity index 51% rename from src/Umbraco.Abstractions/Umbraco.Abstractions.csproj rename to src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index a4d379d2bf..75eeca268b 100644 --- a/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -2,18 +2,18 @@ netstandard2.0 - 7.3 - Umbraco.Core - 9.0.0 - 9.0.0 - 9.0.0 - Umbraco CMS + Umbraco.Infrastructure.PublishedCache + 8 - - - + + + + + + + @@ -24,5 +24,5 @@ <_Parameter1>Umbraco.Tests.Benchmarks - + diff --git a/src/Umbraco.Web/PublishedCache/NuCache/notes.txt b/src/Umbraco.PublishedCache.NuCache/readme.md similarity index 70% rename from src/Umbraco.Web/PublishedCache/NuCache/notes.txt rename to src/Umbraco.PublishedCache.NuCache/readme.md index ff2d8dd48b..c8e8a363e9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/notes.txt +++ b/src/Umbraco.PublishedCache.NuCache/readme.md @@ -1,10 +1,6 @@ NuCache Documentation ====================== -NOTE - RENAMING -Facade = PublishedSnapshot -and everything needs to be renamed accordingly - HOW IT WORKS ------------- @@ -22,12 +18,12 @@ When reading the cache, we read views up the chain until we find a value (which null) for the given id, and finally we read the store itself. -The FacadeService manages a ContentStore for content, and another for media. -When a Facade is created, the FacadeService gets ContentView objects from the stores. +The PublishedSnapshotService manages a ContentStore for content, and another for media. +When a PublishedSnapshot is created, the PublishedSnapshotService gets ContentView objects from the stores. Views provide an immutable snapshot of the content and media. -When the FacadeService is notified of changes, it notifies the stores. -Then it resync the current Facade, so that it requires new views, etc. +When the PublishedSnapshotService is notified of changes, it notifies the stores. +Then it resync the current PublishedSnapshot, so that it requires new views, etc. Whenever a content, media or member is modified or removed, the cmsContentNu table is updated with a json dictionary of alias => property value, so that a content, @@ -50,32 +46,32 @@ Each ContentStore has a _freezeLock object used to protect the 'Frozen' state of the store. It's a disposable object that releases the lock when disposed, so usage would be: using (store.Frozen) { ... }. -The FacadeService has a _storesLock object used to guarantee atomic access to the +The PublishedSnapshotService has a _storesLock object used to guarantee atomic access to the set of content, media stores. CACHE ------ -For each set of views, the FacadeService creates a SnapshotCache. So a SnapshotCache +For each set of views, the PublishedSnapshotService creates a SnapshotCache. So a SnapshotCache is valid until anything changes in the content or media trees. In other words, things that go in the SnapshotCache stay until a content or media is modified. -For each Facade, the FacadeService creates a FacadeCache. So a FacadeCache is valid -for the duration of the Facade (usually, the request). In other words, things that go -in the FacadeCache stay (and are visible to) for the duration of the request only. +For each PublishedSnapshot, the PublishedSnapshotService creates a PublishedSnapshotCache. So a PublishedSnapshotCache is valid +for the duration of the PublishedSnapshot (usually, the request). In other words, things that go +in the PublishedSnapshotCache stay (and are visible to) for the duration of the request only. -The FacadeService defines a static constant FullCacheWhenPreviewing, that defines +The PublishedSnapshotService defines a static constant FullCacheWhenPreviewing, that defines how caches operate when previewing: - when true, the caches in preview mode work normally. -- when false, everything that would go to the SnapshotCache goes to the FacadeCache. +- when false, everything that would go to the SnapshotCache goes to the PublishedSnapshotCache. At the moment it is true in the code, which means that eg converted values for previewed content will go in the SnapshotCache. Makes for faster preview, but uses more memory on the long term... would need some benchmarking to figure out what is best. -Members only live for the duration of the Facade. So, for members SnapshotCache is -never used, and everything goes to the FacadeCache. +Members only live for the duration of the PublishedSnapshot. So, for members SnapshotCache is +never used, and everything goes to the PublishedSnapshotCache. All cache keys are computed in the CacheKeys static class. @@ -85,15 +81,15 @@ TESTS For testing purposes the following mechanisms exist: -The Facade type has a static Current property that is used to obtain the 'current' -facade in many places, going through the PublishedCachesServiceResolver to get the -current service, and asking the current service for the current facade, which by +The PublishedSnapshot type has a static Current property that is used to obtain the 'current' +PublishedSnapshot in many places, going through the PublishedCachesServiceResolver to get the +current service, and asking the current service for the current PublishedSnapshot, which by default relies on UmbracoContext. For test purposes, it is possible to override the -entire mechanism by defining Facade.GetCurrentFacadeFunc which should return a facade. +entire mechanism by defining PublishedSnapshot.GetCurrentPublishedSnapshotFunc which should return a PublishedSnapshot. A PublishedContent keeps only id-references to its parent and children, and needs a way to retrieve the actual objects from the cache - which depends on the current -facade. It is possible to override the entire mechanism by defining PublishedContent. +PublishedSnapshot. It is possible to override the entire mechanism by defining PublishedContent. GetContentByIdFunc or .GetMediaByIdFunc. Setting these functions must be done before Resolution is frozen. @@ -110,7 +106,7 @@ possible to support detached contents & properties, even those that do not have int id, but again this should be refactored entirely anyway. Not doing any row-version checks (see XmlStore) when reloading from database, though it -is maintained in the database. Two FIXME in FacadeService. Should we do it? +is maintained in the database. Two FIXME in PublishedSnapshotService. Should we do it? There is no on-disk cache at all so everything is reloaded from the cmsContentNu table when the site restarts. This is pretty fast, but we should experiment with solutions to @@ -121,4 +117,4 @@ PublishedMember exposes properties that IPublishedContent does not, and that are to be lost soon as the member is wrapped (content set, model...) - so we probably need some sort of IPublishedMember. -/ \ No newline at end of file +/ diff --git a/src/Umbraco.TestData/Properties/AssemblyInfo.cs b/src/Umbraco.TestData/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3c4251cdf6 --- /dev/null +++ b/src/Umbraco.TestData/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Umbraco.TestData")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Umbraco.TestData")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fb5676ed-7a69-492c-b802-e7b24144c0fc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Umbraco.TestData/SegmentTestController.cs b/src/Umbraco.TestData/SegmentTestController.cs new file mode 100644 index 0000000000..650820760e --- /dev/null +++ b/src/Umbraco.TestData/SegmentTestController.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Mvc; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.TestData +{ + public class SegmentTestController : SurfaceController + { + + public ActionResult EnableDocTypeSegments(string alias, string propertyTypeAlias) + { + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return HttpNotFound(); + + var ct = Services.ContentTypeService.Get(alias); + if (ct == null) + return Content($"No document type found by alias {alias}"); + + var propType = ct.PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias); + if (propType == null) + return Content($"The document type {alias} does not have a property type {propertyTypeAlias ?? "null"}"); + + if (ct.Variations.VariesBySegment()) + return Content($"The document type {alias} already allows segments, nothing has been changed"); + + ct.SetVariesBy(ContentVariation.Segment); + propType.SetVariesBy(ContentVariation.Segment); + + Services.ContentTypeService.Save(ct); + return Content($"The document type {alias} and property type {propertyTypeAlias} now allows segments"); + } + + public ActionResult DisableDocTypeSegments(string alias) + { + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return HttpNotFound(); + + var ct = Services.ContentTypeService.Get(alias); + if (ct == null) + return Content($"No document type found by alias {alias}"); + + if (!ct.VariesBySegment()) + return Content($"The document type {alias} does not allow segments, nothing has been changed"); + + ct.SetVariesBy(ContentVariation.Segment, false); + + Services.ContentTypeService.Save(ct); + return Content($"The document type {alias} no longer allows segments"); + } + + public ActionResult AddSegmentData(int contentId, string propertyAlias, string value, string segment, string culture = null) + { + var content = Services.ContentService.GetById(contentId); + if (content == null) + return Content($"No content found by id {contentId}"); + + if (propertyAlias.IsNullOrWhiteSpace() || !content.HasProperty(propertyAlias)) + return Content($"The content by id {contentId} does not contain a property with alias {propertyAlias ?? "null"}"); + + if (content.ContentType.VariesByCulture() && culture.IsNullOrWhiteSpace()) + return Content($"The content by id {contentId} varies by culture but no culture was specified"); + + if (value.IsNullOrWhiteSpace()) + return Content("'value' cannot be null"); + + if (segment.IsNullOrWhiteSpace()) + return Content("'segment' cannot be null"); + + content.SetValue(propertyAlias, value, culture, segment); + Services.ContentService.Save(content); + + return Content($"Segment value has been set on content {contentId} for property {propertyAlias}"); + } + } +} diff --git a/src/Umbraco.TestData/Umbraco.TestData.csproj b/src/Umbraco.TestData/Umbraco.TestData.csproj new file mode 100644 index 0000000000..963b598f26 --- /dev/null +++ b/src/Umbraco.TestData/Umbraco.TestData.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {FB5676ED-7A69-492C-B802-E7B24144C0FC} + Library + Properties + Umbraco.TestData + Umbraco.TestData + v4.7.2 + 512 + true + 8 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + {29aa69d9-b597-4395-8d42-43b1263c240a} + Umbraco.Core + + + {3ae7bf57-966b-45a5-910a-954d7c554441} + Umbraco.Infrastructure + + + {651e1350-91b6-44b7-bd60-7207006d7003} + Umbraco.Web + + + + + 28.4.4 + + + 5.2.7 + + + + \ No newline at end of file diff --git a/src/Umbraco.TestData/UmbracoTestDataController.cs b/src/Umbraco.TestData/UmbracoTestDataController.cs new file mode 100644 index 0000000000..09f9177982 --- /dev/null +++ b/src/Umbraco.TestData/UmbracoTestDataController.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core; +using System.Web.Mvc; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Web; +using Umbraco.Web.Mvc; +using System.Configuration; +using Bogus; +using Umbraco.Core.Scoping; +using Umbraco.Core.Strings; + +namespace Umbraco.TestData +{ + /// + /// Creates test data + /// + public class UmbracoTestDataController : SurfaceController + { + private const string RichTextDataTypeName = "UmbracoTestDataContent.RTE"; + private const string MediaPickerDataTypeName = "UmbracoTestDataContent.MediaPicker"; + private const string TextDataTypeName = "UmbracoTestDataContent.Text"; + private const string TestDataContentTypeAlias = "umbTestDataContent"; + private readonly IScopeProvider _scopeProvider; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IShortStringHelper _shortStringHelper; + + public UmbracoTestDataController(IScopeProvider scopeProvider, PropertyEditorCollection propertyEditors, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, IShortStringHelper shortStringHelper) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) + { + _scopeProvider = scopeProvider; + _propertyEditors = propertyEditors; + _shortStringHelper = shortStringHelper; + } + + /// + /// Creates a content and associated media tree (hierarchy) + /// + /// + /// + /// + /// + /// + /// Each content item created is associated to a media item via a media picker and therefore a relation is created between the two + /// + public ActionResult CreateTree(int count, int depth, string locale = "en") + { + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return HttpNotFound(); + + if (!Validate(count, depth, out var message, out var perLevel)) + throw new InvalidOperationException(message); + + var faker = new Faker(locale); + var company = faker.Company.CompanyName(); + + using (var scope = _scopeProvider.CreateScope()) + { + var imageIds = CreateMediaTree(company, faker, count, depth).ToList(); + var contentIds = CreateContentTree(company, faker, count, depth, imageIds, out var root).ToList(); + + Services.ContentService.SaveAndPublishBranch(root, true); + + scope.Complete(); + } + + + return Content("Done"); + } + + private bool Validate(int count, int depth, out string message, out int perLevel) + { + perLevel = 0; + message = null; + + if (count <= 0) + { + message = "Count must be more than 0"; + return false; + } + + perLevel = count / depth; + if (perLevel < 1) + { + message = "Count not high enough for specified for number of levels required"; + return false; + } + + return true; + } + + /// + /// Utility to create a tree hierarchy + /// + /// + /// + /// + /// + /// + /// A callback that returns a tuple of Content and another callback to produce a Container. + /// For media, a container will be another folder, for content the container will be the Content itself. + /// + /// + private IEnumerable CreateHierarchy( + T parent, int count, int depth, + Func container)> create) + where T: class, IContentBase + { + yield return parent.GetUdi(); + + // This will not calculate a balanced tree but it will ensure that there will be enough nodes deep enough to not fill up the tree. + var totalDescendants = count - 1; + var perLevel = Math.Ceiling(totalDescendants / (double)depth); + var perBranch = Math.Ceiling(perLevel / depth); + + var tracked = new Stack<(T parent, int childCount)>(); + + var currChildCount = 0; + + for (int i = 0; i < count; i++) + { + var created = create(parent); + var contentItem = created.content; + + yield return contentItem.GetUdi(); + + currChildCount++; + + if (currChildCount == perBranch) + { + // move back up... + + var prev = tracked.Pop(); + + // restore child count + currChildCount = prev.childCount; + // restore the parent + parent = prev.parent; + + } + else if (contentItem.Level < depth) + { + // track the current parent and it's current child count + tracked.Push((parent, currChildCount)); + + // not at max depth, create below + parent = created.container(); + + currChildCount = 0; + } + + } + } + + /// + /// Creates the media tree hiearachy + /// + /// + /// + /// + /// + /// + private IEnumerable CreateMediaTree(string company, Faker faker, int count, int depth) + { + var parent = Services.MediaService.CreateMediaWithIdentity(company, -1, Constants.Conventions.MediaTypes.Folder); + + return CreateHierarchy(parent, count, depth, currParent => + { + var imageUrl = faker.Image.PicsumUrl(); + + // we are appending a &ext=.jpg to the end of this for a reason. The result of this url will be something like: + // https://picsum.photos/640/480/?image=106 + // and due to the way that we detect images there must be an extension so we'll change it to + // https://picsum.photos/640/480/?image=106&ext=.jpg + // which will trick our app into parsing this and thinking it's an image ... which it is so that's good. + // if we don't do this we don't get thumbnails in the back office. + imageUrl += "&ext=.jpg"; + + var media = Services.MediaService.CreateMedia(faker.Commerce.ProductName(), currParent, Constants.Conventions.MediaTypes.Image); + media.SetValue(Constants.Conventions.Media.File, imageUrl); + Services.MediaService.Save(media); + return (media, () => + { + // create a folder to contain child media + var container = Services.MediaService.CreateMediaWithIdentity(faker.Commerce.Department(), currParent, Constants.Conventions.MediaTypes.Folder); + return container; + }); + }); + } + + /// + /// Creates the content tree hiearachy + /// + /// + /// + /// + /// + /// + /// + private IEnumerable CreateContentTree(string company, Faker faker, int count, int depth, List imageIds, out IContent root) + { + var random = new Random(company.GetHashCode()); + + var docType = GetOrCreateContentType(); + + var parent = Services.ContentService.Create(company, -1, docType.Alias); + parent.SetValue("review", faker.Rant.Review()); + parent.SetValue("desc", company); + parent.SetValue("media", imageIds[random.Next(0, imageIds.Count - 1)]); + Services.ContentService.Save(parent); + + root = parent; + + return CreateHierarchy(parent, count, depth, currParent => + { + 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("media", imageIds[random.Next(0, imageIds.Count - 1)]); + + Services.ContentService.Save(content); + return (content, () => content); + }); + + } + + private IContentType GetOrCreateContentType() + { + var docType = Services.ContentTypeService.Get(TestDataContentTypeAlias); + if (docType != null) + return docType; + + docType = new ContentType(_shortStringHelper, -1) + { + Alias = TestDataContentTypeAlias, + Name = "Umbraco Test Data Content", + Icon = "icon-science color-green" + }; + docType.AddPropertyGroup("Content"); + docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateRichText(), "review") + { + Name = "Review" + }); + docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateMediaPicker(), "media") + { + Name = "Media" + }); + docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateText(), "desc") + { + Name = "Description" + }); + Services.ContentTypeService.Save(docType); + docType.AllowedContentTypes = new[] { new ContentTypeSort(docType.Id, 0) }; + Services.ContentTypeService.Save(docType); + return docType; + } + + private IDataType GetOrCreateRichText() => GetOrCreateDataType(RichTextDataTypeName, Constants.PropertyEditors.Aliases.TinyMce); + + private IDataType GetOrCreateMediaPicker() => GetOrCreateDataType(MediaPickerDataTypeName, Constants.PropertyEditors.Aliases.MediaPicker); + + private IDataType GetOrCreateText() => GetOrCreateDataType(TextDataTypeName, Constants.PropertyEditors.Aliases.TextBox); + + private IDataType GetOrCreateDataType(string name, string editorAlias) + { + var dt = Services.DataTypeService.GetDataType(name); + if (dt != null) return dt; + + var editor = _propertyEditors.FirstOrDefault(x => x.Alias == editorAlias); + if (editor == null) + throw new InvalidOperationException($"No {editorAlias} editor found"); + + dt = new DataType(editor) + { + Name = name, + Configuration = editor.GetConfigurationEditor().DefaultConfigurationObject, + DatabaseType = ValueStorageType.Ntext + }; + + Services.DataTypeService.Save(dt); + return dt; + } + } +} diff --git a/src/Umbraco.TestData/readme.md b/src/Umbraco.TestData/readme.md new file mode 100644 index 0000000000..f943326303 --- /dev/null +++ b/src/Umbraco.TestData/readme.md @@ -0,0 +1,51 @@ +## Umbraco Test Data + +This project is a utility to be able to generate large amounts of content and media in an +Umbraco installation for testing. + +Currently this project is referenced in the Umbraco.Web.UI project but only when it's being built +in Debug mode (i.e. when testing within Visual Studio). + +## Usage + +You must use SQL Server for this, using SQLCE will die if you try to bulk create huge amounts of data. + +It has to be enabled by an appSetting: + +```xml + +``` + +Once this is enabled this endpoint can be executed: + +`/umbraco/surface/umbracotestdata/CreateTree?count=100&depth=5` + +The query string options are: + +* `count` = the number of content and media nodes to create +* `depth` = how deep the trees created will be +* `locale` (optional, default = "en") = the language that the data will be generated in + +This creates a content and associated media tree (hierarchy). Each content item created is associated +to a media item via a media picker and therefore a relation is created between the two. Each content and +media tree created have the same root node name so it's easy to know which content branch relates to +which media branch. + +All values are generated using the very handy `Bogus` package. + +## Schema + +This will install some schema items: + +* `umbTestDataContent` Document Type. __TIP__: If you want to delete all of the content data generated with this tool, just delete this content type +* `UmbracoTestDataContent.RTE` Data Type +* `UmbracoTestDataContent.MediaPicker` Data Type +* `UmbracoTestDataContent.Text` Data Type + +For media, the normal folder and image is used + +## Media + +This does not upload physical files, it just uses a randomized online image as the `umbracoFile` value. +This works when viewing the media item in the media section and the image will show up and with recent changes this will also work +when editing content to view the thumbnail for the picked media. diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs new file mode 100644 index 0000000000..7b4322bfac --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -0,0 +1,30 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [MediumRunJob] + [MemoryDiagnoser] + public class TypeFinderBenchmarks + { + + [Benchmark(Baseline = true)] + public void WithGetReferencingAssembliesCheck() + { + var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var found = typeFinder1.FindClassesOfType().Count(); + } + + [Benchmark] + public void WithoutGetReferencingAssembliesCheck() + { + var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + typeFinder2.QueryWithReferencingAssemblies = false; + var found = typeFinder2.FindClassesOfType().Count(); + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 45026b974f..84ec535b9d 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -12,6 +12,7 @@ 512 true true + 8 AnyCPU @@ -60,28 +61,25 @@ + - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + {29aa69d9-b597-4395-8d42-43b1263c240a} Umbraco.Core - - {07fbc26b-2927-4a22-8d96-d644c667fecc} - Umbraco.Examine - {3ae7bf57-966b-45a5-910a-954d7c554441} Umbraco.Infrastructure + + {33085570-9bf2-4065-a9b0-a29d920d13ba} + Umbraco.Persistance.SqlCe + {5d3b8245-ada6-453f-a008-50ed04bfe770} Umbraco.Tests diff --git a/src/Umbraco.Tests.Common/SettingsForTests.cs b/src/Umbraco.Tests.Common/SettingsForTests.cs new file mode 100644 index 0000000000..6b484bbcfc --- /dev/null +++ b/src/Umbraco.Tests.Common/SettingsForTests.cs @@ -0,0 +1,181 @@ +using Moq; +using Semver; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Tests.Common +{ + public class SettingsForTests + { + public SettingsForTests() + { + } + + public IGlobalSettings GenerateMockGlobalSettings(IUmbracoVersion umbVersion = null) + { + var semanticVersion = umbVersion?.SemanticVersion ?? new SemVersion(9); + + var config = Mock.Of( + settings => + settings.ConfigurationStatus == semanticVersion.ToSemanticString() && + settings.UseHttps == false && + settings.HideTopLevelNodeFromPath == false && + settings.Path == "~/umbraco" && + settings.TimeOutInMinutes == 20 && + settings.DefaultUILanguage == "en" && + settings.ReservedPaths == (GlobalSettings.StaticReservedPaths + "~/umbraco") && + settings.ReservedUrls == GlobalSettings.StaticReservedUrls && + settings.UmbracoPath == "~/umbraco" && + settings.UmbracoMediaPath == "~/media" && + settings.UmbracoCssPath == "~/css" && + settings.UmbracoScriptsPath == "~/scripts" + ); + + + + return config; + } + + /// + /// Returns generated settings which can be stubbed to return whatever values necessary + /// + /// + public IContentSettings GenerateMockContentSettings() + { + + var content = new Mock(); + + //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! + content.Setup(x => x.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties()); + content.Setup(x => x.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes()); + return content.Object; + } + + //// from appSettings + + //private readonly IDictionary SavedAppSettings = new Dictionary(); + + //static void SaveSetting(string key) + //{ + // SavedAppSettings[key] = ConfigurationManager.AppSettings[key]; + //} + + //static void SaveSettings() + //{ + // SaveSetting("umbracoHideTopLevelNodeFromPath"); + // SaveSetting("umbracoUseDirectoryUrls"); + // SaveSetting("umbracoPath"); + // SaveSetting("umbracoReservedPaths"); + // SaveSetting("umbracoReservedUrls"); + // SaveSetting("umbracoConfigurationStatus"); + //} + + + + // reset & defaults + + //static SettingsForTests() + //{ + // //SaveSettings(); + //} + + public void Reset() + { + ResetSettings(); + GlobalSettings.Reset(); + + //foreach (var kvp in SavedAppSettings) + // ConfigurationManager.AppSettings.Set(kvp.Key, kvp.Value); + + //// set some defaults that are wrong in the config file?! + //// this is annoying, really + //HideTopLevelNodeFromPath = false; + } + + /// + /// This sets all settings back to default settings + /// + private void ResetSettings() + { + _defaultGlobalSettings = null; + } + + private IGlobalSettings _defaultGlobalSettings; + private IHostingSettings _defaultHostingSettings; + + public IGlobalSettings GetDefaultGlobalSettings(IUmbracoVersion umbVersion) + { + if (_defaultGlobalSettings == null) + { + _defaultGlobalSettings = GenerateMockGlobalSettings(umbVersion); + } + return _defaultGlobalSettings; + } + + public IHostingSettings GetDefaultHostingSettings() + { + if (_defaultHostingSettings == null) + { + _defaultHostingSettings = GenerateMockHostingSettings(); + } + return _defaultHostingSettings; + } + + private IHostingSettings GenerateMockHostingSettings() + { + var config = Mock.Of( + settings => + settings.LocalTempStorageLocation == LocalTempStorage.EnvironmentTemp && + settings.DebugMode == false + ); + return config; + } + + public IWebRoutingSettings GenerateMockWebRoutingSettings() + { + var mock = new Mock(); + + mock.Setup(x => x.DisableRedirectUrlTracking).Returns(false); + mock.Setup(x => x.InternalRedirectPreservesTemplate).Returns(false); + mock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); + + return mock.Object; + } + + public IRequestHandlerSettings GenerateMockRequestHandlerSettings() + { + var mock = new Mock(); + + mock.Setup(x => x.AddTrailingSlash).Returns(true); + mock.Setup(x => x.ConvertUrlsToAscii).Returns(false); + mock.Setup(x => x.TryConvertUrlsToAscii).Returns(false); + mock.Setup(x => x.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements); + + return mock.Object; + } + + public ISecuritySettings GenerateMockSecuritySettings() + { + var security = new Mock(); + + return security.Object; + } + + public IUserPasswordConfiguration GenerateMockUserPasswordConfiguration() + { + var mock = new Mock(); + + return mock.Object; + } + + public IMemberPasswordConfiguration GenerateMockMemberPasswordConfiguration() + { + var mock = new Mock(); + + return mock.Object; + } + } +} diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs b/src/Umbraco.Tests.Common/TestDefaultCultureAccessor.cs similarity index 86% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs rename to src/Umbraco.Tests.Common/TestDefaultCultureAccessor.cs index 41bd70626c..e1bec33c94 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs +++ b/src/Umbraco.Tests.Common/TestDefaultCultureAccessor.cs @@ -1,6 +1,6 @@ using Umbraco.Web.PublishedCache; -namespace Umbraco.Tests.Testing.Objects.Accessors +namespace Umbraco.Tests.Common { public class TestDefaultCultureAccessor : IDefaultCultureAccessor { diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs new file mode 100644 index 0000000000..a5810715fb --- /dev/null +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using Moq; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Diagnostics; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Runtime; +using Umbraco.Core.Serialization; +using Umbraco.Core.Strings; +using Umbraco.Core.Sync; +using Umbraco.Net; +using Umbraco.Web; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Common +{ + /// + /// Common helper properties and methods useful to testing + /// + public abstract class TestHelperBase + { + public TestHelperBase() + { + SettingsForTests = new SettingsForTests(); + IOHelper = new IOHelper(GetHostingEnvironment(), SettingsForTests.GenerateMockGlobalSettings()); + MainDom = new MainDom(Mock.Of(), GetHostingEnvironment(), new MainDomSemaphoreLock(Mock.Of(), GetHostingEnvironment())); + UriUtility = new UriUtility(GetHostingEnvironment()); + } + + public ITypeFinder GetTypeFinder() + { + + var typeFinder = new TypeFinder(Mock.Of(), + new DefaultUmbracoAssemblyProvider(typeof(TestHelperBase).Assembly)); + return typeFinder; + } + + public TypeLoader GetMockedTypeLoader() + { + return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); + } + + public Configs GetConfigs() + { + return GetConfigsFactory().Create(); + } + public IRuntimeState GetRuntimeState() + { + return new RuntimeState( + Mock.Of(), + Mock.Of(), + new Lazy(), + new Lazy(), + GetUmbracoVersion(), + GetHostingEnvironment(), + GetBackOfficeInfo() + ); + } + + public abstract IBackOfficeInfo GetBackOfficeInfo(); + + public IConfigsFactory GetConfigsFactory() + { + return new ConfigsFactory(); + } + + /// + /// Gets the current assembly directory. + /// + /// The assembly directory. + public string CurrentAssemblyDirectory + { + get + { + var codeBase = typeof(TestHelperBase).Assembly.CodeBase; + var uri = new Uri(codeBase); + var path = uri.LocalPath; + return Path.GetDirectoryName(path); + } + } + + public IShortStringHelper ShortStringHelper { get; } = new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); + public IJsonSerializer JsonSerializer { get; } = new JsonNetSerializer(); + public IVariationContextAccessor VariationContextAccessor { get; } = new TestVariationContextAccessor(); + public abstract IDbProviderFactoryCreator DbProviderFactoryCreator { get; } + public abstract IBulkSqlInsertProvider BulkSqlInsertProvider { get; } + public abstract IMarchal Marchal { get; } + public ICoreDebug CoreDebug { get; } = new CoreDebug(); + + + public IIOHelper IOHelper { get; } + public IMainDom MainDom { get; } + public UriUtility UriUtility { get; } + public SettingsForTests SettingsForTests { get; } + public IWebRoutingSettings WebRoutingSettings => SettingsForTests.GenerateMockWebRoutingSettings(); + + /// + /// Maps the given making it rooted on . must start with ~/ + /// + /// The relative path. + /// + public string MapPathForTest(string relativePath) + { + if (!relativePath.StartsWith("~/")) + throw new ArgumentException("relativePath must start with '~/'", "relativePath"); + + return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); + } + + public IUmbracoVersion GetUmbracoVersion() + { + return new UmbracoVersion(GetConfigs().Global()); + } + + public IRegister GetRegister() + { + return RegisterFactory.Create(GetConfigs().Global()); + } + + public abstract IHostingEnvironment GetHostingEnvironment(); + + public abstract IIpResolver GetIpResolver(); + + public IRequestCache GetRequestCache() + { + return new DictionaryAppCache(); + } + + public IPublishedUrlProvider GetPublishedUrlProvider() + { + var mock = new Mock(); + + return mock.Object; + } + } +} diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs b/src/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs similarity index 79% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs rename to src/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs index c46915e3a0..ed1ba0b5b9 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs @@ -1,6 +1,6 @@ using Umbraco.Web.PublishedCache; -namespace Umbraco.Tests.Testing.Objects.Accessors +namespace Umbraco.Tests.Common { public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor { diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs b/src/Umbraco.Tests.Common/TestUmbracoContextAccessor.cs similarity index 56% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs rename to src/Umbraco.Tests.Common/TestUmbracoContextAccessor.cs index 4f3b801af9..feaf8eafa5 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs +++ b/src/Umbraco.Tests.Common/TestUmbracoContextAccessor.cs @@ -1,16 +1,16 @@ using Umbraco.Web; -namespace Umbraco.Tests.Testing.Objects.Accessors +namespace Umbraco.Tests.Common { public class TestUmbracoContextAccessor : IUmbracoContextAccessor { - public UmbracoContext UmbracoContext { get; set; } + public IUmbracoContext UmbracoContext { get; set; } public TestUmbracoContextAccessor() { } - public TestUmbracoContextAccessor(UmbracoContext umbracoContext) + public TestUmbracoContextAccessor(IUmbracoContext umbracoContext) { UmbracoContext = umbracoContext; } diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs b/src/Umbraco.Tests.Common/TestVariationContextAccessor.cs similarity index 87% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs rename to src/Umbraco.Tests.Common/TestVariationContextAccessor.cs index 134b709447..b2a07b74f2 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs +++ b/src/Umbraco.Tests.Common/TestVariationContextAccessor.cs @@ -1,6 +1,6 @@ using Umbraco.Core.Models.PublishedContent; -namespace Umbraco.Tests.Testing.Objects.Accessors +namespace Umbraco.Tests.Common { /// /// Provides an implementation of for tests. diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj new file mode 100644 index 0000000000..e6963bc8aa --- /dev/null +++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 77200be86e..e4844cc6be 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Web; using Moq; @@ -12,6 +13,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Tests.Collections; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Cache; namespace Umbraco.Tests.Cache @@ -20,14 +22,17 @@ namespace Umbraco.Tests.Cache public class DeepCloneAppCacheTests : RuntimeAppCacheTests { private DeepCloneAppCache _provider; + private ObjectCacheAppCache _memberCache; - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; + protected override int GetTotalItemCount => _memberCache.MemoryCache.Count(); public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); - _provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, typeFinder)); + var typeFinder = TestHelper.GetTypeFinder(); + _memberCache = new ObjectCacheAppCache(typeFinder); + + _provider = new DeepCloneAppCache(_memberCache); } internal override IAppCache AppCache => _provider; diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index ea7819b14d..6043e7b0d6 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -1,15 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using Moq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Web.Composing; using Umbraco.Core.Sync; -using Umbraco.Tests.Components; -using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Cache.DistributedCache { @@ -21,22 +16,21 @@ namespace Umbraco.Tests.Cache.DistributedCache { private Umbraco.Web.Cache.DistributedCache _distributedCache; + private IServerRegistrar ServerRegistrar { get; set; } + private TestServerMessenger ServerMessenger { get; set; } + [SetUp] public void Setup() { - var register = TestHelper.GetRegister(); + ServerRegistrar = new TestServerRegistrar(); + ServerMessenger = new TestServerMessenger(); - var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); + var cacheRefresherCollection = new CacheRefresherCollection(new [] + { + new TestCacheRefresher() + }); - composition.RegisterUnique(_ => new TestServerRegistrar()); - composition.RegisterUnique(_ => new TestServerMessenger()); - - composition.WithCollectionBuilder() - .Add(); - - Current.Factory = composition.CreateFactory(); - - _distributedCache = new Umbraco.Web.Cache.DistributedCache(); + _distributedCache = new Umbraco.Web.Cache.DistributedCache(ServerMessenger, cacheRefresherCollection); } [TearDown] @@ -52,7 +46,7 @@ namespace Umbraco.Tests.Cache.DistributedCache { _distributedCache.Refresh(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i); } - Assert.AreEqual(10, ((TestServerMessenger)Current.ServerMessenger).IntIdsRefreshed.Count); + Assert.AreEqual(10, ServerMessenger.IntIdsRefreshed.Count); } [Test] @@ -65,7 +59,7 @@ namespace Umbraco.Tests.Cache.DistributedCache x => x.Id, new TestObjectWithId{Id = i}); } - Assert.AreEqual(10, ((TestServerMessenger)Current.ServerMessenger).IntIdsRefreshed.Count); + Assert.AreEqual(10, ServerMessenger.IntIdsRefreshed.Count); } [Test] @@ -75,7 +69,7 @@ namespace Umbraco.Tests.Cache.DistributedCache { _distributedCache.Refresh(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), Guid.NewGuid()); } - Assert.AreEqual(11, ((TestServerMessenger)Current.ServerMessenger).GuidIdsRefreshed.Count); + Assert.AreEqual(11, ServerMessenger.GuidIdsRefreshed.Count); } [Test] @@ -85,7 +79,7 @@ namespace Umbraco.Tests.Cache.DistributedCache { _distributedCache.Remove(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i); } - Assert.AreEqual(12, ((TestServerMessenger)Current.ServerMessenger).IntIdsRemoved.Count); + Assert.AreEqual(12, ServerMessenger.IntIdsRemoved.Count); } [Test] @@ -95,7 +89,7 @@ namespace Umbraco.Tests.Cache.DistributedCache { _distributedCache.RefreshAll(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73")); } - Assert.AreEqual(13, ((TestServerMessenger)Current.ServerMessenger).CountOfFullRefreshes); + Assert.AreEqual(13, ServerMessenger.CountOfFullRefreshes); } #region internal test classes diff --git a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index 862f6da038..eedcc498c5 100644 --- a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -3,18 +3,21 @@ using System.Linq; using System.Threading; using Moq; using NUnit.Framework; +using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Serialization; using Umbraco.Core.Services; +using Umbraco.Tests.Common; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Cache { @@ -39,7 +42,7 @@ namespace Umbraco.Tests.Cache // we should really refactor events entirely - in the meantime, let it be an UmbracoTestBase ;( //var testObjects = new TestObjects(null); //var serviceContext = testObjects.GetServiceContextMock(); - var serviceContext = Current.Services; + var serviceContext = ServiceContext; var definitions = new IEventDefinition[] { @@ -147,21 +150,23 @@ namespace Umbraco.Tests.Cache var definitions = new IEventDefinition[] { // works because that event definition maps to an empty handler - new EventDefinition>(null, Current.Services.ContentTypeService, new SaveEventArgs(Enumerable.Empty()), "Saved"), + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty()), "Saved"), }; + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); + var umbracoContextFactory = new UmbracoContextFactory( new TestUmbracoContextAccessor(), Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), TestObjects.GetGlobalSettings(), - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - IOHelper); + IOHelper, + UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); // just assert it does not throw var refreshers = new DistributedCacheBinder(null, umbracoContextFactory, null); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index b9c948c1de..dbda6fb429 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _ctx = new FakeHttpContextFactory("http://localhost/test"); _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index 0fb8e574a8..7957026ad8 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -1,12 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Moq; +using System.Linq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Cache { @@ -23,7 +18,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _provider = new ObjectCacheAppCache(typeFinder); } diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 5297f7afd6..af4c660112 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -4,10 +4,10 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -24,7 +24,7 @@ namespace Umbraco.Tests.Cache.PublishedCache public class PublishContentCacheTests : BaseWebTest { private FakeHttpContextFactory _httpContextFactory; - private UmbracoContext _umbracoContext; + private IUmbracoContext _umbracoContext; private IPublishedContentCache _cache; private XmlDocument _xml; @@ -58,7 +58,6 @@ namespace Umbraco.Tests.Cache.PublishedCache _httpContextFactory = new FakeHttpContextFactory("~/Home"); - var umbracoSettings = Factory.GetInstance(); var globalSettings = Factory.GetInstance(); var umbracoContextAccessor = Factory.GetInstance(); @@ -66,25 +65,26 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null, HostingEnvironment); var appCache = new DictionaryAppCache(); - var domainCache = new DomainCache(ServiceContext.DomainService, DefaultCultureAccessor); + var domainCache = new DomainCache(Mock.Of(), DefaultCultureAccessor); var publishedShapshot = new PublishedSnapshot( - new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, ContentTypesCache, null, null), - new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, appCache, ContentTypesCache, Factory.GetInstance(), umbracoContextAccessor), - new PublishedMemberCache(null, appCache, Current.Services.MemberService, ContentTypesCache, Current.Services.UserService), + new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, ContentTypesCache, null, VariationContextAccessor, null), + new PublishedMediaCache(xmlStore, Mock.Of(), Mock.Of(), appCache, ContentTypesCache, Factory.GetInstance(), umbracoContextAccessor, VariationContextAccessor), + new PublishedMemberCache(null, appCache, Mock.Of(), ContentTypesCache, Mock.Of(), VariationContextAccessor), domainCache); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedShapshot); + var httpContext = _httpContextFactory.HttpContext; + var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); _umbracoContext = new UmbracoContext( - _httpContextFactory.HttpContext, + httpContextAccessor, publishedSnapshotService.Object, - new WebSecurity(_httpContextFactory.HttpContext, Current.Services.UserService, globalSettings), - umbracoSettings, - Enumerable.Empty(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, Mock.Of(), globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); _cache = _umbracoContext.Content; } diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 8b9507dab8..81214ccdb2 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -6,7 +6,6 @@ using Examine; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; @@ -26,6 +25,7 @@ namespace Umbraco.Tests.Cache.PublishedCache public class PublishMediaCacheTests : BaseWebTest { private Dictionary _mediaTypes; + private int _testWriterAndCreatorId; private IUmbracoContextAccessor _umbracoContextAccessor; protected override void Compose() @@ -52,6 +52,8 @@ namespace Umbraco.Tests.Cache.PublishedCache { testMediaType.Alias, testMediaType } }; ContentTypesCache.GetPublishedContentTypeByAlias = alias => _mediaTypes[alias]; + + _testWriterAndCreatorId = ServiceContext.UserService.CreateUserWithIdentity("Shannon", "test").Id; } private IMediaType MakeNewMediaType(IUser user, string text, int parentId = -1) @@ -79,7 +81,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var mChild2 = MakeNewMedia("Child2", mType, user, mRoot2.Id); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var roots = cache.GetAtRoot(); Assert.AreEqual(2, roots.Count()); Assert.IsTrue(roots.Select(x => x.Id).ContainsAll(new[] {mRoot1.Id, mRoot2.Id})); @@ -97,7 +99,7 @@ namespace Umbraco.Tests.Cache.PublishedCache //var publishedMedia = PublishedMediaTests.GetNode(mRoot.Id, GetUmbracoContext("/test", 1234)); var umbracoContext = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), Current.Services.MediaService, Current.Services.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var publishedMedia = cache.GetById(mRoot.Id); Assert.IsNotNull(publishedMedia); @@ -202,16 +204,16 @@ namespace Umbraco.Tests.Cache.PublishedCache {"path", "-1,1234"}, {"updateDate", DateTime.Parse("2012-07-16T10:34:09").Ticks.ToString()}, {"createDate", DateTime.Parse("2012-07-17T10:34:09").Ticks.ToString()}, - {"creatorID", "0"}, + {"creatorID", _testWriterAndCreatorId.ToString()}, {"creatorName", "Shannon"} }; var result = new SearchResult("1234", 1, () => fields.ToDictionary(x => x.Key, x => new List { x.Value })); - var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); - DoAssert(doc, 1234, key, null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); + DoAssert(doc, 1234, key, null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); Assert.AreEqual(null, doc.Parent); } @@ -224,10 +226,10 @@ namespace Umbraco.Tests.Cache.PublishedCache var xmlDoc = GetMediaXml(); ((XmlElement)xmlDoc.DocumentElement.FirstChild).SetAttribute("key", key.ToString()); var navigator = xmlDoc.SelectSingleNode("/root/Image").CreateNavigator(); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(),VariationContextAccessor); var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); - DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); + DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); Assert.AreEqual(null, doc.Parent); Assert.AreEqual(2, doc.Children.Count()); Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); @@ -244,16 +246,18 @@ namespace Umbraco.Tests.Cache.PublishedCache ]> - + - + - + "; + xml = xml.Replace("[WriterId]", _testWriterAndCreatorId.ToString()); + xml = xml.Replace("[CreatorId]", _testWriterAndCreatorId.ToString()); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); @@ -280,10 +284,8 @@ namespace Umbraco.Tests.Cache.PublishedCache {"urlName", "testing"}, {nodeTypeAliasKey, "myType"}, {"nodeType", "22"}, - {"writerName", "Shannon"}, - {"creatorName", "Shannon"}, - {"writerID", "33"}, - {"creatorID", "33"}, + {"writerID", _testWriterAndCreatorId.ToString()}, + {"creatorID", _testWriterAndCreatorId.ToString()}, {pathKey, "1,2,3,4,5"}, {"createDate", "2012-01-02"}, {"updateDate", "2012-01-02"}, @@ -319,6 +321,7 @@ namespace Umbraco.Tests.Cache.PublishedCache // callback to get a property (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), null, // cache provider + VariationContextAccessor, ContentTypesCache, // no xpath null, @@ -329,6 +332,7 @@ namespace Umbraco.Tests.Cache.PublishedCache // callback to get a property (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), null, // cache provider + VariationContextAccessor, ContentTypesCache, // no xpath null, @@ -348,8 +352,6 @@ namespace Umbraco.Tests.Cache.PublishedCache int nodeTypeIdVal = 22, string writerNameVal = "Shannon", string creatorNameVal = "Shannon", - int writerIdVal = 33, - int creatorIdVal = 33, string pathVal = "1,2,3,4,5", DateTime? createDateVal = null, DateTime? updateDateVal = null, @@ -362,7 +364,7 @@ namespace Umbraco.Tests.Cache.PublishedCache updateDateVal = DateTime.Parse("2012-01-02"); DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, - creatorNameVal, writerIdVal, creatorIdVal, pathVal, createDateVal, updateDateVal, levelVal); + creatorNameVal, pathVal, createDateVal, updateDateVal, levelVal); //now validate the parentId that has been parsed, this doesn't exist on the IPublishedContent Assert.AreEqual(parentIdVal, dicDoc.ParentId); @@ -379,8 +381,6 @@ namespace Umbraco.Tests.Cache.PublishedCache int nodeTypeIdVal = 22, string writerNameVal = "Shannon", string creatorNameVal = "Shannon", - int writerIdVal = 33, - int creatorIdVal = 33, string pathVal = "1,2,3,4,5", DateTime? createDateVal = null, DateTime? updateDateVal = null, @@ -398,10 +398,10 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(urlNameVal, doc.UrlSegment); Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); - Assert.AreEqual(writerNameVal, doc.WriterName); - Assert.AreEqual(creatorNameVal, doc.CreatorName); - Assert.AreEqual(writerIdVal, doc.WriterId); - Assert.AreEqual(creatorIdVal, doc.CreatorId); + Assert.AreEqual(writerNameVal, doc.GetWriterName(ServiceContext.UserService)); + Assert.AreEqual(creatorNameVal, doc.GetCreatorName(ServiceContext.UserService)); + Assert.AreEqual(_testWriterAndCreatorId, doc.WriterId); + Assert.AreEqual(_testWriterAndCreatorId, doc.CreatorId); Assert.AreEqual(pathVal, doc.Path); Assert.AreEqual(createDateVal.Value, doc.CreateDate); Assert.AreEqual(updateDateVal.Value, doc.UpdateDate); diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index b435af9e77..739268b451 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -5,7 +5,6 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Scoping; using Umbraco.Web.PublishedCache.NuCache; -using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Tests.Cache { @@ -689,7 +688,7 @@ namespace Umbraco.Tests.Cache { // gen 3 Assert.AreEqual(2, d.Test.GetValues(1).Length); - d.Set(1, "ein"); + d.SetLocked(1, "ein"); Assert.AreEqual(3, d.Test.GetValues(1).Length); Assert.AreEqual(3, d.Test.LiveGen); @@ -727,31 +726,25 @@ namespace Umbraco.Tests.Cache using (var w1 = d.GetScopedWriteLock(scopeProvider)) { Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.IsLocked); Assert.IsTrue(t.NextGen); - using (var w2 = d.GetScopedWriteLock(scopeProvider)) + Assert.Throws(() => { - Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(2, t.WLocked); - Assert.IsTrue(t.NextGen); - - Assert.AreNotSame(w1, w2); // get a new writer each time - - d.Set(1, "one"); - - Assert.AreEqual(0, d.CreateSnapshot().Gen); - } + using (var w2 = d.GetScopedWriteLock(scopeProvider)) + { + } + }); Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.IsLocked); Assert.IsTrue(t.NextGen); Assert.AreEqual(0, d.CreateSnapshot().Gen); } Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(0, t.WLocked); + Assert.IsFalse(t.IsLocked); Assert.IsTrue(t.NextGen); Assert.AreEqual(1, d.CreateSnapshot().Gen); @@ -772,11 +765,14 @@ namespace Umbraco.Tests.Cache using (var w1 = d.GetScopedWriteLock(scopeProvider)) { + // This one is interesting, although we don't allow recursive locks, since this is + // using the same ScopeContext/key, the lock acquisition is only done once + using (var w2 = d.GetScopedWriteLock(scopeProvider)) { Assert.AreSame(w1, w2); - d.Set(1, "one"); + d.SetLocked(1, "one"); } } } @@ -797,19 +793,16 @@ namespace Umbraco.Tests.Cache using (var w1 = d.GetScopedWriteLock(scopeProvider1)) { Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.IsLocked); Assert.IsTrue(t.NextGen); - using (var w2 = d.GetScopedWriteLock(scopeProvider2)) + Assert.Throws(() => { - Assert.AreEqual(1, t.LiveGen); - Assert.AreEqual(2, t.WLocked); - Assert.IsTrue(t.NextGen); + using (var w2 = d.GetScopedWriteLock(scopeProvider2)) + { + } + }); - Assert.AreNotSame(w1, w2); - - d.Set(1, "one"); - } } } @@ -848,13 +841,13 @@ namespace Umbraco.Tests.Cache Assert.IsFalse(d.Test.NextGen); Assert.AreEqual("uno", s2.Get(1)); - var scopeProvider = GetScopeProvider(); + var scopeProvider = GetScopeProvider(); using (d.GetScopedWriteLock(scopeProvider)) { // gen 3 Assert.AreEqual(2, d.Test.GetValues(1).Length); - d.Set(1, "ein"); + d.SetLocked(1, "ein"); Assert.AreEqual(3, d.Test.GetValues(1).Length); Assert.AreEqual(3, d.Test.LiveGen); @@ -882,6 +875,7 @@ namespace Umbraco.Tests.Cache var d = new SnapDictionary(); d.Test.CollectAuto = false; + // gen 1 d.Set(1, "one"); var s1 = d.CreateSnapshot(); @@ -894,12 +888,11 @@ namespace Umbraco.Tests.Cache Assert.AreEqual("uno", s2.Get(1)); var scopeProvider = GetScopeProvider(); - using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release - d.Set(1, "ein"); + d.SetLocked(1, "ein"); var s3 = d.CreateSnapshot(); Assert.AreEqual(2, s3.Gen); Assert.AreEqual("uno", s3.Get(1)); @@ -934,12 +927,11 @@ namespace Umbraco.Tests.Cache var scopeContext = new ScopeContext(); var scopeProvider = GetScopeProvider(scopeContext); - using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release - d.Set(1, "ein"); + d.SetLocked(1, "ein"); var s3 = d.CreateSnapshot(); Assert.AreEqual(2, s3.Gen); Assert.AreEqual("uno", s3.Get(1)); @@ -984,12 +976,11 @@ namespace Umbraco.Tests.Cache var scopeContext = new ScopeContext(); var scopeProvider = GetScopeProvider(scopeContext); - using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release - d.Set(1, "ein"); + d.SetLocked(1, "ein"); var s3 = d.CreateSnapshot(); Assert.AreEqual(2, s3.Gen); Assert.AreEqual("uno", s3.Get(1)); @@ -997,7 +988,7 @@ namespace Umbraco.Tests.Cache // we made some changes, so a next gen is required Assert.AreEqual(3, t.LiveGen); Assert.IsTrue(t.NextGen); - Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.IsLocked); // but live snapshot contains changes var ls = t.LiveSnapshot; @@ -1008,7 +999,7 @@ namespace Umbraco.Tests.Cache // nothing is committed until scope exits Assert.AreEqual(3, t.LiveGen); Assert.IsTrue(t.NextGen); - Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.IsLocked); // no changes until exit var s4 = d.CreateSnapshot(); @@ -1020,7 +1011,7 @@ namespace Umbraco.Tests.Cache // now things have changed Assert.AreEqual(2, t.LiveGen); Assert.IsFalse(t.NextGen); - Assert.AreEqual(0, t.WLocked); + Assert.IsFalse(t.IsLocked); // no changes since not completed var s5 = d.CreateSnapshot(); @@ -1097,9 +1088,10 @@ namespace Umbraco.Tests.Cache // writer is scope contextual and scoped // when disposed, nothing happens // when the context exists, the writer is released + using (d.GetScopedWriteLock(scopeProvider)) { - d.Set(1, "ein"); + d.SetLocked(1, "ein"); Assert.IsTrue(d.Test.NextGen); Assert.AreEqual(3, d.Test.LiveGen); Assert.IsNotNull(d.Test.GenObj); @@ -1107,7 +1099,7 @@ namespace Umbraco.Tests.Cache } // writer has not released - Assert.AreEqual(1, d.Test.WLocked); + Assert.IsTrue(d.Test.IsLocked); Assert.IsNotNull(d.Test.GenObj); Assert.AreEqual(2, d.Test.GenObj.Gen); @@ -1118,7 +1110,7 @@ namespace Umbraco.Tests.Cache // panic! var s2 = d.CreateSnapshot(); - Assert.AreEqual(1, d.Test.WLocked); + Assert.IsTrue(d.Test.IsLocked); Assert.IsNotNull(d.Test.GenObj); Assert.AreEqual(2, d.Test.GenObj.Gen); Assert.AreEqual(3, d.Test.LiveGen); @@ -1127,7 +1119,7 @@ namespace Umbraco.Tests.Cache // release writer scopeContext.ScopeExit(true); - Assert.AreEqual(0, d.Test.WLocked); + Assert.IsFalse(d.Test.IsLocked); Assert.IsNotNull(d.Test.GenObj); Assert.AreEqual(2, d.Test.GenObj.Gen); Assert.AreEqual(3, d.Test.LiveGen); @@ -1135,7 +1127,7 @@ namespace Umbraco.Tests.Cache var s3 = d.CreateSnapshot(); - Assert.AreEqual(0, d.Test.WLocked); + Assert.IsFalse(d.Test.IsLocked); Assert.IsNotNull(d.Test.GenObj); Assert.AreEqual(3, d.Test.GenObj.Gen); Assert.AreEqual(3, d.Test.LiveGen); @@ -1150,4 +1142,45 @@ namespace Umbraco.Tests.Cache return scopeProvider; } } + + /// + /// Used for tests so that we don't have to wrap every Set/Clear call in locks + /// + public static class SnapDictionaryExtensions + { + internal static void Set(this SnapDictionary d, TKey key, TValue value) + where TValue : class + { + using (d.GetScopedWriteLock(GetScopeProvider())) + { + d.SetLocked(key, value); + } + } + + internal static void Clear(this SnapDictionary d) + where TValue : class + { + using (d.GetScopedWriteLock(GetScopeProvider())) + { + d.ClearLocked(); + } + } + + internal static void Clear(this SnapDictionary d, TKey key) + where TValue : class + { + using (d.GetScopedWriteLock(GetScopeProvider())) + { + d.ClearLocked(key); + } + } + + private static IScopeProvider GetScopeProvider() + { + var scopeProvider = Mock.Of(); + Mock.Get(scopeProvider) + .Setup(x => x.Context).Returns(() => null); + return scopeProvider; + } + } } diff --git a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs b/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs deleted file mode 100644 index 02986e2f78..0000000000 --- a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Diagnostics; -using System.Web; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Web.Cache; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class WebCachingAppCacheTests : RuntimeAppCacheTests - { - private WebCachingAppCache _appCache; - - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; - - public override void Setup() - { - base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); - _appCache = new WebCachingAppCache(HttpRuntime.Cache, typeFinder); - } - - internal override IAppCache AppCache => _appCache; - - internal override IAppPolicyCache AppPolicyCache => _appCache; - - [Test] - public void DoesNotCacheExceptions() - { - string value; - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(1)); }); - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(2)); }); - - // does not throw - value = (string)_appCache.Get("key", () => GetValue(3)); - Assert.AreEqual("succ3", value); - - // cache - value = (string)_appCache.Get("key", () => GetValue(4)); - Assert.AreEqual("succ3", value); - } - - private static string GetValue(int i) - { - Debug.Print("get" + i); - if (i < 3) - throw new Exception("fail"); - return "succ" + i; - } - } -} diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 8e200aacf8..a41d86ee2e 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -32,8 +32,8 @@ namespace Umbraco.Tests.Components var mock = new Mock(); var logger = Mock.Of(); - var typeFinder = new TypeFinder(logger); - var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var typeFinder = TestHelper.GetTypeFinder(); + var f = new UmbracoDatabaseFactory(logger, SettingsForTests.GetDefaultGlobalSettings(), Mock.Of(), new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.DbProviderFactoryCreator); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Mock.Of(); var mediaFileSystem = Mock.Of(); @@ -371,14 +371,15 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache); - var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); + var allComposers = typeLoader.GetTypes().ToList(); + var types = allComposers.Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")).ToList(); var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index 9f18d7a061..2d977e89c7 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Composing; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Composing { @@ -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/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index 7b80324686..6c5ccd5510 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -5,10 +5,9 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Composing { @@ -23,7 +22,7 @@ namespace Umbraco.Tests.Composing { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index 4dfaf6871d..ce3cdfac17 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); diff --git a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs index 86d1104b84..4d0135d6c4 100644 --- a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs +++ b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs @@ -6,10 +6,10 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Composing { diff --git a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs index 987fe7410c..390997173b 100644 --- a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs +++ b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs @@ -24,17 +24,19 @@ namespace Umbraco.Tests.Composing var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); + var expectedPackageActions = TypeLoader.GetPackageActions(); composition.WithCollectionBuilder() - .Add(() => TypeLoader.GetPackageActions()); + .Add(() => expectedPackageActions); - Current.Factory = composition.CreateFactory(); + var factory = composition.CreateFactory(); - var actions = Current.PackageActions; + var actions = factory.GetInstance(); Assert.AreEqual(2, actions.Count()); // order is unspecified, but both must be there var hasAction1 = actions.ElementAt(0) is PackageAction1 || actions.ElementAt(1) is PackageAction1; var hasAction2 = actions.ElementAt(0) is PackageAction2 || actions.ElementAt(1) is PackageAction2; + Assert.IsTrue(hasAction1); Assert.IsTrue(hasAction2); } diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 5fe4c241d6..3bdfd09752 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -65,12 +65,15 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = typeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + + typesFound = typeFinder.FindClassesWithAttribute(); + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static IProfilingLogger GetTestProfilingLogger() diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index db35e71c5a..d0181563a8 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, @@ -46,7 +46,8 @@ namespace Umbraco.Tests.Composing //typeof(TabPage).Assembly, typeof(System.Web.Mvc.ActionResult).Assembly, typeof(TypeFinder).Assembly, - typeof(UmbracoContext).Assembly + typeof(UmbracoContext).Assembly, + typeof(CheckBoxListPropertyEditor).Assembly }); @@ -195,7 +196,7 @@ AnotherContentFinder [Test] public void Create_Cached_Plugin_File() { - var types = new[] { typeof(TypeLoader), typeof(TypeLoaderTests), typeof(UmbracoContext) }; + var types = new[] { typeof(TypeLoader), typeof(TypeLoaderTests), typeof(IUmbracoContext) }; var typeList1 = new TypeLoader.TypeList(typeof(object), null); foreach (var type in types) typeList1.Add(type); @@ -274,7 +275,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(37, types.Count()); + Assert.AreEqual(39, types.Count()); } /// diff --git a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs index 2b7deb23f8..b171199e25 100644 --- a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs @@ -1,8 +1,7 @@ using Moq; using NUnit.Framework; -using NUnit.Framework.Internal; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Configurations @@ -32,13 +31,15 @@ namespace Umbraco.Tests.Configurations [TestCase("~/some-wacky/nestedPath", "/MyVirtualDir/NestedVDir/", "some-wacky-nestedpath")] public void Umbraco_Mvc_Area(string path, string rootPath, string outcome) { + var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); + var ioHelper = new IOHelper(TestHelper.GetHostingEnvironment(), globalSettings); var globalSettingsMock = Mock.Get(globalSettings); - globalSettingsMock.Setup(x => x.Path).Returns(() => TestHelper.IOHelper.ResolveUrl(path)); + globalSettingsMock.Setup(x => x.Path).Returns(() => path); - TestHelper.IOHelper.Root = rootPath; - Assert.AreEqual(outcome, globalSettings.GetUmbracoMvcAreaNoCache(IOHelper)); + ioHelper.Root = rootPath; + Assert.AreEqual(outcome, ioHelper.GetUmbracoMvcAreaNoCache()); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementDefaultTests.cs index 5fb896abb2..b14319b1b7 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementDefaultTests.cs @@ -14,27 +14,27 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public override void DisableHtmlEmail() { - Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail == false); + Assert.IsTrue(ContentSettings.DisableHtmlEmail == false); } [Test] public override void Can_Set_Multiple() { - Assert.IsTrue(SettingsSection.Content.Error404Collection.Count() == 1); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).Culture == null); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId == 1); + Assert.IsTrue(ContentSettings.Error404Collection.Count() == 1); + Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(0).Culture == null); + Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(0).ContentId == 1); } [Test] public override void ImageAutoFillProperties() { - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.Count() == 1); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension"); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.Count() == 1); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile"); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth"); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight"); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes"); + Assert.IsTrue(ContentSettings.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension"); } - + } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs index 0245159c6e..9657091859 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs @@ -16,80 +16,80 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void EmailAddress() { - Assert.AreEqual(SettingsSection.Content.NotificationEmailAddress, "robot@umbraco.dk"); + Assert.AreEqual(ContentSettings.NotificationEmailAddress, "robot@umbraco.dk"); } [Test] public virtual void DisableHtmlEmail() { - Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail); + Assert.IsTrue(ContentSettings.DisableHtmlEmail); } [Test] public virtual void Can_Set_Multiple() { - Assert.AreEqual(SettingsSection.Content.Error404Collection.Count(), 3); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).Culture, "default"); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId, 1047); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentId); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentKey); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).Culture, "en-US"); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath, "$site/error [@name = 'error']"); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentId); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentKey); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).Culture, "en-UK"); - Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey, new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC")); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentKey); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentId); + Assert.AreEqual(ContentSettings.Error404Collection.Count(), 3); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(0).Culture, "default"); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(0).ContentId, 1047); + Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(0).HasContentId); + Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(0).HasContentKey); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(1).Culture, "en-US"); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(1).ContentXPath, "$site/error [@name = 'error']"); + Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(1).HasContentId); + Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(1).HasContentKey); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(2).Culture, "en-UK"); + Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(2).ContentKey, new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC")); + Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(2).HasContentKey); + Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(2).HasContentId); } [Test] public void ImageFileTypes() { - Assert.IsTrue(SettingsSection.Content.ImageFileTypes.All(x => "jpeg,jpg,gif,bmp,png,tiff,tif".Split(',').Contains(x))); + Assert.IsTrue(ContentSettings.ImageFileTypes.All(x => "jpeg,jpg,gif,bmp,png,tiff,tif".Split(',').Contains(x))); } - + [Test] public virtual void ImageAutoFillProperties() { - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.Count(), 2); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias, "umbracoFile"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias, "umbracoWidth"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias, "umbracoHeight"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias, "umbracoBytes"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias, "umbracoExtension"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias, "umbracoFile2"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias, "umbracoWidth2"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias, "umbracoHeight2"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias, "umbracoBytes2"); - Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias, "umbracoExtension2"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.Count(), 2); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).Alias, "umbracoFile"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias, "umbracoWidth"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias, "umbracoHeight"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias, "umbracoBytes"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias, "umbracoExtension"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).Alias, "umbracoFile2"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias, "umbracoWidth2"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias, "umbracoHeight2"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias, "umbracoBytes2"); + Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias, "umbracoExtension2"); } - + [Test] public void PreviewBadge() { - Assert.AreEqual(SettingsSection.Content.PreviewBadge, @"
Preview modeClick to end
"); + Assert.AreEqual(ContentSettings.PreviewBadge, @"
Preview modeClick to end
"); } [Test] public void ResolveUrlsFromTextString() { - Assert.IsFalse(SettingsSection.Content.ResolveUrlsFromTextString); + Assert.IsFalse(ContentSettings.ResolveUrlsFromTextString); } [Test] public void MacroErrors() { - Assert.AreEqual(SettingsSection.Content.MacroErrorBehaviour, MacroErrorBehaviour.Inline); + Assert.AreEqual(ContentSettings.MacroErrorBehaviour, MacroErrorBehaviour.Inline); } [Test] public void DisallowedUploadFiles() { - Assert.IsTrue(SettingsSection.Content.DisallowedUploadFiles.All(x => "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd".Split(',').Contains(x))); + Assert.IsTrue(ContentSettings.DisallowedUploadFiles.All(x => "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd".Split(',').Contains(x))); } [Test] public void AllowedUploadFiles() { - Assert.IsTrue(SettingsSection.Content.AllowedUploadFiles.All(x => "jpg,gif,png".Split(',').Contains(x))); + Assert.IsTrue(ContentSettings.AllowedUploadFiles.All(x => "jpg,gif,png".Split(',').Contains(x))); } [Test] @@ -107,16 +107,16 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings TestingDefaults = false; Debug.WriteLine("Extension being tested", extension); - Debug.WriteLine("AllowedUploadFiles: {0}", SettingsSection.Content.AllowedUploadFiles); - Debug.WriteLine("DisallowedUploadFiles: {0}", SettingsSection.Content.DisallowedUploadFiles); + Debug.WriteLine("AllowedUploadFiles: {0}", ContentSettings.AllowedUploadFiles); + Debug.WriteLine("DisallowedUploadFiles: {0}", ContentSettings.DisallowedUploadFiles); - var allowedContainsExtension = SettingsSection.Content.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)); - var disallowedContainsExtension = SettingsSection.Content.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)); + var allowedContainsExtension = ContentSettings.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)); + var disallowedContainsExtension = ContentSettings.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)); Debug.WriteLine("AllowedContainsExtension: {0}", allowedContainsExtension); Debug.WriteLine("DisallowedContainsExtension: {0}", disallowedContainsExtension); - Assert.AreEqual(SettingsSection.Content.IsFileAllowedForUpload(extension), expected); + Assert.AreEqual(ContentSettings.IsFileAllowedForUpload(extension), expected); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs index c0819ad828..cdd5855730 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public override void MaxLogAge() { - Assert.IsTrue(SettingsSection.Logging.MaxLogAge == -1); + Assert.IsTrue(LoggingSettings.MaxLogAge == -1); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs index 31edc87a7c..be495ad9d0 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs @@ -6,11 +6,11 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [TestFixture] public class LoggingElementTests : UmbracoSettingsTests { - + [Test] public virtual void MaxLogAge() { - Assert.IsTrue(SettingsSection.Logging.MaxLogAge == 1440); + Assert.IsTrue(LoggingSettings.MaxLogAge == 1440); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs index b085c4104c..bb4d1efd1e 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs @@ -10,7 +10,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void AddTrailingSlash() { - Assert.IsTrue(SettingsSection.RequestHandler.AddTrailingSlash == true); + Assert.IsTrue(RequestHandlerSettings.AddTrailingSlash == true); } [Test] @@ -18,14 +18,14 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings { var chars = @" ,"",',%,.,;,/,\,:,#,+,*,&,?,æ,ø,å,ä,ö,ü,ß,Ä,Ö,|,<,>"; var items = chars.Split(','); - Assert.AreEqual(items.Length, SettingsSection.RequestHandler.CharCollection.Count()); - Assert.IsTrue(SettingsSection.RequestHandler.CharCollection + Assert.AreEqual(items.Length, RequestHandlerSettings.CharCollection.Count()); + Assert.IsTrue(RequestHandlerSettings.CharCollection .All(x => items.Contains(x.Char))); var vals = @"-,plus,star,ae,oe,aa,ae,oe,ue,ss,ae,oe,-"; var splitVals = vals.Split(','); - Assert.AreEqual(splitVals.Length, SettingsSection.RequestHandler.CharCollection.Count(x => x.Replacement.IsNullOrWhiteSpace() == false)); - Assert.IsTrue(SettingsSection.RequestHandler.CharCollection + Assert.AreEqual(splitVals.Length, RequestHandlerSettings.CharCollection.Count(x => x.Replacement.IsNullOrWhiteSpace() == false)); + Assert.IsTrue(RequestHandlerSettings.CharCollection .All(x => string.IsNullOrEmpty(x.Replacement) || vals.Split(',').Contains(x.Replacement))); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs index 9300c88a67..93f37a1e35 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs @@ -9,127 +9,127 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void KeepUserLoggedIn() { - Assert.IsTrue(SettingsSection.Security.KeepUserLoggedIn == true); + Assert.IsTrue(SecuritySettings.KeepUserLoggedIn == true); } [Test] public void HideDisabledUsersInBackoffice() { - Assert.IsTrue(SettingsSection.Security.HideDisabledUsersInBackoffice == false); + Assert.IsTrue(SecuritySettings.HideDisabledUsersInBackoffice == false); } [Test] public void AllowPasswordReset() { - Assert.IsTrue(SettingsSection.Security.AllowPasswordReset == true); + Assert.IsTrue(SecuritySettings.AllowPasswordReset == true); } [Test] public void AuthCookieDomain() { - Assert.IsTrue(SettingsSection.Security.AuthCookieDomain == null); + Assert.IsTrue(SecuritySettings.AuthCookieDomain == null); } [Test] public void AuthCookieName() { - Assert.IsTrue(SettingsSection.Security.AuthCookieName == "UMB_UCONTEXT"); + Assert.IsTrue(SecuritySettings.AuthCookieName == "UMB_UCONTEXT"); } [Test] public void UserPasswordConfiguration_RequiredLength() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequiredLength == 12); + Assert.IsTrue(UserPasswordConfiguration.RequiredLength == 12); } [Test] public void UserPasswordConfiguration_RequireNonLetterOrDigit() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit == false); + Assert.IsTrue(UserPasswordConfiguration.RequireNonLetterOrDigit == false); } [Test] public void UserPasswordConfiguration_RequireDigit() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireDigit == false); + Assert.IsTrue(UserPasswordConfiguration.RequireDigit == false); } [Test] public void UserPasswordConfiguration_RequireLowercase() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireLowercase == false); + Assert.IsTrue(UserPasswordConfiguration.RequireLowercase == false); } [Test] public void UserPasswordConfiguration_RequireUppercase() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireUppercase == false); + Assert.IsTrue(UserPasswordConfiguration.RequireUppercase == false); } [Test] public void UserPasswordConfiguration_UseLegacyEncoding() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding == false); + Assert.IsTrue(UserPasswordConfiguration.UseLegacyEncoding == false); } [Test] public void UserPasswordConfiguration_HashAlgorithmType() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + Assert.IsTrue(UserPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); } [Test] public void UserPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + Assert.IsTrue(UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); } [Test] public void MemberPasswordConfiguration_RequiredLength() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequiredLength == 12); + Assert.IsTrue(MemberPasswordConfiguration.RequiredLength == 12); } [Test] public void MemberPasswordConfiguration_RequireNonLetterOrDigit() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireNonLetterOrDigit == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireNonLetterOrDigit == false); } [Test] public void MemberPasswordConfiguration_RequireDigit() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireDigit == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireDigit == false); } [Test] public void MemberPasswordConfiguration_RequireLowercase() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireLowercase == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireLowercase == false); } [Test] public void MemberPasswordConfiguration_RequireUppercase() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireUppercase == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireUppercase == false); } [Test] public void MemberPasswordConfiguration_UseLegacyEncoding() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.UseLegacyEncoding == false); + Assert.IsTrue(MemberPasswordConfiguration.UseLegacyEncoding == false); } [Test] public void MemberPasswordConfiguration_HashAlgorithmType() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + Assert.IsTrue(MemberPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); } [Test] public void MemberPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + Assert.IsTrue(MemberPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs index e537bad504..7a82f3c070 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; using NUnit.Framework; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; @@ -22,16 +23,23 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings Debug.WriteLine("Testing defaults? {0}", TestingDefaults); if (TestingDefaults) { - SettingsSection = configuration.GetSection("umbracoConfiguration/defaultSettings") as UmbracoSettingsSection; + Settings = configuration.GetSection("umbracoConfiguration/defaultSettings") as UmbracoSettingsSection; } else { - SettingsSection = configuration.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; + Settings = configuration.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; } - Assert.IsNotNull(SettingsSection); + Assert.IsNotNull(Settings); } + private UmbracoSettingsSection Settings { get; set; } - protected IUmbracoSettingsSection SettingsSection { get; private set; } + protected ILoggingSettings LoggingSettings => Settings.Logging; + protected IWebRoutingSettings WebRoutingSettings => Settings.WebRouting; + protected IRequestHandlerSettings RequestHandlerSettings => Settings.RequestHandler; + protected ISecuritySettings SecuritySettings => Settings.Security; + protected IUserPasswordConfiguration UserPasswordConfiguration => Settings.Security.UserPasswordConfiguration; + protected IMemberPasswordConfiguration MemberPasswordConfiguration => Settings.Security.MemberPasswordConfiguration; + protected IContentSettings ContentSettings => Settings.Content; } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs index b593e9082e..73483ee8d2 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs @@ -14,31 +14,31 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public override void UrlProviderMode() { - Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "Auto"); + Assert.IsTrue(WebRoutingSettings.UrlProviderMode == "Auto"); } [Test] public void DisableAlternativeTemplates() { - Assert.IsTrue(SettingsSection.WebRouting.DisableAlternativeTemplates == false); + Assert.IsTrue(WebRoutingSettings.DisableAlternativeTemplates == false); } [Test] public void ValidateAlternativeTemplates() { - Assert.IsTrue(SettingsSection.WebRouting.ValidateAlternativeTemplates == false); + Assert.IsTrue(WebRoutingSettings.ValidateAlternativeTemplates == false); } [Test] public void DisableFindContentByIdPath() { - Assert.IsTrue(SettingsSection.WebRouting.DisableFindContentByIdPath == false); + Assert.IsTrue(WebRoutingSettings.DisableFindContentByIdPath == false); } [Test] public void DisableRedirectUrlTracking() { - Assert.IsTrue(SettingsSection.WebRouting.DisableRedirectUrlTracking == false); + Assert.IsTrue(WebRoutingSettings.DisableRedirectUrlTracking == false); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs index 0fa1fb6681..8068c1605f 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs @@ -8,19 +8,19 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void TrySkipIisCustomErrors() { - Assert.IsTrue(SettingsSection.WebRouting.TrySkipIisCustomErrors == false); + Assert.IsTrue(WebRoutingSettings.TrySkipIisCustomErrors == false); } [Test] public void InternalRedirectPreservesTemplate() { - Assert.IsTrue(SettingsSection.WebRouting.InternalRedirectPreservesTemplate == false); + Assert.IsTrue(WebRoutingSettings.InternalRedirectPreservesTemplate == false); } [Test] public virtual void UrlProviderMode() { - Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "Auto"); + Assert.IsTrue(WebRoutingSettings.UrlProviderMode == "Auto"); } } } diff --git a/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs b/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs index 4a0c1d0f41..faa15b0077 100644 --- a/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs +++ b/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs @@ -1,6 +1,7 @@ -using NUnit.Framework; -using Umbraco.Web.Trees; +using System; +using NUnit.Framework; using Umbraco.Core; +using Umbraco.Web.Trees; namespace Umbraco.Tests.CoreThings { @@ -22,6 +23,7 @@ namespace Umbraco.Tests.CoreThings Assert.IsFalse(value.HasFlag(test)); } + [Obsolete] [TestCase(TreeUse.Dialog, TreeUse.Dialog, true)] [TestCase(TreeUse.Dialog, TreeUse.Main, false)] [TestCase(TreeUse.Dialog | TreeUse.Main, TreeUse.Dialog, true)] @@ -51,47 +53,5 @@ namespace Umbraco.Tests.CoreThings else Assert.IsFalse(value.HasFlagAny(test)); } - - [TestCase(TreeUse.None, TreeUse.None, TreeUse.None)] - [TestCase(TreeUse.None, TreeUse.Main, TreeUse.Main)] - [TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.Dialog)] - [TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)] - [TestCase(TreeUse.Main, TreeUse.Main, TreeUse.Main)] - [TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] - public void SetFlagTests(TreeUse value, TreeUse flag, TreeUse expected) - { - Assert.AreEqual(expected, value.SetFlag(flag)); - } - - [TestCase(TreeUse.None, TreeUse.None, TreeUse.None)] - [TestCase(TreeUse.None, TreeUse.Main, TreeUse.None)] - [TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.None)] - [TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] - [TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)] - [TestCase(TreeUse.Main, TreeUse.Main, TreeUse.None)] - [TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main)] - [TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] - [TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)] - [TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.None)] - [TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main)] - [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] - public void UnsetFlagTests(TreeUse value, TreeUse flag, TreeUse expected) - { - Assert.AreEqual(expected, value.UnsetFlag(flag)); - } } } diff --git a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs index 120a7f40c9..fbb89b1c5d 100644 --- a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs +++ b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Strings; using Umbraco.Tests.Testing; @@ -15,7 +14,7 @@ namespace Umbraco.Tests.CoreThings { base.Compose(); - Composition.RegisterUnique(f => new DefaultShortStringHelper(f.GetInstance())); + Composition.RegisterUnique(f => new DefaultShortStringHelper(f.GetInstance())); } [Test] diff --git a/src/Umbraco.Tests/CoreThings/UdiTests.cs b/src/Umbraco.Tests/CoreThings/UdiTests.cs index df5d5363e5..2ab39f3664 100644 --- a/src/Umbraco.Tests/CoreThings/UdiTests.cs +++ b/src/Umbraco.Tests/CoreThings/UdiTests.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; -using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Deploy; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; using Umbraco.Core.Serialization; -using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.CoreThings { @@ -24,7 +16,7 @@ namespace Umbraco.Tests.CoreThings [SetUp] public void SetUp() { - UdiParser.ResetUdiTypes(); + UdiParser.ResetUdiTypes(); } [Test] @@ -284,20 +276,20 @@ namespace Umbraco.Tests.CoreThings Assert.IsNull(udi); UdiParser.ResetUdiTypes(); - + // unless we want to know Assert.IsFalse(UdiParser.TryParse("umb://whatever/1234", true, out udi)); Assert.AreEqual(Constants.UdiEntityType.Unknown, udi.EntityType); Assert.AreEqual("Umbraco.Core.UnknownTypeUdi", udi.GetType().FullName); UdiParser.ResetUdiTypes(); - + // not known Assert.IsFalse(UdiParser.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi)); Assert.AreEqual(Constants.UdiEntityType.Unknown, udi.EntityType); Assert.AreEqual("Umbraco.Core.UnknownTypeUdi", udi.GetType().FullName); - // scanned + // scanned UdiParserServiceConnectors.RegisterServiceConnector(); // this is the equivalent of scanning but we'll just manually register this one Assert.IsTrue(UdiParser.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", out udi)); Assert.IsInstanceOf(udi); diff --git a/src/Umbraco.Tests/CoreThings/UriExtensionsTests.cs b/src/Umbraco.Tests/CoreThings/UriExtensionsTests.cs index 5c0ca7a582..2fda471b70 100644 --- a/src/Umbraco.Tests/CoreThings/UriExtensionsTests.cs +++ b/src/Umbraco.Tests/CoreThings/UriExtensionsTests.cs @@ -47,9 +47,8 @@ namespace Umbraco.Tests.CoreThings { var ioHelper = TestHelper.IOHelper; ioHelper.Root = virtualPath; - var globalConfig = SettingsForTests.GenerateMockGlobalSettings(); var source = new Uri(input); - Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath, globalConfig, ioHelper)); + Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath, ioHelper)); } [TestCase("http://www.domain.com/install", true)] diff --git a/src/Umbraco.Tests/IO/FileSystemsTests.cs b/src/Umbraco.Tests/IO/FileSystemsTests.cs index 9358923f72..bf726c6fd8 100644 --- a/src/Umbraco.Tests/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests/IO/FileSystemsTests.cs @@ -5,8 +5,8 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.IO.MediaPathSchemes; using Umbraco.Core.Logging; @@ -14,7 +14,7 @@ using Umbraco.Core.Services; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Core.Composing.CompositionExtensions; -using Umbraco.Core.Strings; +using Current = Umbraco.Web.Composing.Current; using FileSystems = Umbraco.Core.IO.FileSystems; namespace Umbraco.Tests.IO @@ -34,18 +34,18 @@ namespace Umbraco.Tests.IO composition.Register(_ => Mock.Of()); composition.Register(_ => Mock.Of()); - composition.Register(_ => Mock.Of()); + composition.Register(_ => Mock.Of()); composition.Register(_ => TestHelper.ShortStringHelper); composition.Register(_ => TestHelper.IOHelper); composition.RegisterUnique(); composition.RegisterUnique(TestHelper.IOHelper); composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + composition.Configs.Add(SettingsForTests.GenerateMockContentSettings); composition.ComposeFileSystems(); - composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + composition.Configs.Add(SettingsForTests.GenerateMockContentSettings); _factory = composition.CreateFactory(); diff --git a/src/Umbraco.Tests/IO/IoHelperTests.cs b/src/Umbraco.Tests/IO/IoHelperTests.cs index 9458f384ce..6e876c4705 100644 --- a/src/Umbraco.Tests/IO/IoHelperTests.cs +++ b/src/Umbraco.Tests/IO/IoHelperTests.cs @@ -1,9 +1,6 @@ using System; using NUnit.Framework; using Umbraco.Core.IO; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 7f103e13e4..575215c41f 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -5,7 +5,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories.Implement; @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Integration { base.SetUp(); - _h1 = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _h1 = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _h1.BindEvents(true); _events = new List(); diff --git a/src/Umbraco.Tests/Issues/U9560.cs b/src/Umbraco.Tests/Issues/U9560.cs index e3f1ee087d..c750201b0c 100644 --- a/src/Umbraco.Tests/Issues/U9560.cs +++ b/src/Umbraco.Tests/Issues/U9560.cs @@ -4,8 +4,8 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Tests.Testing; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Composing; namespace Umbraco.Tests.Issues { @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Issues contentType.Name = "test"; var propertyType = new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext, "prop") { Name = "Prop", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }; contentType.PropertyTypeCollection.Add(propertyType); - Core.Composing.Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var aliasName = string.Empty; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index db8dc38d6d..04c02f34d2 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -1,9 +1,9 @@ -using System; +using Examine; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Xml.XPath; -using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; @@ -37,9 +37,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache Func> getChildren, Func getProperty, IAppCache appCache, + IVariationContextAccessor variationContextAccessor, PublishedContentTypeCache contentTypeCache, XPathNavigator nav, - bool fromExamine) + bool fromExamine):base(variationContextAccessor) { if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary)); if (getParent == null) throw new ArgumentNullException(nameof(getParent)); @@ -58,10 +59,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder"); ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName"); ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndex.ItemTypeFieldName); + ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", ExamineFieldNames.ItemTypeFieldName); ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType"); - //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); - ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); ValidateAndSetProperty(valueDictionary, val => _creatorId = Int32.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); @@ -160,10 +159,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override string UrlSegment => _urlName; - public override string WriterName => _creatorName; - - public override string CreatorName => _creatorName; - public override int WriterId => _creatorId; public override int CreatorId => _creatorId; @@ -202,8 +197,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private string _urlName; private string _documentTypeAlias; private int _documentTypeId; - //private string _writerName; - private string _creatorName; //private int _writerId; private int _creatorId; private string _path; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs index 5812fde11c..c058c9ac48 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs @@ -5,6 +5,7 @@ using System.Xml; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Composing; namespace Umbraco.Tests.LegacyXmlPublishedCache @@ -108,7 +109,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _previewXml = _xmlStore.GetPreviewXml(contentId, includeSubs); // make sure the preview folder exists - var dir = new DirectoryInfo(Current.IOHelper.MapPath(Constants.SystemDirectories.Preview)); + var dir = new DirectoryInfo(TestHelper.IOHelper.MapPath(Constants.SystemDirectories.Preview)); if (dir.Exists == false) dir.Create(); @@ -122,7 +123,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // get the full path to the preview set private static string GetPreviewSetPath(int userId, Guid previewSet) { - return Current.IOHelper.MapPath(Path.Combine(Constants.SystemDirectories.Preview, userId + "_" + previewSet + ".config")); + return TestHelper.IOHelper.MapPath(Path.Combine(Constants.SystemDirectories.Preview, userId + "_" + previewSet + ".config")); } // deletes files for the user, and files accessed more than one hour ago diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8ce6b10983..e9a193f35f 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -19,6 +20,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IAppCache _appCache; private readonly IGlobalSettings _globalSettings; private readonly RoutesCache _routesCache; + private readonly IVariationContextAccessor _variationContextAccessor; private readonly IDomainCache _domainCache; private readonly PublishedContentTypeCache _contentTypeCache; @@ -34,12 +36,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache IGlobalSettings globalSettings, PublishedContentTypeCache contentTypeCache, // a PublishedContentType cache RoutesCache routesCache, // a RoutesCache + IVariationContextAccessor variationContextAccessor, string previewToken) // a preview token string (or null if not previewing) : base(previewToken.IsNullOrWhiteSpace() == false) { _appCache = appCache; _globalSettings = globalSettings; _routesCache = routesCache; // may be null for unit-testing + _variationContextAccessor = variationContextAccessor; _contentTypeCache = contentTypeCache; _domainCache = domainCache; @@ -264,7 +268,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache while (hasDomains == false && n != null) // n is null at root { // get the url - var urlName = n.UrlSegment(); + var urlName = n.UrlSegment(TestHelper.VariationContextAccessor); pathParts.Add(urlName); // move to parent node @@ -313,13 +317,13 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing) { - return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache); + return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor); } private IEnumerable ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing) { return xmlNodes.Cast() - .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache)); + .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor)); } #endregion diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index f0785da744..1f68f905c7 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -42,13 +42,15 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly PublishedContentTypeCache _contentTypeCache; private readonly IEntityXmlSerializer _entitySerializer; private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IExamineManager _examineManager = new ExamineManager(); // must be specified by the ctor private readonly IAppCache _appCache; public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, IAppCache appCache, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer, - IUmbracoContextAccessor umbracoContextAccessor) + IUmbracoContextAccessor umbracoContextAccessor, IVariationContextAccessor variationContextAccessor) : base(false) { _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); @@ -59,6 +61,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _contentTypeCache = contentTypeCache; _entitySerializer = entitySerializer; _umbracoContextAccessor = umbracoContextAccessor; + _variationContextAccessor = variationContextAccessor; } /// @@ -117,7 +120,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // +(+parentID:-1) +__IndexType:media var criteria = searchProvider.CreateQuery("media"); - var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); var result = filter.Execute(); if (result != null) @@ -227,29 +230,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override bool HasContent(bool preview) { throw new NotImplementedException(); } - private static IExamineManager GetExamineManagerSafe() - { - try - { - return ExamineManager.Instance; - } - catch (TypeInitializationException) - { - return null; - } - } - private ISearcher GetSearchProviderSafe() { if (_searchProvider != null) return _searchProvider; - var eMgr = GetExamineManagerSafe(); - if (eMgr == null) return null; - try { - return eMgr.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index) ? index.GetSearcher() : null; + return _examineManager.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index) ? index.GetSearcher() : null; } catch (FileNotFoundException) { @@ -301,7 +289,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // note that since the use of the wildcard, it automatically escapes it in Lucene. var criteria = searchProvider.CreateQuery("media"); - var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); var result = filter.Execute().FirstOrDefault(); if (result != null) return ConvertFromSearchResult(result); @@ -483,7 +471,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache { //We are going to check for a special field however, that is because in some cases we store a 'Raw' //value in the index such as for xml/html. - var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineIndex.RawFieldPrefix + alias)); + var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineFieldNames.RawFieldPrefix + alias)); return rawValue ?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } @@ -516,7 +504,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache //first check in Examine as this is WAY faster var criteria = searchProvider.CreateQuery("media"); - var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()) + var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()) .OrderBy(new SortableField("sortOrder", SortType.Int)); //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media @@ -557,7 +545,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // was library.GetMedia which had its own cache, but MediaService *also* caches // so, library.GetMedia is gone and now we directly work with MediaService // (code below copied from what library was doing) - var media = Current.Services.MediaService.GetById(parentId); + var media = _mediaService.GetById(parentId); if (media == null) { return Enumerable.Empty(); @@ -672,6 +660,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache GetChildrenMedia, GetProperty, _appCache, + _variationContextAccessor, _contentTypeCache, cacheValues.XPath, // though, outside of tests, that should be null cacheValues.FromExamine diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs index 6990ffe8a2..e763fd8510 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs @@ -1,11 +1,9 @@ -using System; -using System.Text; +using System.Text; using System.Xml.XPath; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; @@ -19,14 +17,16 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly XmlStore _xmlStore; private readonly PublishedContentTypeCache _contentTypeCache; private readonly IUserService _userService; + private readonly IVariationContextAccessor _variationContextAccessor; - public PublishedMemberCache(XmlStore xmlStore, IAppCache requestCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, IUserService userService) + public PublishedMemberCache(XmlStore xmlStore, IAppCache requestCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, IUserService userService, IVariationContextAccessor variationContextAccessor) { _requestCache = requestCache; _memberService = memberService; _xmlStore = xmlStore; _contentTypeCache = contentTypeCache; _userService = userService; + _variationContextAccessor = variationContextAccessor; } public IPublishedContent GetByProviderKey(object key) @@ -39,7 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByProviderKey(key); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _userService).CreateModel(Current.PublishedModelFactory); + return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory); }); } @@ -53,7 +53,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetById(memberId); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _userService).CreateModel(Current.PublishedModelFactory); + return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory); }); } @@ -67,7 +67,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByUsername(username); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _userService).CreateModel(Current.PublishedModelFactory); + return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory); }); } @@ -81,14 +81,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByEmail(email); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _userService).CreateModel(Current.PublishedModelFactory); + return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory); }); } public IPublishedContent GetByMember(IMember member) { var type = _contentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId); - return new PublishedMember(member, type, _userService).CreateModel(Current.PublishedModelFactory); + return new PublishedMember(member, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs index 5d48e9eae3..e967a1ea12 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs @@ -6,8 +6,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache { static class UmbracoContextCache { - static readonly ConditionalWeakTable> Caches - = new ConditionalWeakTable>(); + static readonly ConditionalWeakTable> Caches + = new ConditionalWeakTable>(); public static ConcurrentDictionary Current { diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index f2dbeb954d..b1ee6a7c4d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -26,19 +26,22 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache XmlNode xmlNode, bool isPreviewing, IAppCache appCache, - PublishedContentTypeCache contentTypeCache) + PublishedContentTypeCache contentTypeCache, + IVariationContextAccessor variationContextAccessor): base(variationContextAccessor) { _xmlNode = xmlNode; _isPreviewing = isPreviewing; _appCache = appCache; _contentTypeCache = contentTypeCache; + _variationContextAccessor = variationContextAccessor; } private readonly XmlNode _xmlNode; private readonly bool _isPreviewing; private readonly IAppCache _appCache; // at snapshot/request level (see PublishedContentCache) private readonly PublishedContentTypeCache _contentTypeCache; + private readonly IVariationContextAccessor _variationContextAccessor; private readonly object _initializeLock = new object(); @@ -58,8 +61,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private string _name; private string _docTypeAlias; private int _docTypeId; - private string _writerName; - private string _creatorName; private int _writerId; private int _creatorId; private string _urlName; @@ -155,24 +156,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); - public override string WriterName - { - get - { - EnsureNodeInitialized(); - return _writerName; - } - } - - public override string CreatorName - { - get - { - EnsureNodeInitialized(); - return _creatorName; - } - } - public override int WriterId { get @@ -272,7 +255,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache if (parent == null) return; if (parent.Attributes?.GetNamedItem("isDoc") != null) - _parent = Get(parent, _isPreviewing, _appCache, _contentTypeCache); + _parent = Get(parent, _isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor); _parentInitialized = true; } @@ -298,8 +281,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private void InitializeNode() { InitializeNode(this, _xmlNode, _isPreviewing, - out _id, out _key, out _template, out _sortOrder, out _name, out _writerName, - out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path, + out _id, out _key, out _template, out _sortOrder, out _name, + out _urlName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path, out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties, _contentTypeCache.Get); @@ -308,18 +291,17 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // internal for some benchmarks internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing, - out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName, - out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path, + out int id, out Guid key, out int template, out int sortOrder, out string name, out string urlName, + out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft, out IPublishedContentType contentType, out Dictionary properties, Func getPublishedContentType) { //initialize the out params with defaults: - writerName = null; docTypeAlias = null; id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int); key = default(Guid); - name = writerName = urlName = creatorName = docTypeAlias = path = null; + name = docTypeAlias = urlName = path = null; createDate = updateDate = default(DateTime); isDraft = false; contentType = null; @@ -338,12 +320,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value); if (xmlNode.Attributes.GetNamedItem("nodeName") != null) name = xmlNode.Attributes.GetNamedItem("nodeName").Value; - if (xmlNode.Attributes.GetNamedItem("writerName") != null) - writerName = xmlNode.Attributes.GetNamedItem("writerName").Value; if (xmlNode.Attributes.GetNamedItem("urlName") != null) urlName = xmlNode.Attributes.GetNamedItem("urlName").Value; - if (xmlNode.Attributes.GetNamedItem("creatorName") != null) - creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value; //Added the actual userID, as a user cannot be looked up via full name only... if (xmlNode.Attributes.GetNamedItem("creatorID") != null) @@ -429,7 +407,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var iterator = nav.Select(expr); _children = iterator.Cast() - .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _appCache, _contentTypeCache)) + .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor)) .OrderBy(x => x.SortOrder) .ToList(); @@ -444,12 +422,13 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// A cache. /// A content type cache. /// A umbraco context accessor + /// /// The IPublishedContent corresponding to the Xml cache node. /// Maintains a per-request cache of IPublishedContent items in order to make /// sure that we create only one instance of each for the duration of a request. The /// returned IPublishedContent is a model, if models are enabled. public static IPublishedContent Get(XmlNode node, bool isPreviewing, IAppCache appCache, - PublishedContentTypeCache contentTypeCache) + PublishedContentTypeCache contentTypeCache, IVariationContextAccessor variationContextAccessor) { // only 1 per request @@ -457,12 +436,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var id = attrs?.GetNamedItem("id").Value; if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute."); var key = CacheKeyPrefix + id; // dont bother with preview, wont change during request in Xml cache - return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache)).CreateModel(Current.PublishedModelFactory)); - } - - public static void ClearRequest() - { - Current.AppCaches.RequestCache.ClearByKey(CacheKeyPrefix); + return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache, variationContextAccessor)).CreateModel(Current.PublishedModelFactory)); } private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_"; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs index cbff346c4d..609dcd98b8 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Runtime; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; @@ -38,6 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly ISiteDomainHelper _siteDomainHelper; private readonly IEntityXmlSerializer _entitySerializer; + private readonly IVariationContextAccessor _variationContextAccessor; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IHostingEnvironment _hostingEnvironment; @@ -58,6 +60,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache IShortStringHelper shortStringHelper, ISiteDomainHelper siteDomainHelper, IEntityXmlSerializer entitySerializer, + MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, @@ -103,7 +106,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _mediaService = serviceContext.MediaService; _userService = serviceContext.UserService; _defaultCultureAccessor = defaultCultureAccessor; - + _variationContextAccessor = variationContextAccessor; _requestCache = requestCache; _umbracoContextAccessor = umbracoContextAccessor; _globalSettings = globalSettings; @@ -153,9 +156,9 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var domainCache = new DomainCache(_domainService, _defaultCultureAccessor); return new PublishedSnapshot( - new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache, previewToken), - new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer, _umbracoContextAccessor), - new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache, _userService), + new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache,_variationContextAccessor, previewToken), + new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer, _umbracoContextAccessor, _variationContextAccessor), + new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache, _userService, _variationContextAccessor), domainCache); } @@ -265,5 +268,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } #endregion + + public override string GetStatus() + { + return "Test status"; + } } } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index a744a8d488..238da68370 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -15,12 +15,14 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.Runtime; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Xml; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; @@ -93,7 +95,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _hostingEnvironment = hostingEnvironment; _shortStringHelper = shortStringHelper; - _xmlFileName = Current.IOHelper.MapPath(SystemFiles.GetContentCacheXml(_hostingEnvironment)); + _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(_hostingEnvironment)); if (testing) { @@ -117,7 +119,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _mediaRepository = mediaRepository; _memberRepository = memberRepository; _xmlFileEnabled = false; - _xmlFileName = Current.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); + _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); // do not plug events, we may not have what it takes to handle them } @@ -131,7 +133,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _memberRepository = memberRepository; GetXmlDocument = getXmlDocument ?? throw new ArgumentNullException(nameof(getXmlDocument)); _xmlFileEnabled = false; - _xmlFileName = Current.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); + _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); // do not plug events, we may not have what it takes to handle them } @@ -148,7 +150,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache LongRunning = true, KeepAlive = true, Hosted = false // main domain will take care of stopping the runner (see below) - }, logger); + }, logger, _hostingEnvironment); // create (and add to runner) _persisterTask = new XmlStoreFilePersister(runner, this, logger); diff --git a/src/Umbraco.Tests/Logging/LogviewerTests.cs b/src/Umbraco.Tests/Logging/LogviewerTests.cs index cddd01c178..87cc19a2c6 100644 --- a/src/Umbraco.Tests/Logging/LogviewerTests.cs +++ b/src/Umbraco.Tests/Logging/LogviewerTests.cs @@ -4,8 +4,6 @@ using System; using System.IO; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; using Umbraco.Core.Logging.Viewer; using Umbraco.Tests.TestHelpers; @@ -166,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.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index cf56f407ce..d6ed8f33c2 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; @@ -18,28 +17,21 @@ namespace Umbraco.Tests.Macros [SetUp] public void Setup() { - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); //we DO want cache enabled for these tests var cacheHelper = new AppCaches( new ObjectCacheAppCache(typeFinder), NoAppCache.Instance, new IsolatedCaches(type => new ObjectCacheAppCache(typeFinder))); - //Current.ApplicationContext = new ApplicationContext(cacheHelper, new ProfilingLogger(Mock.Of(), Mock.Of())); - - Current.Reset(); - Current.UnlockConfigs(TestHelper.GetConfigsFactory(), TestHelper.IOHelper); - Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); } - [TestCase("PartialView", true)] - [TestCase("Unknown", false)] - public void Macro_Is_File_Based(string macroTypeString, bool expectedNonNull) + [TestCase("anything", true)] + [TestCase("", false)] + public void Macro_Is_File_Based(string macroSource, bool expectedNonNull) { - var macroType = Enum.Parse(macroTypeString); var model = new MacroModel { - MacroType = macroType, - MacroSource = "anything" + MacroSource = macroSource }; var filename = MacroRenderer.GetMacroFileName(model); if (expectedNonNull) diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs index f08706a9d0..661c9cff0e 100644 --- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs @@ -6,14 +6,12 @@ using NUnit.Framework; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; using Umbraco.Core.Services; using Umbraco.Core.Dashboards; -using Umbraco.Core.IO; using Umbraco.Core.Serialization; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; diff --git a/src/Umbraco.Tests/Misc/UriUtilityTests.cs b/src/Umbraco.Tests/Misc/UriUtilityTests.cs index 33a4b45249..a10d6e2179 100644 --- a/src/Umbraco.Tests/Misc/UriUtilityTests.cs +++ b/src/Umbraco.Tests/Misc/UriUtilityTests.cs @@ -12,6 +12,8 @@ namespace Umbraco.Tests.Misc [TestFixture] public class UriUtilityTests { + + public UriUtility UriUtility { get; } = TestHelper.UriUtility; [TearDown] public void TearDown() { @@ -75,15 +77,15 @@ namespace Umbraco.Tests.Misc { var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var requestMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var requestMock = Mock.Get(settings); requestMock.Setup(x => x.AddTrailingSlash).Returns(trailingSlash); UriUtility.SetAppDomainAppVirtualPath("/"); var expectedUri = NewUri(expectedUrl); var sourceUri = NewUri(sourceUrl); - var resultUri = UriUtility.UriFromUmbraco(sourceUri, globalConfig.Object, settings.RequestHandler); + var resultUri = UriUtility.UriFromUmbraco(sourceUri, globalConfig.Object, settings); Assert.AreEqual(expectedUri.ToString(), resultUri.ToString()); } diff --git a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs index efd59f88bf..bab3a540be 100644 --- a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs +++ b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; @@ -30,7 +29,7 @@ namespace Umbraco.Tests.Models Composition.ComposeFileSystems(); Composition.Register(_ => Mock.Of()); - Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); // all this is required so we can validate properties... var editor = new TextboxPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of(), IOHelper, ShortStringHelper, LocalizedTextService) { Alias = "test" }; diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index c7bb4415bc..07b7ae7cba 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -16,14 +16,13 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Models { @@ -41,7 +40,7 @@ namespace Umbraco.Tests.Models Composition.ComposeFileSystems(); Composition.Register(_ => Mock.Of()); - Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); // all this is required so we can validate properties... var editor = new TextboxPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of(), IOHelper, ShortStringHelper, LocalizedTextService) { Alias = "test" }; @@ -271,7 +270,8 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of())); + var typeFinder = TestHelper.GetTypeFinder(); + var runtimeCache = new ObjectCacheAppCache(typeFinder); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index fe58889d05..70307b9bad 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -2,8 +2,6 @@ using System.Xml.Linq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; diff --git a/src/Umbraco.Tests/Models/ImageProcessorImageUrlGeneratorTest.cs b/src/Umbraco.Tests/Models/ImageProcessorImageUrlGeneratorTest.cs new file mode 100644 index 0000000000..30ead90de9 --- /dev/null +++ b/src/Umbraco.Tests/Models/ImageProcessorImageUrlGeneratorTest.cs @@ -0,0 +1,232 @@ +using System; +using System.Globalization; +using Moq; +using Newtonsoft.Json; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Services; +using Umbraco.Tests.Components; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Models; +using Umbraco.Web; +using Umbraco.Web.PropertyEditors; +using System.Text; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class ImageProcessorImageUrlGeneratorTest + { + private const string MediaPath = "/media/1005/img_0671.jpg"; + private static readonly ImageUrlGenerationOptions.CropCoordinates Crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m); + private static readonly ImageUrlGenerationOptions.FocalPointPosition Focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.80827067669172936m, 0.96m); + private static readonly ImageUrlGenerationOptions.FocalPointPosition Focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.41m, 0.4275m); + private static readonly ImageProcessorImageUrlGenerator Generator = new ImageProcessorImageUrlGenerator(); + + [Test] + public void GetCropUrl_CropAliasTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = Crop, Width = 100, Height = 100 }); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + } + + [Test] + public void GetCropUrl_WidthHeightTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 200, Height = 300 }); + Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString); + } + + [Test] + public void GetCropUrl_FocalPointTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 100, Height = 100 }); + Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString); + } + + [Test] + public void GetCropUrlFurtherOptionsTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" }); + Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); + } + + /// + /// Test that if a crop alias has been specified that doesn't exist the method returns null + /// + [Test] + public void GetCropUrlNullTest() + { + var urlString = Generator.GetImageUrl(null); + Assert.AreEqual(null, urlString); + } + + /// + /// Test that if a crop alias has been specified that doesn't exist the method returns null + /// + [Test] + public void GetCropUrlEmptyTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(null)); + Assert.AreEqual("?mode=crop", urlString); + } + + /// + /// Test the GetCropUrl method on the ImageCropDataSet Model + /// + [Test] + public void GetBaseCropUrlFromModelTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = Crop, Width = 100, Height = 100 }); + Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + } + + /// + /// Test the height ratio mode with predefined crop dimensions + /// + [Test] + public void GetCropUrl_CropAliasHeightRatioModeTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = Crop, Width = 100, HeightRatio = 1 }); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString); + } + + /// + /// Test the height ratio mode with manual width/height dimensions + /// + [Test] + public void GetCropUrl_WidthHeightRatioModeTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 300, HeightRatio = 0.5m }); + Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&heightratio=0.5&width=300", urlString); + } + + /// + /// Test the height ratio mode with width/height dimensions + /// + [Test] + public void GetCropUrl_HeightWidthRatioModeTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Height = 150, WidthRatio = 2 }); + Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&widthratio=2&height=150", urlString); + } + + /// + /// Test that if Crop mode is specified as anything other than Crop the image doesn't use the crop + /// + [Test] + public void GetCropUrl_SpecifiedCropModeTest() + { + var urlStringMin = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Min", Width = 300, Height = 150 }); + var urlStringBoxPad = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "BoxPad", Width = 300, Height = 150 }); + var urlStringPad = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Pad", Width = 300, Height = 150 }); + var urlStringMax = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Max", Width = 300, Height = 150 }); + var urlStringStretch = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Stretch", Width = 300, Height = 150 }); + + Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin); + Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad); + Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad); + Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlStringMax); + Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch); + } + + /// + /// Test for upload property type + /// + [Test] + public void GetCropUrl_UploadTypeTest() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Crop", ImageCropAnchor = "Center", Width = 100, Height = 270 }); + Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString); + } + + /// + /// Test for preferFocalPoint when focal point is centered + /// + [Test] + public void GetCropUrl_PreferFocalPointCenter() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 }); + Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString); + } + + /// + /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point in centre when a width parameter is passed + /// + [Test] + public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); + Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + } + + /// + /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point is custom when a width parameter is passed + /// + [Test] + public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); + Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + } + + /// + /// Test to check if crop ratio is ignored if useCropDimensions is true + /// + [Test] + public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus2, Width = 270, Height = 161 }); + Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString); + } + + /// + /// Test to check if width ratio is returned for a predefined crop without coordinates and focal point in centre when a height parameter is passed + /// + [Test] + public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m }); + Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString); + } + + /// + /// Test to check result when only a width parameter is passed, effectivly a resize only + /// + [Test] + public void GetCropUrl_WidthOnlyParameter() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 }); + Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString); + } + + /// + /// Test to check result when only a height parameter is passed, effectivly a resize only + /// + [Test] + public void GetCropUrl_HeightOnlyParameter() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 }); + Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString); + } + + /// + /// Test to check result when using a background color with padding + /// + [Test] + public void GetCropUrl_BackgroundColorParameter() + { + var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Pad", Width = 400, Height = 400, FurtherOptions = "&bgcolor=fff" }); + Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString); + } + } +} diff --git a/src/Umbraco.Tests/Models/MacroTests.cs b/src/Umbraco.Tests/Models/MacroTests.cs index 429649a6c1..9ebe57a847 100644 --- a/src/Umbraco.Tests/Models/MacroTests.cs +++ b/src/Umbraco.Tests/Models/MacroTests.cs @@ -1,8 +1,6 @@ using System; using System.Linq; using NUnit.Framework; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Tests.TestHelpers; @@ -12,18 +10,10 @@ namespace Umbraco.Tests.Models [TestFixture] public class MacroTests { - [SetUp] - public void Init() - { - Current.Reset(); - Current.UnlockConfigs(TestHelper.GetConfigsFactory(), TestHelper.IOHelper); - Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); - } - [Test] public void Can_Deep_Clone() { - var macro = new Macro(TestHelper.ShortStringHelper, 1, Guid.NewGuid(), true, 3, "test", "Test", false, true, true, "~/script.cshtml", MacroTypes.PartialView); + var macro = new Macro(TestHelper.ShortStringHelper, 1, Guid.NewGuid(), true, 3, "test", "Test", false, true, true, "~/script.cshtml"); macro.Properties.Add(new MacroProperty(6, Guid.NewGuid(), "rewq", "REWQ", 1, "asdfasdf")); var clone = (Macro)macro.DeepClone(); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index 36d467a0be..cc923df4b4 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -3,10 +3,8 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; -using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -18,6 +16,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; using Current = Umbraco.Web.Composing.Current; +using Umbraco.Core.Composing.CompositionExtensions; namespace Umbraco.Tests.Models.Mapping { @@ -35,7 +34,7 @@ namespace Umbraco.Tests.Models.Mapping Composition.ComposeFileSystems(); Composition.Register(_ => Mock.Of()); - Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); // all this is required so we can validate properties... var editor = new TextboxPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of(), IOHelper, ShortStringHelper, LocalizedTextService) { Alias = "test" }; @@ -253,8 +252,8 @@ namespace Umbraco.Tests.Models.Mapping } Assert.AreEqual(contentType.CompositionPropertyGroups.Count(), invariantContent.Tabs.Count() - 1); - Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general/properties"))); - Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); + Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == ServiceContext.TextService.Localize("general/properties"))); + Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == ServiceContext.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); } #region Assertions @@ -349,7 +348,7 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(p.PropertyType.ValidationRegExp, pDto.ValidationRegExp); Assert.AreEqual(p.PropertyType.Description, pDto.Description); Assert.AreEqual(p.PropertyType.Name, pDto.Label); - Assert.AreEqual(Current.Services.DataTypeService.GetDataType(p.PropertyType.DataTypeId), pDto.DataType); + Assert.AreEqual(ServiceContext.DataTypeService.GetDataType(p.PropertyType.DataTypeId), pDto.DataType); Assert.AreEqual(Current.PropertyEditors[p.PropertyType.PropertyEditorAlias], pDto.PropertyEditor); } diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index f312744db6..632f433c5b 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -31,12 +31,10 @@ namespace Umbraco.Tests.Models // and then, this will reset the width, height... because the file does not exist, of course ;-( var logger = Mock.Of(); var scheme = Mock.Of(); - var config = Mock.Of(); - var dataTypeService = Mock.Of(); - var localizationService = Mock.Of(); + var config = Mock.Of(); var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, ShortStringHelper); - var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config, dataTypeService, localizationService, ShortStringHelper); + var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/Models/MemberTests.cs b/src/Umbraco.Tests/Models/MemberTests.cs index 049d7d72a8..e0cd826536 100644 --- a/src/Umbraco.Tests/Models/MemberTests.cs +++ b/src/Umbraco.Tests/Models/MemberTests.cs @@ -13,15 +13,6 @@ namespace Umbraco.Tests.Models [TestFixture] public class MemberTests { - [SetUp] - public void Setup() - { - Current.Reset(); - Current.UnlockConfigs(TestHelper.GetConfigsFactory(), TestHelper.IOHelper); - Current.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); - } - [Test] public void Can_Deep_Clone() { diff --git a/src/Umbraco.Tests/Models/PropertyTypeTests.cs b/src/Umbraco.Tests/Models/PropertyTypeTests.cs index 0df1470169..73fba856be 100644 --- a/src/Umbraco.Tests/Models/PropertyTypeTests.cs +++ b/src/Umbraco.Tests/Models/PropertyTypeTests.cs @@ -1,9 +1,10 @@ using System; using System.Diagnostics; using Newtonsoft.Json; +using System.Linq; +using System.Reflection; using NUnit.Framework; using Umbraco.Core.Models; -using Umbraco.Core.Serialization; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Models @@ -50,9 +51,9 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.ValidationRegExp, pt.ValidationRegExp); Assert.AreEqual(clone.ValueStorageType, pt.ValueStorageType); - //This double verifies by reflection + //This double verifies by reflection (don't test properties marked with [DoNotClone] var allProps = clone.GetType().GetProperties(); - foreach (var propertyInfo in allProps) + foreach (var propertyInfo in allProps.Where(p => p.GetCustomAttribute(false) == null)) { var expected = propertyInfo.GetValue(pt, null); var actual = propertyInfo.GetValue(clone, null); diff --git a/src/Umbraco.Tests/Models/UserTests.cs b/src/Umbraco.Tests/Models/UserTests.cs index ba98c69729..531112e4b6 100644 --- a/src/Umbraco.Tests/Models/UserTests.cs +++ b/src/Umbraco.Tests/Models/UserTests.cs @@ -14,15 +14,7 @@ namespace Umbraco.Tests.Models [TestFixture] public class UserTests { - private IGlobalSettings GlobalSettings { get; } = SettingsForTests.GenerateMockGlobalSettings(); - - [SetUp] - public void Setup() - { - Current.Reset(); - Current.UnlockConfigs(TestHelper.GetConfigsFactory(), TestHelper.IOHelper); - Current.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - } + private IGlobalSettings GlobalSettings { get; } = SettingsForTests.GetDefaultGlobalSettings(); [Test] public void Can_Deep_Clone() @@ -35,7 +27,6 @@ namespace Umbraco.Tests.Models CreateDate = DateTime.Now, Name = "Test", Comments = "comments", - DefaultToLiveEditing = false, Email = "test@test.com", Language = "en", FailedPasswordAttempts = 3, @@ -77,7 +68,6 @@ namespace Umbraco.Tests.Models CreateDate = DateTime.Now, Name = "Test", Comments = "comments", - DefaultToLiveEditing = false, Email = "test@test.com", Language = "en", FailedPasswordAttempts = 3, diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index e07658ea1c..f353ee3c8a 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -1,23 +1,23 @@ using System; using Moq; using NUnit.Framework; -using NUnit.Framework.Internal; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using ILogger = Umbraco.Core.Logging.ILogger; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Models { [TestFixture] public class VariationTests { + private IFactory _factory; private IShortStringHelper ShortStringHelper { get; } = TestHelper.ShortStringHelper; [SetUp] @@ -35,10 +35,9 @@ namespace Umbraco.Tests.Models var configs = TestHelper.GetConfigs(); configs.Add(SettingsForTests.GetDefaultGlobalSettings); - configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + configs.Add(SettingsForTests.GenerateMockContentSettings); - var factory = Mock.Of(); - Current.Factory = factory; + _factory = Mock.Of(); var dataTypeService = Mock.Of(); var localizationService = Mock.Of(); @@ -62,7 +61,7 @@ namespace Umbraco.Tests.Models dataTypeService: dataTypeService, localizedTextService: Mock.Of()); - Mock.Get(factory) + Mock.Get(_factory) .Setup(x => x.GetInstance(It.IsAny())) .Returns(x => { @@ -449,7 +448,7 @@ namespace Umbraco.Tests.Models [Test] public void ContentPublishValuesWithMixedPropertyTypeVariations() { - var propertyValidationService = new PropertyValidationService(Current.Factory.GetInstance(), Current.Factory.GetInstance().DataTypeService); + var propertyValidationService = new PropertyValidationService(_factory.GetInstance(), _factory.GetInstance().DataTypeService); const string langFr = "fr-FR"; // content type varies by Culture @@ -581,7 +580,7 @@ namespace Umbraco.Tests.Models prop.SetValue("a"); Assert.AreEqual("a", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - var propertyValidationService = new PropertyValidationService(Current.Factory.GetInstance(), Current.Factory.GetInstance().DataTypeService); + var propertyValidationService = new PropertyValidationService(_factory.GetInstance(), _factory.GetInstance().DataTypeService); Assert.IsTrue(propertyValidationService.IsPropertyValid(prop)); diff --git a/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs b/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs index e1c3ecc891..6065570b13 100644 --- a/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs +++ b/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using Moq; using NUnit.Framework; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; using Umbraco.ModelsBuilder.Embedded; using Umbraco.ModelsBuilder.Embedded.Building; -using Umbraco.ModelsBuilder.Embedded.Configuration; namespace Umbraco.Tests.ModelsBuilder { @@ -54,7 +54,7 @@ namespace Umbraco.Tests.ModelsBuilder // // This code was generated by a tool. // -// Umbraco.ModelsBuilder v" + version + @" +// Umbraco.ModelsBuilder.Embedded v" + version + @" // // Changes to this file will be lost if the code is regenerated. // @@ -76,14 +76,14 @@ namespace Umbraco.Web.PublishedModels { // helpers #pragma warning disable 0109 // new is redundant - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const string ModelTypeAlias = ""type1""; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const PublishedItemType ModelItemType = PublishedItemType.Content; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new static IPublishedContentType GetModelContentType() => PublishedModelUtility.GetModelContentType(ModelItemType, ModelTypeAlias); - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public static IPublishedPropertyType GetModelPropertyType(Expression> selector) => PublishedModelUtility.GetModelPropertyType(GetModelContentType(), selector); #pragma warning restore 0109 @@ -95,7 +95,7 @@ namespace Umbraco.Web.PublishedModels // properties - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] [ImplementPropertyType(""prop1"")] public string Prop1 => this.Value(""prop1""); } @@ -169,7 +169,7 @@ namespace Umbraco.Web.PublishedModels // // This code was generated by a tool. // -// Umbraco.ModelsBuilder v" + version + @" +// Umbraco.ModelsBuilder.Embedded v" + version + @" // // Changes to this file will be lost if the code is regenerated. // @@ -191,14 +191,14 @@ namespace Umbraco.Web.PublishedModels { // helpers #pragma warning disable 0109 // new is redundant - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const string ModelTypeAlias = ""type1""; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const PublishedItemType ModelItemType = PublishedItemType.Content; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new static IPublishedContentType GetModelContentType() => PublishedModelUtility.GetModelContentType(ModelItemType, ModelTypeAlias); - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public static IPublishedPropertyType GetModelPropertyType(Expression> selector) => PublishedModelUtility.GetModelPropertyType(GetModelContentType(), selector); #pragma warning restore 0109 @@ -210,7 +210,7 @@ namespace Umbraco.Web.PublishedModels // properties - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] [ImplementPropertyType(""foo"")] public global::System.Collections.Generic.IEnumerable Foo => this.Value>(""foo""); } @@ -251,7 +251,7 @@ namespace Umbraco.Web.PublishedModels { Alias = "prop3", ClrName = "Prop3", - ModelClrType = typeof(global::Umbraco.Core.IO.FileSecurityException), + ModelClrType = typeof(global::Umbraco.Core.Exceptions.BootFailedException), }); var types = new[] { type1 }; @@ -272,7 +272,7 @@ namespace Umbraco.Web.PublishedModels Assert.IsTrue(gen.Contains(" global::Umbraco.Core.Models.PublishedContent.IPublishedContent Prop1")); Assert.IsTrue(gen.Contains(" global::System.Text.StringBuilder Prop2")); - Assert.IsTrue(gen.Contains(" global::Umbraco.Core.IO.FileSecurityException Prop3")); + Assert.IsTrue(gen.Contains(" global::Umbraco.Core.Exceptions.BootFailedException Prop3")); } [TestCase("int", typeof(int))] diff --git a/src/Umbraco.Tests/ModelsBuilder/ConfigTests.cs b/src/Umbraco.Tests/ModelsBuilder/ConfigTests.cs index ff49bb3f97..d2acc90439 100644 --- a/src/Umbraco.Tests/ModelsBuilder/ConfigTests.cs +++ b/src/Umbraco.Tests/ModelsBuilder/ConfigTests.cs @@ -1,7 +1,8 @@ using System.Configuration; using NUnit.Framework; -using Umbraco.ModelsBuilder.Embedded.Configuration; -using Umbraco.Tests.TestHelpers; +using Umbraco.Configuration; +using Umbraco.Core; +using Umbraco.Core.Configuration; namespace Umbraco.Tests.ModelsBuilder { @@ -11,22 +12,22 @@ namespace Umbraco.Tests.ModelsBuilder [Test] public void Test1() { - var config = new ModelsBuilderConfig(TestHelper.IOHelper, modelsNamespace: "test1"); + var config = new ModelsBuilderConfig(modelsNamespace: "test1"); Assert.AreEqual("test1", config.ModelsNamespace); } [Test] public void Test2() { - var config = new ModelsBuilderConfig(TestHelper.IOHelper, modelsNamespace: "test2"); + var config = new ModelsBuilderConfig(modelsNamespace: "test2"); Assert.AreEqual("test2", config.ModelsNamespace); } [Test] public void DefaultModelsNamespace() { - var config = new ModelsBuilderConfig(TestHelper.IOHelper); - Assert.AreEqual(ModelsBuilderConfig.DefaultModelsNamespace, config.ModelsNamespace); + var config = new ModelsBuilderConfig(); + Assert.AreEqual(Constants.ModelsBuilder.DefaultModelsNamespace, config.ModelsNamespace); } [TestCase("c:/path/to/root", "~/dir/models", false, "c:\\path\\to\\root\\dir\\models")] @@ -34,7 +35,7 @@ namespace Umbraco.Tests.ModelsBuilder [TestCase("c:/path/to/root", "c:/another/path/to/elsewhere", true, "c:\\another\\path\\to\\elsewhere")] public void GetModelsDirectoryTests(string root, string config, bool acceptUnsafe, string expected) { - Assert.AreEqual(expected, ModelsBuilderConfig.GetModelsDirectory(root, config, acceptUnsafe)); + Assert.AreEqual(expected, ModelsBuilderConfigExtensions.GetModelsDirectory(root, config, acceptUnsafe)); } [TestCase("c:/path/to/root", "~/../../dir/models", false)] @@ -43,7 +44,7 @@ namespace Umbraco.Tests.ModelsBuilder { Assert.Throws(() => { - var modelsDirectory = ModelsBuilderConfig.GetModelsDirectory(root, config, acceptUnsafe); + var modelsDirectory = ModelsBuilderConfigExtensions.GetModelsDirectory(root, config, acceptUnsafe); }); } } diff --git a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs index d7a99aedfd..013e2e5c5f 100644 --- a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -150,8 +150,8 @@ namespace Umbraco.Tests.Packaging { var file1 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/package.manifest"; var file2 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/styles.css"; - var mappedFile1 = Current.IOHelper.MapPath(file1); - var mappedFile2 = Current.IOHelper.MapPath(file2); + var mappedFile1 = IOHelper.MapPath(file1); + var mappedFile2 = IOHelper.MapPath(file2); Directory.CreateDirectory(Path.GetDirectoryName(mappedFile1)); Directory.CreateDirectory(Path.GetDirectoryName(mappedFile2)); File.WriteAllText(mappedFile1, "hello world"); @@ -175,7 +175,7 @@ namespace Umbraco.Tests.Packaging def = PackageBuilder.GetById(def.Id); //re-get Assert.IsNotNull(def.PackagePath); - using (var archive = ZipFile.OpenRead(Current.IOHelper.MapPath(zip))) + using (var archive = ZipFile.OpenRead(IOHelper.MapPath(zip))) { Assert.AreEqual(3, archive.Entries.Count); diff --git a/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs index b0702f0063..d089c4aaa2 100644 --- a/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs +++ b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs @@ -17,7 +17,6 @@ using Umbraco.Tests.Services; using Umbraco.Tests.Services.Importing; using Umbraco.Tests.Testing; using Umbraco.Core.Composing.CompositionExtensions; -using Umbraco.Core.Configuration; using Umbraco.Core.Strings; namespace Umbraco.Tests.Packaging diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 17dbe431d9..52f2365cbc 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -37,7 +37,9 @@ namespace Umbraco.Tests.Persistence _sqlSyntaxProviders = new[] { (ISqlSyntaxProvider) _sqlCeSyntaxProvider }; _logger = Mock.Of(); _umbracoVersion = TestHelper.GetUmbracoVersion(); - _databaseFactory = new UmbracoDatabaseFactory(_logger, new Lazy(() => Mock.Of()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var globalSettings = TestHelper.GetConfigs().Global(); + var connectionStrings = TestHelper.GetConfigs().ConnectionStrings(); + _databaseFactory = new UmbracoDatabaseFactory(_logger, globalSettings, connectionStrings, new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator); } [TearDown] @@ -70,7 +72,7 @@ namespace Umbraco.Tests.Persistence } // re-create the database factory and database context with proper connection string - _databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _logger, new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + _databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _logger, new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator); // test get database type (requires an actual database) using (var database = _databaseFactory.CreateDatabase()) diff --git a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs index 41a65d3c7e..4a9e8e2b26 100644 --- a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs +++ b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling { const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco"; const string providerName = Constants.DbProviderNames.SqlServer; - var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator); using (var database = factory.CreateDatabase()) { @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling { const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco"; const string providerName = Constants.DbProviderNames.SqlServer; - var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator); using (var database = factory.CreateDatabase()) { diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 07f74e29e6..53a632132d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -971,6 +971,32 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Verify_Content_Type_Has_Content_Nodes() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + ContentTypeRepository repository; + var contentRepository = CreateRepository((IScopeAccessor)provider, out repository); + var contentTypeId = NodeDto.NodeIdSeed + 1; + var contentType = repository.Get(contentTypeId); + + // Act + var result = repository.HasContentNodes(contentTypeId); + + var subpage = MockedContent.CreateTextpageContent(contentType, "Test Page 1", contentType.Id); + contentRepository.Save(subpage); + + var result2 = repository.HasContentNodes(contentTypeId); + + // Assert + Assert.That(result, Is.False); + Assert.That(result2, Is.True); + } + } + public void CreateTestData() { //Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed) diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 069d93f409..e1c79b9d26 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -36,9 +36,9 @@ namespace Umbraco.Tests.Persistence.Repositories using (provider.CreateScope()) { var dtRepo = CreateRepository(); - IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) { Name = "dt1" }; + IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper)) { Name = "dt1" }; dtRepo.Save(dataType1); - IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) { Name = "dt2" }; + IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper)) { Name = "dt2" }; dtRepo.Save(dataType2); var ctRepo = Factory.GetInstance(); @@ -106,14 +106,14 @@ namespace Umbraco.Tests.Persistence.Repositories var container2 = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah2", ParentId = container1.Id }; containerRepository.Save(container2); - var dataType = (IDataType) new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container2.Id) + var dataType = (IDataType) new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper), container2.Id) { Name = "dt1" }; repository.Save(dataType); //create a - var dataType2 = (IDataType)new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), dataType.Id) + var dataType2 = (IDataType)new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper), dataType.Id) { Name = "dt2" }; @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Persistence.Repositories var container = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah" }; containerRepository.Save(container); - var dataTypeDefinition = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container.Id) { Name = "test" }; + var dataTypeDefinition = new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper), container.Id) { Name = "test" }; repository.Save(dataTypeDefinition); Assert.AreEqual(container.Id, dataTypeDefinition.ParentId); @@ -205,7 +205,7 @@ namespace Umbraco.Tests.Persistence.Repositories var container = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah" }; containerRepository.Save(container); - IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container.Id) { Name = "test" }; + IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper), container.Id) { Name = "test" }; repository.Save(dataType); // Act @@ -228,7 +228,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (provider.CreateScope()) { var repository = CreateRepository(); - IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) {Name = "test"}; + IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, IOHelper, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper)) {Name = "test"}; repository.Save(dataType); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 17a01b128a..30bf5be17b 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Persistence.Repositories TemplateRepository tr; var ctRepository = CreateRepository(scopeAccessor, out contentTypeRepository, out tr); var editors = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - dtdRepository = new DataTypeRepository(scopeAccessor, appCaches, new Lazy(() => editors), Logger, IOHelper, new Lazy(() => DataTypeService), LocalizedTextService, LocalizationService, ShortStringHelper); + dtdRepository = new DataTypeRepository(scopeAccessor, appCaches, new Lazy(() => editors), Logger); return ctRepository; } @@ -359,7 +359,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out DataTypeRepository dataTypeDefinitionRepository); - var editor = new DecimalPropertyEditor(Logger, ShortStringHelper); + var editor = new DecimalPropertyEditor(Logger, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper); var dtd = new DataType(editor) { Name = "test", DatabaseType = ValueStorageType.Decimal }; dataTypeDefinitionRepository.Save(dtd); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs index 4b9d0436ec..facfb9c012 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs @@ -37,7 +37,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - var macro = new Macro(ShortStringHelper, "test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "test1", "Test", "~/views/macropartials/test.cshtml"); ; Assert.Throws(() => repository.Save(macro)); @@ -168,7 +168,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); // Act - var macro = new Macro(ShortStringHelper, "test", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "test", "Test", "~/views/macropartials/test.cshtml"); macro.Properties.Add(new MacroProperty("test", "Test", 0, "test")); repository.Save(macro); @@ -289,7 +289,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml"); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -314,7 +314,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml"); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -338,7 +338,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "newmacro", "A new macro", "~/views/macropartials/test1.cshtml"); var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); var prop2 = new MacroProperty("blah2", "New2", 3, "test.editor"); @@ -424,9 +424,9 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - repository.Save(new Macro(ShortStringHelper, "test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); - repository.Save(new Macro(ShortStringHelper, "test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); - repository.Save(new Macro(ShortStringHelper, "test3", "Tet3", "~/views/macropartials/test3.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro(ShortStringHelper, "test1", "Test1", "~/views/macropartials/test1.cshtml")); + repository.Save(new Macro(ShortStringHelper, "test2", "Test2", "~/views/macropartials/test2.cshtml")); + repository.Save(new Macro(ShortStringHelper, "test3", "Tet3", "~/views/macropartials/test3.cshtml")); scope.Complete(); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index ca24b051d0..efabddb2cd 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -44,8 +44,9 @@ namespace Umbraco.Tests.Persistence.Repositories var entityRepository = new EntityRepository(scopeAccessor); var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository, entityRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); + var mediaUrlGenerators = new MediaUrlGeneratorCollection(Enumerable.Empty()); var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); - var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences, DataTypeService); + var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, mediaUrlGenerators, dataValueReferences, DataTypeService); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 70c946dabe..d4341cd128 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -981,8 +981,9 @@ namespace Umbraco.Tests.Persistence.Repositories var entityRepository = new EntityRepository(accessor); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); + var mediaUrlGenerators = new MediaUrlGeneratorCollection(Enumerable.Empty()); var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); - var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences, DataTypeService); + var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, mediaUrlGenerators, dataValueReferences, DataTypeService); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 909c6098fb..201b84f29a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -36,8 +36,9 @@ namespace Umbraco.Tests.Persistence.Repositories var entityRepository = new EntityRepository(accessor); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); + var mediaUrlGenerators = new MediaUrlGeneratorCollection(Enumerable.Empty()); var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); - var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences, DataTypeService); + var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, mediaUrlGenerators, dataValueReferences, DataTypeService); return repository; } @@ -422,6 +423,35 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Invalidate_SecurityStamp_On_Username_Change() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + var userGroupRepository = CreateUserGroupRepository(provider); + + var user = CreateAndCommitUserWithGroup(repository, userGroupRepository); + var originalSecurityStamp = user.SecurityStamp; + + // Ensure when user generated a security stamp is present + Assert.That(user.SecurityStamp, Is.Not.Null); + Assert.That(user.SecurityStamp, Is.Not.Empty); + + // Update username + user.Username = user.Username + "UPDATED"; + repository.Save(user); + + // Get the user + var updatedUser = repository.Get(user.Id); + + // Ensure the Security Stamp is invalidated & no longer the same + Assert.AreNotEqual(originalSecurityStamp, updatedUser.SecurityStamp); + } + } + private void AssertPropertyValues(IUser updatedItem, IUser originalUser) { Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); diff --git a/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs new file mode 100644 index 0000000000..24ac9cdbf4 --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -0,0 +1,255 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PropertyEditors; +using static Umbraco.Core.Models.Property; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class DataValueReferenceFactoryCollectionTests + { + IDataTypeService DataTypeService { get; } = Mock.Of(); + private IIOHelper IOHelper { get; } = TestHelper.IOHelper; + ILocalizedTextService LocalizedTextService { get; } = Mock.Of(); + ILocalizationService LocalizationService { get; } = Mock.Of(); + IShortStringHelper ShortStringHelper { get; } = Mock.Of(); + + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory() + { + var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield()); + + + // label does not implement IDataValueReference + var labelEditor = new LabelPropertyEditor( + Mock.Of(), + IOHelper, + DataTypeService, + LocalizedTextService, + LocalizationService, + ShortStringHelper + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(labelEditor.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(labelEditor)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor( + Mock.Of(), + DataTypeService, + LocalizationService, + IOHelper, + ShortStringHelper, + LocalizedTextService + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_Invariant_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor( + Mock.Of(), + DataTypeService, + LocalizationService, + IOHelper, + ShortStringHelper, + LocalizedTextService + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker)) + { + Variations = ContentVariation.Nothing | ContentVariation.Segment + }) + { + Values = new List + { + new PropertyValue + { + EditedValue = trackedUdi1 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Segment = "B", + EditedValue = trackedUdi4 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi1, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString()); + } + + private class TestDataValueReferenceFactory : IDataValueReferenceFactory + { + public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference(); + + public bool IsForEditor(IDataEditor dataEditor) => dataEditor.Alias == Constants.PropertyEditors.Aliases.Label; + + private class TestMediaDataValueReference : IDataValueReference + { + public IEnumerable GetReferences(object value) + { + // This is the same as the media picker, it will just try to parse the value directly as a UDI + + var asString = value is string str ? str : value?.ToString(); + + if (string.IsNullOrEmpty(asString)) yield break; + + if (UdiParser.TryParse(asString, out var udi)) + yield return new UmbracoEntityReference(udi); + } + } + } + } +} diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index ad4e77d81b..919386f491 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -5,9 +5,7 @@ using Newtonsoft.Json; using NUnit.Framework; using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -22,6 +20,9 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Web.Models; using Umbraco.Web; using Umbraco.Web.PropertyEditors; +using System.Text; +using Current = Umbraco.Web.Composing.Current; +using Umbraco.Core.Cache; namespace Umbraco.Tests.PropertyEditors { @@ -85,7 +86,7 @@ namespace Umbraco.Tests.PropertyEditors var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, shortStringHelper); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new ImageCropperPropertyEditor(Mock.Of(), mediaFileSystem, Mock.Of(), Mock.Of(), Mock.Of(), TestHelper.IOHelper, TestHelper.ShortStringHelper, Mock.Of())) { Id = 1 }); + new DataType(new ImageCropperPropertyEditor(Mock.Of(), mediaFileSystem, Mock.Of(), Mock.Of(), Mock.Of(), TestHelper.IOHelper, TestHelper.ShortStringHelper, Mock.Of())) { Id = 1 }); var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); @@ -111,8 +112,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_CropAliasTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true); + Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } /// @@ -121,29 +122,29 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_CropAliasIgnoreWidthHeightTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, width: 50, height: 50); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, width: 50, height: 50); + Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } [Test] public void GetCropUrl_WidthHeightTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 200, height: 300); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300", urlString); } [Test] public void GetCropUrl_FocalPointTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=100&h=100", urlString); } [Test] public void GetCropUrlFurtherOptionsTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff"); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff"); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); } /// @@ -152,7 +153,7 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrlNullTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Banner", useCropDimensions: true); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Banner", useCropDimensions: true); Assert.AreEqual(null, urlString); } @@ -163,8 +164,8 @@ namespace Umbraco.Tests.PropertyEditors public void GetBaseCropUrlFromModelTest() { var cropDataSet = CropperJson1.DeserializeImageCropperValue(); - var urlString = cropDataSet.GetCropUrl("thumb"); - Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + var urlString = cropDataSet.GetCropUrl("thumb", new TestImageUrlGenerator()); + Assert.AreEqual("?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } /// @@ -173,8 +174,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_CropAliasHeightRatioModeTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode:ImageCropRatioMode.Height); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&heightratio=1", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode:ImageCropRatioMode.Height); + Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&hr=1&w=100", urlString); } /// @@ -183,8 +184,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_WidthHeightRatioModeTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode:ImageCropRatioMode.Height); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=300&heightratio=0.5", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode:ImageCropRatioMode.Height); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&hr=0.5&w=300", urlString); } /// @@ -193,8 +194,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_HeightWidthRatioModeTest() { - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&height=150&widthratio=2", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&wr=2&h=150", urlString); } /// @@ -203,17 +204,17 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_SpecifiedCropModeTest() { - var urlStringMin = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Min); - var urlStringBoxPad = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.BoxPad); - var urlStringPad = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Pad); - var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode:ImageCropMode.Max); - var urlStringStretch = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Stretch); + var urlStringMin = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Min); + var urlStringBoxPad = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.BoxPad); + var urlStringPad = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Pad); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode:ImageCropMode.Max); + var urlStringStretch = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Stretch); - Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin); - Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad); - Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad); - Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlString); - Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch); + Assert.AreEqual(MediaPath + "?m=min&w=300&h=150", urlStringMin); + Assert.AreEqual(MediaPath + "?m=boxpad&w=300&h=150", urlStringBoxPad); + Assert.AreEqual(MediaPath + "?m=pad&w=300&h=150", urlStringPad); + Assert.AreEqual(MediaPath + "?m=max&w=300&h=150", urlString); + Assert.AreEqual(MediaPath + "?m=stretch&w=300&h=150", urlStringStretch); } /// @@ -222,8 +223,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void GetCropUrl_UploadTypeTest() { - var urlString = MediaPath.GetCropUrl(width: 100, height: 270, imageCropMode: ImageCropMode.Crop, imageCropAnchor: ImageCropAnchor.Center); - Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), width: 100, height: 270, imageCropMode: ImageCropMode.Crop, imageCropAnchor: ImageCropAnchor.Center); + Assert.AreEqual(MediaPath + "?m=crop&a=center&w=100&h=270", urlString); } /// @@ -234,8 +235,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint:true); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint:true); + Assert.AreEqual(MediaPath + "?m=defaultcrop&w=300&h=150", urlString); } /// @@ -246,8 +247,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200); + Assert.AreEqual(MediaPath + "?m=defaultcrop&hr=0.5962962962962962962962962963&w=200", urlString); } /// @@ -258,8 +259,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200); - Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200); + Assert.AreEqual(MediaPath + "?f=0.41x0.4275&hr=0.5962962962962962962962962963&w=200", urlString); } /// @@ -270,8 +271,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true); - Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true); + Assert.AreEqual(MediaPath + "?f=0.41x0.4275&w=270&h=161", urlString); } /// @@ -282,8 +283,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", height: 200); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", height: 200); + Assert.AreEqual(MediaPath + "?m=defaultcrop&wr=1.6770186335403726708074534161&h=200", urlString); } /// @@ -294,8 +295,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, width: 200); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 200); + Assert.AreEqual(MediaPath + "?m=defaultcrop&w=200", urlString); } /// @@ -306,8 +307,8 @@ namespace Umbraco.Tests.PropertyEditors { const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, height: 200); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, height: 200); + Assert.AreEqual(MediaPath + "?m=defaultcrop&h=200", urlString); } /// @@ -318,8 +319,55 @@ namespace Umbraco.Tests.PropertyEditors { var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + MediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff"); - Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff"); + Assert.AreEqual(MediaPath + "?m=pad&w=400&h=400&bgcolor=fff", urlString); + } + + internal class TestImageUrlGenerator : IImageUrlGenerator + { + public string GetImageUrl(ImageUrlGenerationOptions options) + { + var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty); + + if (options.FocalPoint != null) + { + imageProcessorUrl.Append("?f="); + imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)); + imageProcessorUrl.Append("x"); + imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); + } + else if (options.Crop != null) + { + imageProcessorUrl.Append("?c="); + imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); + imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); + } + else if (options.DefaultCrop) + { + imageProcessorUrl.Append("?m=defaultcrop"); + } + else + { + imageProcessorUrl.Append("?m=" + options.ImageCropMode.ToString().ToLower()); + if (options.ImageCropAnchor != null)imageProcessorUrl.Append("&a=" + options.ImageCropAnchor.ToString().ToLower()); + } + + var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&f="); + if (options.Quality != null && hasFormat == false) imageProcessorUrl.Append("&q=" + options.Quality); + if (options.HeightRatio != null) imageProcessorUrl.Append("&hr=" + options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture)); + if (options.WidthRatio != null) imageProcessorUrl.Append("&wr=" + options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture)); + if (options.Width != null) imageProcessorUrl.Append("&w=" + options.Width); + if (options.Height != null) imageProcessorUrl.Append("&h=" + options.Height); + if (options.UpScale == false) imageProcessorUrl.Append("&u=no"); + if (options.AnimationProcessMode != null) imageProcessorUrl.Append("&apm=" + options.AnimationProcessMode); + if (options.FurtherOptions != null) imageProcessorUrl.Append(options.FurtherOptions); + if (options.Quality != null && hasFormat) imageProcessorUrl.Append("&q=" + options.Quality); + if (options.CacheBusterValue != null) imageProcessorUrl.Append("&r=").Append(options.CacheBusterValue); + + return imageProcessorUrl.ToString(); + } } } } diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs index 134a1e3711..6301be051d 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -8,10 +8,10 @@ using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.PropertyEditors { @@ -28,7 +28,7 @@ namespace Umbraco.Tests.PropertyEditors var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); register.Register(_ - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()))); + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()))); Current.Factory = composition.CreateFactory(); } diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 43f42455af..7a456f427a 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -15,13 +15,14 @@ using Umbraco.Core.Strings; using Umbraco.Tests.Components; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.PublishedCache; namespace Umbraco.Tests.Published { [TestFixture] - public class ConvertersTests + public class ConvertersTests : UmbracoTestBase { #region SimpleConverter1 @@ -178,7 +179,7 @@ namespace Umbraco.Tests.Published [Test] public void SimpleConverter3Test() { - Current.Reset(); + // Current.Reset(); var register = TestHelper.GetRegister(); var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); @@ -194,7 +195,7 @@ namespace Umbraco.Tests.Published }); register.Register(f => factory); - Current.Factory = composition.CreateFactory(); + var registerFactory = composition.CreateFactory(); var cacheMock = new Mock(); var cacheContent = new Dictionary(); @@ -205,7 +206,7 @@ namespace Umbraco.Tests.Published publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); register.Register(f => publishedSnapshotAccessorMock.Object); - var converters = Current.Factory.GetInstance(); + var converters = registerFactory.GetInstance(); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(Mock.Of(), Mock.Of(), Mock.Of(),Mock.Of(), Mock.Of())) { Id = 1 }, @@ -236,8 +237,9 @@ namespace Umbraco.Tests.Published Properties = new[] { new SolidPublishedProperty { Alias = "prop2", SolidHasValue = true, SolidValue = "1003" } } }; - cacheContent[cnt1.Id] = cnt1.CreateModel(Current.PublishedModelFactory); - cacheContent[cnt2.Id] = cnt2.CreateModel(Current.PublishedModelFactory); + var publishedModelFactory = registerFactory.GetInstance(); + cacheContent[cnt1.Id] = cnt1.CreateModel(publishedModelFactory); + cacheContent[cnt2.Id] = cnt2.CreateModel(publishedModelFactory); // can get the actual property Clr type // ie ModelType gets properly mapped by IPublishedContentModelFactory diff --git a/src/Umbraco.Tests/Published/ModelTypeTests.cs b/src/Umbraco.Tests/Published/ModelTypeTests.cs index 1dab67b351..f698c20fa2 100644 --- a/src/Umbraco.Tests/Published/ModelTypeTests.cs +++ b/src/Umbraco.Tests/Published/ModelTypeTests.cs @@ -53,7 +53,7 @@ namespace Umbraco.Tests.Published // there's an "*" there because the arrays are not true SZArray - but that changes when we map Assert.AreEqual("{alias1}[*]", ModelType.For("alias1").MakeArrayType().FullName); // note the inner assembly qualified name - Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[{alias1}[*], Umbraco.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]]", typeof(IEnumerable<>).MakeGenericType(ModelType.For("alias1").MakeArrayType()).FullName); + Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[{alias1}[*], Umbraco.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]]", typeof(IEnumerable<>).MakeGenericType(ModelType.For("alias1").MakeArrayType()).FullName); } [Test] diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index f3e5b500af..3bb2c0a5e4 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -5,8 +5,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; +using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -15,20 +14,18 @@ using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Web; -using Umbraco.Web.Models; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; +using Umbraco.Tests.Testing; namespace Umbraco.Tests.Published { [TestFixture] - public class NestedContentTests + public class NestedContentTests : UmbracoTestBase { private (IPublishedContentType, IPublishedContentType) CreateContentTypes() { - Current.Reset(); - var logger = Mock.Of(); var profiler = Mock.Of(); var proflog = new ProfilingLogger(logger, profiler); diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 8f8e4ae7a9..769985d515 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -12,13 +12,14 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.PublishedCache; namespace Umbraco.Tests.Published { [TestFixture] - public class PropertyCacheLevelTests + public class PropertyCacheLevelTests : UmbracoTestBase { [TestCase(PropertyCacheLevel.None, 2)] [TestCase(PropertyCacheLevel.Element, 1)] @@ -126,7 +127,7 @@ namespace Umbraco.Tests.Published var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var elementsCache = new FastDictionaryAppCache(typeFinder); var snapshotCache = new FastDictionaryAppCache(typeFinder); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 2f10b23f5a..de411c96ec 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -9,6 +9,8 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Hosting; +using Umbraco.Core.Install; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -18,15 +20,16 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Tests.Common; using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.PublishedContent { @@ -34,6 +37,7 @@ namespace Umbraco.Tests.PublishedContent public class NuCacheChildrenTests { private IPublishedModelFactory PublishedModelFactory { get; } = new NoopPublishedModelFactory(); + private IVariationContextAccessor VariationContextAccessor { get; } = TestHelper.VariationContextAccessor; private IPublishedSnapshotService _snapshotService; private IVariationContextAccessor _variationAccesor; @@ -57,9 +61,9 @@ namespace Umbraco.Tests.PublishedContent var configs = TestHelper.GetConfigs(); Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); - var globalSettings = new GlobalSettings(TestHelper.IOHelper); + var globalSettings = new GlobalSettings(); var hostingEnvironment = Mock.Of(); - configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + configs.Add(TestHelpers.SettingsForTests.GenerateMockContentSettings); configs.Add(() => globalSettings); Mock.Get(factory).Setup(x => x.GetInstance(typeof(IPublishedModelFactory))).Returns(PublishedModelFactory); @@ -128,7 +132,7 @@ namespace Umbraco.Tests.PublishedContent // create a published content type factory var contentTypeFactory = new PublishedContentTypeFactory( - Mock.Of(), + PublishedModelFactory, new PropertyValueConverterCollection(Array.Empty()), dataTypeService); @@ -139,7 +143,9 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); + var settings = Mock.Of(); + // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; @@ -148,7 +154,6 @@ namespace Umbraco.Tests.PublishedContent runtime, serviceContext, contentTypeFactory, - null, _snapshotAccessor, _variationAccesor, Mock.Of(), @@ -160,11 +165,13 @@ namespace Umbraco.Tests.PublishedContent _source, globalSettings, Mock.Of(), - Mock.Of(), + PublishedModelFactory, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), typeFinder, hostingEnvironment, - new MockShortStringHelper()); + new MockShortStringHelper(), + TestHelper.IOHelper, + settings); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); @@ -400,19 +407,19 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); - documents = snapshot.Content.GetById(3).Children().ToArray(); + documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N10"); - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N11", "N12"); - documents = snapshot.Content.GetById(10).Children().ToArray(); + documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); AssertDocuments(documents); } @@ -478,7 +485,7 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3", "N10"); - documents = snapshot.Content.GetById(3).Children().ToArray(); + documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); AssertDocuments(documents); Assert.IsNull(snapshot.Content.GetById(10).Parent); @@ -520,7 +527,7 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N2", "N3"); - documents = snapshot.Content.GetById(10).Children().ToArray(); + documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N1"); Assert.AreEqual(10, snapshot.Content.GetById(1).Parent?.Id); @@ -597,7 +604,7 @@ namespace Umbraco.Tests.PublishedContent _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); // changes that *I* make are immediately visible on the current snapshot - var documents = snapshot.Content.GetById(kit.Node.ParentContentId).Children().ToArray(); + var documents = snapshot.Content.GetById(kit.Node.ParentContentId).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N7", "N9", "N8"); } @@ -696,10 +703,10 @@ namespace Umbraco.Tests.PublishedContent }, out _, out _); // changes that *I* make are immediately visible on the current snapshot - var documents = snapshot.Content.GetById(1).Children().ToArray(); + var documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N7", "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9", "N8"); Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id); @@ -721,15 +728,15 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-en-US"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N7-en-US"); //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N10-en-US", "N11"); //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(7).Children().ToArray(); + documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N12-en-US", "N13"); //TEST with fr-fr variation context @@ -739,15 +746,15 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-fr-FR"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N7-fr-FR"); //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N10-fr-FR", "N11"); //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(7).Children().ToArray(); + documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N12-fr-FR", "N13"); //TEST specific cultures @@ -755,19 +762,19 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot("fr-FR").ToArray(); AssertDocuments(documents, "N1-fr-FR"); - documents = snapshot.Content.GetById(1).Children("fr-FR").ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor, "fr-FR").ToArray(); AssertDocuments(documents, "N4", "N7-fr-FR"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(1).Children("").ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor, "").ToArray(); AssertDocuments(documents, "N4"); //Only returns invariant since that is what was requested - documents = snapshot.Content.GetById(4).Children("fr-FR").ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor, "fr-FR").ToArray(); AssertDocuments(documents, "N10-fr-FR", "N11"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(4).Children("").ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor, "").ToArray(); AssertDocuments(documents, "N11"); //Only returns invariant since that is what was requested - documents = snapshot.Content.GetById(7).Children("fr-FR").ToArray(); + documents = snapshot.Content.GetById(7).Children(_variationAccesor, "fr-FR").ToArray(); AssertDocuments(documents, "N12-fr-FR", "N13"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(7).Children("").ToArray(); + documents = snapshot.Content.GetById(7).Children(_variationAccesor, "").ToArray(); AssertDocuments(documents, "N13"); //Only returns invariant since that is what was requested //TEST without variation context @@ -783,15 +790,15 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot("fr-FR").ToArray(); Assert.AreEqual(1, documents.Length); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4"); //Get the invariant and list children - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N11"); //Get the variant and list children - documents = snapshot.Content.GetById(7).Children().ToArray(); + documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N13"); } @@ -808,19 +815,19 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-en-US", "N2-en-US", "N3-en-US"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4-en-US", "N5-en-US", "N6-en-US"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9-en-US", "N8-en-US", "N7-en-US"); - documents = snapshot.Content.GetById(3).Children().ToArray(); + documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N10-en-US"); - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N11-en-US", "N12-en-US"); - documents = snapshot.Content.GetById(10).Children().ToArray(); + documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); AssertDocuments(documents); @@ -829,26 +836,26 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-fr-FR", "N3-fr-FR"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4-fr-FR", "N6-fr-FR"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9-fr-FR", "N7-fr-FR"); - documents = snapshot.Content.GetById(3).Children().ToArray(); + documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N10-fr-FR"); - documents = snapshot.Content.GetById(4).Children().ToArray(); + documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N12-fr-FR"); - documents = snapshot.Content.GetById(10).Children().ToArray(); + documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); AssertDocuments(documents); - documents = snapshot.Content.GetById(1).Children("*").ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor, "*").ToArray(); AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); - documents = snapshot.Content.GetById(1).Children("en-US").ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor, "en-US").ToArray(); AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); @@ -878,10 +885,10 @@ namespace Umbraco.Tests.PublishedContent var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); // notify @@ -895,10 +902,10 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N8", "N7"); // notify @@ -912,7 +919,7 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N2"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents); } @@ -924,13 +931,21 @@ namespace Umbraco.Tests.PublishedContent var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshotService = (PublishedSnapshotService)_snapshotService; + var contentStore = snapshotService.GetContentStore(); + + var parentNodes = contentStore.Test.GetValues(1); + var parentNode = parentNodes[0]; + AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6); + Assert.AreEqual(1, parentNode.gen); + var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); // notify @@ -940,14 +955,25 @@ namespace Umbraco.Tests.PublishedContent new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshNode), }, out _, out _); + parentNodes = contentStore.Test.GetValues(1); + Assert.AreEqual(2, parentNodes.Length); + parentNode = parentNodes[1]; // get the first gen + AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6); // the structure should have remained the same + Assert.AreEqual(1, parentNode.gen); + parentNode = parentNodes[0]; // get the latest gen + AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6); // the structure should have remained the same + Assert.AreEqual(2, parentNode.gen); + documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children().ToArray(); + documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children().ToArray(); + documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); + + } [Test] @@ -1319,7 +1345,7 @@ namespace Umbraco.Tests.PublishedContent { Assert.AreEqual(names.Length, documents.Length); for (var i = 0; i < names.Length; i++) - Assert.AreEqual(names[i], documents[i].Name(culture)); + Assert.AreEqual(names[i], documents[i].Name(_variationAccesor, culture)); } } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index c0ed53d281..096f2dcf59 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; +using Umbraco.Core.Install; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -17,15 +18,16 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Tests.Common; using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.PublishedContent { @@ -52,8 +54,8 @@ namespace Umbraco.Tests.PublishedContent var configs = TestHelper.GetConfigs(); Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); - var globalSettings = new GlobalSettings(TestHelper.IOHelper); - configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + var globalSettings = new GlobalSettings(); + configs.Add(TestHelpers.SettingsForTests.GenerateMockContentSettings); configs.Add(() => globalSettings); var publishedModelFactory = new NoopPublishedModelFactory(); @@ -175,14 +177,15 @@ namespace Umbraco.Tests.PublishedContent // create a published content type factory var contentTypeFactory = new PublishedContentTypeFactory( - Mock.Of(), + publishedModelFactory, new PropertyValueConverterCollection(Array.Empty()), dataTypeService); // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); + var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; @@ -191,7 +194,6 @@ namespace Umbraco.Tests.PublishedContent runtime, serviceContext, contentTypeFactory, - null, new TestPublishedSnapshotAccessor(), _variationAccesor, Mock.Of(), @@ -203,11 +205,13 @@ namespace Umbraco.Tests.PublishedContent dataSource, globalSettings, Mock.Of(), - Mock.Of(), + publishedModelFactory, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), typeFinder, TestHelper.GetHostingEnvironment(), - new MockShortStringHelper()); + new MockShortStringHelper(), + TestHelper.IOHelper, + settings); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); @@ -232,30 +236,30 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK")); - Assert.IsNull(publishedContent.Name()); // no invariant name for varying content - Assert.AreEqual("name-fr1", publishedContent.Name("fr-FR")); - Assert.AreEqual("name-uk1", publishedContent.Name("en-UK")); + Assert.IsNull(publishedContent.Name(_variationAccesor)); // no invariant name for varying content + Assert.AreEqual("name-fr1", publishedContent.Name(_variationAccesor, "fr-FR")); + Assert.AreEqual("name-uk1", publishedContent.Name(_variationAccesor, "en-UK")); var draftContent = snapshot.Content.GetById(true, 1); Assert.AreEqual("val2", draftContent.Value("prop")); Assert.AreEqual("val-fr2", draftContent.Value("prop", "fr-FR")); Assert.AreEqual("val-uk2", draftContent.Value("prop", "en-UK")); - Assert.IsNull(draftContent.Name()); // no invariant name for varying content - Assert.AreEqual("name-fr2", draftContent.Name("fr-FR")); - Assert.AreEqual("name-uk2", draftContent.Name("en-UK")); + Assert.IsNull(draftContent.Name(_variationAccesor)); // no invariant name for varying content + Assert.AreEqual("name-fr2", draftContent.Name(_variationAccesor, "fr-FR")); + Assert.AreEqual("name-uk2", draftContent.Name(_variationAccesor, "en-UK")); // now french is default _variationAccesor.VariationContext = new VariationContext("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); - Assert.AreEqual("name-fr1", publishedContent.Name()); - Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.CultureDate()); + Assert.AreEqual("name-fr1", publishedContent.Name(_variationAccesor)); + Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.CultureDate(_variationAccesor)); // now uk is default _variationAccesor.VariationContext = new VariationContext("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); - Assert.AreEqual("name-uk1", publishedContent.Name()); - Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.CultureDate()); + Assert.AreEqual("name-uk1", publishedContent.Name(_variationAccesor)); + Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.CultureDate(_variationAccesor)); // invariant needs to be retrieved explicitly, when it's not default Assert.AreEqual("val1", publishedContent.Value("prop", culture: "")); @@ -275,7 +279,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.GetPropertyType("prop").Variations); // now, "no culture" means "invariant" - Assert.AreEqual("It Works1!", againContent.Name()); + Assert.AreEqual("It Works1!", againContent.Name(_variationAccesor)); Assert.AreEqual("val1", againContent.Value("prop")); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index f01c1f0b6e..91e662d46b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -4,8 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -13,6 +12,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; using Umbraco.Web; using PublishedContentExtensions = Umbraco.Web.PublishedContentExtensions; @@ -53,8 +53,8 @@ namespace Umbraco.Tests.PublishedContent {"NodeTypeAlias", "NodeTypeAlias"}, {"CreateDate", "CreateDate"}, {"UpdateDate", "UpdateDate"}, - {"CreatorName", "CreatorName"}, - {"WriterName", "WriterName"}, + {"CreatorId", "CreatorId"}, + {"WriterId", "WriterId"}, {"Url", "Url"} }; foreach (var f in userFields.Where(f => !allFields.ContainsKey(f.Key))) @@ -79,7 +79,7 @@ namespace Umbraco.Tests.PublishedContent public void To_DataTable() { var doc = GetContent(true, 1); - var dt = doc.ChildrenAsTable(Current.Services); + var dt = doc.ChildrenAsTable(ServiceContext); Assert.AreEqual(11, dt.Columns.Count); Assert.AreEqual(3, dt.Rows.Count); @@ -102,7 +102,7 @@ namespace Umbraco.Tests.PublishedContent var c = (SolidPublishedContent)doc.Children.ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); - var dt = doc.ChildrenAsTable(Current.Services, "Child"); + var dt = doc.ChildrenAsTable(ServiceContext, "Child"); Assert.AreEqual(11, dt.Columns.Count); Assert.AreEqual(2, dt.Rows.Count); @@ -118,7 +118,7 @@ namespace Umbraco.Tests.PublishedContent public void To_DataTable_No_Rows() { var doc = GetContent(false, 1); - var dt = doc.ChildrenAsTable(Current.Services); + var dt = doc.ChildrenAsTable(ServiceContext); //will return an empty data table Assert.AreEqual(0, dt.Columns.Count); Assert.AreEqual(0, dt.Rows.Count); @@ -136,7 +136,6 @@ namespace Umbraco.Tests.PublishedContent { CreateDate = DateTime.Now, CreatorId = 1, - CreatorName = "Shannon", Id = 3, SortOrder = 4, TemplateId = 5, @@ -146,7 +145,6 @@ namespace Umbraco.Tests.PublishedContent Name = "Page" + Guid.NewGuid(), Version = Guid.NewGuid(), WriterId = 1, - WriterName = "Shannon", Parent = null, Level = 1, Children = new List() diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index 85e880e8bb..3cff4d4e9d 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using NUnit.Framework; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.Testing; @@ -12,7 +13,7 @@ namespace Umbraco.Tests.PublishedContent [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class PublishedContentExtensionTests : PublishedContentTestBase { - private UmbracoContext _ctx; + private IUmbracoContext _ctx; private string _xmlContent = ""; private bool _createContentTypes = true; private Dictionary _contentTypes; @@ -73,7 +74,7 @@ namespace Umbraco.Tests.PublishedContent _ctx = GetUmbracoContext("/", 1, null, true); if (_createContentTypes) { - var contentTypeService = Current.Services.ContentTypeService; + var contentTypeService = ServiceContext.ContentTypeService; var baseType = new ContentType(ShortStringHelper, -1) { Alias = "base", Name = "Base" }; const string contentTypeAlias = "inherited"; var inheritedType = new ContentType(ShortStringHelper, baseType, contentTypeAlias) { Alias = contentTypeAlias, Name = "Inherited" }; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 26b56a07d4..99633b141f 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -128,7 +128,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-1", Path = "/1", Level = 1, - Url = "/content-1", ParentId = -1, ChildIds = new[] { 2 }, Properties = new Collection @@ -145,7 +144,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-2", Path = "/1/2", Level = 2, - Url = "/content-1/content-2", ParentId = 1, ChildIds = new int[] { 3 }, Properties = new Collection @@ -170,7 +168,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-3", Path = "/1/2/3", Level = 3, - Url = "/content-1/content-2/content-3", ParentId = 2, ChildIds = new int[] { }, Properties = new Collection diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 440474ae74..8f62320a65 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -7,6 +7,8 @@ using Umbraco.Web; using Umbraco.Core; using Umbraco.Tests.Testing; using Umbraco.Web.Composing; +using Moq; +using Examine; namespace Umbraco.Tests.PublishedContent { @@ -33,7 +35,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-1", Path = "/1", Level = 1, - Url = "/content-1", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -57,7 +58,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-2", Path = "/2", Level = 1, - Url = "/content-2", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -81,7 +81,6 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "content-2sub", Path = "/3", Level = 1, - Url = "/content-2sub", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -102,7 +101,7 @@ namespace Umbraco.Tests.PublishedContent public void First() { var content = Current.UmbracoContext.Content.GetAtRoot().First(); - Assert.AreEqual("Content 1", content.Name()); + Assert.AreEqual("Content 1", content.Name(VariationContextAccessor)); } [Test] @@ -201,7 +200,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void PublishedContentQueryTypedContentList() { - var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor); + var examineManager = new Mock(); + var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor, examineManager.Object); var result = query.Content(new[] { 1, 2, 4 }).ToArray(); Assert.AreEqual(2, result.Length); Assert.AreEqual(1, result[0].Id); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index 0c4549c3ee..06a53db4ff 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Reflection; using System.Web.Routing; using Moq; using Umbraco.Core; @@ -10,10 +9,8 @@ using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; -using Umbraco.Core.Composing; -using Current = Umbraco.Core.Composing.Current; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -23,6 +20,8 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects.Accessors; +using Current = Umbraco.Web.Composing.Current; +using Umbraco.Tests.Common; namespace Umbraco.Tests.PublishedContent { @@ -58,7 +57,7 @@ namespace Umbraco.Tests.PublishedContent .ToList()); } - private UmbracoContext GetUmbracoContext() + private IUmbracoContext GetUmbracoContext() { RouteData routeData = null; @@ -70,16 +69,17 @@ namespace Umbraco.Tests.PublishedContent var globalSettings = TestObjects.GetGlobalSettings(); var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; + + var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); var umbracoContext = new UmbracoContext( - httpContext, + httpContextAccessor, publishedSnapshotService.Object, - new WebSecurity(httpContext, Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); return umbracoContext; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 177561d1ea..d99fc90813 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -14,6 +14,8 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web; using Umbraco.Web.Templates; +using Umbraco.Web.Models; +using Umbraco.Web.Routing; namespace Umbraco.Tests.PublishedContent { @@ -42,16 +44,15 @@ namespace Umbraco.Tests.PublishedContent var converters = Factory.GetInstance(); var umbracoContextAccessor = Mock.Of(); + var publishedUrlProvider = Mock.Of(); var logger = Mock.Of(); - var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, IOHelper, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper); - var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, IOHelper, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper, publishedUrlProvider); + var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor( Mock.Of(), - Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), @@ -60,7 +61,8 @@ namespace Umbraco.Tests.PublishedContent pastedImages, ShortStringHelper, IOHelper, - LocalizedTextService)) { Id = 1 }); + LocalizedTextService, + Mock.Of())) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index d08a573917..e852c22828 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -1,32 +1,30 @@ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Web; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Web; using Umbraco.Web.PublishedCache; -using Umbraco.Core.Composing; using Moq; -using Newtonsoft.Json; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PropertyEditors; using Umbraco.Web.Templates; +using Umbraco.Web.Models; +using Umbraco.Web.Routing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.PublishedContent { @@ -52,15 +50,16 @@ namespace Umbraco.Tests.PublishedContent var mediaFileService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); - var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, IOHelper, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper); - var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor); + var publishedUrlProvider = Mock.Of(); + var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, IOHelper, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider); + var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var localizationService = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger, Mock.Of(), localizationService, LocalizedTextService, ShortStringHelper)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger, Mock.Of(), localizationService, IOHelper, ShortStringHelper, LocalizedTextService)) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(logger, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor, Mock.Of(), localizationService, imageSourceParser, linkParser, pastedImages, ShortStringHelper, IOHelper, LocalizedTextService)) { Id = 1002 }, + new DataType(new RichTextPropertyEditor(logger,umbracoContextAccessor, Mock.Of(), localizationService, imageSourceParser, linkParser, pastedImages, ShortStringHelper, IOHelper, LocalizedTextService, Mock.Of())) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger, Mock.Of(), localizationService, ShortStringHelper, LocalizedTextService)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger, Mock.Of(), localizationService, IOHelper, ShortStringHelper, LocalizedTextService)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger, Mock.Of(), localizationService, IOHelper, ShortStringHelper, LocalizedTextService)) { Id = 1005 }); @@ -184,7 +183,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children().Where(x => x.IsVisible()).ToIndexedArray(); + var items = doc.Children(VariationContextAccessor).Where(x => x.IsVisible()).ToIndexedArray(); foreach (var item in items) { @@ -205,7 +204,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1173); var items = doc - .Children() + .Children(VariationContextAccessor) .Where(x => x.IsVisible()) .ToIndexedArray(); @@ -260,7 +259,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1173); var ct = doc.ContentType; - var items = doc.Children() + var items = doc.Children(VariationContextAccessor) .Select(x => x.CreateModel(Current.PublishedModelFactory)) // linq, returns IEnumerable // only way around this is to make sure every IEnumerable extension @@ -292,7 +291,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children().Take(4).ToIndexedArray(); + var items = doc.Children(VariationContextAccessor).Take(4).ToIndexedArray(); foreach (var item in items) { @@ -312,7 +311,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - foreach (var d in doc.Children().Skip(1).ToIndexedArray()) + foreach (var d in doc.Children(VariationContextAccessor).Skip(1).ToIndexedArray()) { if (d.Content.Id != 1176) { @@ -330,7 +329,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children() + var items = doc.Children(VariationContextAccessor) .Concat(new[] { GetNode(1175), GetNode(4444) }) .ToIndexedArray(); @@ -415,7 +414,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1046); - var found1 = doc.Children().GroupBy(x => x.ContentType.Alias).ToArray(); + var found1 = doc.Children(VariationContextAccessor).GroupBy(x => x.ContentType.Alias).ToArray(); Assert.AreEqual(2, found1.Length); Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count()); @@ -436,8 +435,8 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1046); - var found1 = doc.Children().Where(x => x.ContentType.Alias == "CustomDocument"); - var found2 = doc.Children().Where(x => x.ContentType.Alias == "Home"); + var found1 = doc.Children(VariationContextAccessor).Where(x => x.ContentType.Alias == "CustomDocument"); + var found2 = doc.Children(VariationContextAccessor).Where(x => x.ContentType.Alias == "Home"); Assert.AreEqual(1, found1.Count()); Assert.AreEqual(2, found2.Count()); @@ -448,7 +447,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var ordered = doc.Children().OrderBy(x => x.UpdateDate); + var ordered = doc.Children(VariationContextAccessor).OrderBy(x => x.UpdateDate); var correctOrder = new[] { 1178, 1177, 1174, 1176 }; for (var i = 0; i < correctOrder.Length; i++) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 1ca0d82835..1aa049e25e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -23,7 +23,7 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Tests.Common; namespace Umbraco.Tests.PublishedContent { @@ -68,11 +68,11 @@ namespace Umbraco.Tests.PublishedContent /// /// /// - internal IPublishedContent GetNode(int id, UmbracoContext umbracoContext) + internal IPublishedContent GetNode(int id, IUmbracoContext umbracoContext) { var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, - Factory.GetInstance(), Factory.GetInstance()); + Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var doc = cache.GetById(id); Assert.IsNotNull(doc); return doc; @@ -133,7 +133,7 @@ namespace Umbraco.Tests.PublishedContent //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); - var rootChildren = publishedMedia.Children().ToArray(); + var rootChildren = publishedMedia.Children(VariationContextAccessor).ToArray(); var currSort = 0; for (var i = 0; i < rootChildren.Count(); i++) { @@ -210,11 +210,11 @@ namespace Umbraco.Tests.PublishedContent //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); - var rootChildren = publishedMedia.Children(); + var rootChildren = publishedMedia.Children(VariationContextAccessor); Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { 2222, 1113, 1114, 1115, 1116 })); var publishedChild1 = cache.GetById(2222); - var subChildren = publishedChild1.Children(); + var subChildren = publishedChild1.Children(VariationContextAccessor); Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { 2112 })); } } @@ -342,11 +342,11 @@ namespace Umbraco.Tests.PublishedContent var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); var publishedMedia = GetNode(mRoot.Id); - var rootChildren = publishedMedia.Children(); + var rootChildren = publishedMedia.Children(VariationContextAccessor); Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id })); var publishedChild1 = GetNode(mChild1.Id); - var subChildren = publishedChild1.Children(); + var subChildren = publishedChild1.Children(VariationContextAccessor); Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); } @@ -485,7 +485,7 @@ namespace Umbraco.Tests.PublishedContent "); var node = xml.DescendantsAndSelf("Image").Single(x => (int)x.Attribute("id") == nodeId); - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var nav = node.CreateNavigator(); @@ -505,7 +505,7 @@ namespace Umbraco.Tests.PublishedContent var errorXml = new XElement("error", string.Format("No media is maching '{0}'", 1234)); var nav = errorXml.CreateNavigator(); - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance()); + var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetInstance(), Factory.GetInstance(), VariationContextAccessor); var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/"), 1234); Assert.IsNull(converted); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index d8dbabb569..70cc26eaf8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -39,28 +39,11 @@ namespace Umbraco.Tests.PublishedContent Assert.IsFalse(result); } - - [Test] - public void ConfigureRequest_Sets_UmbracoPage_When_Published_Content_Assigned() - { - var umbracoContext = GetUmbracoContext("/test"); - var publishedRouter = CreatePublishedRouter(); - var request = publishedRouter.CreateRequest(umbracoContext); - var content = GetPublishedContentMock(); - request.Culture = new CultureInfo("en-AU"); - request.PublishedContent = content.Object; - publishedRouter.ConfigureRequest(request); - - Assert.IsNotNull(request.LegacyContentHashTable); - } - private Mock GetPublishedContentMock() { var pc = new Mock(); pc.Setup(content => content.Id).Returns(1); pc.Setup(content => content.Name).Returns("test"); - pc.Setup(content => content.WriterName).Returns("admin"); - pc.Setup(content => content.CreatorName).Returns("admin"); pc.Setup(content => content.CreateDate).Returns(DateTime.Now); pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); pc.Setup(content => content.Path).Returns("-1,1"); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 271af62ba4..2e348fa7e8 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -5,7 +5,7 @@ using System.Linq; using Moq; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -167,7 +167,6 @@ namespace Umbraco.Tests.PublishedContent { // initialize boring stuff TemplateId = 0; - WriterName = CreatorName = string.Empty; WriterId = CreatorId = 0; CreateDate = UpdateDate = DateTime.Now; Version = Guid.Empty; @@ -193,8 +192,6 @@ namespace Umbraco.Tests.PublishedContent public string Name { get; set; } public IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); public string UrlSegment { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } public int WriterId { get; set; } public int CreatorId { get; set; } public string Path { get; set; } @@ -202,7 +199,6 @@ namespace Umbraco.Tests.PublishedContent public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public string Url { get; set; } public PublishedItemType ItemType => PublishedItemType.Content; public bool IsDraft(string culture = null) => false; diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs index 8d0a544c06..c9d11055ac 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs @@ -48,7 +48,8 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrlAlias(Logger); + var lookup = + new ContentFinderByUrlAlias(Logger, Mock.Of(), VariationContextAccessor); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index 60ed41593d..1a08153b4f 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -20,10 +20,16 @@ namespace Umbraco.Tests.Routing var properties = new[] { - new PublishedPropertyType("umbracoUrlAlias", Constants.DataTypes.Textbox, false, ContentVariation.Nothing, - new PropertyValueConverterCollection(Enumerable.Empty()), - Mock.Of(), - Mock.Of()), + new PublishedPropertyType( + propertyTypeAlias:"umbracoUrlAlias", + dataTypeId: Constants.DataTypes.Textbox, + isUserProperty:false, + variations: ContentVariation.Nothing, + propertyValueConverters:new PropertyValueConverterCollection(Enumerable.Empty()), + contentType:Mock.Of(), + publishedModelFactory:Mock.Of(), + factory:Mock.Of() + ) }; _publishedContentType = new PublishedContentType(0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); } @@ -57,7 +63,7 @@ namespace Umbraco.Tests.Routing if (expectedNode > 0) Assert.AreEqual(expectedCulture, request.Culture.Name); - var finder = new ContentFinderByUrlAlias(Logger); + var finder = new ContentFinderByUrlAlias(Logger, Mock.Of(), VariationContextAccessor); var result = finder.TryFindContent(request); if (expectedNode > 0) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 72848753e5..ee1e056aff 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Request; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; @@ -18,7 +18,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByIdPath(Factory.GetInstance().WebRouting, Logger); + var lookup = new ContentFinderByIdPath(SettingsForTests.GenerateMockWebRoutingSettings(), Logger, Factory.GetInstance()); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs index c1abb5a3a5..d18353eb87 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs @@ -1,6 +1,8 @@ using Moq; using NUnit.Framework; +using Umbraco.Core.Request; using Umbraco.Tests.TestHelpers; +using Umbraco.Web; using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing @@ -16,15 +18,13 @@ namespace Umbraco.Tests.Routing public void Lookup_By_Page_Id(string urlAsString, int nodeMatch) { var umbracoContext = GetUmbracoContext(urlAsString); + var httpContext = GetHttpContextFactory(urlAsString).HttpContext; var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByPageIdQuery(); + var mockRequestAccessor = new Mock(); + mockRequestAccessor.Setup(x => x.GetRequestValue("umbPageID")).Returns(httpContext.Request.QueryString["umbPageID"]); - //we need to manually stub the return output of HttpContext.Request["umbPageId"] - var requestMock = Mock.Get(umbracoContext.HttpContext.Request); - - requestMock.Setup(x => x["umbPageID"]) - .Returns(umbracoContext.HttpContext.Request.QueryString["umbPageID"]); + var lookup = new ContentFinderByPageIdQuery(mockRequestAccessor.Object); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index f74c68777e..e8b8bab22b 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Routing { var template = new Template(ShortStringHelper, alias, alias); template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); + ServiceContext.FileService.SaveTemplate(template); return template; } @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService); + var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService, ServiceContext.ContentTypeService, SettingsForTests.GenerateMockWebRoutingSettings()); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 85168e4490..f7b6762774 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -3,9 +3,8 @@ using System.Globalization; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; -using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs index 8ff5d1cedc..4f11802b43 100644 --- a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs @@ -1,15 +1,20 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; +using Umbraco.Web.Composing; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Web; using Umbraco.Web.Routing; +using Umbraco.Tests.Common; +using SettingsForTests = Umbraco.Tests.TestHelpers.SettingsForTests; namespace Umbraco.Tests.Routing { @@ -54,7 +59,10 @@ namespace Umbraco.Tests.Routing var urls = content.GetContentUrls(publishedRouter, umbContext, GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - Logger).ToList(); + VariationContextAccessor, + Logger, + UriUtility, + PublishedUrlProvider).ToList(); Assert.AreEqual(1, urls.Count); Assert.AreEqual("content/itemNotPublished", urls[0].Text); @@ -70,16 +78,29 @@ namespace Umbraco.Tests.Routing content.Path = "-1,1046"; content.Published = true; - var umbracoSettings = Current.Configs.Settings(); + var umbracoSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); + + var umbContext = GetUmbracoContext("http://localhost:8000"); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbContext); + var urlProvider = new DefaultUrlProvider(umbracoSettings, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), + umbracoContextAccessor, UriUtility); + var publishedUrlProvider = new UrlProvider( + umbracoContextAccessor, + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{urlProvider}), + new MediaUrlProviderCollection(Enumerable.Empty()), + Mock.Of() + ); - var umbContext = GetUmbracoContext("http://localhost:8000", - urlProviders: new []{ new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); var publishedRouter = CreatePublishedRouter(Factory, contentFinders:new ContentFinderCollection(new[]{new ContentFinderByUrl(Logger) })); var urls = content.GetContentUrls(publishedRouter, umbContext, GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - Logger).ToList(); + VariationContextAccessor, + Logger, + UriUtility, + publishedUrlProvider).ToList(); Assert.AreEqual(1, urls.Count); Assert.AreEqual("/home/", urls[0].Text); @@ -102,16 +123,30 @@ namespace Umbraco.Tests.Routing child.Path = "-1,1046,1173"; child.Published = true; - var umbracoSettings = Current.Configs.Settings(); + var umbracoSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); + + + var umbContext = GetUmbracoContext("http://localhost:8000"); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbContext); + var urlProvider = new DefaultUrlProvider(umbracoSettings, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = new UrlProvider( + umbracoContextAccessor, + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{urlProvider}), + new MediaUrlProviderCollection(Enumerable.Empty()), + Mock.Of() + ); - var umbContext = GetUmbracoContext("http://localhost:8000", - urlProviders: new[] { new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); var publishedRouter = CreatePublishedRouter(Factory, contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(Logger) })); var urls = child.GetContentUrls(publishedRouter, umbContext, GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - Logger).ToList(); + VariationContextAccessor, + Logger, + UriUtility, + publishedUrlProvider + ).ToList(); Assert.AreEqual(1, urls.Count); Assert.AreEqual("/home/sub1/", urls[0].Text); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index e2ce265ae3..5cff94d6bc 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -12,9 +12,11 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web; using Umbraco.Web.PropertyEditors; using Umbraco.Web.Routing; @@ -32,15 +34,14 @@ namespace Umbraco.Tests.Routing var logger = Mock.Of(); var mediaFileSystemMock = Mock.Of(); - var contentSection = Mock.Of(); + var contentSection = Mock.Of(); var dataTypeService = Mock.Of(); - - var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(new IDataEditor[] + var propertyEditors = new MediaUrlGeneratorCollection(new IMediaUrlGenerator[] { - new FileUploadPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService, LocalizationService, ShortStringHelper), + new FileUploadPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper), new ImageCropperPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService), - })); - _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors); + }); + _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors, UriUtility); } public override void TearDown() @@ -55,10 +56,10 @@ namespace Umbraco.Tests.Routing { const string expected = "/media/rfeiw584/test.jpg"; - var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("/"); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Auto); Assert.AreEqual(expected, resolvedUrl); } @@ -74,10 +75,10 @@ namespace Umbraco.Tests.Routing Src = expected }); - var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("/"); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Auto); Assert.AreEqual(expected, resolvedUrl); } @@ -88,10 +89,10 @@ namespace Umbraco.Tests.Routing const string mediaUrl = "/media/rfeiw584/test.jpg"; var expected = $"http://localhost{mediaUrl}"; - var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("http://localhost"); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, mediaUrl, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Absolute); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Absolute); Assert.AreEqual(expected, resolvedUrl); } @@ -101,10 +102,10 @@ namespace Umbraco.Tests.Routing { const string expected = "http://localhost/media/rfeiw584/test.jpg"; - var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("http://localhost"); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Relative); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Relative); Assert.AreEqual(expected, resolvedUrl); } @@ -112,10 +113,10 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Media_Url_Returns_Empty_String_When_PropertyType_Is_Not_Supported() { - var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("/"); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.Boolean, "0", null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Absolute, propertyAlias: "test"); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Absolute, propertyAlias: "test"); Assert.AreEqual(string.Empty, resolvedUrl); } @@ -123,7 +124,7 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Media_Url_Can_Resolve_Variant_Property_Url() { - var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); + var umbracoContext = GetUmbracoContext("http://localhost"); var umbracoFilePropertyType = CreatePropertyType(Constants.PropertyEditors.Aliases.UploadField, null, ContentVariation.Culture); @@ -142,10 +143,21 @@ namespace Umbraco.Tests.Routing var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto, "da"); + var resolvedUrl = GetPublishedUrlProvider(umbracoContext).GetMediaUrl(publishedContent, UrlMode.Auto, "da"); Assert.AreEqual(daMediaUrl, resolvedUrl); } + private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext) + { + return new UrlProvider( + new TestUmbracoContextAccessor(umbracoContext), + TestHelper.WebRoutingSettings, + new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(new []{_mediaUrlProvider}), + Mock.Of() + ); + } + private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, string propertyValue, object dataTypeConfiguration) { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 7bf520b0cf..5698d41009 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -2,10 +2,8 @@ using System.Linq; using System.Web.Mvc; using System.Web.Routing; -using System.Web.Security; using Moq; using NUnit.Framework; -using NUnit.Framework.Internal; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; @@ -17,23 +15,18 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Core.Strings; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; -using Umbraco.Web.PublishedCache; using Umbraco.Web.Runtime; -using Umbraco.Web.Security; using Current = Umbraco.Web.Composing.Current; -using Umbraco.Web.Security.Providers; using ILogger = Umbraco.Core.Logging.ILogger; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Routing { @@ -50,13 +43,14 @@ namespace Umbraco.Tests.Routing TestObjects.GetGlobalSettings(), ShortStringHelper, new SurfaceControllerTypeCollection(Enumerable.Empty()), - new UmbracoApiControllerTypeCollection(Enumerable.Empty())); + new UmbracoApiControllerTypeCollection(Enumerable.Empty()), + IOHelper); } public class TestRuntime : WebRuntime { - public TestRuntime(UmbracoApplicationBase umbracoApplication, Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - : base(umbracoApplication, configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom) + public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) + : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) { } @@ -75,7 +69,7 @@ namespace Umbraco.Tests.Routing var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(Composition.TypeLoader.GetUmbracoApiControllers()); Composition.RegisterUnique(umbracoApiControllerTypes); - Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings())); } public override void TearDown() @@ -89,7 +83,7 @@ namespace Umbraco.Tests.Routing var name = "Template"; var template = new Template(ShortStringHelper, name, alias); template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); + ServiceContext.FileService.SaveTemplate(template); return template; } @@ -99,10 +93,12 @@ namespace Umbraco.Tests.Routing [Test] public void Umbraco_Route_Umbraco_Defined_Controller_Action() { + var url = "~/dummy-page"; var template = CreateTemplate("homePage"); var route = RouteTable.Routes["Umbraco_default"]; var routeData = new RouteData { Route = route }; - var umbracoContext = GetUmbracoContext("~/dummy-page", template.Id, routeData); + var umbracoContext = GetUmbracoContext(url, template.Id, routeData); + var httpContext = GetHttpContextFactory(url, routeData).HttpContext; var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); frequest.PublishedContent = umbracoContext.Content.GetById(1174); @@ -111,7 +107,7 @@ namespace Umbraco.Tests.Routing var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of()), ShortStringHelper); - handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest); + handler.GetHandlerForRoute(httpContext.Request.RequestContext, frequest); Assert.AreEqual("RenderMvc", routeData.Values["controller"].ToString()); //the route action will still be the one we've asked for because our RenderActionInvoker is the thing that decides // if the action matches. @@ -135,10 +131,12 @@ namespace Umbraco.Tests.Routing // could exist in the database... yet creating templates should sanitize // aliases one way or another... + var url = "~/dummy-page"; var template = CreateTemplate(templateName); var route = RouteTable.Routes["Umbraco_default"]; - var routeData = new RouteData() {Route = route}; + var routeData = new RouteData() { Route = route }; var umbracoContext = GetUmbracoContext("~/dummy-page", template.Id, routeData, true); + var httpContext = GetHttpContextFactory(url, routeData).HttpContext; var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); frequest.PublishedContent = umbracoContext.Content.GetById(1172); @@ -150,17 +148,15 @@ namespace Umbraco.Tests.Routing var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of(), context => { - var membershipHelper = new MembershipHelper( - umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper); - return new CustomDocumentController(Factory.GetInstance(), + + return new CustomDocumentController(Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance(), - new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper)); + Factory.GetInstance()); }), ShortStringHelper); - handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest); + handler.GetHandlerForRoute(httpContext.Request.RequestContext, frequest); Assert.AreEqual("CustomDocument", routeData.Values["controller"].ToString()); Assert.AreEqual( //global::umbraco.cms.helpers.Casing.SafeAlias(template.Alias), @@ -194,8 +190,8 @@ namespace Umbraco.Tests.Routing /// public class CustomDocumentController : RenderMvcController { - public CustomDocumentController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger, umbracoHelper) + public CustomDocumentController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger) + : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger) { } diff --git a/src/Umbraco.Tests/Routing/RoutableDocumentFilterTests.cs b/src/Umbraco.Tests/Routing/RoutableDocumentFilterTests.cs index 4079ea6c84..bedd8a8744 100644 --- a/src/Umbraco.Tests/Routing/RoutableDocumentFilterTests.cs +++ b/src/Umbraco.Tests/Routing/RoutableDocumentFilterTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Routing public void Is_Reserved_Path_Or_Url(string url) { var globalSettings = TestObjects.GetGlobalSettings(); - var routableDocFilter = new RoutableDocumentFilter(globalSettings); + var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper); Assert.IsTrue(routableDocFilter.IsReservedPathOrUrl(url)); } @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Routing public void Is_Not_Reserved_Path_Or_Url(string url) { var globalSettings = TestObjects.GetGlobalSettings(); - var routableDocFilter = new RoutableDocumentFilter(globalSettings); + var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper); Assert.IsFalse(routableDocFilter.IsReservedPathOrUrl(url)); } @@ -55,7 +55,7 @@ namespace Umbraco.Tests.Routing globalSettingsMock.Setup(x => x.ReservedPaths).Returns(""); globalSettingsMock.Setup(x => x.ReservedUrls).Returns(""); - var routableDocFilter = new RoutableDocumentFilter(globalSettingsMock.Object); + var routableDocFilter = new RoutableDocumentFilter(globalSettingsMock.Object, IOHelper); var routes = new RouteCollection(); diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index bfb3bc6ac0..561a6d945e 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -32,17 +32,19 @@ namespace Umbraco.Tests.Routing //create the module var logger = Mock.Of(); var globalSettings = TestObjects.GetGlobalSettings(); - var runtime = new RuntimeState(logger, Mock.Of(), globalSettings, + var runtime = new RuntimeState(logger, globalSettings, new Lazy(), new Lazy(), UmbracoVersion, HostingEnvironment, BackOfficeInfo); _module = new UmbracoInjectedModule ( - globalSettings, runtime, logger, null, // FIXME: PublishedRouter complexities... Mock.Of(), - new RoutableDocumentFilter(globalSettings) + new RoutableDocumentFilter(globalSettings, IOHelper), + UriUtility, + AppCaches.RequestCache, + IOHelper ); runtime.Level = RuntimeLevel.Run; diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 02aa95cd2e..ee53e2ac52 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -5,7 +5,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -13,8 +13,10 @@ using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Routing { @@ -22,6 +24,8 @@ namespace Umbraco.Tests.Routing [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class UrlProviderTests : BaseWebTest { + private IUmbracoContextAccessor UmbracoContextAccessor { get; } = new TestUmbracoContextAccessor(); + protected override void Compose() { base.Compose(); @@ -30,8 +34,8 @@ namespace Umbraco.Tests.Routing protected override void ComposeSettings() { - Composition.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); - Composition.Configs.Add(SettingsForTests.GenerateMockGlobalSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockContentSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockGlobalSettings); } /// @@ -44,14 +48,15 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings: globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); - var requestHandlerMock = Mock.Get(umbracoSettings.RequestHandler); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var requestHandlerMock = Mock.Get(requestHandlerSettings); requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) var samples = new Dictionary { @@ -67,14 +72,14 @@ namespace Umbraco.Tests.Routing foreach (var sample in samples) { - var result = umbracoContext.UrlProvider.GetUrl(sample.Key); + var result = publishedUrlProvider.GetUrl(sample.Key); Assert.AreEqual(sample.Value, result); } var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); for (int i = 0; i < 5; i++) { - var result = umbracoContext.UrlProvider.GetUrl(randomSample.Key); + var result = publishedUrlProvider.GetUrl(randomSample.Key); Assert.AreEqual(randomSample.Value, result); } @@ -93,6 +98,17 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(0, cachedIds.Count); } + private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, DefaultUrlProvider urlProvider) + { + return new UrlProvider( + new TestUmbracoContextAccessor(umbracoContext), + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{urlProvider}), + new MediaUrlProviderCollection(Enumerable.Empty()), + Mock.Of() + ); + } + // test hideTopLevelNodeFromPath false [TestCase(1046, "/home/")] [TestCase(1173, "/home/sub1/")] @@ -107,15 +123,16 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings: globalSettings.Object); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); - var result = umbracoContext.UrlProvider.GetUrl(nodeId); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + + var result = publishedUrlProvider.GetUrl(nodeId); Assert.AreEqual(niceUrlMatch, result); } @@ -135,15 +152,14 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings: globalSettings.Object); - - - var result = umbracoContext.UrlProvider.GetUrl(nodeId); + var result = publishedUrlProvider.GetUrl(nodeId); Assert.AreEqual(niceUrlMatch, result); } @@ -155,7 +171,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); @@ -177,15 +193,17 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, - urlProviders: new[] { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, + + var umbracoContext = GetUmbracoContext(currentUri, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative url. - var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); + var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); Assert.AreEqual("/home/test-fr/", url); } @@ -201,7 +219,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; @@ -231,15 +249,17 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, - urlProviders: new[] { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, + + var umbracoContext = GetUmbracoContext(currentUri, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); + var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); Assert.AreEqual("/home/test-fr/", url); } @@ -255,7 +275,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; @@ -285,15 +305,16 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, - urlProviders: new[] { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, + var umbracoContext = GetUmbracoContext(currentUri, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); - var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain Assert.AreEqual("http://example.fr/home/test-fr/", url); @@ -305,17 +326,19 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: umbracoSettings, urlProviders: new[] - { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings: globalSettings.Object); - Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - umbracoContext.UrlProvider.Mode = UrlMode.Absolute; - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + Assert.AreEqual("/home/sub1/custom-sub-1/", publishedUrlProvider.GetUrl(1177)); + + publishedUrlProvider.Mode = UrlMode.Absolute; + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", publishedUrlProvider.GetUrl(1177)); } [Test] @@ -324,20 +347,20 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings: globalSettings.Object); + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, + new SiteDomainHelper(), UmbracoContextAccessor, UriUtility); + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: globalSettings.Object); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); //mock the Umbraco settings that we need - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + Assert.AreEqual("#", publishedUrlProvider.GetUrl(999999)); - umbracoContext.UrlProvider.Mode = UrlMode.Absolute; + publishedUrlProvider.Mode = UrlMode.Absolute; - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + Assert.AreEqual("#", publishedUrlProvider.GetUrl(999999)); } } } diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 0a34fb8041..d8e373b428 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -4,13 +4,14 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; +using Umbraco.Web; using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing @@ -18,6 +19,7 @@ namespace Umbraco.Tests.Routing [TestFixture] public class UrlsProviderWithDomainsTests : UrlRoutingTestBase { + private IUmbracoContextAccessor UmbracoContextAccessor { get; } = new TestUmbracoContextAccessor(); protected override void Compose() { base.Compose(); @@ -176,21 +178,22 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "https://domain1.com", false, "/1001-1/")] public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains1(); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current: currentUri); + var result = publishedUrlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } @@ -209,21 +212,22 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains2(); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } @@ -234,21 +238,22 @@ namespace Umbraco.Tests.Routing [TestCase(1002, "http://domain1.com", false, "/1002/")] public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains3(); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } @@ -265,51 +270,53 @@ namespace Umbraco.Tests.Routing [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] public void Get_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains4(); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } [Test] public void Get_Url_DomainsAndCache() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains4(); string ignore; - ignore = umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain2.com")); var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); @@ -328,15 +335,15 @@ namespace Umbraco.Tests.Routing CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); // use the cache - Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/", publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/", publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/1001-1-1/", publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/", publishedUrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/1001-2-1/", publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1001-3/", publishedUrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1002/", publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain2.com"))); + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain2.com"))); } private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) @@ -349,46 +356,48 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_Relative_Or_Absolute() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains4(); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + Assert.AreEqual("/en/1001-1-1/", publishedUrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", publishedUrlProvider.GetUrl(100311)); - umbracoContext.UrlProvider.Mode = UrlMode.Absolute; + publishedUrlProvider.Mode = UrlMode.Absolute; - Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + Assert.AreEqual("http://domain1.com/en/1001-1-1/", publishedUrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", publishedUrlProvider.GetUrl(100311)); } [Test] public void Get_Url_Alternate() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); SetDomains5(); - var url = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Absolute); + var url = publishedUrlProvider.GetUrl(100111, UrlMode.Absolute); Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); - var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); + var result = publishedUrlProvider.GetOtherUrls(100111).ToArray(); foreach (var x in result) Console.WriteLine(x); @@ -396,5 +405,16 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(result[0].Text, "http://domain1b.com/en/1001-1-1/"); Assert.AreEqual(result[1].Text, "http://domain1a.com/en/1001-1-1/"); } + + private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, DefaultUrlProvider urlProvider) + { + return new UrlProvider( + new TestUmbracoContextAccessor(umbracoContext), + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{urlProvider}), + new MediaUrlProviderCollection(Enumerable.Empty()), + Mock.Of() + ); + } } } diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 6587b2e4f6..13ee5afa3e 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -1,8 +1,8 @@ using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -10,6 +10,8 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using Umbraco.Core.Services; using Umbraco.Tests.LegacyXmlPublishedCache; +using Umbraco.Web; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Routing { @@ -34,18 +36,20 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings(); SetDomains1(); const string url = "http://domain1.com/1001-1/1001-1-1"; // get the nice url for 100111 - var umbracoContext = GetUmbracoContext(url, 9999, umbracoSettings: settings, urlProviders: new [] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Absolute)); + var umbracoContext = GetUmbracoContext(url, 9999, globalSettings:globalSettings.Object); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, + new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + + Assert.AreEqual("http://domain2.com/1001-1-1/", publishedUrlProvider.GetUrl(100111, UrlMode.Absolute)); // check that the proper route has been cached var cache = umbracoContext.Content as PublishedContentCache; @@ -72,10 +76,15 @@ namespace Umbraco.Tests.Routing //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes // what's the nice url now? - Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); // good + Assert.AreEqual("http://domain2.com/1001-1-1/", publishedUrlProvider.GetUrl(100111)); // good //Assert.AreEqual("http://domain1.com/1001-1/1001-1-1", routingContext.NiceUrlProvider.GetNiceUrl(100111, true)); // bad } + private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, object urlProvider) + { + throw new NotImplementedException(); + } + void SetDomains1() { SetupDomainServiceMock(new[] @@ -86,6 +95,17 @@ namespace Umbraco.Tests.Routing } + private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, DefaultUrlProvider urlProvider) + { + return new UrlProvider( + new TestUmbracoContextAccessor(umbracoContext), + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{urlProvider}), + new MediaUrlProviderCollection(Enumerable.Empty()), + Mock.Of() + ); + } + protected override string GetXmlContent(int templateId) { return @" diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 64716754b0..3c0097f27b 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; using System.Data; -using System.Web.Hosting; using Examine; using Moq; -using NPoco.Expressions; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Compose; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; @@ -24,6 +21,7 @@ using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Hosting; using Umbraco.Web.Runtime; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Runtimes { @@ -84,7 +82,7 @@ namespace Umbraco.Tests.Runtimes // test application public class TestUmbracoApplication : UmbracoApplicationBase { - public TestUmbracoApplication() : base(_logger, _configs, _ioHelper, _profiler, new AspNetHostingEnvironment(_hostingSettings), new AspNetBackOfficeInfo(_globalSettings, _ioHelper, _settings, _logger)) + public TestUmbracoApplication() : base(_logger, _configs, _ioHelper, _profiler, new AspNetHostingEnvironment(_hostingSettings), new AspNetBackOfficeInfo(_globalSettings, _ioHelper, _logger, _settings)) { } @@ -92,16 +90,17 @@ namespace Umbraco.Tests.Runtimes private static readonly IIOHelper _ioHelper = TestHelper.IOHelper; private static readonly IProfiler _profiler = new TestProfiler(); private static readonly Configs _configs = GetConfigs(); - private static readonly IGlobalSettings _globalSettings = _configs.Global(); - private static readonly IHostingSettings _hostingSettings = _configs.Hosting(); - private static readonly IUmbracoSettingsSection _settings = _configs.Settings(); + private static readonly IGlobalSettings _globalSettings = SettingsForTests.GetDefaultGlobalSettings(); + private static readonly IHostingSettings _hostingSettings = SettingsForTests.GetDefaultHostingSettings(); + private static readonly IContentSettings _contentSettings = SettingsForTests.GenerateMockContentSettings(); + private static readonly IWebRoutingSettings _settings = _configs.WebRouting(); private static Configs GetConfigs() { - var configs = new ConfigsFactory().Create(_ioHelper); - configs.Add(SettingsForTests.GetDefaultGlobalSettings); - configs.Add(SettingsForTests.GetDefaultUmbracoSettings); - configs.Add(SettingsForTests.GetDefaultHostingSettings); + var configs = new ConfigsFactory().Create(); + configs.Add(() => _globalSettings); + configs.Add(() => _contentSettings); + configs.Add(() => _hostingSettings); return configs; } @@ -122,11 +121,15 @@ namespace Umbraco.Tests.Runtimes public class TestRuntime : CoreRuntime { public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom) + :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) { } + // override because we cannot use Assembly.GetEntryAssembly in Nunit tests since that is always null + protected override ITypeFinder GetTypeFinder() + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + // must override the database factory // else BootFailedException because U cannot connect to the configured db protected internal override IUmbracoDatabaseFactory GetDatabaseFactory() @@ -210,7 +213,7 @@ namespace Umbraco.Tests.Runtimes public void Compose(Composition composition) { - composition.Register(factory => SettingsForTests.GetDefaultUmbracoSettings()); + composition.Register(factory => SettingsForTests.GenerateMockContentSettings()); composition.RegisterUnique(); composition.Components().Append(); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 63bcd87bd1..213074caa7 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -27,7 +27,6 @@ using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Tests.Composing; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.Macros; @@ -35,6 +34,8 @@ using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Runtime; using File = System.IO.File; +using Current = Umbraco.Web.Composing.Current; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Runtimes { @@ -48,7 +49,7 @@ namespace Umbraco.Tests.Runtimes IFactory factory = null; // clear - foreach (var file in Directory.GetFiles(Path.Combine(Current.IOHelper.MapPath("~/App_Data")), "NuCache.*")) + foreach (var file in Directory.GetFiles(Path.Combine(TestHelper.IOHelper.MapPath("~/App_Data")), "NuCache.*")) File.Delete(file); // settings @@ -61,24 +62,27 @@ namespace Umbraco.Tests.Runtimes var profiler = new LogProfiler(logger); var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; - var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); - var typeFinder = new TypeFinder(logger); + var globalSettings = TestHelper.GetConfigs().Global(); + var connectionStrings = TestHelper.GetConfigs().ConnectionStrings(); + var databaseFactory = new UmbracoDatabaseFactory(logger,globalSettings, connectionStrings, new Lazy(() => factory.GetInstance()), TestHelper.DbProviderFactoryCreator); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var mainDom = new SimpleMainDom(); var umbracoVersion = TestHelper.GetUmbracoVersion(); var backOfficeInfo = TestHelper.GetBackOfficeInfo(); - var runtimeState = new RuntimeState(logger, null, null, new Lazy(() => mainDom), new Lazy(() => factory.GetInstance()), umbracoVersion, hostingEnvironment, backOfficeInfo); + var runtimeState = new RuntimeState(logger, null, new Lazy(() => mainDom), new Lazy(() => factory.GetInstance()), umbracoVersion, hostingEnvironment, backOfficeInfo); var configs = TestHelper.GetConfigs(); + var variationContextAccessor = TestHelper.VariationContextAccessor; // create the register and the composition var register = TestHelper.GetRegister(); var composition = new Composition(register, typeLoader, profilingLogger, runtimeState, configs, ioHelper, appCaches); - composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom);coreRuntime.Compose(composition); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom);coreRuntime.Compose(composition); // determine actual runtime level runtimeState.DetermineRuntimeLevel(databaseFactory, logger); @@ -103,10 +107,11 @@ namespace Umbraco.Tests.Runtimes composition.Register(Lifetime.Singleton); composition.Register(Lifetime.Singleton); composition.Register(_ => Mock.Of(), Lifetime.Singleton); - composition.RegisterUnique(f => new DistributedCache()); + composition.RegisterUnique(f => new DistributedCache(f.GetInstance(), f.GetInstance())); + composition.Register(_ => Mock.Of(), Lifetime.Singleton); composition.WithCollectionBuilder().Append(); composition.RegisterUnique(); - composition.RegisterUnique(f => ExamineManager.Instance); + composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(_ => new MediaUrlProviderCollection(Enumerable.Empty())); @@ -117,8 +122,8 @@ namespace Umbraco.Tests.Runtimes .Append(); // configure - composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + composition.Configs.Add(TestHelpers.SettingsForTests.GetDefaultGlobalSettings); + composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockContentSettings); // create and register the factory Current.Factory = factory = composition.CreateFactory(); @@ -156,7 +161,7 @@ namespace Umbraco.Tests.Runtimes var scopeProvider = factory.GetInstance(); using (var scope = scopeProvider.CreateScope()) { - var creator = new DatabaseSchemaCreator(scope.Database, logger, umbracoVersion, SettingsForTests.GetDefaultGlobalSettings()); + var creator = new DatabaseSchemaCreator(scope.Database, logger, umbracoVersion, TestHelpers.SettingsForTests.GetDefaultGlobalSettings()); creator.InitializeDatabaseSchema(); scope.Complete(); } @@ -190,10 +195,8 @@ namespace Umbraco.Tests.Runtimes Assert.AreEqual("test", content.Name); // need an UmbracoCOntext to access the cache - // FIXME: not exactly pretty, should not depend on HttpContext - var httpContext = Mock.Of(); var umbracoContextFactory = factory.GetInstance(); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(httpContext); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbracoContext = umbracoContextReference.UmbracoContext; // assert that there is no published document @@ -203,7 +206,7 @@ namespace Umbraco.Tests.Runtimes // but a draft document pcontent = umbracoContext.Content.GetById(true, content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("test", pcontent.Name()); + Assert.AreEqual("test", pcontent.Name(variationContextAccessor)); Assert.IsTrue(pcontent.IsDraft()); // no published url @@ -217,7 +220,7 @@ namespace Umbraco.Tests.Runtimes // assert that snapshot has been updated and there is now a published document pcontent = umbracoContext.Content.GetById(content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("test", pcontent.Name()); + Assert.AreEqual("test", pcontent.Name(variationContextAccessor)); Assert.IsFalse(pcontent.IsDraft()); // but the url is the published one - no draft url @@ -226,7 +229,7 @@ namespace Umbraco.Tests.Runtimes // and also an updated draft document pcontent = umbracoContext.Content.GetById(true, content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("testx", pcontent.Name()); + Assert.AreEqual("testx", pcontent.Name(variationContextAccessor)); Assert.IsTrue(pcontent.IsDraft()); // and the published document has a url @@ -255,7 +258,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); @@ -270,10 +273,10 @@ namespace Umbraco.Tests.Runtimes var register = TestHelper.GetRegister(); var composition = new Composition(register, typeLoader, profilingLogger, runtimeState, configs, ioHelper, appCaches); var umbracoVersion = TestHelper.GetUmbracoVersion(); - composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom); coreRuntime.Compose(composition); // get the components diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 5658434017..f10b141916 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Scheduling; @@ -18,17 +19,19 @@ namespace Umbraco.Tests.Scheduling public class BackgroundTaskRunnerTests { private ILogger _logger; + private IHostingEnvironment _hostingEnvironment; [OneTimeSetUp] public void InitializeFixture() { _logger = new ConsoleLogger(new MessageTemplates()); + _hostingEnvironment = TestHelper.GetHostingEnvironment(); } [Test] public async Task ShutdownWhenRunningWithWait() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var stopped = false; runner.Stopped += (sender, args) => { stopped = true; }; @@ -51,7 +54,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task ShutdownWhenRunningWithoutWait() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var stopped = false; runner.Stopped += (sender, args) => { stopped = true; }; @@ -78,7 +81,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task ShutdownCompletesTheRunner() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { Assert.IsFalse(runner.IsRunning); // because AutoStart is false @@ -101,7 +104,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task ShutdownFlushesTheQueue() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { MyTask t1, t2, t3; @@ -123,7 +126,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task ShutdownForceTruncatesTheQueue() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { MyTask t1, t2, t3; @@ -150,7 +153,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task ShutdownThenForce() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { Assert.IsFalse(runner.IsRunning); // because AutoStart is false @@ -185,7 +188,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task HostingStopNonImmediate() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { MyTask t; @@ -219,7 +222,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task HostingStopImmediate() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { MyTask t; @@ -254,7 +257,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void Create_IsNotRunning() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { Assert.IsFalse(runner.IsRunning); } @@ -268,7 +271,7 @@ namespace Umbraco.Tests.Scheduling { AutoStart = true, KeepAlive = true // else stops! - }, _logger)) + }, _logger, _hostingEnvironment)) { Assert.IsTrue(runner.IsRunning); // because AutoStart is true await runner.StopInternal(false); // keepalive = must be stopped @@ -278,7 +281,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void Create_AutoStartAndKeepAlive_IsRunning() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }, _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }, _logger, _hostingEnvironment)) { Assert.IsTrue(runner.IsRunning); // because AutoStart is true Thread.Sleep(800); // for long @@ -291,7 +294,7 @@ namespace Umbraco.Tests.Scheduling public async Task Dispose_IsRunning() { BackgroundTaskRunner runner; - using (runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }, _logger)) + using (runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }, _logger, _hostingEnvironment)) { Assert.IsTrue(runner.IsRunning); // dispose will stop it @@ -315,7 +318,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void Startup_KeepAlive_IsRunning() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true }, _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true }, _logger, _hostingEnvironment)) { Assert.IsFalse(runner.IsRunning); runner.StartUp(); @@ -327,7 +330,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task Create_AddTask_IsRunning() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var waitHandle = new ManualResetEvent(false); runner.TaskCompleted += (sender, args) => @@ -345,7 +348,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void Create_KeepAliveAndAddTask_IsRunning() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true }, _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true }, _logger, _hostingEnvironment)) { var waitHandle = new ManualResetEvent(false); runner.TaskCompleted += (sender, args) => @@ -363,7 +366,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task WaitOnRunner_OneTask() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyTask(); Assert.IsTrue(task.Ended == default(DateTime)); @@ -382,7 +385,7 @@ namespace Umbraco.Tests.Scheduling for (var i = 0; i < 10; i++) tasks.Add(new MyTask()); - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = false, LongRunning = true, PreserveRunningTask = true }, _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = false, LongRunning = true, PreserveRunningTask = true }, _logger, _hostingEnvironment)) { tasks.ForEach(runner.Add); @@ -400,7 +403,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task WaitOnTask() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyTask(); var waitHandle = new ManualResetEvent(false); @@ -420,7 +423,7 @@ namespace Umbraco.Tests.Scheduling for (var i = 0; i < 10; i++) tasks.Add(new MyTask(), new ManualResetEvent(false)); - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { runner.TaskCompleted += (sender, task) => tasks[task.Task].Set(); foreach (var t in tasks) runner.Add(t.Key); @@ -449,7 +452,7 @@ namespace Umbraco.Tests.Scheduling IDictionary tasks = getTasks(); BackgroundTaskRunner tManager; - using (tManager = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { LongRunning = true, KeepAlive = true }, _logger)) + using (tManager = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { LongRunning = true, KeepAlive = true }, _logger, _hostingEnvironment)) { tManager.TaskCompleted += (sender, task) => tasks[task.Task].Set(); @@ -495,7 +498,7 @@ namespace Umbraco.Tests.Scheduling List tasks = getTasks(); - using (var tManager = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { LongRunning = true, PreserveRunningTask = true }, _logger)) + using (var tManager = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { LongRunning = true, PreserveRunningTask = true }, _logger, _hostingEnvironment)) { tasks.ForEach(tManager.Add); @@ -537,7 +540,7 @@ namespace Umbraco.Tests.Scheduling { var runCount = 0; var waitHandle = new ManualResetEvent(false); - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { runner.TaskCompleted += (sender, args) => { @@ -568,7 +571,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task LatchedTaskRuns() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyLatchedTask(200, false); runner.Add(task); @@ -588,7 +591,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task LatchedTaskStops_Runs_On_Shutdown() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyLatchedTask(200, true); runner.Add(task); @@ -608,7 +611,7 @@ namespace Umbraco.Tests.Scheduling { var runCount = 0; var waitHandle = new ManualResetEvent(false); - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { runner.TaskCompleted += (sender, args) => { @@ -635,7 +638,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task FailingTaskSync() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var exceptions = new ConcurrentQueue(); runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception); @@ -652,7 +655,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task FailingTaskDisposing() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var exceptions = new ConcurrentQueue(); runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception); @@ -669,7 +672,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task FailingTaskAsync() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var exceptions = new ConcurrentQueue(); runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception); @@ -685,7 +688,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task FailingTaskDisposingAsync() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var exceptions = new ConcurrentQueue(); runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception); @@ -702,7 +705,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task CancelAsyncTask() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyAsyncTask(4000); runner.Add(task); @@ -718,7 +721,7 @@ namespace Umbraco.Tests.Scheduling [Test] public async Task CancelLatchedTask() { - using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) + using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger, _hostingEnvironment)) { var task = new MyLatchedTask(4000, false); runner.Add(task); @@ -931,7 +934,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void SourceTaskTest() { - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, _logger); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, _logger, TestHelper.GetHostingEnvironment()); var task = new SourceTask(); runner.Add(task); diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs index 6a65f3afbc..c65c7e3efb 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Scheduling public async Task ThreadResumeIssue() { var logger = new DebugDiagnosticsLogger(new MessageTemplates()); - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironment()); var work = new ThreadResumeIssueWorkItem(); runner.Add(work); @@ -77,7 +77,7 @@ namespace Umbraco.Tests.Scheduling public async Task DebuggerInterferenceIssue() { var logger = new DebugDiagnosticsLogger(new MessageTemplates()); - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironment()); var taskCompleted = false; runner.TaskCompleted += (sender, args) => { diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 56d8e320e1..fd37192da2 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -4,6 +4,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.IO; @@ -11,10 +12,10 @@ using Umbraco.Core.Logging; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Core.Composing; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Services; using Umbraco.Tests.Components; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Scoping { @@ -41,7 +42,7 @@ namespace Umbraco.Tests.Scoping composition.WithCollectionBuilder(); composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + composition.Configs.Add(SettingsForTests.GenerateMockContentSettings); Current.Reset(); Current.Factory = composition.CreateFactory(); diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index 1ed1eac663..6b16f49d7d 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -4,7 +4,7 @@ using System.Text; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.IO; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 73f8f38d6c..8d410e3570 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; +using Umbraco.Core.Install; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -18,6 +19,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Tests.Common; using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -29,6 +31,7 @@ using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Scoping { @@ -83,7 +86,8 @@ namespace Umbraco.Tests.Scoping var memberRepository = Mock.Of(); var hostingEnvironment = TestHelper.GetHostingEnvironment(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); + var settings = Mock.Of(); return new PublishedSnapshotService( options, @@ -91,41 +95,41 @@ namespace Umbraco.Tests.Scoping runtimeStateMock.Object, ServiceContext, contentTypeFactory, - null, publishedSnapshotAccessor, Mock.Of(), ProfilingLogger, ScopeProvider, documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, - new DatabaseDataSource(), + new DatabaseDataSource(Mock.Of()), Factory.GetInstance(), Factory.GetInstance(), - Mock.Of(), + new NoopPublishedModelFactory(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }), typeFinder, hostingEnvironment, - new MockShortStringHelper()); + new MockShortStringHelper(), + IOHelper, + settings); } - protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) + protected IUmbracoContext GetUmbracoContextNu(string url, RouteData routeData = null, bool setSingleton = false) { // ensure we have a PublishedSnapshotService var service = PublishedSnapshotService as PublishedSnapshotService; var httpContext = GetHttpContextFactory(url, routeData).HttpContext; - + var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( - httpContext, + httpContextAccessor, service, - new WebSecurity(httpContext, Current.Services.UserService, globalSettings), - umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), - urlProviders ?? Enumerable.Empty(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; @@ -140,12 +144,12 @@ namespace Umbraco.Tests.Scoping var umbracoContext = GetUmbracoContextNu("http://example.com/", setSingleton: true); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // create document type, document var contentType = new ContentType(ShortStringHelper, -1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var item = new Content("name", -1, contentType); // event handler @@ -158,26 +162,26 @@ namespace Umbraco.Tests.Scoping // during events, due to LiveSnapshot, we see the changes Assert.IsNotNull(e); - Assert.AreEqual("changed", e.Name()); + Assert.AreEqual("changed", e.Name(VariationContextAccessor)); }; using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); scope.Complete(); } // been created var x = umbracoContext.Content.GetById(item.Id); Assert.IsNotNull(x); - Assert.AreEqual("name", x.Name()); + Assert.AreEqual("name", x.Name(VariationContextAccessor)); ContentService.Published += OnPublishedAssert; using (var scope = ScopeProvider.CreateScope()) { item.Name = "changed"; - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); if (complete) scope.Complete(); @@ -192,7 +196,7 @@ namespace Umbraco.Tests.Scoping // else changes have been rolled back x = umbracoContext.Content.GetById(item.Id); Assert.IsNotNull(x); - Assert.AreEqual(complete ? "changed" : "name", x.Name()); + Assert.AreEqual(complete ? "changed" : "name", x.Name(VariationContextAccessor)); } } } diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 7b3f9cbe70..d1963a1d2e 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Scoping; @@ -60,7 +60,7 @@ namespace Umbraco.Tests.Scoping public void DefaultRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.UserService; + var service = ServiceContext.UserService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof(IUser)); var user = (IUser)new User(TestObjects.GetGlobalSettings(), "name", "email", "username", "rawPassword"); @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Scoping // get user again - else we'd modify the one that's in the cache user = service.GetUserById(user.Id); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); @@ -137,7 +137,7 @@ namespace Umbraco.Tests.Scoping public void FullDataSetRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.LocalizationService; + var service = ServiceContext.LocalizationService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof (ILanguage)); var lang = (ILanguage) new Language(TestObjects.GetGlobalSettings(), "fr-FR"); @@ -156,7 +156,7 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(lang.Id, globalCached.Id); Assert.AreEqual("fr-FR", globalCached.IsoCode); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); @@ -229,7 +229,7 @@ namespace Umbraco.Tests.Scoping public void SingleItemsOnlyRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.LocalizationService; + var service = ServiceContext.LocalizationService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof (IDictionaryItem)); var lang = (ILanguage)new Language(TestObjects.GetGlobalSettings(), "fr-FR"); @@ -248,7 +248,7 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(item.Id, globalCached.Id); Assert.AreEqual("item-key", globalCached.ItemKey); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index d57845ec10..852872fca0 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -5,7 +5,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Logging; @@ -44,7 +44,7 @@ namespace Umbraco.Tests.Scoping protected override void ComposeSettings() { - Composition.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockContentSettings); Composition.Configs.Add(SettingsForTests.GenerateMockGlobalSettings); } @@ -89,11 +89,11 @@ namespace Umbraco.Tests.Scoping // create document type, document var contentType = new ContentType(ShortStringHelper, -1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var item = new Content("name", -1, contentType); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // check xml in context = "before" @@ -126,9 +126,9 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); // should create an xml clone + ServiceContext.ContentService.SaveAndPublish(item); // should create an xml clone item.Name = "changed"; - Current.Services.ContentService.SaveAndPublish(item); // should re-use the xml clone + ServiceContext.ContentService.SaveAndPublish(item); // should re-use the xml clone // this should never change Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); @@ -203,10 +203,10 @@ namespace Umbraco.Tests.Scoping // create document type var contentType = new ContentType(ShortStringHelper,-1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // check xml in context = "before" @@ -225,12 +225,12 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); for (var i = 0; i < count; i++) { var temp = new Content("content_" + i, -1, contentType); - Current.Services.ContentService.SaveAndPublish(temp); + ServiceContext.ContentService.SaveAndPublish(temp); ids[i] = temp.Id; } diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 90c2f6c4bf..8b84579fec 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -7,16 +7,15 @@ using Microsoft.Owin; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Composing; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; - +using Umbraco.Tests.Common; namespace Umbraco.Tests.Security { @@ -30,17 +29,20 @@ namespace Umbraco.Tests.Security //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set(Constants.AppSettings.ConfigurationStatus, ""); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( - Mock.Of(), + httpContextAccessor, Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), new List(), Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), IOHelper); + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, + new TestVariationContextAccessor(), + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( - Mock.Of(accessor => accessor.UmbracoContext == umbracoContext), runtime, TestObjects.GetGlobalSettings(), TestHelper.IOHelper); + Mock.Of(accessor => accessor.UmbracoContext == umbracoContext), runtime, IOHelper, AppCaches.RequestCache); var result = mgr.ShouldAuthenticateRequest(Mock.Of(), new Uri("http://localhost/umbraco")); @@ -50,16 +52,20 @@ namespace Umbraco.Tests.Security [Test] public void ShouldAuthenticateRequest_When_Configured() { + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var globalSettings = TestObjects.GetGlobalSettings(); var umbCtx = new UmbracoContext( - Mock.Of(), + httpContextAccessor, Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), new List(), Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), IOHelper); + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), + globalSettings, + new TestVariationContextAccessor(), + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); - var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings(), TestHelper.IOHelper); + var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, IOHelper, AppCaches.RequestCache); var request = new Mock(); request.Setup(owinRequest => owinRequest.Uri).Returns(new Uri("http://localhost/umbraco")); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index e51d8e7a7a..fa47f085c9 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2372,8 +2372,6 @@ namespace Umbraco.Tests.Services //MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading Assert.That(sut.GetValue("dateTime").ToString("G"), Is.EqualTo(content.GetValue("dateTime").ToString("G"))); Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); - //that one is gone in 7.4 - //Assert.That(sut.GetValue("folderBrowser"), Is.Null); Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235")); Assert.That(sut.GetValue("rbList"), Is.EqualTo("random")); Assert.That(sut.GetValue("date").ToString("G"), Is.EqualTo(content.GetValue("date").ToString("G"))); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 0f1b1fed32..33e8b0010e 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; +using Umbraco.Core.Install; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -19,6 +20,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Tests.Strings; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; @@ -56,8 +58,8 @@ namespace Umbraco.Tests.Services var memberRepository = Mock.Of(); var hostingEnvironment = Mock.Of(); - - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); + var settings = Mock.Of(); return new PublishedSnapshotService( options, @@ -65,21 +67,22 @@ namespace Umbraco.Tests.Services runtimeStateMock.Object, ServiceContext, contentTypeFactory, - null, publishedSnapshotAccessor, Mock.Of(), ProfilingLogger, ScopeProvider, documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, - new DatabaseDataSource(), + new DatabaseDataSource(Mock.Of()), Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }), typeFinder, hostingEnvironment, - new MockShortStringHelper()); + new MockShortStringHelper(), + IOHelper, + settings); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs b/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs index cf1a974742..e10dd99482 100644 --- a/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs +++ b/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Services public void Can_Export_Macro() { // Arrange - var macro = new Macro(ShortStringHelper, "test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); + var macro = new Macro(ShortStringHelper, "test1", "Test", "~/views/macropartials/test.cshtml"); ServiceContext.MacroService.Save(macro); // Act diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index 1d8642b402..ceecb72156 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -28,9 +28,9 @@ namespace Umbraco.Tests.Services { var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of(), ShortStringHelper); - repository.Save(new Macro(ShortStringHelper, "test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); - repository.Save(new Macro(ShortStringHelper, "test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); - repository.Save(new Macro(ShortStringHelper, "test3", "Tet3", "~/views/macropartials/test3.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro(ShortStringHelper, "test1", "Test1", "~/views/macropartials/test1.cshtml")); + repository.Save(new Macro(ShortStringHelper, "test2", "Test2", "~/views/macropartials/test2.cshtml")); + repository.Save(new Macro(ShortStringHelper, "test3", "Tet3", "~/views/macropartials/test3.cshtml")); scope.Complete(); } } @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Services var macroService = ServiceContext.MacroService; // Act - var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); macroService.Save(macro); //assert @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); macroService.Save(macro); // Act @@ -119,7 +119,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); macroService.Save(macro); // Act @@ -143,7 +143,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); macro.Properties.Add(new MacroProperty("blah", "Blah", 0, "blah")); macroService.Save(macro); @@ -174,7 +174,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + IMacro macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); macro.Properties.Add(new MacroProperty("blah1", "Blah1", 0, "blah1")); macro.Properties.Add(new MacroProperty("blah2", "Blah2", 1, "blah2")); macro.Properties.Add(new MacroProperty("blah3", "Blah3", 2, "blah3")); @@ -218,7 +218,7 @@ namespace Umbraco.Tests.Services public void Can_Add_And_Remove_Properties() { var macroService = ServiceContext.MacroService; - var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + var macro = new Macro(ShortStringHelper, "test", "Test", "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); //adds some properties macro.Properties.Add(new MacroProperty("blah1", "Blah1", 0, "blah1")); @@ -253,7 +253,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - var macro = new Macro(ShortStringHelper, "test", string.Empty, "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); + var macro = new Macro(ShortStringHelper, "test", string.Empty, "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); // Act & Assert Assert.Throws(() => macroService.Save(macro)); diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index 510ca99b7a..6138607e9d 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -2,12 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Web.Security; -using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Events; +using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; @@ -23,10 +20,10 @@ using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.Security.Providers; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Services { diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 2471b8061d..67de1c91d7 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -6,7 +6,7 @@ using System.Threading; using NPoco; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; diff --git a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs index ea2041cd9c..84ffa3b696 100644 --- a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs +++ b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Strings [TestCase("WhoIsNumber6InTheVillage", "Who Is Number6 In The Village")] // issue is fixed public void CompatibleDefaultReplacement(string input, string expected) { - var helper = new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings()); + var helper = new DefaultShortStringHelper(SettingsForTests.GenerateMockRequestHandlerSettings()); var output = input.Length < 2 ? input : helper.SplitPascalCasing(input, ' ').ToFirstUpperInvariant(); Assert.AreEqual(expected, output); } diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 5fd5710a79..109142f51b 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Strings // NOTE pre-filters runs _before_ Recode takes place // so there still may be utf8 chars even though you want ascii - _helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + _helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.FileName, new DefaultShortStringHelperConfig.Config { //PreFilter = ClearFileChars, // done in IsTerm @@ -94,8 +94,8 @@ namespace Umbraco.Tests.Strings [Test] public void U4_4056() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -119,8 +119,8 @@ namespace Umbraco.Tests.Strings [Test] public void U4_4056_TryAscii() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -145,7 +145,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringUnderscoreInTerm() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // underscore is accepted within terms @@ -155,7 +155,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo_bar*nil", helper.CleanString("foo_bar nil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // underscore is not accepted within terms @@ -169,7 +169,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringLeadingChars() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // letters and digits are valid leading chars @@ -179,7 +179,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("0123foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // only letters are valid leading chars @@ -190,14 +190,14 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123 foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings())); + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings())); Assert.AreEqual("child2", helper.CleanStringForSafeAlias("1child2")); } [Test] public void CleanStringTermOnUpper() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -207,7 +207,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo*Bar", helper.CleanString("fooBar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -221,7 +221,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringAcronymOnNonUpper() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -234,7 +234,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BAnil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -251,7 +251,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringGreedyAcronyms() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -264,7 +264,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BA*nil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -281,7 +281,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringWhiteSpace() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -294,7 +294,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSeparator() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -302,7 +302,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo*bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -310,14 +310,14 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged })); Assert.AreEqual("foobar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -329,7 +329,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSymbols() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -383,7 +383,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringEncoding() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -392,7 +392,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("中文测试", helper.CleanString("中文测试", CleanStringType.Alias)); Assert.AreEqual("léger*中文测试*ZÔRG", helper.CleanString("léger 中文测试 ZÔRG", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Ascii | CleanStringType.Unchanged, @@ -405,8 +405,8 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringDefaultConfig() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -431,7 +431,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringCasing() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index fa00539e57..ef8ababf6e 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -5,9 +5,8 @@ using System.Linq; using System.Text; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Strings; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web; diff --git a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs index 08c12473de..7135c63c43 100644 --- a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs @@ -1,8 +1,6 @@ using Umbraco.Core.Logging; using Moq; using NUnit.Framework; -using Umbraco.Core.Services; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Templates; using Umbraco.Web; using Umbraco.Core.Models.PublishedContent; @@ -13,12 +11,12 @@ using System; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core; -using Umbraco.Web.PropertyEditors; +using System.Diagnostics; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Templates { - - [TestFixture] public class HtmlImageSourceParserTests { @@ -31,9 +29,7 @@ namespace Umbraco.Tests.Templates

"; - var logger = Mock.Of(); - var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(Mock.Of()); var result = imageSourceParser.FindUdisFromDataAttributes(input).ToList(); Assert.AreEqual(2, result.Count); @@ -44,9 +40,7 @@ namespace Umbraco.Tests.Templates [Test] public void Remove_Image_Sources() { - var logger = Mock.Of(); - var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(Mock.Of()); var result = imageSourceParser.RemoveImageSources(@"

@@ -74,21 +68,27 @@ namespace Umbraco.Tests.Templates var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); - mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/media/1001/my-image.jpg")); var umbracoContextAccessor = new TestUmbracoContextAccessor(); var umbracoContextFactory = TestUmbracoContextFactory.Create( - mediaUrlProvider: mediaUrlProvider.Object, umbracoContextAccessor: umbracoContextAccessor); - using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) + + var publishedUrlProvider = new UrlProvider(umbracoContextAccessor, + TestHelper.WebRoutingSettings, + new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(new []{mediaUrlProvider.Object}), + Mock.Of() + ); + using (var reference = umbracoContextFactory.EnsureUmbracoContext()) { var mediaCache = Mock.Get(reference.UmbracoContext.Media); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); var result = imageSourceParser.EnsureImageSources(@"

@@ -111,10 +111,83 @@ namespace Umbraco.Tests.Templates

", result); - } + } + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Empty source is not updated with no data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Empty source is updated with data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Filled source is overwritten with data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Attributes are persisted" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Source is trimmed and parameters are prefixed" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Parameters are prefixed" + )] + [TestCase( + @"
+ + + +
", + ExpectedResult = + @"
+ + + +
", + TestName = "Multiple img tags are handled" + )] + [Category("Ensure image sources")] + public string Ensure_ImageSources_Processing(string sourceHtml) + { + var fakeMediaUrl = "/media/1001/image.jpg"; + var parser = new HtmlImageSourceParser((guid) => fakeMediaUrl); + var actual = parser.EnsureImageSources(sourceHtml); + + return actual; + } + + [Category("Ensure image sources")] + [Test] + public void Ensure_Large_Html_Is_Processed_Quickly() + { + int symbolCount = 25000; + int maxMsToRun = 200; + + var longText = new string('*', symbolCount); + var text = $@""; + + var fakeMediaUrl = "/media/1001/image.jpg"; + var parser = new HtmlImageSourceParser((guid) => fakeMediaUrl); + + var timer = new Stopwatch(); + timer.Start(); + var actual = parser.EnsureImageSources(text); + timer.Stop(); + + Assert.IsTrue(timer.ElapsedMilliseconds <= maxMsToRun); } } } diff --git a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs index 17f0471252..f24fad442f 100644 --- a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs @@ -6,8 +6,9 @@ using System.Web; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.Common; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Routing; using Umbraco.Web.Templates; @@ -30,7 +31,7 @@ namespace Umbraco.Tests.Templates

"; var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var parser = new HtmlLocalLinkParser(umbracoContextAccessor); + var parser = new HtmlLocalLinkParser(umbracoContextAccessor, Mock.Of()); var result = parser.FindUdisFromLocalLinks(input).ToList(); @@ -52,7 +53,7 @@ namespace Umbraco.Tests.Templates //setup a mock url provider which we'll use for testing var contentUrlProvider = new Mock(); contentUrlProvider - .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.GetUrl( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/my-test-url")); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = new Mock(); @@ -63,17 +64,21 @@ namespace Umbraco.Tests.Templates var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); - mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/media/1001/my-image.jpg")); var umbracoContextAccessor = new TestUmbracoContextAccessor(); var umbracoContextFactory = TestUmbracoContextFactory.Create( - urlProvider: contentUrlProvider.Object, - mediaUrlProvider: mediaUrlProvider.Object, umbracoContextAccessor: umbracoContextAccessor); - using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) + var publishedUrlProvider = new UrlProvider(umbracoContextAccessor, + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new []{contentUrlProvider.Object}), + new MediaUrlProviderCollection(new []{mediaUrlProvider.Object}), + Mock.Of() + ); + using (var reference = umbracoContextFactory.EnsureUmbracoContext()) { var contentCache = Mock.Get(reference.UmbracoContext.Content); contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); @@ -83,7 +88,7 @@ namespace Umbraco.Tests.Templates mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor); + var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var output = linkParser.EnsureInternalLinks(input); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index f5f9fbc96f..e035eaa807 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -5,14 +5,13 @@ using NPoco; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Persistence; using Umbraco.Tests.Components; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.TestHelpers { @@ -39,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index f76a8452d8..579ca104ea 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -10,13 +10,19 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Request; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Tests.Common; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; +using Umbraco.Web.Composing; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Routing; +using Umbraco.Web.Security; namespace Umbraco.Tests.TestHelpers { @@ -27,6 +33,7 @@ namespace Umbraco.Tests.TestHelpers protected override void Compose() { base.Compose(); + base.Compose(); Composition.RegisterUnique(); Composition.RegisterUnique(); @@ -39,10 +46,6 @@ namespace Umbraco.Tests.TestHelpers // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(Mock.Of(), Mock.Of(), Mock.Of(),Mock.Of(), Mock.Of())) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => GetPublishedContentTypeByAlias(alias) ?? type; } @@ -88,18 +91,25 @@ namespace Umbraco.Tests.TestHelpers internal PublishedRouter CreatePublishedRouter(IFactory container = null, ContentFinderCollection contentFinders = null) { - return CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting, container, contentFinders); + return CreatePublishedRouter(SettingsForTests.GenerateMockWebRoutingSettings(), container ?? Factory, contentFinders); } - internal static PublishedRouter CreatePublishedRouter(IWebRoutingSection webRoutingSection, IFactory container = null, ContentFinderCollection contentFinders = null) + internal static PublishedRouter CreatePublishedRouter(IWebRoutingSettings webRoutingSettings, IFactory container = null, ContentFinderCollection contentFinders = null) { return new PublishedRouter( - webRoutingSection, + webRoutingSettings, contentFinders ?? new ContentFinderCollection(Enumerable.Empty()), new TestLastChanceFinder(), new TestVariationContextAccessor(), - container?.TryGetInstance() ?? ServiceContext.CreatePartial(), - new ProfilingLogger(Mock.Of(), Mock.Of())); + new ProfilingLogger(Mock.Of(), Mock.Of()), + Mock.Of(), + Mock.Of(), + container?.GetInstance() ?? Current.Factory.GetInstance(), + container?.GetInstance()?? Current.Factory.GetInstance(), + container?.GetInstance()?? Current.Factory.GetInstance(), + container?.GetInstance() ?? Current.Factory.GetInstance(), + container?.GetInstance() ?? Current.Factory.GetInstance() + ); } } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs index 6b67377202..d3cc51b38d 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs @@ -7,16 +7,16 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting { public class TestControllerActivator : TestControllerActivatorBase { - private readonly Func _factory; + private readonly Func _factory; - public TestControllerActivator(Func factory) + public TestControllerActivator(Func factory) { _factory = factory; } - protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor) { - return _factory(msg, umbracoContextAccessor, helper); + return _factory(msg, umbracoContextAccessor); } } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index cf6438b673..a39219a570 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -11,6 +11,7 @@ using Moq; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; +using Umbraco.Core.IO; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; @@ -24,6 +25,7 @@ using Umbraco.Core.Logging; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Security.Providers; using Umbraco.Tests.Strings; +using Umbraco.Tests.Common; namespace Umbraco.Tests.TestHelpers.ControllerTesting { @@ -101,7 +103,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var backofficeIdentity = (UmbracoBackOfficeIdentity) owinContext.Authentication.User.Identity; - var webSecurity = new Mock(null, null, globalSettings); + var webSecurity = new Mock(); //mock CurrentUser var groups = new List(); @@ -136,35 +138,26 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var umbracoContextAccessor = Umbraco.Web.Composing.Current.UmbracoContextAccessor; - var umbCtx = new UmbracoContext(httpContext, + var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); + var umbCtx = new UmbracoContext(httpContextAccessor, publishedSnapshotService.Object, webSecurity.Object, - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), - Enumerable.Empty(), - Enumerable.Empty(), globalSettings, new TestVariationContextAccessor(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + new AspNetCookieManager(httpContextAccessor)); //replace it umbracoContextAccessor.UmbracoContext = umbCtx; var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); - var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), new MockShortStringHelper()); - - var umbHelper = new UmbracoHelper(Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - membershipHelper); - - return CreateController(controllerType, request, umbracoContextAccessor, umbHelper); + return CreateController(controllerType, request, umbracoContextAccessor); } - protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper); + protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor); } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs index 9f9f933d72..34b649d3bb 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs @@ -15,9 +15,9 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting { public class TestRunner { - private readonly Func _controllerFactory; + private readonly Func _controllerFactory; - public TestRunner(Func controllerFactory) + public TestRunner(Func controllerFactory) { _controllerFactory = controllerFactory; } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs index 0827a1f786..f038112b0b 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs @@ -16,10 +16,10 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting ///
public class TestStartup { - private readonly Func _controllerFactory; + private readonly Func _controllerFactory; private readonly Action _initialize; - public TestStartup(Action initialize, Func controllerFactory) + public TestStartup(Action initialize, Func controllerFactory) { _controllerFactory = controllerFactory; _initialize = initialize; diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index 19a57d7775..dfb7834aff 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -122,8 +122,6 @@ namespace Umbraco.Tests.TestHelpers.Entities content.SetValue("label", "Non-editable label"); content.SetValue("dateTime", DateTime.Now.AddDays(-20)); content.SetValue("colorPicker", "black"); - //that one is gone in 7.4 - //content.SetValue("folderBrowser", ""); content.SetValue("ddlMultiple", "1234,1235"); content.SetValue("rbList", "random"); content.SetValue("date", DateTime.Now.AddDays(-10)); diff --git a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs index 45f7042e31..cc14a7a023 100644 --- a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs @@ -109,12 +109,13 @@ namespace Umbraco.Tests.TestHelpers requestContextMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object); - if (routeData != null) + + if (routeData is null) { - requestContextMock.Setup(x => x.RouteData).Returns(routeData); + routeData = new RouteData(); } - + requestContextMock.Setup(x => x.RouteData).Returns(routeData); } } diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 56fdc96aa7..fc3d3f6b52 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -1,73 +1,19 @@ -using System.IO; -using System.Configuration; -using Moq; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; namespace Umbraco.Tests.TestHelpers { public class SettingsForTests { - public static IGlobalSettings GenerateMockGlobalSettings() - { - var config = Mock.Of( - settings => - settings.ConfigurationStatus == TestHelper.GetUmbracoVersion().SemanticVersion.ToSemanticString() && - settings.UseHttps == false && - settings.HideTopLevelNodeFromPath == false && - settings.Path == TestHelper.IOHelper.ResolveUrl("~/umbraco") && - settings.TimeOutInMinutes == 20 && - settings.DefaultUILanguage == "en" && - settings.ReservedPaths == (GlobalSettings.StaticReservedPaths + "~/umbraco") && - settings.ReservedUrls == GlobalSettings.StaticReservedUrls && - settings.UmbracoPath == "~/umbraco" && - settings.UmbracoMediaPath == "~/media" && - settings.UmbracoCssPath == "~/css" && - settings.UmbracoScriptsPath == "~/scripts" - ); + private static Common.SettingsForTests _settingsForTests = new Common.SettingsForTests(); - - - return config; - } + public static IGlobalSettings GenerateMockGlobalSettings() => _settingsForTests.GenerateMockGlobalSettings(TestHelper.GetUmbracoVersion()); /// /// Returns generated settings which can be stubbed to return whatever values necessary /// /// - public static IUmbracoSettingsSection GenerateMockUmbracoSettings() - { - var settings = new Mock(); - - var content = new Mock(); - var security = new Mock(); - var requestHandler = new Mock(); - var logging = new Mock(); - var routing = new Mock(); - - var userPasswordConfig = new Mock(); - var memberPasswordConfig = new Mock(); - security.Setup(x => x.UserPasswordConfiguration).Returns(userPasswordConfig.Object); - security.Setup(x => x.MemberPasswordConfiguration).Returns(memberPasswordConfig.Object); - - settings.Setup(x => x.Content).Returns(content.Object); - settings.Setup(x => x.Security).Returns(security.Object); - settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); - settings.Setup(x => x.Logging).Returns(logging.Object); - settings.Setup(x => x.WebRouting).Returns(routing.Object); - - //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! - settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties()); - settings.Setup(x => x.Content.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes()); - settings.Setup(x => x.RequestHandler.AddTrailingSlash).Returns(true); - settings.Setup(x => x.RequestHandler.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements()); - settings.Setup(x => x.WebRouting.UrlProviderMode).Returns("Auto"); - - return settings.Object; - } + public static IContentSettings GenerateMockContentSettings() => _settingsForTests.GenerateMockContentSettings(); //// from appSettings @@ -97,73 +43,20 @@ namespace Umbraco.Tests.TestHelpers // //SaveSettings(); //} - public static void Reset() - { - ResetSettings(); - GlobalSettings.Reset(); + public static void Reset() => _settingsForTests.Reset(); - //foreach (var kvp in SavedAppSettings) - // ConfigurationManager.AppSettings.Set(kvp.Key, kvp.Value); + internal static IGlobalSettings GetDefaultGlobalSettings() => _settingsForTests.GetDefaultGlobalSettings(TestHelper.GetUmbracoVersion()); - //// set some defaults that are wrong in the config file?! - //// this is annoying, really - //HideTopLevelNodeFromPath = false; - } + internal static IHostingSettings GetDefaultHostingSettings() => _settingsForTests.GetDefaultHostingSettings(); - /// - /// This sets all settings back to default settings - /// - private static void ResetSettings() - { - _defaultGlobalSettings = null; - } + public static IWebRoutingSettings GenerateMockWebRoutingSettings() => _settingsForTests.GenerateMockWebRoutingSettings(); - private static IUmbracoSettingsSection _defaultUmbracoSettings; - private static IGlobalSettings _defaultGlobalSettings; - private static IHostingSettings _defaultHostingSettings; + public static IRequestHandlerSettings GenerateMockRequestHandlerSettings() => _settingsForTests.GenerateMockRequestHandlerSettings(); - internal static IGlobalSettings GetDefaultGlobalSettings() - { - if (_defaultGlobalSettings == null) - { - _defaultGlobalSettings = GenerateMockGlobalSettings(); - } - return _defaultGlobalSettings; - } + public static ISecuritySettings GenerateMockSecuritySettings() => _settingsForTests.GenerateMockSecuritySettings(); - internal static IHostingSettings GetDefaultHostingSettings() - { - if (_defaultHostingSettings == null) - { - _defaultHostingSettings = GenerateMockHostingSettings(); - } - return _defaultHostingSettings; - } + public static IUserPasswordConfiguration GenerateMockUserPasswordConfiguration() => _settingsForTests.GenerateMockUserPasswordConfiguration(); - private static IHostingSettings GenerateMockHostingSettings() - { - var config = Mock.Of( - settings => - settings.LocalTempStorageLocation == LocalTempStorage.EnvironmentTemp && - settings.DebugMode == false - ); - return config; - } - - internal static IUmbracoSettingsSection GetDefaultUmbracoSettings() - { - if (_defaultUmbracoSettings == null) - { - // TODO: Just make this mocks instead of reading from the config - - var config = new FileInfo(TestHelper.MapPathForTest("~/Configurations/UmbracoSettings/web.config")); - - var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = config.FullName }; - var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); - _defaultUmbracoSettings = configuration.GetSection("umbracoConfiguration/defaultSettings") as UmbracoSettingsSection; - } - - return _defaultUmbracoSettings; - } + public static IMemberPasswordConfiguration GenerateMockMemberPasswordConfiguration() => _settingsForTests.GenerateMockMemberPasswordConfiguration(); } } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 273f7a996e..f5d18e05ba 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -7,9 +7,10 @@ using System.Web.Routing; using System.Web.SessionState; using Moq; using Umbraco.Core; -using Umbraco.Core.Logging; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Web; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.TestHelpers.Stubs { @@ -39,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { if (_factory != null) return _factory(requestContext); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs index 6a43f6180d..48517f85dd 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs @@ -4,7 +4,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestLastChanceFinder : IContentLastChanceFinder { - public bool TryFindContent(PublishedRequest frequest) + public bool TryFindContent(IPublishedRequest frequest) { return false; } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index c7ebb88185..693fd73231 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; +using System.Web; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -19,14 +20,17 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Core.Sync; using Umbraco.Net; +using Umbraco.Tests.Common; using Umbraco.Web; using Umbraco.Web.Hosting; +using Umbraco.Web.Routing; using File = System.IO.File; namespace Umbraco.Tests.TestHelpers @@ -36,75 +40,66 @@ namespace Umbraco.Tests.TestHelpers ///
public static class TestHelper { - - public static TypeLoader GetMockedTypeLoader() + private static TestHelperInternal _testHelperInternal = new TestHelperInternal(); + private class TestHelperInternal : TestHelperBase { - return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); + public override IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(Constants.DbProviderNames.SqlCe); + + public override IBulkSqlInsertProvider BulkSqlInsertProvider { get; } = new SqlCeBulkSqlInsertProvider(); + + public override IMarchal Marchal { get; } = new FrameworkMarchal(); + + public override IBackOfficeInfo GetBackOfficeInfo() + => new AspNetBackOfficeInfo( + SettingsForTests.GenerateMockGlobalSettings(GetUmbracoVersion()), + TestHelper.IOHelper, Mock.Of(), SettingsForTests.GenerateMockWebRoutingSettings()); + + public override IHostingEnvironment GetHostingEnvironment() + => new AspNetHostingEnvironment(SettingsForTests.GetDefaultHostingSettings()); + + public override IIpResolver GetIpResolver() + => new AspNetIpResolver(); } - public static Configs GetConfigs() - { - return GetConfigsFactory().Create(IOHelper); - } - public static IRuntimeState GetRuntimeState() - { - return new RuntimeState( - Mock.Of(), - Mock.Of(), - Mock.Of(), - new Lazy(), - new Lazy(), - TestHelper.GetUmbracoVersion(), - TestHelper.GetHostingEnvironment(), - TestHelper.GetBackOfficeInfo() - ); - } + public static ITypeFinder GetTypeFinder() => _testHelperInternal.GetTypeFinder(); - public static IBackOfficeInfo GetBackOfficeInfo() - { - return new AspNetBackOfficeInfo(SettingsForTests.GenerateMockGlobalSettings(), TestHelper.IOHelper, SettingsForTests.GenerateMockUmbracoSettings(), Mock.Of()); - } + public static TypeLoader GetMockedTypeLoader() => _testHelperInternal.GetMockedTypeLoader(); - public static IConfigsFactory GetConfigsFactory() - { - return new ConfigsFactory(); - } + public static Configs GetConfigs() => _testHelperInternal.GetConfigs(); + + public static IRuntimeState GetRuntimeState() => _testHelperInternal.GetRuntimeState(); + + public static IBackOfficeInfo GetBackOfficeInfo() => _testHelperInternal.GetBackOfficeInfo(); + + public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.GetConfigsFactory(); /// /// Gets the current assembly directory. /// /// The assembly directory. - public static string CurrentAssemblyDirectory - { - get - { - var codeBase = typeof(TestHelper).Assembly.CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - return Path.GetDirectoryName(path); - } - } + public static string CurrentAssemblyDirectory => _testHelperInternal.CurrentAssemblyDirectory; - public static IShortStringHelper ShortStringHelper { get; } = new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); - public static IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(Constants.DbProviderNames.SqlCe); - public static IBulkSqlInsertProvider BulkSqlInsertProvider { get; } = new SqlCeBulkSqlInsertProvider(); - public static IMarchal Marchal { get; } = new FrameworkMarchal(); - public static ICoreDebug CoreDebug { get; } = new CoreDebug(); + public static IShortStringHelper ShortStringHelper => _testHelperInternal.ShortStringHelper; + public static IJsonSerializer JsonSerializer => _testHelperInternal.JsonSerializer; + public static IVariationContextAccessor VariationContextAccessor => _testHelperInternal.VariationContextAccessor; + public static IDbProviderFactoryCreator DbProviderFactoryCreator => _testHelperInternal.DbProviderFactoryCreator; + public static IBulkSqlInsertProvider BulkSqlInsertProvider => _testHelperInternal.BulkSqlInsertProvider; + public static IMarchal Marchal => _testHelperInternal.Marchal; + public static ICoreDebug CoreDebug => _testHelperInternal.CoreDebug; + + + public static IIOHelper IOHelper => _testHelperInternal.IOHelper; + public static IMainDom MainDom => _testHelperInternal.MainDom; + public static UriUtility UriUtility => _testHelperInternal.UriUtility; + + public static IWebRoutingSettings WebRoutingSettings => _testHelperInternal.WebRoutingSettings; - public static IIOHelper IOHelper { get; } = new IOHelper(GetHostingEnvironment()); - public static IMainDom MainDom { get; } = new MainDom(Mock.Of(), GetHostingEnvironment()); /// /// Maps the given making it rooted on . must start with ~/ /// /// The relative path. /// - public static string MapPathForTest(string relativePath) - { - if (!relativePath.StartsWith("~/")) - throw new ArgumentException("relativePath must start with '~/'", "relativePath"); - - return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); - } + public static string MapPathForTest(string relativePath) => _testHelperInternal.MapPathForTest(relativePath); public static void InitializeContentDirectories() { @@ -151,6 +146,7 @@ namespace Umbraco.Tests.TestHelpers File.Delete(umbracoSettingsFile); } + // TODO: Move to Assertions or AssertHelper // FIXME: obsolete the dateTimeFormat thing and replace with dateDelta public static void AssertPropertyValuesAreEqual(object actual, object expected, string dateTimeFormat = null, Func sorter = null, string[] ignoreProperties = null) { @@ -294,6 +290,7 @@ namespace Umbraco.Tests.TestHelpers } } + // TODO: Move to MockedValueEditors.cs public static DataValueEditor CreateDataValueEditor(string name) { var valueType = (ValueTypes.IsValue(name)) ? name : ValueTypes.String; @@ -312,29 +309,35 @@ namespace Umbraco.Tests.TestHelpers } - public static IUmbracoVersion GetUmbracoVersion() + public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.GetUmbracoVersion(); + + public static IRegister GetRegister() => _testHelperInternal.GetRegister(); + + public static IHostingEnvironment GetHostingEnvironment() => _testHelperInternal.GetHostingEnvironment(); + + public static IIpResolver GetIpResolver() => _testHelperInternal.GetIpResolver(); + + public static IRequestCache GetRequestCache() => _testHelperInternal.GetRequestCache(); + + public static IHttpContextAccessor GetHttpContextAccessor(HttpContextBase httpContextBase = null) { - return new UmbracoVersion(GetConfigs().Global()); + if (httpContextBase is null) + { + var httpContextMock = new Mock(); + + httpContextMock.Setup(x => x.DisposeOnPipelineCompleted(It.IsAny())) + .Returns(Mock.Of()); + + httpContextBase = httpContextMock.Object; + } + + var mock = new Mock(); + + mock.Setup(x => x.HttpContext).Returns(httpContextBase); + + return mock.Object; } - public static IRegister GetRegister() - { - return RegisterFactory.Create(GetConfigs().Global()); - } - - public static IHostingEnvironment GetHostingEnvironment() - { - return new AspNetHostingEnvironment(SettingsForTests.GetDefaultHostingSettings()); - } - - public static IIpResolver GetIpResolver() - { - return new AspNetIpResolver(); - } - - public static IRequestCache GetRequestCache() - { - return new DictionaryAppCache(); - } + public static IPublishedUrlProvider GetPublishedUrlProvider() => _testHelperInternal.GetPublishedUrlProvider(); } } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 7230d1101e..a07be868a5 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -17,7 +17,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; -using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Tests.Common; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -109,9 +109,8 @@ namespace Umbraco.Tests.TestHelpers ///
/// An Umbraco context. /// This should be the minimum Umbraco context. - public UmbracoContext GetUmbracoContextMock(IUmbracoContextAccessor accessor = null) + public IUmbracoContext GetUmbracoContextMock(IUmbracoContextAccessor accessor = null) { - var httpContext = Mock.Of(); var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Members).Returns(Mock.Of()); @@ -120,38 +119,25 @@ namespace Umbraco.Tests.TestHelpers publishedSnapshotServiceMock.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); var publishedSnapshotService = publishedSnapshotServiceMock.Object; - var umbracoSettings = GetUmbracoSettings(); var globalSettings = GetGlobalSettings(); - var urlProviders = new UrlProviderCollection(Enumerable.Empty()); - var mediaUrlProviders = new MediaUrlProviderCollection(Enumerable.Empty()); if (accessor == null) accessor = new TestUmbracoContextAccessor(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); + var umbracoContextFactory = new UmbracoContextFactory( accessor, publishedSnapshotService, new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - umbracoSettings, globalSettings, - urlProviders, - mediaUrlProviders, Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - return umbracoContextFactory.EnsureUmbracoContext(httpContext).UmbracoContext; - } - - public IUmbracoSettingsSection GetUmbracoSettings() - { - // FIXME: Why not use the SettingsForTest.GenerateMock ... ? - // FIXME: Shouldn't we use the default ones so they are the same instance for each test? - - var umbracoSettingsMock = new Mock(); - var webRoutingSectionMock = new Mock(); - webRoutingSectionMock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); - umbracoSettingsMock.Setup(x => x.WebRouting).Returns(webRoutingSectionMock.Object); - return umbracoSettingsMock.Object; + return umbracoContextFactory.EnsureUmbracoContext().UmbracoContext; } public IGlobalSettings GetGlobalSettings() @@ -337,5 +323,7 @@ namespace Umbraco.Tests.TestHelpers } #endregion + + } } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 73b09fb1f0..be8f7db7e8 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -22,6 +22,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers.Stubs; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.TestHelpers { @@ -94,7 +95,7 @@ namespace Umbraco.Tests.TestHelpers ILogger logger, IIOHelper ioHelper, IGlobalSettings globalSettings, - IUmbracoSettingsSection umbracoSettings, + IContentSettings contentSettings, IEventMessagesFactory eventMessagesFactory, UrlSegmentProviderCollection urlSegmentProviders, TypeLoader typeLoader, @@ -151,16 +152,16 @@ namespace Umbraco.Tests.TestHelpers logger)); var runtimeState = Mock.Of(); - var idkMap = new IdkMap(scopeProvider); + var idkMap = new IdKeyMap(scopeProvider); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var localizationService = GetLazyService(factory, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c))); var userService = GetLazyService(factory, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo(c), GetRepo(c),globalSettings)); - var dataTypeService = GetLazyService(factory, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var dataTypeService = GetLazyService(factory, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), ioHelper, localizedTextService.Value, localizationService.Value, TestHelper.ShortStringHelper)); var propertyValidationService = new Lazy(() => new PropertyValidationService(propertyEditorCollection, dataTypeService.Value)); var contentService = GetLazyService(factory, c => new ContentService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), propertyValidationService)); - var notificationService = GetLazyService(factory, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, ioHelper, GetRepo(c), globalSettings, umbracoSettings.Content)); + var notificationService = GetLazyService(factory, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, ioHelper, GetRepo(c), globalSettings, contentSettings)); var serverRegistrationService = GetLazyService(factory, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), TestHelper.GetHostingEnvironment())); var memberGroupService = GetLazyService(factory, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); var memberService = GetLazyService(factory, c => new MemberService(scopeProvider, logger, eventMessagesFactory, memberGroupService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); @@ -243,12 +244,18 @@ namespace Umbraco.Tests.TestHelpers // mappersBuilder.AddCore(); // var mappers = mappersBuilder.CreateCollection(); var mappers = Current.Factory.GetInstance(); - databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy(() => mappers), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + databaseFactory = new UmbracoDatabaseFactory( + Constants.System.UmbracoConnectionName, + SettingsForTests.GetDefaultGlobalSettings(), + new ConnectionStrings(), + logger, + new Lazy(() => mappers), + TestHelper.DbProviderFactoryCreator); } - typeFinder = typeFinder ?? new TypeFinder(logger); + typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); - var coreDebug = Current.Configs.CoreDebug(); + var coreDebug = TestHelper.CoreDebug; var mediaFileSystem = Mock.Of(); var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, coreDebug, mediaFileSystem, logger, typeFinder, NoAppCache.Instance); return scopeProvider; diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index d38dcfc888..5cf2c7b6bd 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -22,7 +22,7 @@ using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; using Umbraco.Web.Routing; using File = System.IO.File; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; using Umbraco.Tests.Testing; @@ -30,7 +30,8 @@ using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web.WebApi; +using Umbraco.Tests.Common; namespace Umbraco.Tests.TestHelpers { @@ -53,8 +54,7 @@ namespace Umbraco.Tests.TestHelpers protected PublishedContentTypeCache ContentTypesCache { get; private set; } protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider(); - - protected ServiceContext ServiceContext => Current.Services; + protected IVariationContextAccessor VariationContextAccessor => new TestVariationContextAccessor(); internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider; @@ -80,13 +80,16 @@ namespace Umbraco.Tests.TestHelpers .Clear() .Add(() => Composition.TypeLoader.GetDataEditors()); + Composition.WithCollectionBuilder() + .Add(Composition.TypeLoader.GetUmbracoApiControllers()); + Composition.RegisterUnique(f => { if (Options.Database == UmbracoTestOptions.Database.None) return TestObjects.GetDatabaseFactoryMock(); var lazyMappers = new Lazy(f.GetInstance); - var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), f.GetInstance(), lazyMappers, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), f.GetInstance(), lazyMappers, TestHelper.DbProviderFactoryCreator); factory.ResetForTests(); return factory; }); @@ -154,14 +157,7 @@ namespace Umbraco.Tests.TestHelpers return @"Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;"; } - protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) - { - var factory = routeData != null - ? new FakeHttpContextFactory(url, routeData) - : new FakeHttpContextFactory(url); - return factory; - } /// /// Creates the SqlCe database if required @@ -356,7 +352,7 @@ namespace Umbraco.Tests.TestHelpers } } - protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IEnumerable mediaUrlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) + protected IUmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) { // ensure we have a PublishedCachesService var service = snapshotService ?? PublishedSnapshotService as XmlPublishedSnapshotService; @@ -375,18 +371,17 @@ namespace Umbraco.Tests.TestHelpers } var httpContext = GetHttpContextFactory(url, routeData).HttpContext; - + var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); var umbracoContext = new UmbracoContext( - httpContext, + httpContextAccessor, service, - new WebSecurity(httpContext, Factory.GetInstance(), - Factory.GetInstance()), - umbracoSettings ?? Factory.GetInstance(), - urlProviders ?? Enumerable.Empty(), - mediaUrlProviders ?? Enumerable.Empty(), + new WebSecurity(httpContextAccessor, Factory.GetInstance(), + Factory.GetInstance(), IOHelper), globalSettings ?? Factory.GetInstance(), new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs index 9b37389241..e34ff7bb45 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs @@ -5,6 +5,6 @@ namespace Umbraco.Tests.Testing.Objects.Accessors { public class NoHttpContextAccessor : IHttpContextAccessor { - public HttpContext HttpContext { get; set; } = null; + public HttpContextBase HttpContext { get; set; } = null; } } diff --git a/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs b/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs index f28613735d..d6a1606853 100644 --- a/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs +++ b/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs @@ -3,8 +3,8 @@ using NUnit.Framework.Internal; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -16,14 +16,15 @@ namespace Umbraco.Tests.Testing.Objects /// public class TestUmbracoContextFactory { - public static IUmbracoContextFactory Create(IGlobalSettings globalSettings = null, IUrlProvider urlProvider = null, - IMediaUrlProvider mediaUrlProvider = null, - IUmbracoContextAccessor umbracoContextAccessor = null) + public static IUmbracoContextFactory Create(IGlobalSettings globalSettings = null, + IUmbracoContextAccessor umbracoContextAccessor = null, + IHttpContextAccessor httpContextAccessor = null, + IPublishedUrlProvider publishedUrlProvider = null) { - if (globalSettings == null) globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - if (urlProvider == null) urlProvider = Mock.Of(); - if (mediaUrlProvider == null) mediaUrlProvider = Mock.Of(); + if (globalSettings == null) globalSettings = TestHelpers.SettingsForTests.GenerateMockGlobalSettings(); if (umbracoContextAccessor == null) umbracoContextAccessor = new TestUmbracoContextAccessor(); + if (httpContextAccessor == null) httpContextAccessor = TestHelper.GetHttpContextAccessor(); + if (publishedUrlProvider == null) publishedUrlProvider = TestHelper.GetPublishedUrlProvider(); var contentCache = new Mock(); var mediaCache = new Mock(); @@ -33,17 +34,19 @@ namespace Umbraco.Tests.Testing.Objects var snapshotService = new Mock(); snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot.Object); + + var umbracoContextFactory = new UmbracoContextFactory( umbracoContextAccessor, snapshotService.Object, new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), globalSettings, - new UrlProviderCollection(new[] { urlProvider }), - new MediaUrlProviderCollection(new[] { mediaUrlProvider }), Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); return umbracoContextFactory; } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 52e86b36db..72186dd081 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Web; using System.Web.Security; using Moq; using NUnit.Framework; @@ -15,9 +16,9 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -56,8 +57,6 @@ namespace Umbraco.Tests.Testing.TestingTests [Test] public void Can_Mock_Umbraco_Helper() { - var umbracoContext = TestObjects.GetUmbracoContextMock(); - // unless we can inject them in MembershipHelper, we need need this Composition.Register(_ => Mock.Of()); Composition.Register(_ => Mock.Of()); @@ -67,11 +66,9 @@ namespace Umbraco.Tests.Testing.TestingTests // ReSharper disable once UnusedVariable var helper = new UmbracoHelper(Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of(), - new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper)); + Mock.Of()); Assert.Pass(); } @@ -80,12 +77,18 @@ namespace Umbraco.Tests.Testing.TestingTests { var umbracoContext = TestObjects.GetUmbracoContextMock(); + var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); var urlProvider = urlProviderMock.Object; - var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, Enumerable.Empty(), umbracoContext.VariationContextAccessor); + var theUrlProvider = new UrlProvider( + new TestUmbracoContextAccessor(umbracoContext), + TestHelper.WebRoutingSettings, + new UrlProviderCollection(new [] { urlProvider }), + new MediaUrlProviderCollection( Enumerable.Empty()) + , umbracoContext.VariationContextAccessor); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = Mock.Of(); @@ -97,17 +100,14 @@ namespace Umbraco.Tests.Testing.TestingTests [Test] public void Can_Mock_UmbracoApiController_Dependencies_With_Injected_UmbracoMapper() { - var umbracoContext = TestObjects.GetUmbracoContextMock(); - var logger = Mock.Of(); var memberService = Mock.Of(); var memberTypeService = Mock.Of(); var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver()); - var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger, ShortStringHelper); - var umbracoHelper = new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper); + var membershipHelper = new MembershipHelper(Mock.Of(), Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger, ShortStringHelper, Mock.Of()); var umbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of() })); - var umbracoApiController = new FakeUmbracoApiController(Mock.Of(), Mock.Of(), Mock.Of(), ServiceContext.CreatePartial(), AppCaches.NoCache, logger, Mock.Of(), umbracoHelper, umbracoMapper); + var umbracoApiController = new FakeUmbracoApiController(Mock.Of(), Mock.Of(), Mock.Of(), ServiceContext.CreatePartial(), AppCaches.NoCache, logger, Mock.Of(), umbracoMapper, Mock.Of()); Assert.Pass(); } @@ -115,7 +115,7 @@ namespace Umbraco.Tests.Testing.TestingTests internal class FakeUmbracoApiController : UmbracoApiController { - public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } - public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper) { } + public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) { } } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 0ea43742da..72c8d8b981 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; -using System.Threading; +using System.Web.Routing; +using System.Web.Security; using System.Xml.Linq; using Examine; using Moq; @@ -33,12 +35,10 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Services; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Actions; using Umbraco.Web.ContentApps; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; -using Umbraco.Web.Trees; using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Hosting; using Umbraco.Core.Mapping; @@ -46,15 +46,23 @@ using Umbraco.Core.Serialization; using Umbraco.Web.Composing.CompositionExtensions; using Umbraco.Web.Hosting; using Umbraco.Web.Sections; -using Current = Umbraco.Core.Composing.Current; using FileSystems = Umbraco.Core.IO.FileSystems; using Umbraco.Web.Templates; using Umbraco.Web.PropertyEditors; using Umbraco.Core.Dictionary; -using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Models; +using Umbraco.Core.Request; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Net; +using Umbraco.Tests.LegacyXmlPublishedCache; +using Umbraco.Web.AspNet; +using Umbraco.Web.Install; +using Umbraco.Web.Security; +using Umbraco.Web.Security.Providers; +using Umbraco.Web.Trees; +using Current = Umbraco.Web.Composing.Current; +using Umbraco.Tests.Common; namespace Umbraco.Tests.Testing { @@ -109,10 +117,14 @@ namespace Umbraco.Tests.Testing private TypeLoader _featureTypeLoader; #region Accessors + protected ServiceContext ServiceContext => Factory.GetInstance(); protected ILogger Logger => Factory.GetInstance(); + protected IJsonSerializer JsonNetSerializer { get; } = new JsonNetSerializer(); protected IIOHelper IOHelper { get; private set; } + protected UriUtility UriUtility => new UriUtility(HostingEnvironment); + protected IPublishedUrlProvider PublishedUrlProvider => Factory.GetInstance(); protected IDataTypeService DataTypeService => Factory.GetInstance(); protected IPasswordHasher PasswordHasher => Factory.GetInstance(); protected Lazy PropertyEditorCollection => new Lazy(() => Factory.GetInstance()); @@ -127,7 +139,7 @@ namespace Umbraco.Tests.Testing protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance(); - protected IHostingEnvironment HostingEnvironment => Factory.GetInstance(); + protected IHostingEnvironment HostingEnvironment { get; } = new AspNetHostingEnvironment(TestHelpers.SettingsForTests.GetDefaultHostingSettings()); protected IIpResolver IpResolver => Factory.GetInstance(); protected IBackOfficeInfo BackOfficeInfo => Factory.GetInstance(); protected AppCaches AppCaches => Factory.GetInstance(); @@ -137,6 +149,7 @@ namespace Umbraco.Tests.Testing protected IMapperCollection Mappers => Factory.GetInstance(); protected UmbracoMapper Mapper => Factory.GetInstance(); + protected IHttpContextAccessor HttpContextAccessor => Factory.GetInstance(); protected IRuntimeState RuntimeState => ComponentTests.MockRuntimeState(RuntimeLevel.Run); #endregion @@ -159,20 +172,18 @@ namespace Umbraco.Tests.Testing var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; - - TypeFinder = new TypeFinder(logger); + TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var appCaches = GetAppCaches(); - var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); - var hostingSettings = SettingsForTests.GetDefaultHostingSettings(); - var settings = SettingsForTests.GetDefaultUmbracoSettings(); - IHostingEnvironment hostingEnvironment = new AspNetHostingEnvironment(hostingSettings); - IBackOfficeInfo backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, IOHelper, settings, logger); + var globalSettings = TestHelpers.SettingsForTests.GetDefaultGlobalSettings(); + var settings = TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings(); + + IBackOfficeInfo backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, IOHelper, logger, settings); IIpResolver ipResolver = new AspNetIpResolver(); UmbracoVersion = new UmbracoVersion(globalSettings); LocalizedTextService = new LocalizedTextService(new Dictionary>(), logger); - var typeLoader = GetTypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, hostingEnvironment, proflogger, Options.TypeLoader); + var typeLoader = GetTypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, HostingEnvironment, proflogger, Options.TypeLoader); var register = TestHelper.GetRegister(); @@ -183,6 +194,7 @@ namespace Umbraco.Tests.Testing Composition.RegisterUnique(IOHelper); + Composition.RegisterUnique(UriUtility); Composition.RegisterUnique(UmbracoVersion); Composition.RegisterUnique(TypeFinder); Composition.RegisterUnique(LocalizedTextService); @@ -191,11 +203,23 @@ namespace Umbraco.Tests.Testing Composition.RegisterUnique(profiler); Composition.RegisterUnique(proflogger); Composition.RegisterUnique(appCaches); - Composition.RegisterUnique(hostingEnvironment); + Composition.RegisterUnique(HostingEnvironment); Composition.RegisterUnique(backOfficeInfo); Composition.RegisterUnique(ipResolver); Composition.RegisterUnique(); Composition.RegisterUnique(TestHelper.ShortStringHelper); + Composition.RegisterUnique(); + Composition.RegisterUnique(); + + + var memberService = Mock.Of(); + var memberTypeService = Mock.Of(); + var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver()); + var membershipHelper = new MembershipHelper(Mock.Of(), Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger, ShortStringHelper, Mock.Of()); + + Composition.RegisterUnique(membershipHelper); + + TestObjects = new TestObjects(register); @@ -294,6 +318,18 @@ namespace Umbraco.Tests.Testing Composition.RegisterUnique(); Composition.RegisterUnique(); Composition.RegisterUnique(); + Composition.RegisterUnique(); + Composition.RegisterUnique(factory => + new UrlProvider( + factory.GetInstance(), + TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings(), + new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), + factory.GetInstance() + + )); + + } @@ -303,6 +339,7 @@ namespace Umbraco.Tests.Testing var runtimeStateMock = new Mock(); runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run); Composition.RegisterUnique(f => runtimeStateMock.Object); + Composition.Register(_ => Mock.Of()); // ah... Composition.WithCollectionBuilder(); @@ -318,6 +355,9 @@ namespace Umbraco.Tests.Testing // manifest Composition.ManifestValueValidators(); Composition.ManifestFilters(); + Composition.MediaUrlGenerators() + .Add() + .Add(); } @@ -372,9 +412,15 @@ namespace Umbraco.Tests.Testing protected virtual void ComposeSettings() { - Composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); - Composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); - Composition.Configs.Add(SettingsForTests.GetDefaultHostingSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GetDefaultGlobalSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GetDefaultHostingSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockRequestHandlerSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockSecuritySettings); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockUserPasswordConfiguration); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockMemberPasswordConfiguration); + Composition.Configs.Add(TestHelpers.SettingsForTests.GenerateMockContentSettings); + //Composition.Configs.Add(() => new DefaultUserPasswordConfig()); } @@ -387,13 +433,11 @@ namespace Umbraco.Tests.Testing // default Datalayer/Repositories/SQL/Database/etc... Composition.ComposeRepositories(); - // register basic stuff that might need to be there for some container resolvers to work - Composition.RegisterUnique(factory => factory.GetInstance().Content); - Composition.RegisterUnique(factory => factory.GetInstance().WebRouting); - - Composition.RegisterUnique(factory => ExamineManager.Instance); + Composition.RegisterUnique(); Composition.RegisterUnique(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); // register filesystems Composition.RegisterUnique(factory => TestObjects.GetFileSystemsMock()); @@ -413,13 +457,17 @@ namespace Umbraco.Tests.Testing .AddCoreMappers(); Composition.RegisterUnique(_ => new TransientEventMessagesFactory()); + + var globalSettings = TestHelper.GetConfigs().Global(); + var connectionStrings = TestHelper.GetConfigs().ConnectionStrings(); + Composition.RegisterUnique(f => new UmbracoDatabaseFactory( Constants.System.UmbracoConnectionName, + globalSettings, + connectionStrings, Logger, new Lazy(f.GetInstance), - TestHelper.GetConfigs(), - TestHelper.DbProviderFactoryCreator, - TestHelper.BulkSqlInsertProvider)); + TestHelper.DbProviderFactoryCreator)); Composition.RegisterUnique(f => f.TryGetInstance().SqlContext); Composition.WithCollectionBuilder(); // empty @@ -443,10 +491,22 @@ namespace Umbraco.Tests.Testing Composition.WithCollectionBuilder(); Composition.RegisterUnique(); Composition.RegisterUnique(); + + + Composition.RegisterUnique(TestHelper.GetHttpContextAccessor(GetHttpContextFactory("/").HttpContext)); } #endregion + protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) + { + var factory = routeData != null + ? new FakeHttpContextFactory(url, routeData) + : new FakeHttpContextFactory(url); + + return factory; + } + #region Initialize protected virtual void InitializeApplication(bool withApplication) @@ -493,8 +553,8 @@ namespace Umbraco.Tests.Testing Current.Reset(); // disposes the factory // reset all other static things that should not be static ;( - UriUtility.ResetAppDomainAppVirtualPath(); - SettingsForTests.Reset(); // FIXME: should it be optional? + UriUtility.ResetAppDomainAppVirtualPath(HostingEnvironment); + TestHelpers.SettingsForTests.Reset(); // FIXME: should it be optional? // clear static events DocumentRepository.ClearScopeEvents(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 6ad9ca5f78..29d69db0d2 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -42,7 +42,7 @@ prompt 4 false - latest + 8 pdbonly @@ -77,14 +77,15 @@ - - + + + 2.0.0-alpha.20200128.15 + 1.8.14 - + - @@ -99,11 +100,11 @@ - + - + - + @@ -141,11 +142,14 @@ + + + @@ -218,9 +222,7 @@ - - @@ -236,10 +238,8 @@ - - @@ -282,6 +282,7 @@ + @@ -318,7 +319,6 @@ - @@ -554,18 +554,18 @@ - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - {fbe7c065-dac0-4025-a78b-63b24d3ab00b} Umbraco.Configuration - {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + {29aa69d9-b597-4395-8d42-43b1263c240a} Umbraco.Core + + {0fad7d2a-d7dd-45b1-91fd-488bb6cdacea} + Umbraco.Examine.Lucene + {3ae7bf57-966b-45a5-910a-954d7c554441} Umbraco.Infrastructure @@ -574,14 +574,22 @@ {52ac0ba8-a60e-4e36-897b-e8b97a54ed1c} Umbraco.ModelsBuilder.Embedded + + {33085570-9bf2-4065-a9b0-a29d920d13ba} + Umbraco.Persistance.SqlCe + + + {f6de8da0-07cc-4ef2-8a59-2bc81dbb3830} + Umbraco.PublishedCache.NuCache + + + {a499779c-1b3b-48a8-b551-458e582e6e96} + Umbraco.Tests.Common + {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web - - {07fbc26b-2927-4a22-8d96-d644c667fecc} - Umbraco.Examine - diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index 2266bd0104..98c4dc96ca 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine { base.Compose(); - Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GenerateMockRequestHandlerSettings())); } } } diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs new file mode 100644 index 0000000000..9cca58719e --- /dev/null +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs @@ -0,0 +1,134 @@ +using Examine; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Xml.Linq; + +namespace Umbraco.Tests.UmbracoExamine +{ + /// + /// LEGACY!! Static methods to help query umbraco xml + /// + /// + /// This should be deleted when we remove the old xml published content with tests which should be replaced with nucache tests + /// + internal static class ExamineExtensions + { + /// + /// Returns true if the XElement is recognized as an umbraco xml NODE (doc type) + /// + /// + /// + internal static bool IsExamineElement(this XElement x) + { + var id = (string)x.Attribute("id"); + if (string.IsNullOrEmpty(id)) + return false; + int parsedId; + if (int.TryParse(id, out parsedId)) + if (parsedId > 0) + return true; + return false; + } + + /// + /// This takes into account both schemas and returns the node type alias. + /// If this isn't recognized as an element node, this returns an empty string + /// + /// + /// + internal static string ExamineNodeTypeAlias(this XElement x) + { + return string.IsNullOrEmpty((string)x.Attribute("nodeTypeAlias")) + ? x.Name.LocalName + : (string)x.Attribute("nodeTypeAlias"); + } + + /// + /// Returns umbraco value for a data element with the specified alias. + /// + /// + /// + /// + internal static string SelectExamineDataValue(this XElement xml, string alias) + { + XElement nodeData = null; + + //if there is data children with attributes, we're on the old + if (xml.Elements("data").Any(x => x.HasAttributes)) + nodeData = xml.Elements("data").SingleOrDefault(x => string.Equals((string)x.Attribute("alias"), alias, StringComparison.InvariantCultureIgnoreCase)); + else + nodeData = xml.Elements().FirstOrDefault(x => string.Equals(x.Name.ToString(), alias, StringComparison.InvariantCultureIgnoreCase)); + + if (nodeData == null) + return string.Empty; + + if (!nodeData.HasElements) + return nodeData.Value; + + //it has sub elements so serialize them + var reader = nodeData.CreateReader(); + reader.MoveToContent(); + return reader.ReadInnerXml(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static ValueSet ConvertToValueSet(this XElement xml, string indexCategory) + { + if (!xml.IsExamineElement()) + throw new InvalidOperationException("Not a supported Examine XML structure"); + var allVals = xml.SelectExamineAllValues(); + var id = (string)xml.Attribute("id"); + //we will use this as the item type, but we also need to add this as the 'nodeTypeAlias' as part of the properties + //since this is what Umbraco expects + var nodeTypeAlias = xml.ExamineNodeTypeAlias(); + var set = new ValueSet(id, indexCategory, nodeTypeAlias, allVals); + set.Set("nodeTypeAlias", nodeTypeAlias); + return set; + } + + internal static Dictionary SelectExamineAllValues(this XElement xml) + { + var attributeValues = xml.Attributes().ToDictionary(x => x.Name.LocalName, x => x.Value); + var dataValues = xml.SelectExamineDataValues(); + foreach (var v in attributeValues) + //override the data values with attribute values if they do match, otherwise add + dataValues[v.Key] = v.Value; + return dataValues; + } + + internal static Dictionary SelectExamineDataValues(this XElement xml) + { + //resolve all element data at once since it is much faster to do this than to relookup all of the XML data + //using Linq and the node.Elements() methods re-gets all of them. + var elementValues = new Dictionary(); + foreach (var x in xml.Elements()) + { + if (x.Attribute("id") != null) + continue; + + string key; + if (x.Name.LocalName == "data") + //it's the legacy schema + key = (string)x.Attribute("alias"); + else + key = x.Name.LocalName; + + if (string.IsNullOrEmpty(key)) + continue; + + if (!x.HasElements) + elementValues[key] = x.Value; + else + //it has sub elements so serialize them + using (var reader = x.CreateReader()) + { + reader.MoveToContent(); + elementValues[key] = reader.ReadInnerXml(); + } + } + return elementValues; + } + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index d20a87eba9..76ada2169e 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.UmbracoExamine public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), GetMockUserService(), GetMockLogger(), TestHelper.ShortStringHelper); + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), GetMockUserService(), GetMockLogger(), TestHelper.ShortStringHelper, TestHelper.JsonSerializer); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } @@ -68,8 +68,8 @@ namespace Umbraco.Tests.UmbracoExamine m.SortOrder == (int)x.Attribute("sortOrder") && m.CreateDate == (DateTime)x.Attribute("createDate") && m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute("nodeName") && - m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && + m.Name == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.GetCultureName(It.IsAny()) == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => @@ -108,8 +108,8 @@ namespace Umbraco.Tests.UmbracoExamine m.SortOrder == (int)x.Attribute("sortOrder") && m.CreateDate == (DateTime)x.Attribute("createDate") && m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute("nodeName") && - m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && + m.Name == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.GetCultureName(It.IsAny()) == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index e41169c6ec..e6fe5170e4 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -114,8 +114,8 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual("value2", result.AllValues["grid.row1"][1]); Assert.IsTrue(result.Values.ContainsKey("grid")); Assert.AreEqual("value1 value2 ", result["grid"]); - Assert.IsTrue(result.Values.ContainsKey($"{UmbracoExamineIndex.RawFieldPrefix}grid")); - Assert.AreEqual(json, result[$"{UmbracoExamineIndex.RawFieldPrefix}grid"]); + Assert.IsTrue(result.Values.ContainsKey($"{UmbracoExamineFieldNames.RawFieldPrefix}grid")); + Assert.AreEqual(json, result[$"{UmbracoExamineFieldNames.RawFieldPrefix}grid"]); } } @@ -165,12 +165,12 @@ namespace Umbraco.Tests.UmbracoExamine var protectedQuery = new BooleanQuery(); protectedQuery.Add( new BooleanClause( - new TermQuery(new Term(LuceneIndex.CategoryFieldName, IndexTypes.Content)), + new TermQuery(new Term(ExamineFieldNames.CategoryFieldName, IndexTypes.Content)), Occur.MUST)); protectedQuery.Add( new BooleanClause( - new TermQuery(new Term(LuceneIndex.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), + new TermQuery(new Term(ExamineFieldNames.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), Occur.MUST)); var collector = TopScoreDocCollector.Create(100, true); @@ -287,7 +287,7 @@ namespace Umbraco.Tests.UmbracoExamine //create the whole thing rebuilder.Populate(indexer); - var result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); + var result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); //delete all content @@ -298,13 +298,13 @@ namespace Umbraco.Tests.UmbracoExamine //ensure it's all gone - result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); + result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods rebuilder.Populate(indexer); - result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); + result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); } } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index fb6fa4c0c1..dc4f68e823 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -38,8 +38,8 @@ namespace Umbraco.Tests.UmbracoExamine m.SortOrder == (int)x.Attribute("sortOrder") && m.CreateDate == (DateTime)x.Attribute("createDate") && m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute("nodeName") && - m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && + m.Name == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.GetCultureName(It.IsAny()) == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.Published == true && diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 8bdb0c71c7..643c56250d 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -179,7 +179,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndex.PublishedFieldName] = "y" + [UmbracoExamineFieldNames.PublishedFieldName] = "y" })); Assert.AreEqual(ValueSetValidationResult.Valid, result); } @@ -213,7 +213,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndex.PublishedFieldName] = "n" + [UmbracoExamineFieldNames.PublishedFieldName] = "n" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -222,7 +222,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndex.PublishedFieldName] = "y" + [UmbracoExamineFieldNames.PublishedFieldName] = "y" })); Assert.AreEqual(ValueSetValidationResult.Valid, result); } @@ -237,8 +237,8 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = "y", - [UmbracoExamineIndex.PublishedFieldName] = "n" + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y", + [UmbracoExamineFieldNames.PublishedFieldName] = "n" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -247,8 +247,8 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = "y", - [UmbracoExamineIndex.PublishedFieldName] = "y" + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y", + [UmbracoExamineFieldNames.PublishedFieldName] = "y" })); Assert.AreEqual(ValueSetValidationResult.Valid, result); @@ -257,17 +257,17 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = "y", - [$"{UmbracoExamineIndex.PublishedFieldName}_en-us"] = "y", + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y", + [$"{UmbracoExamineFieldNames.PublishedFieldName}_en-us"] = "y", ["hello_en-us"] = "world", ["title_en-us"] = "my title", - [$"{UmbracoExamineIndex.PublishedFieldName}_es-es"] = "n", + [$"{UmbracoExamineFieldNames.PublishedFieldName}_es-es"] = "n", ["hello_es-ES"] = "world", ["title_es-ES"] = "my title", - [UmbracoExamineIndex.PublishedFieldName] = "y" + [UmbracoExamineFieldNames.PublishedFieldName] = "y" }); Assert.AreEqual(10, valueSet.Values.Count()); - Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineIndex.PublishedFieldName}_es-es")); + Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineFieldNames.PublishedFieldName}_es-es")); Assert.IsTrue(valueSet.Values.ContainsKey("hello_es-ES")); Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); @@ -275,7 +275,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.AreEqual(7, valueSet.Values.Count()); //filtered to 7 values (removes es-es values) - Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndex.PublishedFieldName}_es-es")); + Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineFieldNames.PublishedFieldName}_es-es")); Assert.IsFalse(valueSet.Values.ContainsKey("hello_es-ES")); Assert.IsFalse(valueSet.Values.ContainsKey("title_es-ES")); } diff --git a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs index a3263259c4..0e757bbc6d 100644 --- a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs @@ -14,7 +14,7 @@ using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; @@ -32,6 +32,9 @@ using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.Models.ContentEditing; using IUser = Umbraco.Core.Models.Membership.IUser; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Web.Routing; namespace Umbraco.Tests.Web.Controllers { @@ -58,10 +61,10 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task GetCurrentUser_Fips() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceMock.Setup(service => service.GetUserById(It.IsAny())) .Returns(() => null); @@ -84,8 +87,11 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - Factory.GetInstance()); + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance() + ); return usersController; } diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index b71624a12e..e3c2a89593 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -12,7 +12,6 @@ using Newtonsoft.Json.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; @@ -22,7 +21,6 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; -using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; using Umbraco.Tests.TestHelpers.Entities; @@ -34,7 +32,10 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.PropertyEditors; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Umbraco.Web.Composing; using Task = System.Threading.Tasks.Task; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Tests.Web.Controllers { @@ -159,10 +160,10 @@ namespace Umbraco.Tests.Web.Controllers if (_contentTypeForMockedContent == null) { _contentTypeForMockedContent = GetMockedContentType(); - Mock.Get(Current.Services.ContentTypeService) + Mock.Get(ServiceContext.ContentTypeService) .Setup(x => x.Get(_contentTypeForMockedContent.Id)) .Returns(_contentTypeForMockedContent); - Mock.Get(Current.Services.ContentTypeService) + Mock.Get(ServiceContext.ContentTypeService) .As() .Setup(x => x.Get(_contentTypeForMockedContent.Id)) .Returns(_contentTypeForMockedContent); @@ -251,9 +252,9 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_Existing_Content() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); @@ -268,8 +269,9 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance()); return controller; } @@ -290,7 +292,7 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var controller = new ContentController( @@ -303,8 +305,9 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance()); return controller; } @@ -330,9 +333,9 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_Properties_Exist() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); @@ -346,8 +349,9 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance()); return controller; } @@ -376,9 +380,9 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success @@ -394,8 +398,9 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance()); return controller; } @@ -416,9 +421,9 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success @@ -434,8 +439,10 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance() + ); return controller; } @@ -462,9 +469,9 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success @@ -480,8 +487,10 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance() + ); return controller; } diff --git a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs index ed0c02b806..23131b04e6 100644 --- a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.Web.Controllers public void Ensure_Same_Area1() { Assert.Throws(() => - new PluginControllerArea(TestObjects.GetGlobalSettings(), + new PluginControllerArea(TestObjects.GetGlobalSettings(), IOHelper, new PluginControllerMetadata[] { PluginController.GetMetadata(typeof(Plugin1Controller)), @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Web.Controllers public void Ensure_Same_Area3() { Assert.Throws(() => - new PluginControllerArea(TestObjects.GetGlobalSettings(), + new PluginControllerArea(TestObjects.GetGlobalSettings(), IOHelper, new PluginControllerMetadata[] { PluginController.GetMetadata(typeof(Plugin1Controller)), @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Web.Controllers [Test] public void Ensure_Same_Area2() { - var area = new PluginControllerArea(TestObjects.GetGlobalSettings(), + var area = new PluginControllerArea(TestObjects.GetGlobalSettings(), IOHelper, new PluginControllerMetadata[] { PluginController.GetMetadata(typeof(Plugin1Controller)), @@ -54,7 +54,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin1Controller : PluginController { public Plugin1Controller(IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor, null, null, null, null, null, null) + : base(umbracoContextAccessor, null, null, null, null, null) { } } @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin2Controller : PluginController { public Plugin2Controller(IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor, null, null, null, null, null, null) + : base(umbracoContextAccessor, null, null, null, null, null) { } } @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin3Controller : PluginController { public Plugin3Controller(IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor, null, null, null, null, null, null) + : base(umbracoContextAccessor, null, null, null, null, null) { } } @@ -80,7 +80,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin4Controller : PluginController { public Plugin4Controller(IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor, null, null, null, null, null, null) + : base(umbracoContextAccessor, null, null, null, null, null) { } } diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index ccb25b1b9a..14bbc5f1fb 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -12,7 +12,7 @@ using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -34,6 +34,9 @@ using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.Models.ContentEditing; using IUser = Umbraco.Core.Models.Membership.IUser; +using Umbraco.Core.Mapping; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Web.Routing; namespace Umbraco.Tests.Web.Controllers { @@ -59,12 +62,9 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task Save_User() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { - //setup some mocks - Umbraco.Core.Configuration.GlobalSettings.HasSmtpServer = true; - - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceMock.Setup(service => service.Save(It.IsAny(), It.IsAny())) .Callback((IUser u, bool raiseEvents) => @@ -86,9 +86,16 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, Factory.GetInstance(), - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance() + + ); return usersController; } @@ -142,7 +149,7 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task GetPagedUsers_Empty() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { var usersController = new UsersController( Factory.GetInstance(), @@ -152,9 +159,15 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, Factory.GetInstance(), - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance() + ); return usersController; } @@ -170,10 +183,10 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task GetPagedUsers_10() { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); var users = MockedUser.CreateMulipleUsers(10); long outVal = 10; userServiceMock.Setup(service => service.GetAll( @@ -189,9 +202,15 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, Factory.GetInstance(), - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance() + ); return usersController; } @@ -247,10 +266,10 @@ namespace Umbraco.Tests.Web.Controllers Action> verification, object routeDefaults = null, string url = null) { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceSetup(userServiceMock); var usersController = new UsersController( @@ -261,9 +280,15 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - helper, Factory.GetInstance(), - ShortStringHelper); + ShortStringHelper, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance() + ); return usersController; } diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs index 04bd4f6e15..36ddecc676 100644 --- a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs @@ -15,14 +15,6 @@ namespace Umbraco.Tests.Web.Mvc _htmlStringUtilities = new HtmlStringUtilities(); } - [Test] - public void ReplaceLineBreaksWithHtmlBreak() - { - var output = _htmlStringUtilities.ReplaceLineBreaksForHtml("

hello world

hello world\r\nhello world\rhello world\nhello world

").ToString(); - var expected = "

hello world

hello world
hello world
hello world
hello world

"; - Assert.AreEqual(expected, output); - } - [Test] public void TruncateWithElipsis() { diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 89a2e789d3..7906047520 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -13,9 +13,9 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Web.Mvc public void SetUp() { Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); - Core.Composing.Current.Factory = Mock.Of(); + Current.Factory = Mock.Of(); } [TearDown] @@ -63,20 +63,21 @@ namespace Umbraco.Tests.Web.Mvc var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbCtx = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); @@ -94,20 +95,21 @@ namespace Umbraco.Tests.Web.Mvc var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbCtx = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); @@ -125,20 +127,21 @@ namespace Umbraco.Tests.Web.Mvc var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbCtx = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); @@ -156,20 +159,21 @@ namespace Umbraco.Tests.Web.Mvc var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - TestHelper.IOHelper); + TestHelper.IOHelper, + TestHelper.UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbCtx = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs index 901c737584..aa94272964 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs @@ -7,8 +7,8 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Current = Umbraco.Web.Composing.Current; diff --git a/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs new file mode 100644 index 0000000000..e728d75dc5 --- /dev/null +++ b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs @@ -0,0 +1,54 @@ +using System.Web.Mvc; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Web.Mvc +{ + [TestFixture] + public class RenderNoContentControllerTests + { + [Test] + public void Redirects_To_Root_When_Content_Published() + { + var mockUmbracoContext = new Mock(); + mockUmbracoContext.Setup(x => x.Content.HasContent()).Returns(true); + var mockIOHelper = new Mock(); + var mockGlobalSettings = new Mock(); + var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + + var result = controller.Index() as RedirectResult; + + Assert.IsNotNull(result); + Assert.AreEqual("~/", result.Url); + } + + [Test] + public void Renders_View_When_No_Content_Published() + { + const string UmbracoPathSetting = "~/umbraco"; + const string UmbracoPath = "/umbraco"; + const string ViewPath = "~/config/splashes/NoNodes.cshtml"; + var mockUmbracoContext = new Mock(); + mockUmbracoContext.Setup(x => x.Content.HasContent()).Returns(false); + var mockIOHelper = new Mock(); + mockIOHelper.Setup(x => x.ResolveUrl(It.Is(y => y == UmbracoPathSetting))).Returns(UmbracoPath); + var mockGlobalSettings = new Mock(); + mockGlobalSettings.SetupGet(x => x.UmbracoPath).Returns(UmbracoPathSetting); + mockGlobalSettings.SetupGet(x => x.NoNodesViewPath).Returns(ViewPath); + var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + + var result = controller.Index() as ViewResult; + Assert.IsNotNull(result); + Assert.AreEqual(ViewPath, result.ViewName); + + var model = result.Model as NoNodesViewModel; + Assert.IsNotNull(model); + Assert.AreEqual(UmbracoPath, model.UmbracoPath); + } + } +} diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 0660564a52..2a9b85aba5 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -1,24 +1,22 @@ using System; -using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using System.Web.Security; using Moq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; +using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; using Current = Umbraco.Web.Composing.Current; @@ -29,6 +27,7 @@ namespace Umbraco.Tests.Web.Mvc [UmbracoTest(WithApplication = true)] public class SurfaceControllerTests : UmbracoTestBase { + public override void SetUp() { base.SetUp(); @@ -39,25 +38,26 @@ namespace Umbraco.Tests.Web.Mvc public void Can_Construct_And_Get_Result() { var globalSettings = TestObjects.GetGlobalSettings(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - IOHelper); + IOHelper, + UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbracoContext = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var ctrl = new TestSurfaceController(umbracoContextAccessor); + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of()); var result = ctrl.Index(); @@ -68,25 +68,26 @@ namespace Umbraco.Tests.Web.Mvc public void Umbraco_Context_Not_Null() { var globalSettings = TestObjects.GetGlobalSettings(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - TestObjects.GetUmbracoSettings(), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - IOHelper); + IOHelper, + UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbCtx = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); - var ctrl = new TestSurfaceController(umbracoContextAccessor); + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of()); Assert.IsNotNull(ctrl.UmbracoContext); } @@ -100,33 +101,28 @@ namespace Umbraco.Tests.Web.Mvc content.Setup(x => x.Id).Returns(2); var publishedSnapshotService = new Mock(); var globalSettings = TestObjects.GetGlobalSettings(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, publishedSnapshotService.Object, new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - IOHelper); + IOHelper, + UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbracoContext = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var helper = new UmbracoHelper( - content.Object, - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(query => query.Content(2) == content.Object), - new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper)); + var publishedContentQuery = Mock.Of(query => query.Content(2) == content.Object); - var ctrl = new TestSurfaceController(umbracoContextAccessor, helper); + var ctrl = new TestSurfaceController(umbracoContextAccessor,publishedContentQuery); var result = ctrl.GetContent(2) as PublishedContentResult; Assert.IsNotNull(result); @@ -137,30 +133,30 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void Mock_Current_Page() { - var webRoutingSettings = Mock.Of(section => section.UrlProviderMode == "Auto"); var globalSettings = TestObjects.GetGlobalSettings(); + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - Mock.Of(section => section.WebRouting == webRoutingSettings), globalSettings, - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of(), - IOHelper); + IOHelper, + UriUtility, + httpContextAccessor, + new AspNetCookieManager(httpContextAccessor)); - var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); var umbracoContext = umbracoContextReference.UmbracoContext; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var content = Mock.Of(publishedContent => publishedContent.Id == 12345); - var contextBase = umbracoContext.HttpContext; - var publishedRouter = BaseWebTest.CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting); + + var publishedRouter = BaseWebTest.CreatePublishedRouter(TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings()); var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/test")); frequest.PublishedContent = content; @@ -172,8 +168,8 @@ namespace Umbraco.Tests.Web.Mvc var routeData = new RouteData(); routeData.DataTokens.Add(Core.Constants.Web.UmbracoRouteDefinitionDataToken, routeDefinition); - var ctrl = new TestSurfaceController(umbracoContextAccessor, new UmbracoHelper()); - ctrl.ControllerContext = new ControllerContext(contextBase, routeData, ctrl); + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of()); + ctrl.ControllerContext = new ControllerContext(Mock.Of(), routeData, ctrl); var result = ctrl.GetContentFromCurrentPage() as PublishedContentResult; @@ -182,9 +178,12 @@ namespace Umbraco.Tests.Web.Mvc public class TestSurfaceController : SurfaceController { - public TestSurfaceController(IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper = null) - : base(umbracoContextAccessor, null, ServiceContext.CreatePartial(), AppCaches.Disabled, null, null, helper) + private readonly IPublishedContentQuery _publishedContentQuery; + + public TestSurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IPublishedContentQuery publishedContentQuery) + : base(umbracoContextAccessor, null, ServiceContext.CreatePartial(), AppCaches.Disabled, null, null) { + _publishedContentQuery = publishedContentQuery; } public ActionResult Index() @@ -195,7 +194,7 @@ namespace Umbraco.Tests.Web.Mvc public ActionResult GetContent(int id) { - var content = Umbraco.Content(id); + var content = _publishedContentQuery.Content(id); return new PublishedContentResult(content); } diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 50d18bb52a..5e5197a28a 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -7,7 +7,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; @@ -16,7 +16,7 @@ using Umbraco.Core.Strings; using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Tests.Common; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -385,13 +385,9 @@ namespace Umbraco.Tests.Web.Mvc ViewContext GetViewContext() { - var settings = SettingsForTests.GetDefaultUmbracoSettings(); - var logger = Mock.Of(); - var umbracoContext = GetUmbracoContext( - logger, settings, - "/dang", 0); + var umbracoContext = GetUmbracoContext("/dang", 0); - var publishedRouter = BaseWebTest.CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting); + var publishedRouter = BaseWebTest.CreatePublishedRouter(TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings()); var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/dang")); frequest.Culture = CultureInfo.InvariantCulture; @@ -404,7 +400,7 @@ namespace Umbraco.Tests.Web.Mvc return context; } - protected UmbracoContext GetUmbracoContext(ILogger logger, IUmbracoSettingsSection umbracoSettings, string url, int templateId, RouteData routeData = null, bool setSingleton = false) + protected IUmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) { var svcCtx = GetServiceContext(); @@ -435,18 +431,18 @@ namespace Umbraco.Tests.Web.Mvc var http = GetHttpContextFactory(url, routeData).HttpContext; + var httpContextAccessor = TestHelper.GetHttpContextAccessor(http); var globalSettings = TestObjects.GetGlobalSettings(); var ctx = new UmbracoContext( - http, + httpContextAccessor, _service, - new WebSecurity(http, Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); //if (setSingleton) //{ diff --git a/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs index d4c3b7c887..9d9c965440 100644 --- a/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using Umbraco.Composing; using Umbraco.Web; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs b/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs index a3505aeb0e..74ab279fb8 100644 --- a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs +++ b/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs @@ -42,17 +42,17 @@ namespace Umbraco.Tests.Web indexer.IndexItem(new ValueSet("1", "content", new Dictionary { [fieldNames[0]] = "Hello world, there are products here", - [UmbracoContentIndex.VariesByCultureFieldName] = "n" + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "n" })); indexer.IndexItem(new ValueSet("2", "content", new Dictionary { [fieldNames[1]] = "Hello world, there are products here", - [UmbracoContentIndex.VariesByCultureFieldName] = "y" + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); indexer.IndexItem(new ValueSet("3", "content", new Dictionary { [fieldNames[2]] = "Hello world, there are products here", - [UmbracoContentIndex.VariesByCultureFieldName] = "y" + [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); } diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index 826b62e599..62d7e941d7 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -1,19 +1,15 @@ using System; using System.IO; using System.Text; -using Examine.LuceneEngine; -using Lucene.Net.Analysis; -using Lucene.Net.Analysis.Standard; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Web { @@ -28,239 +24,11 @@ namespace Umbraco.Tests.Web Current.Reset(); } - - - // ------- Int32 conversion tests - [Test] - public static void Converting_Boxed_34_To_An_Int_Returns_34() - { - // Arrange - const int sample = 34; - - // Act - bool success = UmbracoHelper.ConvertIdObjectToInt( - sample, - out int result - ); - - // Assert - Assert.IsTrue(success); - Assert.That(result, Is.EqualTo(34)); - } - - [Test] - public static void Converting_String_54_To_An_Int_Returns_54() - { - // Arrange - const string sample = "54"; - - // Act - bool success = UmbracoHelper.ConvertIdObjectToInt( - sample, - out int result - ); - - // Assert - Assert.IsTrue(success); - Assert.That(result, Is.EqualTo(54)); - } - - [Test] - public static void Converting_Hello_To_An_Int_Returns_False() - { - // Arrange - const string sample = "Hello"; - - // Act - bool success = UmbracoHelper.ConvertIdObjectToInt( - sample, - out int result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.EqualTo(0)); - } - - [Test] - public static void Converting_Unsupported_Object_To_An_Int_Returns_False() - { - // Arrange - var clearlyWillNotConvertToInt = new StringBuilder(0); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToInt( - clearlyWillNotConvertToInt, - out int result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.EqualTo(0)); - } - - // ------- GUID conversion tests - [Test] - public static void Converting_Boxed_Guid_To_A_Guid_Returns_Original_Guid_Value() - { - // Arrange - Guid sample = Guid.NewGuid(); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToGuid( - sample, - out Guid result - ); - - // Assert - Assert.IsTrue(success); - Assert.That(result, Is.EqualTo(sample)); - } - - [Test] - public static void Converting_String_Guid_To_A_Guid_Returns_Original_Guid_Value() - { - // Arrange - Guid sample = Guid.NewGuid(); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToGuid( - sample.ToString(), - out Guid result - ); - - // Assert - Assert.IsTrue(success); - Assert.That(result, Is.EqualTo(sample)); - } - - [Test] - public static void Converting_Hello_To_A_Guid_Returns_False() - { - // Arrange - const string sample = "Hello"; - - // Act - bool success = UmbracoHelper.ConvertIdObjectToGuid( - sample, - out Guid result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.EqualTo(new Guid("00000000-0000-0000-0000-000000000000"))); - } - - [Test] - public static void Converting_Unsupported_Object_To_A_Guid_Returns_False() - { - // Arrange - var clearlyWillNotConvertToGuid = new StringBuilder(0); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToGuid( - clearlyWillNotConvertToGuid, - out Guid result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.EqualTo(new Guid("00000000-0000-0000-0000-000000000000"))); - } - - // ------- UDI Conversion Tests - /// - /// This requires PluginManager.Current to be initialised before running. - /// - [Test] - public static void Converting_Boxed_Udi_To_A_Udi_Returns_Original_Udi_Value() - { - // Arrange - UdiParser.ResetUdiTypes(); - Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid()); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToUdi( - sample, - out Udi result - ); - - // Assert - Assert.IsTrue(success); - Assert.That(result, Is.EqualTo(sample)); - } - - /// - /// This requires PluginManager.Current to be initialised before running. - /// - [Test] - public void Converting_String_Udi_To_A_Udi_Returns_Original_Udi_Value() - { - // Arrange - SetUpDependencyContainer(); - UdiParser.ResetUdiTypes(); - Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid()); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToUdi( - sample.ToString(), - out Udi result - ); - - // Assert - Assert.IsTrue(success, "Conversion of UDI failed."); - Assert.That(result, Is.EqualTo(sample)); - } - - /// - /// This requires PluginManager.Current to be initialised before running. - /// - [Test] - public void Converting_Hello_To_A_Udi_Returns_False() - { - // Arrange - SetUpDependencyContainer(); - UdiParser.ResetUdiTypes(); - const string sample = "Hello"; - - // Act - bool success = UmbracoHelper.ConvertIdObjectToUdi( - sample, - out Udi result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.Null); - } - - /// - /// This requires PluginManager.Current to be initialised before running. - /// - [Test] - public static void Converting_Unsupported_Object_To_A_Udi_Returns_False() - { - // Arrange - UdiParser.ResetUdiTypes(); - - var clearlyWillNotConvertToGuid = new StringBuilder(0); - - // Act - bool success = UmbracoHelper.ConvertIdObjectToUdi( - clearlyWillNotConvertToGuid, - out Udi result - ); - - // Assert - Assert.IsFalse(success); - Assert.That(result, Is.Null); - } - private void SetUpDependencyContainer() { // FIXME: bad in a unit test - but Udi has a static ctor that wants it?! var container = new Mock(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index bb3db7273c..123c5d3f62 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -7,9 +7,10 @@ using System.Web.Routing; using Moq; using NUnit.Framework; using Umbraco.Core.Services; +using Umbraco.Tests.Common; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; @@ -26,16 +27,17 @@ namespace Umbraco.Tests.Web [Test] public void RouteDataExtensions_GetUmbracoContext() { + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); + var umbCtx = new UmbracoContext( - Mock.Of(), + httpContextAccessor, Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), - new List(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -46,16 +48,17 @@ namespace Umbraco.Tests.Web [Test] public void ControllerContextExtensions_GetUmbracoContext_From_RouteValues() { + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); + var umbCtx = new UmbracoContext( - Mock.Of(), + httpContextAccessor, Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), - new List(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -76,16 +79,17 @@ namespace Umbraco.Tests.Web [Test] public void ControllerContextExtensions_GetUmbracoContext_From_Current() { + var httpContextAccessor = TestHelper.GetHttpContextAccessor(); + var umbCtx = new UmbracoContext( - Mock.Of(), + httpContextAccessor, Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), - new List(), - Enumerable.Empty(), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), - IOHelper); + IOHelper, + UriUtility, + new AspNetCookieManager(httpContextAccessor)); var httpContext = Mock.Of(); diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs new file mode 100644 index 0000000000..ba12b64dfe --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs @@ -0,0 +1,16 @@ +using Umbraco.Core; +using Umbraco.Core.Configuration; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public class AspNetCoreBackOfficeInfo : IBackOfficeInfo + { + public AspNetCoreBackOfficeInfo(IGlobalSettings globalSettings) + { + GetAbsoluteUrl = globalSettings.UmbracoPath; + } + + public string GetAbsoluteUrl { get; } + + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs new file mode 100644 index 0000000000..5cd2b590c8 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Threading; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Hosting; +using Umbraco.Core; +using Umbraco.Core.Configuration; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public class AspNetCoreHostingEnvironment : Umbraco.Core.Hosting.IHostingEnvironment + { + private readonly ConcurrentDictionary _registeredObjects = + new ConcurrentDictionary(); + + private readonly IHostingSettings _hostingSettings; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private string _localTempPath; + + public AspNetCoreHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor, IHostApplicationLifetime hostHostApplicationLifetime) + { + _hostingSettings = hostingSettings ?? throw new ArgumentNullException(nameof(hostingSettings)); + _webHostEnvironment = webHostEnvironment; + _httpContextAccessor = httpContextAccessor; + _hostApplicationLifetime = hostHostApplicationLifetime; + + SiteName = webHostEnvironment.ApplicationName; + ApplicationId = AppDomain.CurrentDomain.Id.ToString(); + ApplicationPhysicalPath = webHostEnvironment.ContentRootPath; + + ApplicationVirtualPath = "/"; //TODO how to find this, This is a server thing, not application thing. + IISVersion = new Version(0, 0); // TODO not necessary IIS + IsDebugMode = _hostingSettings.DebugMode; + } + public bool IsHosted { get; } = true; + public string SiteName { get; } + public string ApplicationId { get; } + public string ApplicationPhysicalPath { get; } + + public string ApplicationVirtualPath { get; } + public bool IsDebugMode { get; } + + public Version IISVersion { get; } + public string LocalTempPath + { + get + { + if (_localTempPath != null) + return _localTempPath; + + switch (_hostingSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + return _localTempPath = System.IO.Path.Combine(Path.GetTempPath(),ApplicationId, "UmbracoData"); + + case LocalTempStorage.EnvironmentTemp: + + // environment temp is unique, we need a folder per site + + // use a hash + // combine site name and application id + // site name is a Guid on Cloud + // application id is eg /LM/W3SVC/123456/ROOT + // the combination is unique on one server + // and, if a site moves from worker A to B and then back to A... + // hopefully it gets a new Guid or new application id? + + var hashString = SiteName + "::" + ApplicationId; + var hash = hashString.GenerateHash(); + var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash); + + return _localTempPath = siteTemp; + + //case LocalTempStorage.Default: + //case LocalTempStorage.Unknown: + default: + return _localTempPath = MapPath("~/App_Data/TEMP"); + } + } + } + + + public string MapPath(string path) => Path.Combine(_webHostEnvironment.WebRootPath, path); + + public string ToAbsolute(string virtualPath, string root) + { + if (Uri.TryCreate(virtualPath, UriKind.Absolute, out _)) + { + return virtualPath; + } + + var segment = new PathString(virtualPath.Substring(1)); + var applicationPath = _httpContextAccessor.HttpContext.Request.PathBase; + + return applicationPath.Add(segment).Value; + } + + public void RegisterObject(IRegisteredObject registeredObject) + { + var wrapped = new RegisteredObjectWrapper(registeredObject); + if (!_registeredObjects.TryAdd(registeredObject, wrapped)) + { + throw new InvalidOperationException("Could not register object"); + } + + var cancellationTokenRegistration = _hostApplicationLifetime.ApplicationStopping.Register(() => wrapped.Stop(true)); + wrapped.CancellationTokenRegistration = cancellationTokenRegistration; + } + + public void UnregisterObject(IRegisteredObject registeredObject) + { + if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) + { + wrapped.CancellationTokenRegistration.Unregister(); + } + } + + + private class RegisteredObjectWrapper + { + private readonly IRegisteredObject _inner; + + public RegisteredObjectWrapper(IRegisteredObject inner) + { + _inner = inner; + } + + public CancellationTokenRegistration CancellationTokenRegistration { get; set; } + + public void Stop(bool immediate) + { + _inner.Stop(immediate); + } + } + } + + +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHttpContextAccessor.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHttpContextAccessor.cs new file mode 100644 index 0000000000..696005f2fb --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHttpContextAccessor.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + internal class AspNetCoreHttpContextAccessor //: IHttpContextAccessor + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCoreHttpContextAccessor(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public HttpContext HttpContext => _httpContextAccessor.HttpContext; + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs new file mode 100644 index 0000000000..8f231191f2 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Net; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + internal class AspNetIpResolver : IIpResolver + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetIpResolver(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public string GetCurrentRequestIpAddress() => _httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? string.Empty; + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs new file mode 100644 index 0000000000..98e040d338 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; +using Umbraco.Core.Diagnostics; + +namespace Umbraco.Web.BackOffice +{ + + public class AspNetCoreMarchal : IMarchal + { + // This thing is not available in net standard, but exists in both .Net 4 and .Net Core 3 + public IntPtr GetExceptionPointers() => Marshal.GetExceptionPointers(); + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs new file mode 100644 index 0000000000..5470516cf7 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Net; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + internal class AspNetCoreSessionIdResolver : ISessionIdResolver + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCoreSessionIdResolver(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public string SessionId => _httpContextAccessor?.HttpContext.Session?.Id; + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs new file mode 100644 index 0000000000..20cfef352d --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -0,0 +1,36 @@ +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Hosting; +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime + { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + + public AspNetCoreUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor, IHostApplicationLifetime hostApplicationLifetime) + { + _httpContextAccessor = httpContextAccessor; + _hostApplicationLifetime = hostApplicationLifetime; + } + + 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; + _hostApplicationLifetime.StopApplication(); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUserAgentProvider.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUserAgentProvider.cs new file mode 100644 index 0000000000..f9c9884704 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUserAgentProvider.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Net; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public class AspNetCoreUserAgentProvider : IUserAgentProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCoreUserAgentProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public string GetUserAgent() + { + return _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString(); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..1f06a818d6 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.AspNetCore.Builder; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public static class UmbracoBackOfficeApplicationBuilderExtensions + { + public static IApplicationBuilder UseUmbracoBackOffice(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + return app; + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs new file mode 100644 index 0000000000..c05f4af4f8 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -0,0 +1,56 @@ +using System.Configuration; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Umbraco.Composing; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Logging.Serilog; +using Umbraco.Core.Runtime; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public static class UmbracoBackOfficeServiceCollectionExtensions + { + public static IServiceCollection AddUmbracoBackOffice(this IServiceCollection services) + { + + + services.AddSingleton(); + + CreateCompositionRoot(services); + + return services; + } + + + private static void CreateCompositionRoot(IServiceCollection services) + { + var serviceProvider = services.BuildServiceProvider(); + + var httpContextAccessor = serviceProvider.GetService(); + var webHostEnvironment = serviceProvider.GetService(); + var hostApplicationLifetime = serviceProvider.GetService(); + + var configFactory = new ConfigsFactory(); + + var hostingSettings = configFactory.HostingSettings; + var coreDebug = configFactory.CoreDebug; + var globalSettings = configFactory.GlobalSettings; + + var hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, httpContextAccessor, hostApplicationLifetime); + var ioHelper = new IOHelper(hostingEnvironment, globalSettings); + var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, new AspNetCoreMarchal()); + var configs = configFactory.Create(); + + var backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings); + var profiler = new LogProfiler(logger); + + Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj new file mode 100644 index 0000000000..36238f1a6d --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + Library + 8 + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/code.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/code.less index 0b90a13059..5eb8b638a2 100644 --- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/code.less +++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/code.less @@ -9,14 +9,13 @@ pre.code { padding: 0 3px 2px; #font > #family > .monospace; font-size: @baseFontSize - 2; - color: @grayDark; + color: @blueExtraDark; .border-radius(3px); } // Inline code code { padding: 2px 4px; - color: #d14; background-color: #f7f7f9; border: 1px solid #e1e1e8; white-space: nowrap; diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/dropdowns.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/dropdowns.less index 94f229a191..5d0e1c8e7e 100644 --- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/dropdowns.less +++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/dropdowns.less @@ -79,11 +79,9 @@ // Hover/Focus state // ----------- .dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, .dropdown-menu > li > button:hover, -.dropdown-menu > li > button:focus, .dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { +.dropdown-submenu:hover > button { text-decoration: none; color: @dropdownLinkColorHover; #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); @@ -92,8 +90,7 @@ // Active state // ------------ .dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { +.dropdown-menu > .active > a:hover { color: @dropdownLinkColorActive; text-decoration: none; outline: 0; @@ -104,13 +101,11 @@ // -------------- // Gray out text and ensure the hover/focus state remains gray .dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { +.dropdown-menu > .disabled > a:hover { color: @grayLight; } // Nuke hover/focus effects -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { +.dropdown-menu > .disabled > a:hover { text-decoration: none; background-color: transparent; background-image: none; // Remove CSS gradient diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/type.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/type.less index bf1167f950..3f93deaf56 100644 --- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/type.less +++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/type.less @@ -132,6 +132,10 @@ ol.inline { display: inline-block; padding-left: 5px; padding-right: 5px; + + &.-no-padding-left{ + padding-left: 0; + } } } diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js index a75a7f1f3c..60118dbdb3 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js @@ -60,6 +60,7 @@ * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. */ + hooks.addFalse("insertLinkDialog"); this.getConverter = function () { return markdownConverter; } @@ -1636,7 +1637,7 @@ var that = this; // The function to be executed when you enter a link and press OK or Cancel. // Marks up the link and adds the ref. - var linkEnteredCallback = function (link) { + var linkEnteredCallback = function (link, title) { if (link !== null) { // ( $1 @@ -1667,10 +1668,10 @@ if (!chunk.selection) { if (isImage) { - chunk.selection = "enter image description here"; + chunk.selection = title || "enter image description here"; } else { - chunk.selection = "enter link description here"; + chunk.selection = title || "enter link description here"; } } } @@ -1683,7 +1684,8 @@ ui.prompt('Insert Image', imageDialogText, imageDefaultText, linkEnteredCallback); } else { - ui.prompt('Insert Link', linkDialogText, linkDefaultText, linkEnteredCallback); + if (!this.hooks.insertLinkDialog(linkEnteredCallback)) + ui.prompt('Insert Link', linkDialogText, linkDefaultText, linkEnteredCallback); } return true; } diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/af_ZA.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/af_ZA.js new file mode 100644 index 0000000000..b6068bc7ec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/af_ZA.js @@ -0,0 +1,230 @@ +tinymce.addI18n('af_ZA',{ +"Cut": "Sny", +"Heading 5": "Opskrif 5", +"Header 2": "Hooflyn 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Jou webblaaier ondersteun nie toegang tot die knipbord nie. Gebruik asb. Ctrl+X\/C\/V", +"Heading 4": "Opskrif 4", +"Div": "Div", +"Heading 2": "Opskrif 2", +"Paste": "Plak", +"Close": "Sluit", +"Font Family": "Font Familie", +"Pre": "Pre", +"Align right": "Regsgerig", +"New document": "Nuwe Dokument", +"Blockquote": "Aanhaling", +"Numbered list": "Genommerde lys", +"Heading 1": "Opskrif 1", +"Headings": "Opskrifte", +"Increase indent": "Inkeping vergroot", +"Formats": "Formate", +"Headers": "Hooflyn-tekste", +"Select all": "Alles selekteer", +"Header 3": "Hooflyn 3", +"Blocks": "Blokke", +"Undo": "Ongedaan maak", +"Strikethrough": "Deurhaal", +"Bullet list": "Opsommingsteken-lys", +"Header 1": "Hooflyn 1", +"Superscript": "Superskrif", +"Clear formatting": "Herstel Formateering", +"Font Sizes": "Font Groote", +"Subscript": "Subskrif", +"Header 6": "Hooflyn 6", +"Redo": "Herdoen", +"Paragraph": "Paragraaf", +"Ok": "OK", +"Bold": "Vetdruk", +"Code": "Kode", +"Italic": "Kursief", +"Align center": "Senteer", +"Header 5": "Hooflyn 5", +"Heading 6": "Opskrif 6", +"Heading 3": "Opskrif 3", +"Decrease indent": "Inkeping verklein", +"Header 4": "Hooflyn 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Die plak funksie is nou in plat-teks modus. Teks word ingevoeg sonder enige formateering, todat jy hierdie opsie wissel.", +"Underline": "Onderlyn", +"Cancel": "Kanselleer", +"Justify": "Gerigstelling", +"Inline": "Inlyn", +"Copy": "Kopieer", +"Align left": "Linksgerig", +"Visual aids": "Hulpmiddels", +"Lower Greek": "Griekse letters", +"Square": "Vierkantjie", +"Default": "Verstek", +"Lower Alpha": "Klein letters", +"Circle": "Sirkeltjie", +"Disc": "Balletjie", +"Upper Alpha": "Hoofletters", +"Upper Roman": "Romeinse syfers groot", +"Lower Roman": "Romeinse syfers klein", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id moet met 'n letter begin en kan slegs deur letters, koppeltekens, syfers, punte en onderstreep-karakters gevolg word.", +"Name": "Geen", +"Anchor": "Anker", +"Id": "Id", +"You have unsaved changes are you sure you want to navigate away?": "Jy het ongestoorde wysigings op hierdier bladsy - is jy seker dat jy die bladsy wil verlaat?", +"Restore last draft": "Herstel die laatste konsep", +"Special character": "Spesiaale karakter", +"Source code": "Bron kode", +"Language": "Taal", +"Insert\/Edit code sample": "Voeg\/Redigeer voorbeeld-kode", +"B": "Blou", +"R": "Rooi", +"G": "Groen", +"Color": "Kleur", +"Right to left": "Regs na links", +"Left to right": "Links na regs", +"Emoticons": "Emoticons", +"Robots": "Robotte", +"Document properties": "Dokument eienskappe", +"Title": "Titel", +"Keywords": "Sleutelwoorde", +"Encoding": "Enkodeering", +"Description": "Beskrywing", +"Author": "Outeur", +"Fullscreen": "Volskerm", +"Horizontal line": "Horisontale lyn", +"Horizontal space": "Horisontale Spasie", +"Insert\/edit image": "Afbeelding invoeg\/bewerk", +"General": "Algemeen", +"Advanced": "Gevorderd", +"Source": "Bron", +"Border": "Rand", +"Constrain proportions": "Behou verhoudings", +"Vertical space": "Vertikale Spasie", +"Image description": "Afbeelding bemskrywing", +"Style": "Styl", +"Dimensions": "Afmetings", +"Insert image": "Afbeelding invoeg", +"Image": "Afbeelding", +"Zoom in": "Inzoem", +"Contrast": "Kontras", +"Back": "Terug", +"Gamma": "Gamma", +"Flip horizontally": "Horisontaal weerspie\\u00ebl", +"Resize": "Grootte wysig", +"Sharpen": "Verskerp", +"Zoom out": "Uitzoem", +"Image options": "Afbeelding opsies", +"Apply": "Toepas", +"Brightness": "Helderheid", +"Rotate clockwise": "Regsom draai", +"Rotate counterclockwise": "Linksom draai", +"Edit image": "Bewerk afbeelding", +"Color levels": "Kleurvlakke", +"Crop": "Afknip", +"Orientation": "Orienteering", +"Flip vertically": "Vertikaal weerspie\\u00ebl", +"Invert": "Omkeer", +"Date\/time": "Datum\/tyd", +"Insert date\/time": "Voeg datum\/tyd in", +"Remove link": "Verwyder skakel", +"Url": "URL", +"Text to display": "Skakelteks", +"Anchors": "Ankers", +"Insert link": "Skakel invoeg", +"Link": "Skakel", +"New window": "Nuwe Skerm", +"None": "Geen", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Die URL verwys na 'n eksterne adres. Wil jy die \"http:\/\/\" voorvoegsel byvoeg?", +"Paste or type a link": "Plak of tik 'n skalel in", +"Target": "Teiken", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Die URL lyk soos 'n eposadres. Wil jy die \"mailto:\" voorvoegsel byvoeg?", +"Insert\/edit link": "Skakel invoeg\/bewerk", +"Insert\/edit video": "Video invoeg\/bewerk", +"Media": "Media", +"Alternative source": "Alternatiewe bron", +"Paste your embed code below:": "Plak jou ingesluite kode hieronder in:", +"Insert video": "Video invoeg", +"Poster": "Plakaat", +"Insert\/edit media": "Media invoeg\/bewerk", +"Embed": "Insluit", +"Nonbreaking space": "Vaste spasie invoeg", +"Page break": "Nuwe Bladsy", +"Paste as text": "As teks plak", +"Preview": "Voorskou", +"Print": "Druk", +"Save": "Stoor", +"Could not find the specified string.": "Kon nie die gesoekde string vind nie", +"Replace": "Vervang", +"Next": "Volgende", +"Whole words": "Hele woorde", +"Find and replace": "Soek en vervang", +"Replace with": "Vervang Met", +"Find": "Soek", +"Replace all": "Vervang alles", +"Match case": "Kassensitief", +"Prev": "Vorige", +"Spellcheck": "Toets spelling", +"Finish": "Einde", +"Ignore all": "Ignoreer alles", +"Ignore": "Ignoreer", +"Add to Dictionary": "Voeg by woordeboek", +"Insert row before": "Voeg nuwe ry boaan", +"Rows": "Rye", +"Height": "Hoogte", +"Paste row after": "Plak ry na", +"Alignment": "Gerigdheid", +"Border color": "Randkleur", +"Column group": "Kolom Groep", +"Row": "Ry", +"Insert column before": "Voeg kolom vooraan", +"Split cell": "Sel split", +"Cell padding": "Ruimte binnein sel", +"Cell spacing": "Ruimte rondom sel", +"Row type": "Ry tipe", +"Insert table": "Tabel invoeg", +"Body": "Tabel Inhoud", +"Caption": "Onderskrif", +"Footer": "Voetskrif", +"Delete row": "Verwyder ry", +"Paste row before": "Plak ry vooraan", +"Scope": "Bereik", +"Delete table": "Skrap tabel", +"H Align": "Horisontaal-gerigdheid", +"Top": "Bo", +"Header cell": "Kop Sel", +"Column": "Kolom", +"Row group": "Ry Groep", +"Cell": "Sel", +"Middle": "Middel", +"Cell type": "Sel tipe", +"Copy row": "Kopieer ry", +"Row properties": "Ry eienskappe", +"Table properties": "Tabel eienskappe", +"Bottom": "Onder", +"V Align": "Vertikaal-rerigdheid", +"Header": "Kopteks", +"Right": "Regs", +"Insert column after": "Voeg kolom na", +"Cols": "Kolomme", +"Insert row after": "Voeg nuwe ry na", +"Width": "Wydte", +"Cell properties": "Sel eienskappe", +"Left": "Links", +"Cut row": "Knip ry", +"Delete column": "Verwyder kolom", +"Center": "Middel", +"Merge cells": "Selle saamvoeg", +"Insert template": "Sjabloon invoeg", +"Templates": "Sjablone", +"Background color": "Agtergrond Kleur", +"Custom...": "Spesifiek...", +"Custom color": "Spesifieke Kleur", +"No color": "Geen Kleur", +"Text color": "Teks Kleur", +"Table of Contents": "Inhoudsopgawe", +"Show blocks": "Blokke vertoon", +"Show invisible characters": "Onsigbare karakters vertoon", +"Words: {0}": "Woorde: {0}", +"Insert": "Invoeg", +"File": "L\u00eaer", +"Edit": "Wysig", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Ryk Teks Area. Druk ALT-F9 vir menu, ALT-F10 vir die nutsbalk, ALT-0 vir hulp.", +"Tools": "Gereedskap", +"View": "Formaat", +"Table": "Tabel", +"Format": "Formateer" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ar.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ar.js new file mode 100644 index 0000000000..2bd07a8a91 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ar.js @@ -0,0 +1,262 @@ +tinymce.addI18n('ar',{ +"Redo": "\u0625\u0639\u0627\u062f\u0629", +"Undo": "\u062a\u0631\u0627\u062c\u0639", +"Cut": "\u0642\u0635", +"Copy": "\u0646\u0633\u062e", +"Paste": "\u0644\u0635\u0642", +"Select all": "\u062a\u062d\u062f\u064a\u062f \u0627\u0644\u0643\u0644", +"New document": "\u0645\u0633\u062a\u0646\u062f \u062c\u062f\u064a\u062f", +"Ok": "\u0645\u0648\u0627\u0641\u0642", +"Cancel": "\u0625\u0644\u063a\u0627\u0621", +"Visual aids": "\u0627\u0644\u0645\u0639\u064a\u0646\u0627\u062a \u0627\u0644\u0628\u0635\u0631\u064a\u0629", +"Bold": "\u063a\u0627\u0645\u0642", +"Italic": "\u0645\u0627\u0626\u0644", +"Underline": "\u062a\u0633\u0637\u064a\u0631", +"Strikethrough": "\u064a\u062a\u0648\u0633\u0637 \u062e\u0637", +"Superscript": "\u0645\u0631\u062a\u0641\u0639", +"Subscript": "\u0645\u0646\u062e\u0641\u0636", +"Clear formatting": "\u0645\u0633\u062d \u0627\u0644\u062a\u0646\u0633\u064a\u0642", +"Align left": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635 \u0644\u0644\u064a\u0633\u0627\u0631", +"Align center": "\u062a\u0648\u0633\u064a\u0637", +"Align right": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635 \u0644\u0644\u064a\u0645\u064a\u0646", +"Justify": "\u0636\u0628\u0637", +"Bullet list": "\u062a\u0639\u062f\u0627\u062f \u0646\u0642\u0637\u064a", +"Numbered list": "\u062a\u0631\u0642\u064a\u0645", +"Decrease indent": "\u0625\u0646\u0642\u0627\u0635 \u0627\u0644\u0645\u0633\u0627\u0641\u0629 \u0627\u0644\u0628\u0627\u062f\u0626\u0629", +"Increase indent": "\u0632\u064a\u0627\u062f\u0629 \u0627\u0644\u0645\u0633\u0627\u0641\u0629 \u0627\u0644\u0628\u0627\u062f\u0626\u0629", +"Close": "\u0625\u063a\u0644\u0627\u0642", +"Formats": "\u0627\u0644\u062a\u0646\u0633\u064a\u0642\u0627\u062a", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0645\u062a\u0635\u0641\u062d\u0643 \u0644\u0627 \u064a\u062f\u0639\u0645 \u0627\u0644\u0648\u0635\u0648\u0644 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0625\u0644\u0649 \u0627\u0644\u062d\u0627\u0641\u0638\u0629. \u0627\u0644\u0631\u062c\u0627\u0621 \u0627\u0633\u062a\u062e\u062f\u0627\u0645 \u0627\u062e\u062a\u0635\u0627\u0631\u0627\u062a \u0644\u0648\u062d\u0629 \u0627\u0644\u0645\u0641\u0627\u062a\u064a\u062d Ctrl+X\/C\/V \u0628\u062f\u0644\u0627 \u0645\u0646 \u0630\u0644\u0643.", +"Headers": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646", +"Header 1": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 1", +"Header 2": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 2", +"Header 3": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 3", +"Header 4": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 4", +"Header 5": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 5", +"Header 6": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 6", +"Headings": "\u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629", +"Heading 1": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 1", +"Heading 2": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 2", +"Heading 3": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 3", +"Heading 4": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 4", +"Heading 5": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 5", +"Heading 6": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0631\u0626\u064a\u0633\u064a 6", +"Preformatted": "\u0645\u0647\u064a\u0623 \u0645\u0633\u0628\u0642\u0627", +"Div": "Div", +"Pre": "\u0633\u0627\u0628\u0642", +"Code": "\u0631\u0645\u0632", +"Paragraph": "\u0641\u0642\u0631\u0629", +"Blockquote": "\u0639\u0644\u0627\u0645\u0627\u062a \u0627\u0644\u0627\u0642\u062a\u0628\u0627\u0633", +"Inline": "\u062e\u0644\u0627\u0644", +"Blocks": "\u0627\u0644\u0623\u0642\u0633\u0627\u0645", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u064a\u062a\u0645 \u0627\u0644\u0644\u0635\u0642 \u062d\u0627\u0644\u064a\u0627\u064b \u0643\u0646\u0635 \u0639\u0627\u062f\u064a. \u0627\u0644\u0645\u062d\u062a\u0648\u0649 \u0633\u064a\u0628\u0642\u0649 \u0643\u0646\u0635 \u0639\u0627\u062f\u064a \u062d\u062a\u0649 \u062a\u0642\u0648\u0645 \u0628\u062a\u0639\u0637\u064a\u0644 \u0647\u0630\u0627 \u0627\u0644\u062e\u064a\u0627\u0631.", +"Font Family": "\u0645\u062c\u0645\u0648\u0639\u0629 \u0627\u0644\u062e\u0637", +"Font Sizes": "\u062d\u062c\u0645 \u0627\u0644\u062e\u0637", +"Class": "\u0627\u0644\u0641\u0626\u0629", +"Browse for an image": "\u0627\u0633\u062a\u0639\u0631\u0627\u0636 \u0635\u0648\u0631\u0629", +"OR": "\u0623\u0648", +"Drop an image here": "\u0627\u0633\u0642\u0637 \u0627\u0644\u0635\u0648\u0631\u0629 \u0647\u0646\u0627", +"Upload": "\u0631\u0641\u0639", +"Block": "\u0642\u0633\u0645", +"Align": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0623\u0641\u0642\u064a\u0629", +"Default": "\u0627\u0644\u0627\u0641\u062a\u0631\u0627\u0636\u064a", +"Circle": "\u062f\u0627\u0626\u0631\u0629", +"Disc": "\u0642\u0631\u0635", +"Square": "\u0645\u0631\u0628\u0639", +"Lower Alpha": "\u062a\u0631\u0642\u064a\u0645 \u0623\u062e\u0631\u0641 \u0635\u063a\u064a\u0631\u0629", +"Lower Greek": "\u062a\u0631\u0642\u064a\u0645 \u064a\u0648\u0646\u0627\u0646\u064a \u0635\u063a\u064a\u0631", +"Lower Roman": "\u062a\u0631\u0642\u064a\u0645 \u0631\u0648\u0645\u0627\u0646\u064a \u0635\u063a\u064a\u0631", +"Upper Alpha": "\u062a\u0631\u0642\u064a\u0645 \u0623\u062d\u0631\u0641 \u0643\u0628\u064a\u0631\u0629", +"Upper Roman": "\u062a\u0631\u0642\u064a\u0645 \u0631\u0648\u0645\u0627\u0646\u064a \u0643\u0628\u064a\u0631", +"Anchor": "\u0645\u0631\u0633\u0627\u0629", +"Name": "\u0627\u0644\u0627\u0633\u0645", +"Id": "\u0631\u0642\u0645 \u0627\u0644\u0645\u0639\u0631\u0641", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0631\u0642\u0645 \u0627\u0644\u0645\u0639\u0631\u0641 \u064a\u062c\u0628 \u0623\u0646 \u062a\u0628\u062f\u0623 \u0628\u062d\u0631\u0641\u060c \u064a\u062a\u0628\u0639 \u0641\u0642\u0637 \u0628\u062d\u0631\u0648\u0641 \u0648\u0623\u0631\u0642\u0627\u0645\u060c \u0634\u0631\u0637\u0627\u062a\u060c \u0623\u0648 \u0627\u0644\u0646\u0642\u0627\u0637\u060c \u0627\u0644\u0646\u0642\u0637\u062a\u064a\u0646 \u0623\u0648 \u0627\u0644\u0634\u0631\u0637\u0627\u062a \u0627\u0644\u0633\u0641\u0644\u064a\u0629.", +"You have unsaved changes are you sure you want to navigate away?": "\u0644\u062f\u064a\u0643 \u062a\u063a\u064a\u064a\u0631\u0627\u062a \u0644\u0645 \u064a\u062a\u0645 \u062d\u0641\u0638\u0647\u0627 \u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0627\u0644\u0627\u0646\u062a\u0642\u0627\u0644 \u0628\u0639\u064a\u062f\u0627\u061f", +"Restore last draft": "\u0627\u0633\u062a\u0639\u0627\u062f\u0629 \u0623\u062e\u0631 \u0645\u0633\u0648\u062f\u0629", +"Special character": "\u0631\u0645\u0632", +"Source code": "\u0634\u0641\u0631\u0629 \u0627\u0644\u0645\u0635\u062f\u0631", +"Insert\/Edit code sample": "\u0625\u062f\u0631\u0627\u062c\/\u062a\u062d\u0631\u064a\u0631 \u0627\u0644\u0643\u0648\u062f", +"Language": "\u0627\u0644\u0644\u063a\u0629", +"Code sample": "\u0639\u064a\u0651\u0646\u0629 \u0639\u0646 \u0627\u0644\u0643\u0648\u062f \u0627\u0644\u0628\u0631\u0645\u062c\u064a", +"Color": "\u0627\u0644\u0644\u0648\u0646", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0645\u0646 \u0627\u0644\u064a\u0633\u0627\u0631 \u0644\u0644\u064a\u0645\u064a\u0646", +"Right to left": "\u0645\u0646 \u0627\u0644\u064a\u0645\u064a\u0646 \u0644\u0644\u064a\u0633\u0627\u0631", +"Emoticons": "\u0627\u0644\u0631\u0645\u0648\u0632", +"Document properties": "\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0645\u0633\u062a\u0646\u062f", +"Title": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646", +"Keywords": "\u0643\u0644\u0645\u0627\u062a \u0627\u0644\u0628\u062d\u062b", +"Description": "\u0627\u0644\u0648\u0635\u0641", +"Robots": "\u0627\u0644\u0631\u0648\u0628\u0648\u062a\u0627\u062a", +"Author": "\u0627\u0644\u0643\u0627\u062a\u0628", +"Encoding": "\u0627\u0644\u062a\u0631\u0645\u064a\u0632", +"Fullscreen": "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629", +"Action": "\u0627\u0644\u0639\u0645\u0644\u064a\u0629", +"Shortcut": "\u0627\u0644\u0627\u062e\u062a\u0635\u0627\u0631", +"Help": "\u0627\u0644\u0645\u0633\u0627\u0639\u062f\u0629", +"Address": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646", +"Focus to menubar": "\u0627\u0644\u062a\u0631\u0643\u064a\u0632 \u0639\u0644\u0649 \u0634\u0631\u064a\u0637 \u0627\u0644\u0642\u0648\u0627\u0626\u0645", +"Focus to toolbar": "\u0627\u0644\u062a\u0631\u0643\u064a\u0632 \u0639\u0644\u0649 \u0634\u0631\u064a\u0637 \u0627\u0644\u0623\u062f\u0648\u0627\u062a", +"Focus to element path": "\u0627\u0644\u062a\u0631\u0643\u064a\u0632 \u0639\u0644\u0649 \u0645\u0633\u0627\u0631 \u0627\u0644\u0639\u0646\u0635\u0631", +"Focus to contextual toolbar": "\u0627\u0644\u062a\u0631\u0643\u064a\u0632 \u0639\u0644\u0649 \u0634\u0631\u064a\u0637 \u0623\u062f\u0648\u0627\u062a \u0627\u0644\u0633\u064a\u0627\u0642", +"Insert link (if link plugin activated)": "\u0625\u0636\u0627\u0641\u0629 \u0631\u0627\u0628\u0637 (\u0625\u0630\u0627 \u0643\u0627\u0646\u062a \u0625\u0636\u0627\u0641\u0629 \u0627\u0644\u0631\u0648\u0627\u0628\u0637 \u0645\u0641\u0639\u0644\u0629)", +"Save (if save plugin activated)": "\u062d\u0641\u0638 (\u0625\u0630\u0627 \u0643\u0627\u0646\u062a \u0625\u0636\u0627\u0641\u0629 \u0627\u0644\u062d\u0641\u0638 \u0645\u0641\u0639\u0644\u0629)", +"Find (if searchreplace plugin activated)": "\u0627\u0644\u0628\u062d\u062b (\u0625\u0630\u0627 \u0643\u0627\u0646\u062a \u0625\u0636\u0627\u0641\u0629 \u0627\u0644\u0628\u062d\u062b \u0645\u0641\u0639\u0644\u0629)", +"Plugins installed ({0}):": "\u0627\u0644\u0625\u0636\u0627\u0641\u0627\u062a \u0627\u0644\u0645\u062b\u0628\u062a\u0629 ({0}):", +"Premium plugins:": "\u0627\u0644\u0625\u0636\u0627\u0641\u0627\u062a \u0627\u0644\u0645\u0645\u064a\u0632\u0629:", +"Learn more...": "\u0645\u0639\u0631\u0641\u0629 \u0627\u0644\u0645\u0632\u064a\u062f...", +"You are using {0}": "\u0623\u0646\u062a \u062a\u0633\u062a\u062e\u062f\u0645 {0}", +"Plugins": "\u0627\u0644\u0625\u0636\u0627\u0641\u0627\u062a", +"Handy Shortcuts": "\u0627\u062e\u062a\u0635\u0627\u0631\u0627\u062a \u0645\u0633\u0627\u0639\u0650\u062f\u0629", +"Horizontal line": "\u062e\u0637 \u0623\u0641\u0642\u064a", +"Insert\/edit image": "\u0625\u062f\u0631\u0627\u062c\/\u062a\u062d\u0631\u064a\u0631 \u0635\u0648\u0631\u0629", +"Image description": "\u0648\u0635\u0641 \u0627\u0644\u0635\u0648\u0631\u0629", +"Source": "\u0627\u0644\u0645\u0635\u062f\u0631", +"Dimensions": "\u0627\u0644\u0623\u0628\u0639\u0627\u062f", +"Constrain proportions": "\u0627\u0644\u062a\u0646\u0627\u0633\u0628", +"General": "\u0639\u0627\u0645", +"Advanced": "\u062e\u0635\u0627\u0626\u0635 \u0645\u062a\u0642\u062f\u0645\u0647", +"Style": "\u0627\u0644\u0646\u0645\u0637 \/ \u0627\u0644\u0634\u0643\u0644", +"Vertical space": "\u0645\u0633\u0627\u0641\u0629 \u0639\u0645\u0648\u062f\u064a\u0629", +"Horizontal space": "\u0645\u0633\u0627\u0641\u0629 \u0623\u0641\u0642\u064a\u0629", +"Border": "\u062d\u062f\u0648\u062f", +"Insert image": "\u0625\u062f\u0631\u0627\u062c \u0635\u0648\u0631\u0629", +"Image": "\u0627\u0644\u0635\u0648\u0631\u0629", +"Image list": "\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0635\u0648\u0631", +"Rotate counterclockwise": "\u062a\u062f\u0648\u064a\u0631 \u0639\u0643\u0633 \u0627\u062a\u062c\u0627\u0647 \u0639\u0642\u0627\u0631\u0628 \u0627\u0644\u0633\u0627\u0639\u0629", +"Rotate clockwise": "\u062a\u062f\u0648\u064a\u0631 \u0641\u064a \u0627\u062a\u062c\u0627\u0647 \u0639\u0642\u0627\u0631\u0628 \u0627\u0644\u0633\u0627\u0639\u0629", +"Flip vertically": "\u0627\u0646\u0639\u0643\u0627\u0633 \u0639\u0627\u0645\u0648\u062f\u064a", +"Flip horizontally": "\u0627\u0646\u0639\u0643\u0627\u0633 \u0623\u0641\u0642\u064a", +"Edit image": "\u062a\u062d\u0631\u064a\u0631 \u0627\u0644\u0635\u0648\u0631\u0629", +"Image options": "\u0627\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0635\u0648\u0631\u0629", +"Zoom in": "\u062a\u0643\u0628\u064a\u0631", +"Zoom out": "\u062a\u0635\u063a\u064a\u0631", +"Crop": "\u0642\u0635", +"Resize": "\u062a\u063a\u064a\u064a\u0631 \u062d\u062c\u0645", +"Orientation": "\u0627\u0644\u0645\u062d\u0627\u0630\u0627\u0629", +"Brightness": "\u0627\u0644\u0625\u0636\u0627\u0621\u0629", +"Sharpen": "\u062d\u0627\u062f\u0629", +"Contrast": "\u0627\u0644\u062a\u0628\u0627\u064a\u0646", +"Color levels": "\u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0644\u0648\u0646", +"Gamma": "\u063a\u0627\u0645\u0627", +"Invert": "\u0639\u0643\u0633", +"Apply": "\u062a\u0637\u0628\u064a\u0642", +"Back": "\u0644\u0644\u062e\u0644\u0641", +"Insert date\/time": "\u0625\u062f\u0631\u0627\u062c \u062a\u0627\u0631\u064a\u062e\/\u0648\u0642\u062a", +"Date\/time": "\u0627\u0644\u062a\u0627\u0631\u064a\u062e\/\u0627\u0644\u0648\u0642\u062a", +"Insert link": "\u0625\u062f\u0631\u0627\u062c \u0631\u0627\u0628\u0637", +"Insert\/edit link": "\u0625\u062f\u0631\u0627\u062c\/\u062a\u062d\u0631\u064a\u0631 \u0631\u0627\u0628\u0637", +"Text to display": "\u0627\u0644\u0646\u0635 \u0627\u0644\u0645\u0637\u0644\u0648\u0628 \u0639\u0631\u0636\u0647", +"Url": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646", +"Target": "\u0627\u0644\u0625\u0637\u0627\u0631 \u0627\u0644\u0647\u062f\u0641", +"None": "\u0628\u0644\u0627", +"New window": "\u0646\u0627\u0641\u0630\u0629 \u062c\u062f\u064a\u062f\u0629", +"Remove link": "\u062d\u0630\u0641 \u0627\u0644\u0631\u0627\u0628\u0637", +"Anchors": "\u0627\u0644\u0645\u0631\u0633\u0627\u0629", +"Link": "\u0627\u0644\u0631\u0627\u0628\u0637", +"Paste or type a link": "\u0623\u062f\u062e\u0644 \u0623\u0648 \u0627\u0643\u062a\u0628 \u0627\u0644\u0631\u0627\u0628\u0637", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0627\u0644\u0631\u0627\u0628\u0637 \u0627\u0644\u0630\u064a \u0642\u0645\u062a \u0628\u0625\u062f\u0631\u0627\u062c\u0647 \u064a\u0634\u0627\u0628\u0647 \u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a. \u0647\u0644 \u062a\u0631\u064a\u062f \u0627\u0646 \u062a\u0636\u064a\u0641 \u0627\u0644\u0644\u0627\u062d\u0642\u0629 mailto: \u0645\u0639\u062a\u0628\u0631\u0627\u064b \u0647\u0630\u0627 \u0627\u0644\u0631\u0627\u0628\u0637 \u0628\u0631\u064a\u062f\u0627 \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a\u0627\u064b\u061f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0646\u062a\u0648\u0642\u0639 \u0627\u0646\u0643 \u0642\u0645\u062a \u0628\u0625\u062f\u0631\u0627\u062c \u0631\u0627\u0628\u0637 \u0644\u0645\u0648\u0642\u0639 \u062e\u0627\u0631\u062c\u064a. \u0647\u0644 \u062a\u0631\u064a\u062f \u0627\u0646 \u0646\u0636\u064a\u0641 \u0627\u0644\u0644\u0627\u062d\u0642\u0629 http:\/\/ \u0644\u0644\u0631\u0627\u0628\u0637 \u0627\u0644\u0630\u064a \u0627\u062f\u062e\u0644\u062a\u0647\u061f", +"Link list": "\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0631\u0648\u0627\u0628\u0637", +"Insert video": "\u0625\u062f\u0631\u0627\u062c \u0641\u064a\u062f\u064a\u0648", +"Insert\/edit video": "\u0625\u062f\u0631\u0627\u062c\/\u062a\u062d\u0631\u064a\u0631 \u0641\u064a\u062f\u064a\u0648", +"Insert\/edit media": "\u0625\u062f\u0631\u0627\u062c\/\u062a\u062d\u0631\u064a\u0631 \u0627\u0644\u0648\u0633\u0627\u0626\u0637 \u0627\u0644\u0645\u062a\u0639\u062f\u062f\u0629", +"Alternative source": "\u0645\u0635\u062f\u0631 \u0628\u062f\u064a\u0644", +"Poster": "\u0645\u0644\u0635\u0642", +"Paste your embed code below:": "\u0644\u0635\u0642 \u0643\u0648\u062f \u0627\u0644\u062a\u0636\u0645\u064a\u0646 \u0647\u0646\u0627:", +"Embed": "\u062a\u0636\u0645\u064a\u0646", +"Media": "\u0627\u0644\u0648\u0633\u0627\u0626\u0637 \u0627\u0644\u0645\u062a\u0639\u062f\u062f\u0629", +"Nonbreaking space": "\u0645\u0633\u0627\u0641\u0629 \u063a\u064a\u0631 \u0645\u0646\u0642\u0633\u0645\u0629", +"Page break": "\u0641\u0627\u0635\u0644 \u0644\u0644\u0635\u0641\u062d\u0629", +"Paste as text": "\u0644\u0635\u0642 \u0643\u0646\u0635", +"Preview": "\u0645\u0639\u0627\u064a\u0646\u0629", +"Print": "\u0637\u0628\u0627\u0639\u0629", +"Save": "\u062d\u0641\u0638", +"Find": "\u0628\u062d\u062b", +"Replace with": "\u0627\u0633\u062a\u0628\u062f\u0627\u0644 \u0628\u0640", +"Replace": "\u0627\u0633\u062a\u0628\u062f\u0627\u0644", +"Replace all": "\u0627\u0633\u062a\u0628\u062f\u0627\u0644 \u0627\u0644\u0643\u0644", +"Prev": "\u0627\u0644\u0633\u0627\u0628\u0642", +"Next": "\u0627\u0644\u062a\u0627\u0644\u064a", +"Find and replace": "\u0628\u062d\u062b \u0648\u0627\u0633\u062a\u0628\u062f\u0627\u0644", +"Could not find the specified string.": "\u062a\u0639\u0630\u0631 \u0627\u0644\u0639\u062b\u0648\u0631 \u0639\u0644\u0649 \u0627\u0644\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u062d\u062f\u062f\u0629", +"Match case": "\u0645\u0637\u0627\u0628\u0642\u0629 \u062d\u0627\u0644\u0629 \u0627\u0644\u0623\u062d\u0631\u0641", +"Whole words": "\u0645\u0637\u0627\u0628\u0642\u0629 \u0627\u0644\u0643\u0644\u0645\u0627\u062a \u0628\u0627\u0644\u0643\u0627\u0645\u0644", +"Spellcheck": "\u062a\u062f\u0642\u064a\u0642 \u0625\u0645\u0644\u0627\u0626\u064a", +"Ignore": "\u062a\u062c\u0627\u0647\u0644", +"Ignore all": "\u062a\u062c\u0627\u0647\u0644 \u0627\u0644\u0643\u0644", +"Finish": "\u0627\u0646\u062a\u0647\u064a", +"Add to Dictionary": "\u0627\u0636\u0641 \u0627\u0644\u064a \u0627\u0644\u0642\u0627\u0645\u0648\u0633", +"Insert table": "\u0625\u062f\u0631\u0627\u062c \u062c\u062f\u0648\u0644", +"Table properties": "\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u062c\u062f\u0648\u0644", +"Delete table": "\u062d\u0630\u0641 \u062c\u062f\u0648\u0644", +"Cell": "\u062e\u0644\u064a\u0629", +"Row": "\u0635\u0641", +"Column": "\u0639\u0645\u0648\u062f", +"Cell properties": "\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u062e\u0644\u064a\u0629", +"Merge cells": "\u062f\u0645\u062c \u062e\u0644\u0627\u064a\u0627", +"Split cell": "\u062a\u0642\u0633\u064a\u0645 \u0627\u0644\u062e\u0644\u0627\u064a\u0627", +"Insert row before": "\u0625\u062f\u0631\u0627\u062c \u0635\u0641 \u0644\u0644\u0623\u0639\u0644\u0649", +"Insert row after": "\u0625\u062f\u0631\u0627\u062c \u0635\u0641 \u0644\u0644\u0623\u0633\u0641\u0644", +"Delete row": "\u062d\u0630\u0641 \u0635\u0641", +"Row properties": "\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0635\u0641", +"Cut row": "\u0642\u0635 \u0627\u0644\u0635\u0641", +"Copy row": "\u0646\u0633\u062e \u0627\u0644\u0635\u0641", +"Paste row before": "\u0644\u0635\u0642 \u0627\u0644\u0635\u0641 \u0644\u0644\u0623\u0639\u0644\u0649", +"Paste row after": "\u0644\u0635\u0642 \u0627\u0644\u0635\u0641 \u0644\u0644\u0623\u0633\u0641\u0644", +"Insert column before": "\u0625\u062f\u0631\u0627\u062c \u0639\u0645\u0648\u062f \u0644\u0644\u064a\u0633\u0627\u0631", +"Insert column after": "\u0625\u062f\u0631\u0627\u062c \u0639\u0645\u0648\u062f \u0644\u0644\u064a\u0645\u064a\u0646", +"Delete column": "\u062d\u0630\u0641 \u0639\u0645\u0648\u062f", +"Cols": "\u0639\u062f\u062f \u0627\u0644\u0623\u0639\u0645\u062f\u0629", +"Rows": "\u0639\u062f\u062f \u0627\u0644\u0635\u0641\u0648\u0641", +"Width": "\u0639\u0631\u0636", +"Height": "\u0627\u0631\u062a\u0641\u0627\u0639", +"Cell spacing": "\u0627\u0644\u0645\u0633\u0627\u0641\u0629 \u0628\u064a\u0646 \u0627\u0644\u062e\u0644\u0627\u064a\u0627", +"Cell padding": "\u062a\u0628\u0627\u0639\u062f \u0627\u0644\u062e\u0644\u064a\u0629", +"Caption": "\u0634\u0631\u062d", +"Left": "\u064a\u0633\u0627\u0631", +"Center": "\u062a\u0648\u0633\u064a\u0637", +"Right": "\u064a\u0645\u064a\u0646", +"Cell type": "\u0646\u0648\u0639 \u0627\u0644\u062e\u0644\u064a\u0629", +"Scope": "\u0627\u0644\u0645\u062c\u0627\u0644", +"Alignment": "\u0645\u062d\u0627\u0630\u0627\u0629", +"H Align": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0623\u0641\u0642\u064a\u0629", +"V Align": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0631\u0623\u0633\u064a\u0629", +"Top": "\u0623\u0639\u0644\u064a", +"Middle": "\u0627\u0644\u0648\u0633\u0637", +"Bottom": "\u0627\u0644\u0623\u0633\u0641\u0644", +"Header cell": "\u0631\u0623\u0633 \u0627\u0644\u062e\u0644\u064a\u0629", +"Row group": "\u0645\u062c\u0645\u0648\u0639\u0629 \u0635\u0641", +"Column group": "\u0645\u062c\u0645\u0648\u0639\u0629 \u0639\u0645\u0648\u062f", +"Row type": "\u0646\u0648\u0639 \u0627\u0644\u0635\u0641", +"Header": "\u0627\u0644\u0631\u0623\u0633", +"Body": "\u0647\u064a\u0643\u0644", +"Footer": "\u062a\u0630\u064a\u064a\u0644", +"Border color": "\u0644\u0648\u0646 \u0627\u0644\u0625\u0637\u0627\u0631", +"Insert template": "\u0625\u062f\u0631\u0627\u062c \u0642\u0627\u0644\u0628", +"Templates": "\u0642\u0648\u0627\u0644\u0628", +"Template": "\u0627\u0644\u0642\u0627\u0644\u0628", +"Text color": "\u0644\u0648\u0646 \u0627\u0644\u0646\u0635", +"Background color": "\u0644\u0648\u0646 \u0627\u0644\u062e\u0644\u0641\u064a\u0629", +"Custom...": "\u062a\u062e\u0635\u064a\u0635 ...", +"Custom color": "\u0644\u0648\u0646 \u0645\u062e\u0635\u0635", +"No color": "\u0628\u062f\u0648\u0646 \u0644\u0648\u0646", +"Table of Contents": "\u062c\u062f\u0648\u0644 \u0627\u0644\u0645\u062d\u062a\u0648\u064a\u0627\u062a", +"Show blocks": "\u0645\u0634\u0627\u0647\u062f\u0629 \u0627\u0644\u0643\u062a\u0644", +"Show invisible characters": "\u0623\u0638\u0647\u0631 \u0627\u0644\u0623\u062d\u0631\u0641 \u0627\u0644\u063a\u064a\u0631 \u0645\u0631\u0626\u064a\u0629", +"Words: {0}": "\u0627\u0644\u0643\u0644\u0645\u0627\u062a:{0}", +"{0} words": "{0} \u0643\u0644\u0645\u0627\u062a", +"File": "\u0645\u0644\u0641", +"Edit": "\u062a\u062d\u0631\u064a\u0631", +"Insert": "\u0625\u062f\u0631\u0627\u062c", +"View": "\u0639\u0631\u0636", +"Format": "\u062a\u0646\u0633\u064a\u0642", +"Table": "\u062c\u062f\u0648\u0644", +"Tools": "\u0623\u062f\u0627\u0648\u0627\u062a", +"Powered by {0}": "\u0645\u062f\u0639\u0648\u0645 \u0645\u0646 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0645\u0646\u0637\u0642\u0629 \u0646\u0635 \u0645\u0646\u0633\u0642. \u0627\u0636\u063a\u0637 ALT-F9 \u0644\u0644\u0642\u0627\u0626\u0645\u0629. \u0627\u0636\u063a\u0637 ALT-F10 \u0644\u0634\u0631\u064a\u0637 \u0627\u0644\u0623\u062f\u0648\u0627\u062a. \u0627\u0636\u063a\u0637 ALT-0 \u0644\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u0645\u0633\u0627\u0639\u062f\u0629", +"_dir": "rtl" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/az.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/az.js new file mode 100644 index 0000000000..cbd5d4700e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/az.js @@ -0,0 +1,261 @@ +tinymce.addI18n('az',{ +"Redo": "\u0130r\u0259li", +"Undo": "Geriy\u0259", +"Cut": "K\u0259s", +"Copy": "K\u00f6\u00e7\u00fcr", +"Paste": "\u018flav\u0259 et", +"Select all": "Ham\u0131s\u0131n\u0131 se\u00e7", +"New document": "Yeni s\u0259n\u0259d", +"Ok": "Oldu", +"Cancel": "L\u0259\u011fv et", +"Visual aids": "Konturlar\u0131 g\u00f6st\u0259r", +"Bold": "Qal\u0131n", +"Italic": "Maili", +"Underline": "Alt x\u0259ttli", +"Strikethrough": "Silinmi\u015f", +"Superscript": "Yuxar\u0131 indeks", +"Subscript": "A\u015fa\u011f\u0131 indeks", +"Clear formatting": "Format\u0131 t\u0259mizl\u0259", +"Align left": "Sol t\u0259r\u0259f \u00fczr\u0259", +"Align center": "M\u0259rk\u0259z \u00fczr\u0259", +"Align right": "Sa\u011f t\u0259r\u0259f \u00fczr\u0259", +"Justify": "H\u0259r iki t\u0259r\u0259f \u00fczr\u0259", +"Bullet list": "S\u0131ras\u0131z siyah\u0131", +"Numbered list": "N\u00f6mr\u0259l\u0259nmi\u015f siyah\u0131", +"Decrease indent": "Bo\u015flu\u011fu azalt", +"Increase indent": "Bo\u015flu\u011fu art\u0131r", +"Close": "Ba\u011fla", +"Formats": "Formatlar", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Sizin brauzeriniz m\u00fcbadil\u0259 buferin\u0259 birba\u015fa yolu d\u0259st\u0259kl\u0259mir. Z\u0259hm\u0259t olmasa, bunun yerin\u0259 klaviaturan\u0131n Ctrl+X\/C\/V d\u00fcym\u0259l\u0259rind\u0259n istifad\u0259 edin.", +"Headers": "Ba\u015fl\u0131qlar", +"Header 1": "Ba\u015fl\u0131q 1", +"Header 2": "Ba\u015fl\u0131q 2", +"Header 3": "Ba\u015fl\u0131q 3", +"Header 4": "Ba\u015fl\u0131q 4", +"Header 5": "Ba\u015fl\u0131q 5", +"Header 6": "Ba\u015fl\u0131q 6", +"Headings": "Ba\u015fl\u0131qlar", +"Heading 1": "Ba\u015fl\u0131q 1", +"Heading 2": "Ba\u015fl\u0131q 2", +"Heading 3": "Ba\u015fl\u0131q 3", +"Heading 4": "Ba\u015fl\u0131q 4", +"Heading 5": "Ba\u015fl\u0131q 5", +"Heading 6": "Ba\u015fl\u0131q 6", +"Preformatted": "\u018fvv\u0259lc\u0259d\u0259n formatland\u0131r\u0131lm\u0131\u015f", +"Div": "Div", +"Pre": "Pre", +"Code": "Kod", +"Paragraph": "Paraqraf", +"Blockquote": "Sitat", +"Inline": "S\u0259tir i\u00e7i", +"Blocks": "Bloklar", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Hal-haz\u0131rda adi m\u0259tn rejimind\u0259 yerl\u0259\u015fdirilir. M\u0259zmun sad\u0259 m\u0259tn \u015f\u0259klind\u0259 yerl\u0259\u015fdiril\u0259c\u0259k, h\u0259l\u0259 bu se\u00e7imi d\u0259yi\u015fdirm\u0259.", +"Font Family": "Font stili", +"Font Sizes": "Font \u00f6l\u00e7\u00fcl\u0259ri", +"Class": "Sinif", +"Browse for an image": "\u015e\u0259kil se\u00e7", +"OR": "V\u018f YA", +"Drop an image here": "\u015e\u0259kli buraya s\u00fcr\u00fckl\u0259yin", +"Upload": "Y\u00fckl\u0259", +"Block": "Blokla", +"Align": "D\u00fczl\u0259ndir", +"Default": "Susmaya g\u00f6r\u0259", +"Circle": "Dair\u0259", +"Disc": "Disk", +"Square": "Sah\u0259", +"Lower Alpha": "Ki\u00e7ik Alfa \u0259lifbas\u0131", +"Lower Greek": "Ki\u00e7ik Yunan \u0259lifbas\u0131", +"Lower Roman": "Ki\u00e7ik Roma \u0259lifbas\u0131", +"Upper Alpha": "B\u00f6y\u00fck Alfa \u0259lifbas\u0131", +"Upper Roman": "B\u00f6y\u00fck Roma \u0259lifbas\u0131", +"Anchor": "L\u00f6vb\u0259r", +"Name": "Ad", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0130D h\u0259rfl\u0259 ba\u015flamal\u0131d\u0131r. Daha sonra is\u0259 h\u0259rf, r\u0259q\u0259m, tire, n\u00f6qt\u0259, qo\u015fan\u00f6qt\u0259, v\u0259 altx\u0259tt kimi simvollardan istifad\u0259 oluna bil\u0259r.", +"You have unsaved changes are you sure you want to navigate away?": "Sizd\u0259 yadda saxlan\u0131lmayan d\u0259yi\u015fiklikl\u0259r var \u0259minsiniz ki, getm\u0259k ist\u0259yirsiniz?", +"Restore last draft": "Son layih\u0259nin b\u0259rpas\u0131", +"Special character": "X\u00fcsusi simvollar", +"Source code": "M\u0259nb\u0259 kodu", +"Insert\/Edit code sample": "Kod n\u00fcmun\u0259sin\u0259 \u0259lav\u0259\/d\u00fcz\u0259li\u015f et", +"Language": "Dil", +"Code sample": "Kod n\u00fcmun\u0259si", +"Color": "R\u0259ng", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Soldan sa\u011fa", +"Right to left": "Sa\u011fdan sola", +"Emoticons": "Emosiyalar", +"Document properties": "S\u0259n\u0259din x\u00fcsusiyy\u0259tl\u0259ri", +"Title": "Ba\u015fl\u0131q", +"Keywords": "A\u00e7ar s\u00f6zl\u0259r", +"Description": "T\u0259sviri", +"Robots": "Robotlar", +"Author": "M\u00fc\u0259llif", +"Encoding": "Kodla\u015fd\u0131rma", +"Fullscreen": "Tam ekran rejimi", +"Action": "\u018fmr", +"Shortcut": "Q\u0131sayol", +"Help": "K\u00f6m\u0259k", +"Address": "Adres", +"Focus to menubar": "Menyu \u00e7ubu\u011funa diqq\u0259t et", +"Focus to toolbar": "Al\u0259tl\u0259r \u00e7ubu\u011funa diqq\u0259t et", +"Focus to element path": "Elementin m\u0259nb\u0259yin\u0259 diqq\u0259t et", +"Focus to contextual toolbar": "Kontekst menyuya diqq\u0259t et", +"Insert link (if link plugin activated)": "Link \u0259lav\u0259 et (\u0259g\u0259r link \u0259lav\u0259si aktivdirs\u0259)", +"Save (if save plugin activated)": "Yadda\u015fa yaz (\u0259g\u0259r yadda\u015fa yaz \u0259lav\u0259si aktivdirs\u0259)", +"Find (if searchreplace plugin activated)": "Tap (\u0259g\u0259r axtar\u0131\u015f \u0259lav\u0259si aktivdirs\u0259)", +"Plugins installed ({0}):": "\u018flav\u0259l\u0259r y\u00fckl\u0259ndi ({0}):", +"Premium plugins:": "Premium \u0259lav\u0259l\u0259r", +"Learn more...": "Daha \u00e7ox \u00f6yr\u0259n...", +"You are using {0}": "Siz {0} istifad\u0259 edirsiniz", +"Plugins": "\u018flav\u0259l\u0259r", +"Handy Shortcuts": "Laz\u0131ml\u0131 q\u0131sayollar", +"Horizontal line": "Horizontal x\u0259tt", +"Insert\/edit image": "\u015e\u0259kilin \u0259lav\u0259\/redakt\u0259 edilm\u0259si", +"Image description": "\u015e\u0259kilin t\u0259sviri", +"Source": "M\u0259nb\u0259", +"Dimensions": "\u00d6l\u00e7\u00fcl\u0259r", +"Constrain proportions": "Nisb\u0259tl\u0259rin saxlan\u0131lmas\u0131", +"General": "\u00dcmumi", +"Advanced": "Geni\u015fl\u0259nmi\u015f", +"Style": "Stil", +"Vertical space": "Vertikal sah\u0259", +"Horizontal space": "Horizontal sah\u0259", +"Border": "\u00c7\u0259r\u00e7iv\u0259", +"Insert image": "\u015e\u0259kil yerl\u0259\u015fdir", +"Image": "\u015e\u0259kil", +"Image list": "\u015e\u0259kil listi", +"Rotate counterclockwise": "Saat \u0259qr\u0259binin \u0259ksin\u0259 f\u0131rlat", +"Rotate clockwise": "Saat \u0259qr\u0259bi istiqam\u0259tind\u0259 f\u0131rlat", +"Flip vertically": "\u015eaquli \u00e7evir", +"Flip horizontally": "\u00dcfiqi \u00e7evir", +"Edit image": "\u015e\u0259kili redakt\u0259 et", +"Image options": "\u015e\u0259kil parametrl\u0259ri", +"Zoom in": "Yax\u0131nla\u015fd\u0131r", +"Zoom out": "Uzaqla\u015fd\u0131r", +"Crop": "K\u0259s", +"Resize": "\u00d6l\u00e7\u00fcl\u0259ri d\u0259yi\u015f", +"Orientation": "Oriyentasiya", +"Brightness": "Parlaql\u0131q", +"Sharpen": "K\u0259skinl\u0259\u015fdir", +"Contrast": "Ziddiyy\u0259t", +"Color levels": "R\u0259ng s\u0259viyy\u0259l\u0259ri", +"Gamma": "Qamma", +"Invert": "T\u0259rsin\u0259 \u00e7evir", +"Apply": "T\u0259tbiq et", +"Back": "Geri", +"Insert date\/time": "G\u00fcn\/tarix \u0259lav\u0259 et", +"Date\/time": "Tarix\/saat", +"Insert link": "Linkin \u0259lav\u0259 edilm\u0259si", +"Insert\/edit link": "Linkin \u0259lav\u0259\/redakt\u0259 edilm\u0259si", +"Text to display": "G\u00f6r\u00fcn\u0259n yaz\u0131n\u0131n t\u0259sviri", +"Url": "Linkin \u00fcnvan\u0131", +"Target": "H\u0259d\u0259f", +"None": "Yoxdur", +"New window": "Yeni p\u0259nc\u0259r\u0259d\u0259 a\u00e7\u0131ls\u0131n", +"Remove link": "Linki sil", +"Anchors": "L\u00f6vb\u0259rl\u0259r", +"Link": "Ke\u00e7id", +"Paste or type a link": "Ke\u00e7idi yerl\u0259\u015fdirin v\u0259 ya yaz\u0131n", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Daxil etdiyiniz URL bir e-mail kimi g\u00f6r\u00fcn\u00fcr. \u018fg\u0259r t\u0259l\u0259b olunan mailto: prefix \u0259lav\u0259 etm\u0259k ist\u0259yirsiniz?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Daxil etdiyiniz URL bir e-mail kimi g\u00f6r\u00fcn\u00fcr. \u018fg\u0259r t\u0259l\u0259b olunan mailto: prefix \u0259lav\u0259 etm\u0259k ist\u0259yirsiniz?", +"Link list": "Ke\u00e7id listi", +"Insert video": "Videonun \u0259lav\u0259 edilm\u0259si", +"Insert\/edit video": "Videonun \u0259lav\u0259\/redakt\u0259 edilm\u0259si", +"Insert\/edit media": "Media \u0259lav\u0259\/d\u00fcz\u0259li\u015f et", +"Alternative source": "Alternativ m\u0259nb\u0259", +"Poster": "Poster", +"Paste your embed code below:": "\u00d6z kodunuzu a\u015fa\u011f\u0131 \u0259lav\u0259 edin:", +"Embed": "\u018flav\u0259 etm\u0259k \u00fc\u00e7\u00fcn kod", +"Media": "Media", +"Nonbreaking space": "Q\u0131r\u0131lmaz sah\u0259", +"Page break": "S\u0259hif\u0259nin q\u0131r\u0131lmas\u0131", +"Paste as text": "M\u0259tn kimi \u0259lav\u0259 et", +"Preview": "\u0130lkinbax\u0131\u015f", +"Print": "\u00c7ap et", +"Save": "Yadda saxla", +"Find": "Tap", +"Replace with": "Bununla d\u0259yi\u015fdir", +"Replace": "D\u0259yi\u015fdir", +"Replace all": "Ham\u0131s\u0131n\u0131 d\u0259yi\u015fdir", +"Prev": "\u018fvv\u0259lki", +"Next": "N\u00f6vb\u0259ti", +"Find and replace": "Tap v\u0259 d\u0259yi\u015fdir", +"Could not find the specified string.": "G\u00f6st\u0259ril\u0259n s\u0259tiri tapmaq olmur", +"Match case": "Registri n\u0259z\u0259r\u0259 al", +"Whole words": "Tam s\u00f6zl\u0259r", +"Spellcheck": "Orfoqrafiyan\u0131 yoxla", +"Ignore": "\u0130qnorla", +"Ignore all": "Ham\u0131s\u0131n\u0131 iqnorla", +"Finish": "Bitir", +"Add to Dictionary": "L\u00fc\u011f\u0259t\u0259 \u0259lav\u0259 edilsin", +"Insert table": "S\u0259tir \u0259lav\u0259 et", +"Table properties": "C\u0259dv\u0259lin x\u00fcsusiyy\u0259tl\u0259ri", +"Delete table": "C\u0259dv\u0259li sil", +"Cell": "H\u00fccr\u0259", +"Row": "S\u0259tir", +"Column": "S\u00fctun", +"Cell properties": "H\u00fccr\u0259nin x\u00fcsusiyy\u0259tl\u0259ri", +"Merge cells": "H\u00fccr\u0259l\u0259ri birl\u0259\u015ftir", +"Split cell": "H\u00fccr\u0259l\u0259rin say\u0131", +"Insert row before": "\u018fvv\u0259lin\u0259 s\u0259tir \u0259lav\u0259 et", +"Insert row after": "Sonras\u0131na s\u0259tir \u0259lav\u0259 et", +"Delete row": "S\u0259tri sil", +"Row properties": "S\u0259trin x\u00fcsusiyy\u0259tl\u0259ri", +"Cut row": "S\u0259tiri k\u0259s", +"Copy row": "S\u0259tiri k\u00f6\u00e7\u00fcr", +"Paste row before": "\u018fvv\u0259lin\u0259 s\u0259tir \u0259lav\u0259 et", +"Paste row after": "Sonras\u0131na s\u0259tir \u0259lav\u0259 et", +"Insert column before": "\u018fvv\u0259lin\u0259 s\u0259tir \u0259lav\u0259 et", +"Insert column after": "\u018fvv\u0259lin\u0259 s\u00fctun \u0259lav\u0259 et", +"Delete column": "S\u00fctunu sil", +"Cols": "S\u00fctunlar", +"Rows": "S\u0259tirl\u0259r", +"Width": "Eni", +"Height": "H\u00fcnd\u00fcrl\u00fcy\u00fc", +"Cell spacing": "H\u00fccr\u0259l\u0259rin aras\u0131nda m\u0259saf\u0259", +"Cell padding": "H\u00fccr\u0259l\u0259rin sah\u0259l\u0259ri", +"Caption": "Ba\u015flan\u011f\u0131c", +"Left": "Sol t\u0259r\u0259f \u00fczr\u0259", +"Center": "M\u0259rk\u0259z \u00fczr\u0259", +"Right": "Sa\u011f t\u0259r\u0259f \u00fczr\u0259", +"Cell type": "H\u00fccr\u0259nin tipi", +"Scope": "Sfera", +"Alignment": "D\u00fczl\u0259ndirm\u0259", +"H Align": "H D\u00fczl\u0259ndir", +"V Align": "V D\u00fczl\u0259ndir", +"Top": "Yuxar\u0131", +"Middle": "Orta", +"Bottom": "A\u015fa\u011f\u0131", +"Header cell": "H\u00fccr\u0259nin ba\u015fl\u0131\u011f\u0131", +"Row group": "S\u0259tirin qrupu", +"Column group": "S\u00fctunun qrupu", +"Row type": "S\u0259tirin tipi", +"Header": "Ba\u015fl\u0131q", +"Body": "K\u00fctl\u0259", +"Footer": "\u018fn a\u015fa\u011f\u0131", +"Border color": "\u00c7\u0259r\u00e7iv\u0259 r\u0259ngi", +"Insert template": "\u015eablon \u0259lav\u0259 et", +"Templates": "\u015eablonlar", +"Template": "\u015eablon", +"Text color": "M\u0259tnin r\u0259ngi", +"Background color": "Arxafon r\u0259ngi", +"Custom...": "\u00c7\u0259kilm\u0259...", +"Custom color": "\u00c7\u0259kilm\u0259 r\u0259ng", +"No color": "R\u0259ngsiz", +"Table of Contents": "M\u00fcnd\u0259ricat", +"Show blocks": "Bloklar\u0131 g\u00f6st\u0259r", +"Show invisible characters": "G\u00f6r\u00fcnm\u0259y\u0259n simvollar\u0131 g\u00f6st\u0259r", +"Words: {0}": "S\u00f6zl\u0259r: {0}", +"{0} words": "{0} s\u00f6z", +"File": "Fayl", +"Edit": "Redakt\u0259 et", +"Insert": "\u018flav\u0259 et", +"View": "G\u00f6r\u00fcn\u00fc\u015f", +"Format": "Format", +"Table": "C\u0259dv\u0259l", +"Tools": "Al\u0259tl\u0259r", +"Powered by {0}": "{0} t\u0259r\u0259find\u0259n t\u0259chiz edilib", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "B\u00f6y\u00fck m\u0259tn sah\u0259si \u0259lav\u0259 edilib. Menyu \u00fc\u00e7\u00fcn ALT-F9 d\u00fcym\u0259sini bas\u0131n. Al\u0259tl\u0259r paneli \u00fc\u00e7\u00fcn ALT-F10 d\u00fcym\u0259sini bas\u0131n. K\u00f6m\u0259k \u00fc\u00e7\u00fcn ALT-0 d\u00fcym\u0259l\u0259rin bas\u0131n." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/be.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/be.js new file mode 100644 index 0000000000..bc9fc08f76 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/be.js @@ -0,0 +1,261 @@ +tinymce.addI18n('be',{ +"Redo": "\u0410\u0434\u043c\u044f\u043d\u0456\u0446\u044c", +"Undo": "\u0412\u044f\u0440\u043d\u0443\u0446\u044c", +"Cut": "\u0412\u044b\u0440\u0430\u0437\u0430\u0446\u044c", +"Copy": "\u041a\u0430\u043f\u0456\u0440\u0430\u0432\u0430\u0446\u044c", +"Paste": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c", +"Select all": "\u0412\u044b\u043b\u0443\u0447\u044b\u0446\u044c \u0443\u0441\u0451", +"New document": "\u041d\u043e\u0432\u044b \u0434\u0430\u043a\u0443\u043c\u0435\u043d\u0442", +"Ok": "Ok", +"Cancel": "\u0410\u0434\u043c\u044f\u043d\u0456\u0446\u044c", +"Visual aids": "\u041f\u0430\u043a\u0430\u0437\u0432\u0430\u0446\u044c \u043a\u043e\u043d\u0442\u0443\u0440\u044b", +"Bold": "\u0422\u043b\u0443\u0441\u0442\u044b", +"Italic": "\u041a\u0443\u0440\u0441\u0456\u045e", +"Underline": "\u041f\u0430\u0434\u043a\u0440\u044d\u0441\u043b\u0435\u043d\u044b", +"Strikethrough": "\u0417\u0430\u043a\u0440\u044d\u0441\u043b\u0435\u043d\u044b", +"Superscript": "\u0412\u0435\u0440\u0445\u043d\u0456 \u0456\u043d\u0434\u044d\u043a\u0441", +"Subscript": "\u041d\u0456\u0436\u043d\u0456 \u0456\u043d\u0434\u044d\u043a\u0441", +"Clear formatting": "\u0410\u0447\u044b\u0441\u0446\u0456\u0446\u044c \u0444\u0430\u0440\u043c\u0430\u0442", +"Align left": "\u041f\u0430 \u043b\u0435\u0432\u044b\u043c \u043a\u0440\u0430\u0456", +"Align center": "\u041f\u0430 \u0446\u044d\u043d\u0442\u0440\u044b", +"Align right": "\u041f\u0430 \u043f\u0440\u0430\u0432\u044b\u043c \u043a\u0440\u0430\u0456", +"Justify": "\u041f\u0430 \u0448\u044b\u0440\u044b\u043d\u0456", +"Bullet list": "\u041c\u0430\u0440\u043a\u0456\u0440\u0430\u0432\u0430\u043d\u044b \u0441\u043f\u0456\u0441", +"Numbered list": "\u041d\u0443\u043c\u0430\u0440\u0430\u0432\u0430\u043d\u044b \u0441\u043f\u0456\u0441", +"Decrease indent": "\u041f\u0430\u043c\u0435\u043d\u0448\u044b\u0446\u044c \u0432\u043e\u0434\u0441\u0442\u0443\u043f", +"Increase indent": "\u041f\u0430\u0432\u044f\u043b\u0456\u0447\u044b\u0446\u044c \u0432\u043e\u0434\u0441\u0442\u0443\u043f", +"Close": "\u0417\u0430\u0447\u044b\u043d\u0456\u0446\u044c", +"Formats": "\u0424\u0430\u0440\u043c\u0430\u0442", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448 \u0431\u0440\u0430\u045e\u0437\u044d\u0440 \u043d\u0435 \u043f\u0430\u0434\u0442\u0440\u044b\u043c\u043b\u0456\u0432\u0430\u0435 \u043f\u0440\u0430\u043c\u044b \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u0430 \u0431\u0443\u0444\u0435\u0440\u0430 \u0430\u0431\u043c\u0435\u043d\u0443. \u041a\u0430\u043b\u0456 \u043b\u0430\u0441\u043a\u0430, \u0432\u044b\u043a\u0430\u0440\u044b\u0441\u0442\u043e\u045e\u0432\u0430\u0439\u0446\u0435 \u043d\u0430\u0441\u0442\u0443\u043f\u043d\u044b\u044f \u0441\u043f\u0430\u043b\u0443\u0447\u044d\u043d\u043d\u044f \u043a\u043b\u0430\u0432\u0456\u0448: Ctrl + X\/C\/V.", +"Headers": "\u0417\u0430\u0433\u0430\u043b\u043e\u045e\u043a\u0456", +"Header 1": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 1", +"Header 2": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 2", +"Header 3": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 3", +"Header 4": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 4", +"Header 5": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 5", +"Header 6": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 6", +"Headings": "\u0417\u0430\u0433\u0430\u043b\u043e\u045e\u043a\u0456", +"Heading 1": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 1", +"Heading 2": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 2", +"Heading 3": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 3", +"Heading 4": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 4", +"Heading 5": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 5", +"Heading 6": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a 6", +"Preformatted": "\u0412\u044b\u0440\u0430\u045e\u043d\u0430\u0432\u0430\u043d\u044b", +"Div": "\u0411\u043b\u043e\u043a", +"Pre": "\u041f\u0440\u0430\u0434\u0444\u0430\u0440\u043c\u0430\u0442\u0430\u0432\u0430\u043d\u043d\u0435", +"Code": "\u041a\u043e\u0434", +"Paragraph": "\u041f\u0430\u0440\u0430\u0433\u0440\u0430\u0444", +"Blockquote": "\u0426\u044b\u0442\u0430\u0442\u0430", +"Inline": "\u0420\u0430\u0434\u043a\u043e\u0432\u044b", +"Blocks": "\u0411\u043b\u043e\u043a\u0456", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0423\u0441\u0442\u0430\u045e\u043a\u0430 \u0437\u0434\u0437\u044f\u0439\u0441\u043d\u044f\u0435\u0446\u0446\u0430 \u045e \u0432\u044b\u0433\u043b\u044f\u0434\u0437\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0433\u0430 \u0442\u044d\u043a\u0441\u0442\u0443, \u043f\u0430\u043a\u0443\u043b\u044c \u043d\u0435 \u0430\u0434\u043a\u043b\u044e\u0447\u044b\u0446\u044c \u0434\u0430\u0434\u0437\u0435\u043d\u0443\u044e \u043e\u043f\u0446\u044b\u044e.", +"Font Family": "\u0428\u0440\u044b\u0444\u0442", +"Font Sizes": "\u041f\u0430\u043c\u0435\u0440 \u0448\u0440\u044b\u0444\u0442\u0430", +"Class": "\u041a\u043b\u0430\u0441", +"Browse for an image": "\u041f\u043e\u0448\u0443\u043a \u0432\u044b\u044f\u0432\u044b", +"OR": "\u0410\u0411\u041e", +"Drop an image here": "\u0410\u0434\u043a\u0456\u043d\u044c\u0446\u0435 \u0432\u044b\u044f\u0432\u0443 \u0442\u0443\u0442", +"Upload": "\u0417\u0430\u043f\u0430\u043c\u043f\u0430\u0432\u0430\u0446\u044c", +"Block": "\u0417\u0430\u0431\u043b\u0430\u043a\u0430\u0432\u0430\u0446\u044c", +"Align": "\u0412\u044b\u0440\u0430\u045e\u043d\u043e\u045e\u0432\u0430\u043d\u043d\u0435", +"Default": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b", +"Circle": "\u0410\u043a\u0440\u0443\u0436\u043d\u0430\u0441\u0446\u0456", +"Disc": "\u041a\u0440\u0443\u0433\u0456", +"Square": "\u041a\u0432\u0430\u0434\u0440\u0430\u0442\u044b", +"Lower Alpha": "\u041c\u0430\u043b\u044b\u044f \u043b\u0430\u0446\u0456\u043d\u0441\u043a\u0456\u044f \u043b\u0456\u0442\u0430\u0440\u044b", +"Lower Greek": "\u041c\u0430\u043b\u044b\u044f \u0433\u0440\u044d\u0447\u0430\u0441\u043a\u0456\u044f \u043b\u0456\u0442\u0430\u0440\u044b", +"Lower Roman": "\u041c\u0430\u043b\u044b\u044f \u0440\u044b\u043c\u0441\u043a\u0456\u044f \u043b\u0456\u0447\u0431\u044b", +"Upper Alpha": "\u0417\u0430\u0433\u0430\u043b\u043e\u045e\u043d\u044b\u044f \u043b\u0430\u0446\u0456\u043d\u0441\u043a\u0456\u044f \u043b\u0456\u0442\u0430\u0440\u044b", +"Upper Roman": "\u0417\u0430\u0433\u0430\u043b\u043e\u045e\u043d\u044b\u044f \u0440\u044b\u043c\u0441\u043a\u0456\u044f \u043b\u0456\u0447\u0431\u044b", +"Anchor": "\u042f\u043a\u0430\u0440", +"Name": "\u0406\u043c\u044f", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u043f\u0430\u0432\u0456\u043d\u0435\u043d \u043f\u0430\u0447\u044b\u043d\u0430\u0446\u0446\u0430 \u0437 \u043b\u0456\u0442\u0430\u0440\u044b, \u0430 \u043f\u043e\u0442\u044b\u043c \u0443\u0442\u0440\u044b\u043c\u043b\u0456\u0432\u0430\u0446\u044c \u0442\u043e\u043b\u044c\u043a\u0456 \u043b\u0456\u0442\u0430\u0440\u044b, \u043b\u0456\u0447\u0431\u044b, \u043f\u0440\u0430\u0446\u044f\u0436\u043d\u0456\u043a, \u043a\u0440\u043e\u043f\u043a\u0456, \u0434\u0432\u0443\u043a\u0440\u043e\u043f'\u044f \u0446\u0456 \u043f\u0430\u0434\u043a\u0440\u044d\u0441\u043b\u0456\u0432\u0430\u043d\u043d\u0456.", +"You have unsaved changes are you sure you want to navigate away?": "\u0423 \u0432\u0430\u0441 \u0451\u0441\u0446\u044c \u043d\u0435\u0437\u0430\u0445\u0430\u0432\u0430\u043d\u044b\u044f \u0437\u043c\u0435\u043d\u044b. \u0412\u044b \u045e\u043f\u044d\u045e\u043d\u0435\u043d\u044b\u044f, \u0448\u0442\u043e \u0445\u043e\u0447\u0430\u0446\u0435 \u0432\u044b\u0439\u0441\u0446\u0456?", +"Restore last draft": "\u0410\u0434\u043d\u0430\u045e\u043b\u0435\u043d\u043d\u0435 \u0430\u043f\u043e\u0448\u043d\u044f\u0433\u0430 \u043f\u0440\u0430\u0435\u043a\u0442\u0430", +"Special character": "\u0421\u043f\u0435\u0446\u044b\u044f\u043b\u044c\u043d\u044b\u044f \u0441\u0456\u043c\u0432\u0430\u043b\u044b", +"Source code": "\u0417\u044b\u0445\u043e\u0434\u043d\u044b \u043a\u043e\u0434", +"Insert\/Edit code sample": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c\/\u0440\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u043a\u043e\u0434", +"Language": "\u041c\u043e\u0432\u0430", +"Code sample": "\u041f\u0440\u044b\u043a\u043b\u0430\u0434 \u043a\u043e\u0434\u0430", +"Color": "\u041a\u043e\u043b\u0435\u0440", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0417\u043b\u0435\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u0430", +"Right to left": "\u0421\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u0430", +"Emoticons": "\u0414\u0430\u0434\u0430\u0446\u044c \u0441\u043c\u0430\u0439\u043b", +"Document properties": "\u0423\u043b\u0430\u0441\u0446\u0456\u0432\u0430\u0441\u0446\u0456 \u0434\u0430\u043a\u0443\u043c\u0435\u043d\u0442\u0430", +"Title": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a", +"Keywords": "\u041a\u043b\u044e\u0447\u0430\u0432\u044b\u044f \u0441\u043b\u043e\u0432\u044b", +"Description": "\u0410\u043f\u0456\u0441\u0430\u043d\u043d\u0435", +"Robots": "\u0420\u043e\u0431\u0430\u0442\u044b", +"Author": "\u0410\u045e\u0442\u0430\u0440", +"Encoding": "\u041a\u0430\u0434\u044b\u0440\u043e\u045e\u043a\u0430", +"Fullscreen": "\u041f\u043e\u045e\u043d\u0430\u044d\u043a\u0440\u0430\u043d\u043d\u044b \u0440\u044d\u0436\u044b\u043c", +"Action": "\u0414\u0437\u0435\u044f\u043d\u043d\u0435", +"Shortcut": "\u0428\u043e\u0440\u0442\u043a\u0430\u0442", +"Help": "\u0414\u0430\u043f\u0430\u043c\u043e\u0433\u0430", +"Address": "\u0410\u0434\u0440\u0430\u0441", +"Focus to menubar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0440\u0430\u0434\u043e\u043a \u043c\u0435\u043d\u044e", +"Focus to toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043f\u0430\u043d\u044d\u043b\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u045e", +"Focus to element path": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0448\u043b\u044f\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430", +"Focus to contextual toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043a\u0430\u043d\u0442\u044d\u043a\u0441\u0442\u043d\u0443\u044e \u043f\u0430\u043d\u044d\u043b\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u045e", +"Insert link (if link plugin activated)": "\u040e\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443 (\u043a\u0430\u043b\u0456 \u043f\u043b\u0430\u0433\u0456\u043d \u0441\u043f\u0430\u0441\u044b\u043b\u0430\u043a \u0430\u043a\u0442\u044b\u0432\u0430\u0432\u0430\u043d\u044b)", +"Save (if save plugin activated)": "\u0417\u0430\u0445\u0430\u0432\u0430\u0446\u044c (\u043a\u0430\u043b\u0456 \u043f\u043b\u0430\u0433\u0456\u043d \u0437\u0430\u0445\u0430\u0432\u0430\u043d\u043d\u044f \u0430\u043a\u0442\u044b\u0432\u0430\u0432\u0430\u043d\u044b)", +"Find (if searchreplace plugin activated)": "\u0428\u0443\u043a\u0430\u0446\u044c (\u043a\u0430\u043b\u0456 \u043f\u043b\u0430\u0433\u0456\u043d \u043f\u043e\u0448\u0443\u043a\u0443 \u0430\u043a\u0442\u044b\u0432\u0430\u0432\u0430\u043d\u044b)", +"Plugins installed ({0}):": "\u0423\u0441\u0442\u0430\u043b\u044f\u0432\u0430\u043d\u0430 \u043f\u043b\u0430\u0433\u0456\u043d\u0430\u045e ({0}):", +"Premium plugins:": "\u041f\u0440\u044d\u043c\u0456\u044f\u043b\u044c\u043d\u044b\u044f \u043f\u043b\u0430\u0433\u0456\u043d\u044b:", +"Learn more...": "\u041f\u0430\u0434\u0440\u0430\u0431\u044f\u0437\u043d\u0435\u0439 ...", +"You are using {0}": "\u0412\u044b \u043a\u0430\u0440\u044b\u0441\u0442\u0430\u0435\u0446\u0435\u0441\u044f {0}", +"Plugins": "\u041f\u043b\u0430\u0433\u0456\u043d\u044b", +"Handy Shortcuts": "\u0417\u0440\u0443\u0447\u043d\u044b\u044f \u0448\u043e\u0440\u0442\u043a\u0430\u0442\u044b", +"Horizontal line": "\u0413\u0430\u0440\u044b\u0437\u0430\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u043b\u0456\u043d\u0456\u044f", +"Insert\/edit image": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c\/\u0440\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u0432\u044b\u044f\u0432\u0443", +"Image description": "\u0410\u043f\u0456\u0441\u0430\u043d\u043d\u0435 \u0432\u044b\u044f\u0432\u044b", +"Source": "\u041a\u0440\u044b\u043d\u0456\u0446\u0430", +"Dimensions": "\u041f\u0430\u043c\u0435\u0440", +"Constrain proportions": "\u0417\u0430\u0445\u0430\u0432\u0430\u0446\u044c \u043f\u0440\u0430\u043f\u043e\u0440\u0446\u044b\u0456", +"General": "\u0410\u0433\u0443\u043b\u044c\u043d\u0430\u0435", +"Advanced": "\u041f\u0430\u0448\u044b\u0440\u0430\u043d\u0430\u0435", +"Style": "\u0421\u0442\u044b\u043b\u044c", +"Vertical space": "\u0412\u0435\u0440\u0442\u044b\u043a\u0430\u043b\u044c\u043d\u044b \u0456\u043d\u0442\u044d\u0440\u0432\u0430\u043b", +"Horizontal space": "\u0413\u0430\u0440\u044b\u0437\u0430\u043d\u0442\u0430\u043b\u044c\u043d\u044b \u0456\u043d\u0442\u044d\u0440\u0432\u0430\u043b", +"Border": "\u041c\u044f\u0436\u0430", +"Insert image": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0432\u044b\u044f\u0432\u0443", +"Image": "\u0412\u044b\u044f\u0432\u0430", +"Image list": "\u0421\u043f\u0456\u0441 \u0432\u044b\u044f\u045e", +"Rotate counterclockwise": "\u041f\u0430\u0432\u044f\u0440\u043d\u0443\u0446\u044c counterclockwise", +"Rotate clockwise": "\u041f\u0430\u0432\u044f\u0440\u043d\u0443\u0446\u044c clockwise", +"Flip vertically": "\u0410\u0434\u043b\u044e\u0441\u0442\u0440\u0430\u0432\u0430\u0446\u044c \u0432\u0435\u0440\u0442\u044b\u043a\u0430\u043b\u044c\u043d\u0430", +"Flip horizontally": "\u0410\u0434\u043b\u044e\u0441\u0442\u0440\u0430\u0432\u0430\u0446\u044c \u0433\u0430\u0440\u044b\u0437\u0430\u043d\u0442\u0430\u043b\u044c\u043d\u0430", +"Edit image": "\u0420\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u0432\u044b\u044f\u0432\u0443", +"Image options": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u044b\u044f\u0432\u044b", +"Zoom in": "\u041f\u0430\u0432\u044f\u043b\u0456\u0447\u044b\u0446\u044c", +"Zoom out": "\u041f\u0430\u043c\u0435\u043d\u0448\u044b\u0446\u044c", +"Crop": "\u0410\u0431\u0440\u044d\u0437\u0430\u0446\u044c", +"Resize": "\u0417\u043c\u044f\u043d\u0456\u0446\u044c \u043f\u0430\u043c\u0435\u0440", +"Orientation": "\u0410\u0440\u044b\u0435\u043d\u0442\u0430\u0446\u044b\u044f", +"Brightness": "\u042f\u0440\u043a\u0430\u0441\u0446\u044c", +"Sharpen": "\u0412\u044b\u0440\u0430\u0437\u043d\u0430\u0441\u0446\u044c", +"Contrast": "\u041a\u0430\u043d\u0442\u0440\u0430\u0441\u0442", +"Color levels": "\u0423\u0437\u0440\u043e\u045e\u043d\u0456 \u043a\u043e\u043b\u0435\u0440\u0430\u045e", +"Gamma": "\u0413\u0430\u043c\u0430", +"Invert": "\u0406\u043d\u0432\u0435\u0440\u0442\u0430\u0432\u0430\u0446\u044c", +"Apply": "\u0423\u0436\u044b\u0446\u044c", +"Back": "\u041d\u0430\u0437\u0430\u0434", +"Insert date\/time": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0434\u0430\u0442\u0443\/\u0447\u0430\u0441", +"Date\/time": "\u0414\u0430\u0442\u0430\/\u0447\u0430\u0441", +"Insert link": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443", +"Insert\/edit link": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c\/\u0440\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443", +"Text to display": "\u0422\u044d\u043a\u0441\u0442 \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0456", +"Url": "\u0410\u0434\u0440\u0430\u0441 \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0456", +"Target": "\u0410\u0434\u043a\u0440\u044b\u0432\u0430\u0446\u044c \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443", +"None": "\u041d\u044f\u043c\u0430", +"New window": "\u0423 \u043d\u043e\u0432\u044b\u043c \u0430\u043a\u043d\u0435", +"Remove link": "\u0412\u044b\u0434\u0430\u043b\u0456\u0446\u044c \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443", +"Anchors": "\u042f\u043a\u0430\u0440\u044b", +"Link": "\u0421\u043f\u0430\u0441\u044b\u043b\u043a\u0430", +"Paste or type a link": "\u0423\u0441\u0442\u0430\u045e\u0446\u0435 \u0430\u0431\u043e \u045e\u0432\u044f\u0434\u0437\u0456\u0446\u0435 \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0423\u0432\u0435\u0434\u0437\u0435\u043d\u044b \u0430\u0434\u0440\u0430\u0441 \u043f\u0430\u0434\u043e\u0431\u043d\u044b \u043d\u0430 \u0430\u0434\u0440\u0430\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u0439 \u043f\u043e\u0448\u0442\u044b. \u0416\u0430\u0434\u0430\u0435\u0446\u0435 \u0434\u0430\u0434\u0430\u0446\u044c \u043d\u0435\u0430\u0431\u0445\u043e\u0434\u043d\u044b mailto: \u043f\u0440\u044d\u0444\u0456\u043a\u0441?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0423\u0432\u0435\u0434\u0437\u0435\u043d\u044b \u0430\u0434\u0440\u0430\u0441 \u043f\u0430\u0434\u043e\u0431\u043d\u044b \u043d\u0430 \u0437\u043d\u0435\u0448\u043d\u044e\u044e \u0441\u043f\u0430\u0441\u044b\u043b\u043a\u0443. \u0416\u0430\u0434\u0430\u0435\u0446\u0435 \u0434\u0430\u0434\u0430\u0446\u044c \u043d\u0435\u0430\u0431\u0445\u043e\u0434\u043d\u044b http:\/\/ \u043f\u0440\u044d\u0444\u0456\u043a\u0441?", +"Link list": "\u0421\u043f\u0456\u0441 \u0441\u043f\u0430\u0441\u044b\u043b\u0430\u043a", +"Insert video": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0432\u0456\u0434\u044d\u0430", +"Insert\/edit video": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c\/\u0440\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u0432\u0456\u0434\u044d\u0430", +"Insert\/edit media": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c\/\u0440\u044d\u0434\u0430\u0433\u0430\u0432\u0430\u0446\u044c \u043c\u0435\u0434\u044b\u044f", +"Alternative source": "\u0410\u043b\u044c\u0442\u044d\u0440\u043d\u0430\u0442\u044b\u045e\u043d\u0430\u044f \u043a\u0440\u044b\u043d\u0456\u0446\u0430", +"Poster": "\u0412\u044b\u044f\u0432\u0430", +"Paste your embed code below:": "\u0423\u0441\u0442\u0430\u045e\u0446\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0456\u0436\u044d\u0439:", +"Embed": "\u041a\u043e\u0434 \u0434\u043b\u044f \u045e\u0441\u0442\u0430\u045e\u043a\u0456", +"Media": "\u041c\u0435\u0434\u044b\u044f", +"Nonbreaking space": "\u041d\u0435\u043f\u0430\u0440\u044b\u045e\u043d\u044b \u043f\u0440\u0430\u0431\u0435\u043b", +"Page break": "\u0420\u0430\u0437\u0440\u044b\u045e \u0441\u0442\u0430\u0440\u043e\u043d\u043a\u0456", +"Paste as text": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u044f\u043a \u0442\u044d\u043a\u0441\u0442", +"Preview": "\u041f\u0440\u0430\u0434\u043f\u0440\u0430\u0433\u043b\u044f\u0434", +"Print": "\u0414\u0440\u0443\u043a", +"Save": "\u0417\u0430\u0445\u0430\u0432\u0430\u0446\u044c", +"Find": "\u0417\u043d\u0430\u0439\u0441\u0446\u0456", +"Replace with": "\u0417\u043c\u044f\u043d\u0456\u0446\u044c \u043d\u0430", +"Replace": "\u0417\u043c\u044f\u043d\u0456\u0446\u044c", +"Replace all": "\u0417\u043c\u044f\u043d\u0456\u0446\u044c \u0443\u0441\u0435", +"Prev": "\u0423\u0432\u0435\u0440\u0445", +"Next": "\u0423\u043d\u0456\u0437", +"Find and replace": "\u041f\u043e\u0448\u0443\u043a \u0456 \u0437\u0430\u043c\u0435\u043d\u0430", +"Could not find the specified string.": "\u0417\u0430\u0434\u0430\u0434\u0437\u0435\u043d\u044b \u0440\u0430\u0434\u043e\u043a \u043d\u0435 \u0437\u043d\u043e\u0439\u0434\u0437\u0435\u043d\u044b", +"Match case": "\u0423\u043b\u0456\u0447\u0432\u0430\u0446\u044c \u0440\u044d\u0433\u0456\u0441\u0442\u0440", +"Whole words": "\u0421\u043b\u043e\u0432\u044b \u0446\u0430\u043b\u043a\u0430\u043c", +"Spellcheck": "\u041f\u0440\u0430\u0432\u0435\u0440\u043a\u0430 \u043f\u0440\u0430\u0432\u0430\u043f\u0456\u0441\u0443", +"Ignore": "\u0406\u0433\u043d\u0430\u0440\u0430\u0432\u0430\u0446\u044c", +"Ignore all": "\u0406\u0433\u043d\u0430\u0440\u0430\u0432\u0430\u0446\u044c \u0443\u0441\u0435", +"Finish": "\u0421\u043a\u043e\u043d\u0447\u044b\u0446\u044c", +"Add to Dictionary": "\u0414\u0430\u0434\u0430\u0446\u044c \u0443 \u0441\u043b\u043e\u045e\u043d\u0456\u043a", +"Insert table": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0442\u0430\u0431\u043b\u0456\u0446\u0443", +"Table properties": "\u0423\u043b\u0430\u0441\u0446\u0456\u0432\u0430\u0441\u0446\u0456 \u0442\u0430\u0431\u043b\u0456\u0446\u044b", +"Delete table": "\u0412\u044b\u0434\u0430\u043b\u0456\u0446\u044c \u0442\u0430\u0431\u043b\u0456\u0446\u0443", +"Cell": "\u042f\u0447\u044d\u0439\u043a\u0430", +"Row": "\u0420\u0430\u0434\u043e\u043a", +"Column": "\u0421\u043b\u0443\u043f\u043e\u043a", +"Cell properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u044f\u0447\u044d\u0439\u043a\u0456", +"Merge cells": "\u0410\u0431'\u044f\u0434\u043d\u0430\u0446\u044c \u044f\u0447\u044d\u0439\u043a\u0456", +"Split cell": "\u0420\u0430\u0437\u0431\u0456\u0446\u044c \u044f\u0447\u044d\u0439\u043a\u0443", +"Insert row before": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0440\u0430\u0434\u043e\u043a \u0437\u0432\u0435\u0440\u0445\u0443", +"Insert row after": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0440\u0430\u0434\u043e\u043a \u0437\u043d\u0456\u0437\u0443", +"Delete row": "\u0412\u044b\u0434\u0430\u043b\u0456\u0446\u044c \u0440\u0430\u0434\u043e\u043a", +"Row properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0440\u0430\u0434\u043a\u0430", +"Cut row": "\u0412\u044b\u0440\u0430\u0437\u0430\u0446\u044c \u0440\u0430\u0434\u043e\u043a", +"Copy row": "\u041a\u0430\u043f\u0456\u044f\u0432\u0430\u0446\u044c \u0440\u0430\u0434\u043e\u043a", +"Paste row before": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0440\u0430\u0434\u043e\u043a \u0437\u0432\u0435\u0440\u0445\u0443", +"Paste row after": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0440\u0430\u0434\u043e\u043a \u0437\u043d\u0456\u0437\u0443", +"Insert column before": "\u0414\u0430\u0434\u0430\u0446\u044c \u0441\u043b\u0443\u043f\u043e\u043a \u0437\u043b\u0435\u0432\u0430", +"Insert column after": "\u0414\u0430\u0434\u0430\u0446\u044c \u0441\u043b\u0443\u043f\u043e\u043a \u0441\u043f\u0440\u0430\u0432\u0430", +"Delete column": "\u0412\u044b\u0434\u0430\u043b\u0456\u0446\u044c \u0441\u043b\u0443\u043f\u043e\u043a", +"Cols": "\u0421\u043b\u0443\u043f\u043a\u0456", +"Rows": "\u0420\u0430\u0434\u043a\u0456", +"Width": "\u0428\u044b\u0440\u044b\u043d\u044f", +"Height": "\u0412\u044b\u0448\u044b\u043d\u044f", +"Cell spacing": "\u0417\u043d\u0435\u0448\u043d\u0456 \u0432\u043e\u0434\u0441\u0442\u0443\u043f", +"Cell padding": "\u0423\u043d\u0443\u0442\u0440\u0430\u043d\u044b \u0432\u043e\u0434\u0441\u0442\u0443\u043f", +"Caption": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a", +"Left": "\u041f\u0430 \u043b\u0435\u0432\u044b\u043c \u043a\u0440\u0430\u0456", +"Center": "\u041f\u0430 \u0446\u044d\u043d\u0442\u0440\u044b", +"Right": "\u041f\u0430 \u043f\u0440\u0430\u0432\u044b\u043c \u043a\u0440\u0430\u0456", +"Cell type": "\u0422\u044b\u043f \u044f\u0447\u044d\u0439\u043a\u0456", +"Scope": "\u0421\u0444\u0435\u0440\u0430", +"Alignment": "\u0412\u044b\u0440\u0430\u045e\u043d\u043e\u045e\u0432\u0430\u043d\u043d\u0435", +"H Align": "\u0413\u0430\u0440. \u0432\u044b\u0440\u0430\u045e\u043d\u043e\u045e\u0432\u0430\u043d\u043d\u0435", +"V Align": "\u0412\u0435\u0440. \u0432\u044b\u0440\u0430\u045e\u043d\u043e\u045e\u0432\u0430\u043d\u043d\u0435", +"Top": "\u0412\u0435\u0440\u0445", +"Middle": "\u0421\u044f\u0440\u044d\u0434\u0437\u0456\u043d\u0430", +"Bottom": "\u041d\u0456\u0437", +"Header cell": "\u0417\u0430\u0433\u0430\u043b\u043e\u0432\u0430\u043a", +"Row group": "\u0413\u0440\u0443\u043f\u0430 \u0440\u0430\u0434\u043a\u043e\u045e", +"Column group": "\u0413\u0440\u0443\u043f\u0430 \u0441\u043b\u0443\u043f\u043a\u043e\u045e", +"Row type": "\u0422\u044b\u043f \u0440\u0430\u0434\u043a\u0430", +"Header": "\u0428\u0430\u043f\u043a\u0430", +"Body": "\u0426\u0435\u043b\u0430", +"Footer": "\u041d\u0456\u0437", +"Border color": "\u041a\u043e\u043b\u0435\u0440 \u043c\u044f\u0436\u044b", +"Insert template": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c \u0448\u0430\u0431\u043b\u043e\u043d", +"Templates": "\u0428\u0430\u0431\u043b\u043e\u043d\u044b", +"Template": "\u0428\u0430\u0431\u043b\u043e\u043d", +"Text color": "\u041a\u043e\u043b\u0435\u0440 \u0442\u044d\u043a\u0441\u0442\u0443", +"Background color": "\u041a\u043e\u043b\u0435\u0440 \u0444\u043e\u043d\u0443", +"Custom...": "\u041a\u0430\u0440\u044b\u0441\u0442\u0430\u0446\u043a\u0456...", +"Custom color": "\u041a\u0430\u0440\u044b\u0441\u0442\u0430\u0446\u043a\u0456 \u043a\u043e\u043b\u0435\u0440", +"No color": "\u0411\u0435\u0437 \u043a\u043e\u043b\u0435\u0440\u0443", +"Table of Contents": "\u0422\u0430\u0431\u043b\u0456\u0446\u0443 \u0437\u043c\u0435\u0441\u0442\u0443", +"Show blocks": "\u041f\u0430\u043a\u0430\u0437\u0432\u0430\u0446\u044c \u0431\u043b\u043e\u043a\u0456", +"Show invisible characters": "\u041f\u0430\u043a\u0430\u0437\u0432\u0430\u0446\u044c \u043d\u044f\u0431\u0430\u0447\u043d\u044b\u044f \u0441\u0456\u043c\u0432\u0430\u043b\u044b", +"Words: {0}": "\u041a\u043e\u043b\u044c\u043a\u0430\u0441\u0446\u044c \u0441\u043b\u043e\u045e: {0}", +"{0} words": "{0} \u0441\u043b\u043e\u045e", +"File": "\u0424\u0430\u0439\u043b", +"Edit": "\u0417\u043c\u044f\u043d\u0456\u0446\u044c", +"Insert": "\u0423\u0441\u0442\u0430\u0432\u0456\u0446\u044c", +"View": "\u0412\u044b\u0433\u043b\u044f\u0434", +"Format": "\u0424\u0430\u0440\u043c\u0430\u0442", +"Table": "\u0422\u0430\u0431\u043b\u0456\u0446\u0430", +"Tools": "\u041f\u0440\u044b\u043b\u0430\u0434\u044b", +"Powered by {0}": "\u041f\u0440\u0430\u0446\u0443\u0435 \u043d\u0430 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0422\u044d\u043a\u0441\u0442\u0430\u0432\u0430\u0435 \u043f\u043e\u043b\u0435. \u041d\u0430\u0446\u0456\u0441\u043d\u0456\u0446\u0435 ALT-F9, \u043a\u0430\u0431 \u0432\u044b\u043a\u043b\u0456\u043a\u0430\u0446\u044c \u043c\u0435\u043d\u044e, ALT-F10 - \u043f\u0430\u043d\u044d\u043b\u044c \u043f\u0440\u044b\u043b\u0430\u0434\u0430\u045e, ALT-0 - \u0434\u043b\u044f \u0432\u044b\u043a\u043b\u0456\u043a\u0443 \u0434\u0430\u043f\u0430\u043c\u043e\u0433\u0456." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bg_BG.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bg_BG.js new file mode 100644 index 0000000000..9ef72576a2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bg_BG.js @@ -0,0 +1,261 @@ +tinymce.addI18n('bg_BG',{ +"Redo": "\u041e\u0442\u043c\u0435\u043d\u0438", +"Undo": "\u0412\u044a\u0440\u043d\u0438", +"Cut": "\u0418\u0437\u0440\u044f\u0437\u0432\u0430\u043d\u0435", +"Copy": "\u041a\u043e\u043f\u0438\u0440\u0430\u043d\u0435", +"Paste": "\u041f\u043e\u0441\u0442\u0430\u0432\u044f\u043d\u0435", +"Select all": "\u041c\u0430\u0440\u043a\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0446\u044f\u043b\u043e\u0442\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", +"New document": "\u041d\u043e\u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442", +"Ok": "\u0414\u043e\u0431\u0440\u0435", +"Cancel": "\u041e\u0442\u043a\u0430\u0437", +"Visual aids": "\u0412\u0438\u0437\u0443\u0430\u043b\u043d\u043e \u043e\u0442\u043a\u0440\u043e\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0438 \u0431\u0435\u0437 \u043a\u0430\u043d\u0442\u043e\u0432\u0435 (\u0440\u0430\u043c\u043a\u0438)", +"Bold": "\u0423\u0434\u0435\u0431\u0435\u043b\u0435\u043d (\u043f\u043e\u043b\u0443\u0447\u0435\u0440)", +"Italic": "\u041d\u0430\u043a\u043b\u043e\u043d\u0435\u043d (\u043a\u0443\u0440\u0441\u0438\u0432)", +"Underline": "\u041f\u043e\u0434\u0447\u0435\u0440\u0442\u0430\u043d", +"Strikethrough": "\u0417\u0430\u0447\u0435\u0440\u0442\u0430\u0432\u0430\u043d\u0435", +"Superscript": "\u0413\u043e\u0440\u0435\u043d \u0438\u043d\u0434\u0435\u043a\u0441", +"Subscript": "\u0414\u043e\u043b\u0435\u043d \u0438\u043d\u0434\u0435\u043a\u0441", +"Clear formatting": "\u0418\u0437\u0447\u0438\u0441\u0442\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d\u0435\u0442\u043e", +"Align left": "\u041f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435 \u043e\u0442\u043b\u044f\u0432\u043e", +"Align center": "\u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0430\u043d\u043e", +"Align right": "\u041f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435 \u043e\u0442\u0434\u044f\u0441\u043d\u043e", +"Justify": "\u0414\u0432\u0443\u0441\u0442\u0440\u0430\u043d\u043d\u043e \u043f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435", +"Bullet list": "\u0421\u043f\u0438\u0441\u044a\u043a \u0441 \u0432\u043e\u0434\u0430\u0447\u0438", +"Numbered list": "\u041d\u043e\u043c\u0435\u0440\u0438\u0440\u0430\u043d \u0441\u043f\u0438\u0441\u044a\u043a", +"Decrease indent": "\u041d\u0430\u043c\u0430\u043b\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u043e\u0442\u0441\u0442\u044a\u043f\u0430", +"Increase indent": "\u0423\u0432\u0435\u043b\u0438\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043e\u0442\u0441\u0442\u044a\u043f\u0430", +"Close": "\u0417\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435", +"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d\u0435", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448\u0438\u044f\u0442 \u0431\u0440\u0430\u0443\u0437\u044a\u0440 \u043d\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043a\u043b\u0438\u043f\u0431\u043e\u0440\u0434\u0430. \u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0432\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u043d\u0438\u0442\u0435 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 Ctrl+X (\u0437\u0430 \u0438\u0437\u0440\u044f\u0437\u0432\u0430\u043d\u0435), Ctrl+C (\u0437\u0430 \u043a\u043e\u043f\u0438\u0440\u0430\u043d\u0435) \u0438 Ctrl+V (\u0437\u0430 \u043f\u043e\u0441\u0442\u0430\u0432\u044f\u043d\u0435).", +"Headers": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u044f", +"Header 1": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 1", +"Header 2": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 2", +"Header 3": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 3", +"Header 4": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 4", +"Header 5": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 5", +"Header 6": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 6", +"Headings": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u044f", +"Heading 1": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 1", +"Heading 2": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 2", +"Heading 3": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 3", +"Heading 4": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 4", +"Heading 5": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 5", +"Heading 6": "\u0417\u0430\u0433\u043b\u0430\u0432\u0438\u0435 6", +"Preformatted": "\u041f\u0440\u0435\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d", +"Div": "\u0411\u043b\u043e\u043a", +"Pre": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u043d\u043e \u043e\u0444\u043e\u0440\u043c\u0435\u043d \u0442\u0435\u043a\u0441\u0442", +"Code": "\u041a\u043e\u0434", +"Paragraph": "\u041f\u0430\u0440\u0430\u0433\u0440\u0430\u0444", +"Blockquote": "\u0426\u0438\u0442\u0430\u0442", +"Inline": "\u041d\u0430 \u0435\u0434\u0438\u043d \u0440\u0435\u0434", +"Blocks": "\u0411\u043b\u043e\u043a\u043e\u0432\u0435", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u041f\u043e\u0441\u0442\u0430\u0432\u044f\u043d\u0435\u0442\u043e \u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0435 \u0432 \u043e\u0431\u0438\u043a\u043d\u043e\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c. \u0421\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435\u0442\u043e \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u0435\u043d\u043e \u043a\u0430\u0442\u043e \u043d\u0435\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d \u0442\u0435\u043a\u0441\u0442, \u0434\u043e\u043a\u0430\u0442\u043e \u0438\u0437\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0442\u0430\u0437\u0438 \u043e\u043f\u0446\u0438\u044f.", +"Font Family": "\u0428\u0440\u0438\u0444\u0442", +"Font Sizes": "\u0420\u0430\u0437\u043c\u0435\u0440 \u043d\u0430 \u0448\u0440\u0438\u0444\u0442\u0430", +"Class": "\u041a\u043b\u0430\u0441", +"Browse for an image": "\u041f\u043e\u0442\u044a\u0440\u0441\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430", +"OR": "\u0418\u041b\u0418", +"Drop an image here": "\u041f\u0443\u0441\u043d\u0435\u0442\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0442\u0430 \u0442\u0443\u043a", +"Upload": "\u041a\u0430\u0447\u0438", +"Block": "\u0411\u043b\u043e\u043a", +"Align": "\u041f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435", +"Default": "\u041f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435", +"Circle": "\u041e\u043a\u0440\u044a\u0436\u043d\u043e\u0441\u0442\u0438", +"Disc": "\u041a\u0440\u044a\u0433\u0447\u0435\u0442\u0430", +"Square": "\u0417\u0430\u043f\u044a\u043b\u043d\u0435\u043d\u0438 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0438", +"Lower Alpha": "\u041c\u0430\u043b\u043a\u0438 \u0431\u0443\u043a\u0432\u0438", +"Lower Greek": "\u041c\u0430\u043b\u043a\u0438 \u0433\u0440\u044a\u0446\u043a\u0438 \u0431\u0443\u043a\u0432\u0438", +"Lower Roman": "\u0420\u0438\u043c\u0441\u043a\u0438 \u0447\u0438\u0441\u043b\u0430 \u0441 \u043c\u0430\u043b\u043a\u0438 \u0431\u0443\u043a\u0432\u0438", +"Upper Alpha": "\u0413\u043b\u0430\u0432\u043d\u0438 \u0431\u0443\u043a\u0432\u0438", +"Upper Roman": "\u0420\u0438\u043c\u0441\u043a\u0438 \u0447\u0438\u0441\u043b\u0430 \u0441 \u0433\u043b\u0430\u0432\u043d\u0438 \u0431\u0443\u043a\u0432\u0438", +"Anchor": "\u041a\u043e\u0442\u0432\u0430 (\u0432\u0440\u044a\u0437\u043a\u0430 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430)", +"Name": "\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435", +"Id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 (id)", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 (id) \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u0432\u0430 \u0441 \u0431\u0443\u043a\u0432\u0430, \u043f\u043e\u0441\u043b\u0435\u0434\u0432\u0430\u043d \u043e\u0442 \u0431\u0443\u043a\u0432\u0438, \u0447\u0438\u0444\u0440\u0438, \u0442\u0438\u0440\u0435\u0442\u0430, \u0442\u043e\u0447\u043a\u0438, \u0434\u0432\u043e\u0435\u0442\u043e\u0447\u0438\u0435 \u0438 \u0434\u043e\u043b\u043d\u043e \u0442\u0438\u0440\u0435.", +"You have unsaved changes are you sure you want to navigate away?": "\u0412 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0438\u043c\u0430 \u043d\u0435\u0437\u0430\u043f\u0430\u0437\u0435\u043d\u0438 \u043f\u0440\u043e\u043c\u0435\u043d\u0438. \u0429\u0435 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435 \u043b\u0438?", +"Restore last draft": "\u0412\u044a\u0437\u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0430\u0442\u0430 \u0447\u0435\u0440\u043d\u043e\u0432\u0430", +"Special character": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u0435\u043d \u0437\u043d\u0430\u043a", +"Source code": "\u0418\u0437\u0445\u043e\u0434\u0435\u043d \u043a\u043e\u0434 \u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0432 HTML", +"Insert\/Edit code sample": "\u0412\u043c\u044a\u043a\u043d\u0438\/ \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439 \u043f\u0440\u0438\u043c\u0435\u0440\u0435\u043d \u043a\u043e\u0434", +"Language": "\u0415\u0437\u0438\u043a", +"Code sample": "\u041f\u0440\u0438\u043c\u0435\u0440\u0435\u043d \u043a\u043e\u0434", +"Color": "\u0426\u0432\u044f\u0442", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u041e\u0442\u043b\u044f\u0432\u043e \u043d\u0430\u0434\u044f\u0441\u043d\u043e", +"Right to left": "\u041e\u0442\u0434\u044f\u0441\u043d\u043e \u043d\u0430\u043b\u044f\u0432\u043e", +"Emoticons": "\u0415\u043c\u043e\u0442\u0438\u043a\u043e\u043d\u0438", +"Document properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430", +"Title": "\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435", +"Keywords": "\u041a\u043b\u044e\u0447\u043e\u0432\u0438 \u0434\u0443\u043c\u0438", +"Description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435", +"Robots": "\u0420\u043e\u0431\u043e\u0442\u0438 \u043d\u0430 \u0443\u0435\u0431 \u0442\u044a\u0440\u0441\u0430\u0447\u043a\u0438", +"Author": "\u0410\u0432\u0442\u043e\u0440", +"Encoding": "\u041a\u043e\u0434\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0437\u043d\u0430\u0446\u0438\u0442\u0435", +"Fullscreen": "\u041d\u0430 \u0446\u044f\u043b \u0435\u043a\u0440\u0430\u043d", +"Action": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435", +"Shortcut": "\u0411\u044a\u0440\u0437 \u043a\u043b\u0430\u0432\u0438\u0448", +"Help": "\u041f\u043e\u043c\u043e\u0449", +"Address": "\u0410\u0434\u0440\u0435\u0441", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "\u041f\u043e\u0441\u0442\u0430\u0432\u0438 \u0432\u0440\u044a\u0437\u043a\u0430 (\u0430\u043a\u043e \u043f\u043b\u044a\u0433\u0438\u043d\u0430 \u0437\u0430 \u0432\u0440\u044a\u0437\u043a\u0438 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d)", +"Save (if save plugin activated)": "\u0417\u0430\u043f\u0438\u0448\u0438 (\u0430\u043a\u043e \u043f\u043b\u044a\u0433\u0438\u043d\u0430 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d)", +"Find (if searchreplace plugin activated)": "\u041d\u0430\u043c\u0435\u0440\u0438 (\u0430\u043a\u043e \u043f\u043b\u044a\u0433\u0438\u043d\u0430 \u0437\u0430 \u0442\u044a\u0440\u0441\u0435\u043d\u0435\/\u0437\u0430\u043c\u044f\u043d\u0430 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d)", +"Plugins installed ({0}):": "\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0438 \u043f\u043b\u044a\u0433\u0438\u043d\u0438 ({0}):", +"Premium plugins:": "\u041f\u0440\u0435\u043c\u0438\u0439\u043d\u0438 \u043f\u043b\u044a\u0433\u0438\u043d\u0438:", +"Learn more...": "\u041d\u0430\u0443\u0447\u0435\u0442\u0435 \u043f\u043e\u0432\u0435\u0447\u0435...", +"You are using {0}": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Handy Shortcuts", +"Horizontal line": "\u0425\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u043d\u0430 \u0447\u0435\u0440\u0442\u0430", +"Insert\/edit image": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435\/\u043a\u043e\u0440\u0435\u043a\u0446\u0438\u044f \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430", +"Image description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u0442\u043e", +"Source": "\u0410\u0434\u0440\u0435\u0441", +"Dimensions": "\u0420\u0430\u0437\u043c\u0435\u0440", +"Constrain proportions": "\u0417\u0430\u0432\u0430\u0437\u043d\u0430\u0432\u0435 \u043d\u0430 \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u0438\u0442\u0435", +"General": "\u041e\u0431\u0449\u043e", +"Advanced": "\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e", +"Style": "\u0421\u0442\u0438\u043b", +"Vertical space": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e", +"Horizontal space": "\u0425\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e", +"Border": "\u041a\u0430\u043d\u0442 (\u0440\u0430\u043c\u043a\u0430)", +"Insert image": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", +"Image": "\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0430", +"Image list": "\u0421\u043f\u0438\u0441\u044a\u043a \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438", +"Rotate counterclockwise": "\u0417\u0430\u0432\u044a\u0440\u0442\u0430\u043d\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u0430", +"Rotate clockwise": "\u0417\u0430\u0432\u044a\u0440\u0442\u0430\u043d\u0435 \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u0430", +"Flip vertically": "\u041e\u0431\u044a\u0440\u043d\u0438 \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u043d\u043e", +"Flip horizontally": "\u041e\u0431\u044a\u0440\u043d\u0438 \u0445\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u043d\u043e", +"Edit image": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u0442\u043e", +"Image options": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u0442\u043e", +"Zoom in": "\u041f\u0440\u0438\u0431\u043b\u0438\u0436\u0438", +"Zoom out": "\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0438", +"Crop": "\u0418\u0437\u0440\u044f\u0437\u0432\u0430\u043d\u0435", +"Resize": "\u041f\u0440\u0435\u043e\u0440\u0430\u0437\u043c\u0435\u0440\u044f\u0432\u0430\u043d\u0435", +"Orientation": "\u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f", +"Brightness": "\u042f\u0440\u043a\u043e\u0441\u0442", +"Sharpen": "\u0418\u0437\u043e\u0441\u0442\u0440\u044f\u043d\u0435", +"Contrast": "\u041a\u043e\u043d\u0442\u0440\u0430\u0441\u0442", +"Color levels": "\u0426\u0432\u0435\u0442\u043d\u0438 \u043d\u0438\u0432\u0430", +"Gamma": "\u0413\u0430\u043c\u0430", +"Invert": "\u0418\u043d\u0432\u0435\u0440\u0441\u0438\u044f", +"Apply": "\u041f\u0440\u0438\u043b\u043e\u0436\u0438", +"Back": "\u041d\u0430\u0437\u0430\u0434", +"Insert date\/time": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0434\u0430\u0442\u0430\/\u0447\u0430\u0441", +"Date\/time": "\u0414\u0430\u0442\u0430\/\u0447\u0430\u0441", +"Insert link": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0445\u0438\u043f\u0435\u0440\u0432\u0440\u044a\u0437\u043a\u0430 (\u043b\u0438\u043d\u043a)", +"Insert\/edit link": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435\/\u043a\u043e\u0440\u0435\u043a\u0446\u0438\u044f \u043d\u0430 \u0445\u0438\u043f\u0435\u0440\u0432\u0440\u044a\u0437\u043a\u0430 (\u043b\u0438\u043d\u043a)", +"Text to display": "\u0422\u0435\u043a\u0441\u0442", +"Url": "\u0410\u0434\u0440\u0435\u0441 (URL)", +"Target": "\u0426\u0435\u043b \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430", +"None": "\u0411\u0435\u0437", +"New window": "\u0412 \u043d\u043e\u0432 \u043f\u0440\u043e\u0437\u043e\u0440\u0435\u0446 (\u043f\u043e\u0434\u043f\u0440\u043e\u0437\u043e\u0440\u0435\u0446)", +"Remove link": "\u041f\u0440\u0435\u043c\u0430\u0445\u0432\u0430\u043d\u0435 \u043d\u0430 \u0445\u0438\u043f\u0435\u0440\u0432\u0440\u044a\u0437\u043a\u0430", +"Anchors": "\u041a\u043e\u0442\u0432\u0438", +"Link": "\u0412\u0440\u044a\u0437\u043a\u0430(\u043b\u0438\u043d\u043a)", +"Paste or type a link": "\u041f\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u0438\u043b\u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u0442\u0435 \u0432\u0440\u044a\u0437\u043a\u0430(\u043b\u0438\u043d\u043a)", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL \u0430\u0434\u0440\u0435\u0441\u044a\u0442, \u043a\u043e\u0439\u0442\u043e \u0432\u044a\u0432\u0434\u043e\u0445\u0442\u0435 \u043f\u0440\u0438\u043b\u0438\u0447\u0430 \u043d\u0430 \u0435-\u043c\u0435\u0439\u043b \u0430\u0434\u0440\u0435\u0441. \u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0438\u044f mailto: \u043f\u0440\u0435\u0444\u0438\u043a\u0441?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL \u0430\u0434\u0440\u0435\u0441\u044a\u0442, \u043a\u043e\u0439\u0442\u043e \u0432\u044a\u0432\u0434\u043e\u0445\u0442\u0435 \u043f\u0440\u0438\u043b\u0438\u0447\u0430 \u0432\u044a\u043d\u0448\u0435\u043d \u0430\u0434\u0440\u0435\u0441. \u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0438\u044f http:\/\/ \u043f\u0440\u0435\u0444\u0438\u043a\u0441?", +"Link list": "\u0421\u043f\u0438\u0441\u044a\u043a \u0441 \u0432\u0440\u044a\u0437\u043a\u0438", +"Insert video": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e", +"Insert\/edit video": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435\/\u043a\u043e\u0440\u0435\u043a\u0446\u0438\u044f \u043d\u0430 \u0432\u0438\u0434\u0435\u043e", +"Insert\/edit media": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043c\u0435\u0434\u0438\u044f", +"Alternative source": "\u0410\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0430\u0434\u0440\u0435\u0441", +"Poster": "\u041f\u043e\u0441\u0442\u0435\u0440", +"Paste your embed code below:": "\u041f\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043a\u043e\u0434\u0430 \u0437\u0430 \u0432\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435 \u0432 \u043f\u043e\u043b\u0435\u0442\u043e \u043f\u043e-\u0434\u043e\u043b\u0443:", +"Embed": "\u0412\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435", +"Media": "\u041c\u0435\u0434\u0438\u044f", +"Nonbreaking space": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b", +"Page break": "\u041d\u043e\u0432\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430", +"Paste as text": "\u041f\u043e\u0441\u0442\u0430\u0432\u0438 \u043a\u0430\u0442\u043e \u0442\u0435\u043a\u0441\u0442", +"Preview": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u0435\u043d \u0438\u0437\u0433\u043b\u0435\u0434", +"Print": "\u041f\u0435\u0447\u0430\u0442", +"Save": "\u0421\u044a\u0445\u0440\u0430\u043d\u044f\u0432\u0430\u043d\u0435", +"Find": "\u0422\u044a\u0440\u0441\u0435\u043d\u0435 \u0437\u0430", +"Replace with": "\u0417\u0430\u043c\u044f\u043d\u0430 \u0441", +"Replace": "\u0417\u0430\u043c\u044f\u043d\u0430", +"Replace all": "\u0417\u0430\u043c\u044f\u043d\u0430 \u043d\u0430 \u0432\u0441\u0438\u0447\u043a\u0438 \u0441\u0440\u0435\u0449\u0430\u043d\u0438\u044f", +"Prev": "\u041f\u0440\u0435\u0434\u0438\u0448\u0435\u043d", +"Next": "\u0421\u043b\u0435\u0434\u0432\u0430\u0449", +"Find and replace": "\u0422\u044a\u0440\u0441\u0435\u043d\u0435 \u0438 \u0437\u0430\u043c\u044f\u043d\u0430", +"Could not find the specified string.": "\u0422\u044a\u0440\u0441\u0435\u043d\u0438\u044f\u0442 \u0442\u0435\u043a\u0441\u0442 \u043d\u0435 \u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d.", +"Match case": "\u0421\u044a\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u043d\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u044a\u0440\u0430 (\u043c\u0430\u043b\u043a\u0438\/\u0433\u043b\u0430\u0432\u043d\u0438 \u0431\u0443\u043a\u0432\u0438)", +"Whole words": "\u0421\u0430\u043c\u043e \u0446\u0435\u043b\u0438 \u0434\u0443\u043c\u0438", +"Spellcheck": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u043f\u0440\u0430\u0432\u043e\u043f\u0438\u0441\u0430", +"Ignore": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u0430\u043d\u0435", +"Ignore all": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0432\u0441\u0438\u0447\u043a\u043e", +"Finish": "\u041a\u0440\u0430\u0439", +"Add to Dictionary": "\u0414\u043e\u0431\u0430\u0432\u0438 \u0432 \u0440\u0435\u0447\u043d\u0438\u043a\u0430", +"Insert table": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430", +"Table properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0442\u0430", +"Delete table": "\u0418\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0442\u0430", +"Cell": "\u041a\u043b\u0435\u0442\u043a\u0430", +"Row": "\u0420\u0435\u0434", +"Column": "\u041a\u043e\u043b\u043e\u043d\u0430", +"Cell properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0430 \u043a\u043b\u0435\u0442\u043a\u0430\u0442\u0430", +"Merge cells": "\u0421\u043b\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043b\u0435\u0442\u043a\u0438\u0442\u0435", +"Split cell": "\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u043a\u043b\u0435\u0442\u043a\u0430", +"Insert row before": "\u0412\u043c\u044a\u043a\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434 \u043f\u0440\u0435\u0434\u0438", +"Insert row after": "\u0412\u043c\u044a\u043a\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434 \u0441\u043b\u0435\u0434", +"Delete row": "\u0418\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434\u0430", +"Row properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0430 \u0440\u0435\u0434\u0430", +"Cut row": "\u0418\u0437\u0440\u044f\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434", +"Copy row": "\u041a\u043e\u043f\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434", +"Paste row before": "\u041f\u043e\u0441\u0442\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434 \u043f\u0440\u0435\u0434\u0438", +"Paste row after": "\u041f\u043e\u0441\u0442\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0440\u0435\u0434 \u0441\u043b\u0435\u0434", +"Insert column before": "\u0412\u043c\u044a\u043a\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u0430 \u043f\u0440\u0435\u0434\u0438", +"Insert column after": "\u0412\u043c\u044a\u043a\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u0430 \u0441\u043b\u0435\u0434", +"Delete column": "\u0418\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u0430\u0442\u0430", +"Cols": "\u041a\u043e\u043b\u043e\u043d\u0438", +"Rows": "\u0420\u0435\u0434\u043e\u0432\u0435", +"Width": "\u0428\u0438\u0440\u0438\u043d\u0430", +"Height": "\u0412\u0438\u0441\u043e\u0447\u0438\u043d\u0430", +"Cell spacing": "\u0420\u0430\u0437\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0435\u0442\u043a\u0438\u0442\u0435", +"Cell padding": "\u0420\u0430\u0437\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435\u0442\u043e", +"Caption": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0437\u0430\u0433\u043b\u0430\u0432\u0438\u0435 \u043f\u0440\u0435\u0434\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0442\u0430", +"Left": "\u041b\u044f\u0432\u043e", +"Center": "\u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0430\u043d\u043e", +"Right": "\u0414\u044f\u0441\u043d\u043e", +"Cell type": "\u0422\u0438\u043f \u043d\u0430 \u043a\u043b\u0435\u0442\u043a\u0430\u0442\u0430", +"Scope": "\u041e\u0431\u0445\u0432\u0430\u0442", +"Alignment": "\u041f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435", +"H Align": "\u0425\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u043d\u043e \u043f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435", +"V Align": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u043d\u043e \u043f\u043e\u0434\u0440\u0430\u0432\u043d\u044f\u0432\u0430\u043d\u0435", +"Top": "\u0413\u043e\u0440\u0435", +"Middle": "\u041f\u043e \u0441\u0440\u0435\u0434\u0430\u0442\u0430", +"Bottom": "\u0414\u043e\u043b\u0443", +"Header cell": "\u0417\u0430\u0433\u043b\u0430\u0432\u043d\u0430 \u043a\u043b\u0435\u0442\u043a\u0430 (\u0430\u043d\u0442\u0435\u0442\u043a\u0430)", +"Row group": "Row group", +"Column group": "Column group", +"Row type": "\u0422\u0438\u043f \u043d\u0430 \u0440\u0435\u0434\u0430", +"Header": "\u0413\u043e\u0440\u0435\u043d \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b (header)", +"Body": "\u0421\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435 (body)", +"Footer": "\u0414\u043e\u043b\u0435\u043d \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b (footer)", +"Border color": "\u0426\u0432\u044f\u0442 \u043d\u0430 \u0440\u0430\u043c\u043a\u0430\u0442\u0430", +"Insert template": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0448\u0430\u0431\u043b\u043e\u043d", +"Templates": "\u0428\u0430\u0431\u043b\u043e\u043d\u0438", +"Template": "\u0428\u0430\u0431\u043b\u043e\u043d", +"Text color": "\u0426\u0432\u044f\u0442 \u043d\u0430 \u0448\u0440\u0438\u0444\u0442\u0430", +"Background color": "\u0424\u043e\u043d\u043e\u0432 \u0446\u0432\u044f\u0442", +"Custom...": "\u0418\u0437\u0431\u0440\u0430\u043d...", +"Custom color": "\u0426\u0432\u044f\u0442 \u043f\u043e \u0438\u0437\u0431\u043e\u0440", +"No color": "\u0411\u0435\u0437 \u0446\u0432\u044f\u0442", +"Table of Contents": "\u0421\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", +"Show blocks": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0431\u043b\u043e\u043a\u043e\u0432\u0435\u0442\u0435", +"Show invisible characters": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043d\u0435\u043f\u0435\u0447\u0430\u0442\u0430\u0435\u043c\u0438 \u0437\u043d\u0430\u0446\u0438", +"Words: {0}": "\u0411\u0440\u043e\u0439 \u0434\u0443\u043c\u0438: {0}", +"{0} words": "{0} \u0431\u0440\u043e\u0439 \u0434\u0443\u043c\u0438", +"File": "\u0424\u0430\u0439\u043b", +"Edit": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u043d\u0435", +"Insert": "\u0412\u043c\u044a\u043a\u0432\u0430\u043d\u0435", +"View": "\u0418\u0437\u0433\u043b\u0435\u0434", +"Format": "\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d\u0435", +"Table": "\u0422\u0430\u0431\u043b\u0438\u0446\u0430", +"Tools": "\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438", +"Powered by {0}": "\u0421\u044a\u0437\u0434\u0430\u0434\u0435\u043d\u043e \u0441 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u041f\u043e\u043b\u0435 \u0437\u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0430\u043d \u0442\u0435\u043a\u0441\u0442. \u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 Alt+F9 \u0437\u0430 \u043c\u0435\u043d\u044e; Alt+F10 \u0437\u0430 \u043b\u0435\u043d\u0442\u0430 \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438; Alt+0 \u0437\u0430 \u043f\u043e\u043c\u043e\u0449." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bn_BD.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bn_BD.js new file mode 100644 index 0000000000..0ce5a29140 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/bn_BD.js @@ -0,0 +1,261 @@ +tinymce.addI18n('bn_BD',{ +"Redo": "\u09aa\u09c1\u09a8\u09b0\u09be\u09af\u09bc \u0995\u09b0\u09c1\u09a8", +"Undo": "\u09aa\u09c2\u09b0\u09cd\u09ac\u09be\u09ac\u09b8\u09cd\u09a5\u09be\u09af\u09bc \u09ab\u09bf\u09b0\u09c1\u09a8", +"Cut": "\u0995\u09b0\u09cd\u09a4\u09a8", +"Copy": "\u0985\u09a8\u09c1\u0995\u09b0\u09a3", +"Paste": "\u09aa\u09cd\u09b0\u09a4\u09bf\u09b2\u09c7\u09aa\u09a8 \u0995\u09b0\u09c1\u09a8", +"Select all": "\u09b8\u09ac \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u0995\u09b0\u09c1\u09a8", +"New document": "\u09a8\u09a4\u09c1\u09a8 \u09a6\u09b8\u09cd\u09a4\u09be\u09ac\u09c7\u099c", +"Ok": "\u09a0\u09bf\u0995 \u0986\u099b\u09c7", +"Cancel": "\u09ac\u09be\u09a4\u09bf\u09b2", +"Visual aids": "\u09ac\u09cd\u09af\u09be\u0996\u09cd\u09af\u09be\u09ae\u09c2\u09b2\u0995 \u09b8\u09be\u09b9\u09be\u09af\u09cd\u09af", +"Bold": "\u09b8\u09cd\u09a5\u09c2\u09b2", +"Italic": "\u09a4\u09bf\u09b0\u09cd\u09af\u0995", +"Underline": "\u09a8\u09bf\u09ae\u09cd\u09a8\u09b0\u09c7\u0996\u09be", +"Strikethrough": "\u09b8\u09cd\u099f\u09cd\u09b0\u09be\u0987\u0995\u09a5\u09cd\u09b0\u09c1", +"Superscript": "\u098a\u09b0\u09cd\u09a7\u09cd\u09ac\u09b2\u09bf\u09aa\u09bf", +"Subscript": "\u09a8\u09bf\u09ae\u09cd\u09a8\u09b2\u09bf\u09aa\u09bf", +"Clear formatting": "\u09ac\u09bf\u09a8\u09cd\u09af\u09be\u09b8 \u0985\u09aa\u09b8\u09be\u09b0\u09a3", +"Align left": "\u09ac\u09be\u09ae\u09c7 \u09b8\u09be\u09b0\u09bf\u0995\u09b0\u09a3", +"Align center": "\u09ae\u09a7\u09cd\u09af\u09b8\u09cd\u09a5\u09be\u09a8\u09c7 \u09b8\u09be\u09b0\u09bf\u0995\u09b0\u09a3", +"Align right": "\u09a1\u09be\u09a8\u09c7 \u09b8\u09be\u09b0\u09bf\u0995\u09b0\u09a3", +"Justify": "\u0989\u09ad\u09af\u09bc\u09aa\u09cd\u09b0\u09be\u09a8\u09cd\u09a4\u09c7 \u09b8\u09ae\u09be\u09a8 \u0995\u09b0\u09c1\u09a8", +"Bullet list": "\u09ac\u09c1\u09b2\u09c7\u099f \u09a4\u09be\u09b2\u09bf\u0995\u09be", +"Numbered list": "\u09b8\u0982\u0996\u09cd\u09af\u09be\u09af\u09c1\u0995\u09cd\u09a4 \u09a4\u09be\u09b2\u09bf\u0995\u09be", +"Decrease indent": "\u0987\u09a8\u09cd\u09a1\u09c7\u09a8\u09cd\u099f \u0995\u09ae\u09be\u09a8", +"Increase indent": "\u0987\u09a8\u09cd\u09a1\u09c7\u09a8\u09cd\u099f \u09ac\u09be\u09a1\u09bc\u09be\u09a8", +"Close": "\u09ac\u09a8\u09cd\u09a7", +"Formats": "\u09ac\u09bf\u09a8\u09cd\u09af\u09be\u09b8", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0986\u09aa\u09a8\u09be\u09b0 \u09ac\u09cd\u09b0\u09be\u0989\u099c\u09be\u09b0 \u0995\u09cd\u09b2\u09bf\u09aa\u09ac\u09cb\u09b0\u09cd\u09a1 \u09a5\u09c7\u0995\u09c7 \u09b8\u09b0\u09be\u09b8\u09b0\u09bf \u09aa\u09cd\u09b0\u09ac\u09c7\u09b6\u09be\u09a7\u09bf\u0995\u09be\u09b0 \u09b8\u09ae\u09b0\u09cd\u09a5\u09a8 \u0995\u09b0\u09c7 \u09a8\u09be\u0964 \u0985\u09a8\u09c1\u0997\u09cd\u09b0\u09b9 \u0995\u09b0\u09c7 \u0995\u09c0\u09ac\u09cb\u09b0\u09cd\u09a1 \u09b6\u09b0\u09cd\u099f\u0995\u09be\u099f Ctrl +X\/C\/V \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0 \u0995\u09b0\u09c1\u09a8\u0964", +"Headers": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09b8\u09ae\u09c1\u09b9", +"Header 1": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09e7", +"Header 2": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09e8", +"Header 3": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09e9", +"Header 4": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09ea", +"Header 5": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09eb", +"Header 6": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09ec", +"Headings": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae", +"Heading 1": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09e7", +"Heading 2": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09e8", +"Heading 3": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09e9", +"Heading 4": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09ea", +"Heading 5": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09eb", +"Heading 6": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae \u09ec", +"Preformatted": "\u09aa\u09c2\u09b0\u09cd\u09ac\u09ac\u09bf\u09a8\u09cd\u09af\u09be\u09b8\u09bf\u09a4", +"Div": "\u09a1\u09bf\u09ad", +"Pre": "\u09aa\u09cd\u09b0\u09be\u0995", +"Code": "\u09b8\u0982\u0995\u09c7\u09a4\u09b2\u09bf\u09aa\u09bf", +"Paragraph": "\u09aa\u09cd\u09af\u09be\u09b0\u09be\u0997\u09cd\u09b0\u09be\u09ab", +"Blockquote": "\u09ac\u09cd\u09b2\u0995\u0995\u09cb\u099f", +"Inline": "\u09b8\u0999\u09cd\u0997\u09a4\u09bf\u09aa\u09c2\u09b0\u09cd\u09a3\u09ad\u09be\u09ac\u09c7", +"Blocks": "\u09b8\u09cd\u09a5\u09c2\u09b2 ", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u09aa\u09c7\u09b8\u09cd\u099f \u098f\u0996\u09a8 \u09aa\u09cd\u09b2\u09c7\u0987\u09a8 \u099f\u09c7\u0995\u09cd\u09b8\u099f \u09ae\u09cb\u09a1\u09c7\u0964 \u0986\u09aa\u09a8\u09bf \u098f\u0996\u09a8 \u098f\u0987 \u09ac\u09bf\u0995\u09b2\u09cd\u09aa \u09ac\u09a8\u09cd\u09a7 \u099f\u0997\u09b2 \u09aa\u09b0\u09cd\u09af\u09a8\u09cd\u09a4 \u09ac\u09bf\u09b7\u09af\u09bc\u09ac\u09b8\u09cd\u09a4\u09c1 \u098f\u0996\u09a8 \u09aa\u09cd\u09b2\u09c7\u0987\u09a8 \u099f\u09c7\u0995\u09cd\u09b8\u099f \u09b9\u09bf\u09b8\u09be\u09ac\u09c7 \u0986\u099f\u0995\u09be\u09a8\u09cb \u09b9\u09ac\u09c7\u0964", +"Font Family": "\u09ab\u09a8\u09cd\u099f \u09ab\u09cd\u09af\u09be\u09ae\u09bf\u09b2\u09bf", +"Font Sizes": "\u09ab\u09a8\u09cd\u099f \u09ae\u09be\u09aa", +"Class": "\u0995\u09cd\u09b2\u09be\u09b8", +"Browse for an image": "\u098f\u0995\u099f\u09bf \u099b\u09ac\u09bf \u09ac\u09cd\u09b0\u09be\u0989\u099c \u0995\u09b0\u09c1\u09a8", +"OR": "\u0985\u09a5\u09ac\u09be", +"Drop an image here": "\u098f\u0996\u09be\u09a8\u09c7 \u098f\u0995\u099f\u09bf \u099b\u09ac\u09bf \u09a1\u09cd\u09b0\u09aa \u0995\u09b0\u09c1\u09a8", +"Upload": "\u0986\u09aa\u09b2\u09cb\u09a1", +"Block": "\u09ac\u09cd\u09b2\u0995", +"Align": "\u09ac\u09bf\u09a8\u09cd\u09af\u09b8\u09cd\u09a4\u0995\u09b0\u09c1\u09a8", +"Default": "\u09a1\u09bf\u09ab\u09b2\u09cd\u099f", +"Circle": "\u09ac\u09c3\u09a4\u09cd\u09a4", +"Disc": "\u09a1\u09bf\u09b8\u09cd\u0995", +"Square": "\u09ac\u09b0\u09cd\u0997\u0995\u09cd\u09b7\u09c7\u09a4\u09cd\u09b0", +"Lower Alpha": "\u09a8\u09bf\u09ae\u09cd\u09a8 \u0986\u09b2\u09ab\u09be", +"Lower Greek": "\u09a8\u09bf\u09ae\u09cd\u09a8 \u0997\u09cd\u09b0\u09bf\u0995", +"Lower Roman": "\u09a8\u09bf\u09ae\u09cd\u09a8 \u09b0\u09cb\u09ae\u09be\u09a8", +"Upper Alpha": "\u0989\u099a\u09cd\u099a\u09a4\u09b0 \u0986\u09b2\u09ab\u09be", +"Upper Roman": "\u098a\u09b0\u09cd\u09a7\u09cd\u09ac \u09b0\u09cb\u09ae\u09be\u09a8", +"Anchor": "\u09a8\u09cb\u0999\u09cd\u0997\u09b0", +"Name": "\u09a8\u09be\u09ae", +"Id": "\u0986\u0987\u09a1\u09bf", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0986\u0987\u09a1\u09bf\u099f\u09bf \u0985\u0995\u09cd\u09b7\u09b0, \u09b8\u0982\u0996\u09cd\u09af\u09be, \u09a1\u09cd\u09af\u09be\u09b6, \u09a1\u099f\u09b8, \u0995\u09b2\u09cb\u09a8 \u09ac\u09be \u0986\u09a8\u09cd\u09a1\u09be\u09b0\u09b8\u09cd\u0995\u09cb\u09b0 \u09a6\u09cd\u09ac\u09be\u09b0\u09be \u0985\u09a8\u09c1\u09b8\u09b0\u09a3 \u0995\u09b0\u09be \u098f\u0995\u099f\u09bf \u099a\u09bf\u09a0\u09bf \u09a6\u09bf\u09af\u09bc\u09c7 \u09b6\u09c1\u09b0\u09c1 \u0995\u09b0\u09be \u0989\u099a\u09bf\u09a4\u0964", +"You have unsaved changes are you sure you want to navigate away?": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09bf\u09a4 \u09aa\u09b0\u09bf\u09ac\u09b0\u09cd\u09a4\u09a8\u0997\u09c1\u09b2\u09bf \u0986\u09aa\u09a8\u09bf \u0995\u09bf \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u09af\u09c7 \u0986\u09aa\u09a8\u09bf \u09a8\u09c7\u09ad\u09bf\u0997\u09c7\u099f \u0995\u09b0\u09a4\u09c7 \u099a\u09be\u09a8?", +"Restore last draft": "\u09b6\u09c7\u09b7 \u0996\u09b8\u09a1\u09bc\u09be\u099f\u09bf \u09aa\u09c1\u09a8\u09b0\u09c1\u09a6\u09cd\u09a7\u09be\u09b0 \u0995\u09b0\u09c1\u09a8", +"Special character": "\u09ac\u09bf\u09b6\u09c7\u09b7 \u099a\u09b0\u09bf\u09a4\u09cd\u09b0", +"Source code": "\u09b8\u09cb\u09b0\u09cd\u09b8 \u0995\u09cb\u09a1", +"Insert\/Edit code sample": "\u0995\u09cb\u09a1 \u09a8\u09ae\u09c1\u09a8\u09be \u09a2\u09cb\u0995\u09be\u09a8 \/ \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8", +"Language": "\u09ad\u09be\u09b7\u09be", +"Code sample": "\u09a8\u09ae\u09c1\u09a8\u09be \u0995\u09cb\u09a1", +"Color": "\u09b0\u0999", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u09ac\u09be\u09ae \u09a5\u09c7\u0995\u09c7 \u09a1\u09be\u09a8", +"Right to left": "\u09a1\u09be\u09a8 \u09a5\u09c7\u0995\u09c7 \u09ac\u09be\u09ae", +"Emoticons": "\u0987\u09ae\u09cb\u099f\u09bf\u0995\u09a8", +"Document properties": "\u09a8\u09a5\u09bf\u09b0 \u09ac\u09c8\u09b6\u09bf\u09b7\u09cd\u099f\u09cd\u09af", +"Title": "\u09b6\u09bf\u09b0\u09cb\u09a8\u09be\u09ae", +"Keywords": "\u0995\u09c0\u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1", +"Description": "\u09ac\u09bf\u09ac\u09b0\u09a3", +"Robots": "\u09b0\u09cb\u09ac\u099f", +"Author": "\u09b2\u09c7\u0996\u0995", +"Encoding": "\u098f\u09a8\u0995\u09cb\u09a1\u09bf\u0982", +"Fullscreen": "\u09aa\u09c2\u09b0\u09cd\u09a3 \u09aa\u09b0\u09cd\u09a6\u09be", +"Action": "\u0995\u09b0\u09cd\u09ae", +"Shortcut": "\u09b6\u09b0\u09cd\u099f\u0995\u09be\u099f", +"Help": "\u09b8\u09be\u09b9\u09be\u09af\u09cd\u09af \u0995\u09b0\u09c1\u09a8", +"Address": "\u09a0\u09bf\u0995\u09be\u09a8\u09be", +"Focus to menubar": "\u09ae\u09c7\u09a8\u09c1\u09ac\u09be\u09b0\u09c7 \u09ab\u09cb\u0995\u09be\u09b8 \u0995\u09b0\u09c1\u09a8", +"Focus to toolbar": "\u099f\u09c1\u09b2\u09ac\u09be\u09b0\u09c7 \u09ab\u09cb\u0995\u09be\u09b8 \u0995\u09b0\u09c1\u09a8", +"Focus to element path": "\u0989\u09aa\u09be\u09a6\u09be\u09a8 \u09aa\u09be\u09a5 \u09ab\u09cb\u0995\u09be\u09b8 \u0995\u09b0\u09c1\u09a8", +"Focus to contextual toolbar": "\u09aa\u09cd\u09b0\u09be\u09b8\u0999\u09cd\u0997\u09bf\u0995 \u099f\u09c1\u09b2\u09ac\u09be\u09b0\u09c7 \u09ab\u09cb\u0995\u09be\u09b8 \u0995\u09b0\u09c1\u09a8", +"Insert link (if link plugin activated)": "\u09b2\u09bf\u0999\u09cd\u0995 \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8 (\u09af\u09a6\u09bf \u09b2\u09bf\u0999\u09cd\u0995 \u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8 \u0985\u09cd\u09af\u09be\u0995\u09cd\u099f\u09bf\u09ad\u09c7\u099f \u0995\u09b0\u09be \u09b9\u09af\u09bc)", +"Save (if save plugin activated)": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3 \u0995\u09b0\u09c1\u09a8 (\u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8 \u0985\u09cd\u09af\u09be\u0995\u09cd\u099f\u09bf\u09ad\u09c7\u099f \u09b9\u09b2\u09c7)", +"Find (if searchreplace plugin activated)": "\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u0995\u09b0\u09c1\u09a8 (\u09af\u09a6\u09bf \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09af\u09cb\u0997\u09cd\u09af \u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8 \u09b8\u0995\u09cd\u09b0\u09bf\u09af\u09bc \u0995\u09b0\u09be \u09b9\u09af\u09bc)", +"Plugins installed ({0}):": "\u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8 \u0987\u09a8\u09b8\u09cd\u099f\u09b2 ({0}):", +"Premium plugins:": "\u09aa\u09cd\u09b0\u09bf\u09ae\u09bf\u09af\u09bc\u09be\u09ae \u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8:", +"Learn more...": "\u0986\u09b0\u0993 \u099c\u09be\u09a8\u09c1\u09a8...", +"You are using {0}": "\u0986\u09aa\u09a8\u09bf \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0 \u0995\u09b0\u099b\u09c7\u09a8 {0}", +"Plugins": "\u09aa\u09cd\u09b2\u09be\u0997\u0987\u09a8", +"Handy Shortcuts": "\u09b8\u09b9\u099c \u09b6\u09b0\u09cd\u099f\u0995\u09be\u099f ", +"Horizontal line": "\u0985\u09a8\u09c1\u09ad\u09c2\u09ae\u09bf\u0995 \u09b0\u09c7\u0996\u09be", +"Insert\/edit image": "\u0987\u09ae\u09c7\u099c \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \/ \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8", +"Image description": "\u099b\u09ac\u09bf\u09b0 \u09ac\u09b0\u09cd\u09a3\u09a8\u09be", +"Source": "\u0989\u09ce\u09b8", +"Dimensions": "\u09ae\u09be\u09a4\u09cd\u09b0\u09be", +"Constrain proportions": "\u0985\u09a8\u09c1\u09aa\u09be\u09a4 \u09b8\u09c0\u09ae\u09be\u09ac\u09a6\u09cd\u09a7", +"General": "\u09b8\u09be\u09a7\u09be\u09b0\u09a3", +"Advanced": "\u0985\u0997\u09cd\u09b0\u09b8\u09b0", +"Style": "\u09b6\u09c8\u09b2\u09c0", +"Vertical space": "\u0989\u09b2\u09cd\u09b2\u09ae\u09cd\u09ac \u09b8\u09cd\u09a5\u09be\u09a8", +"Horizontal space": "\u0985\u09a8\u09c1\u09ad\u09c2\u09ae\u09bf\u0995 \u09b8\u09cd\u09a5\u09be\u09a8", +"Border": "\u09b8\u09c0\u09ae\u09be\u09a8\u09cd\u09a4", +"Insert image": "\u099a\u09bf\u09a4\u09cd\u09b0 \u09a2\u09cb\u0995\u09be\u09a8", +"Image": "\u099b\u09ac\u09bf", +"Image list": "\u099a\u09bf\u09a4\u09cd\u09b0 \u09a4\u09be\u09b2\u09bf\u0995\u09be", +"Rotate counterclockwise": "\u09ac\u09be\u09ae\u09be\u09ac\u09b0\u09cd\u09a4\u09c7 \u0998\u09cb\u09b0\u09be\u09a4\u09c7", +"Rotate clockwise": "\u0998\u09a1\u09bc\u09bf\u09b0 \u0995\u09be\u0981\u099f\u09be\u09b0 \u09a6\u09bf\u0995\u09c7 \u0998\u09cb\u09b0\u09be\u09a8", +"Flip vertically": "\u0989\u09b2\u09cd\u09b2\u09ae\u09cd\u09ac\u09ad\u09be\u09ac\u09c7 \u09ab\u09cd\u09b2\u09bf\u09aa \u0995\u09b0\u09c1\u09a8", +"Flip horizontally": "\u0985\u09a8\u09c1\u09ad\u09c2\u09ae\u09bf\u0995\u09ad\u09be\u09ac\u09c7 \u09ab\u09cd\u09b2\u09bf\u09aa \u0995\u09b0\u09c1\u09a8", +"Edit image": "\u099a\u09bf\u09a4\u09cd\u09b0 \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8", +"Image options": "\u099a\u09bf\u09a4\u09cd\u09b0 \u09ac\u09bf\u0995\u09b2\u09cd\u09aa\u0997\u09c1\u09b2\u09bf", +"Zoom in": "\u09aa\u09cd\u09b0\u09b8\u09be\u09b0\u09bf\u09a4 \u0995\u09b0\u09cb", +"Zoom out": "\u099b\u09cb\u099f \u0995\u09b0\u09be", +"Crop": "\u0995\u09be\u099f\u09be", +"Resize": "\u09ae\u09be\u09aa \u09aa\u09b0\u09bf\u09ac\u09b0\u09cd\u09a4\u09a8 \u0995\u09b0\u09c1\u09a8", +"Orientation": "\u099d\u09cb\u0981\u0995", +"Brightness": "\u0989\u099c\u09cd\u099c\u09cd\u09ac\u09b2\u09a4\u09be", +"Sharpen": "\u09a7\u09be\u09b0 \u0995\u09b0\u09be", +"Contrast": "\u09ac\u09bf\u09aa\u09b0\u09c0\u09a4 \u09b9\u09a4\u09cd\u09a4\u09af\u09bc\u09be", +"Color levels": "\u09b0\u0999\u09c7\u09b0 \u09ae\u09be\u09a4\u09cd\u09b0\u09be", +"Gamma": "Gamma", +"Invert": "\u09ac\u09bf\u09a8\u09b7\u09cd\u099f \u0995\u09b0\u09be", +"Apply": "\u09aa\u09cd\u09b0\u09af\u09bc\u09cb\u0997 \u0995\u09b0\u09be", +"Back": "\u09aa\u09bf\u099b\u09a8\u09c7", +"Insert date\/time": "\u09a4\u09be\u09b0\u09bf\u0996 \/ \u09b8\u09ae\u09af\u09bc \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8", +"Date\/time": "\u09a4\u09be\u09b0\u09bf\u0996 \/ \u09b8\u09ae\u09af\u09bc", +"Insert link": "\u09b2\u09bf\u0999\u09cd\u0995 \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8", +"Insert\/edit link": "\u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \/ \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u09b2\u09bf\u0999\u09cd\u0995", +"Text to display": "\u09aa\u09cd\u09b0\u09a6\u09b0\u09cd\u09b6\u09a8 \u099f\u09c7\u0995\u09cd\u09b8\u099f", +"Url": "URL", +"Target": "\u09b2\u0995\u09cd\u09b7\u09cd\u09af", +"None": "\u09a8\u09be", +"New window": "\u09a8\u09a4\u09c1\u09a8 \u0989\u0987\u09a8\u09cd\u09a1\u09cb", +"Remove link": "\u09b2\u09bf\u0999\u09cd\u0995 \u09b8\u09b0\u09be\u09a8", +"Anchors": "\u09a8\u09cb\u0999\u09cd\u0997\u09b0", +"Link": "\u09b2\u09bf\u0982\u0995", +"Paste or type a link": "\u098f\u0995\u099f\u09bf \u09b2\u09bf\u0999\u09cd\u0995 \u0986\u099f\u0995\u09be\u09a8 \u09ac\u09be \u099f\u09be\u0987\u09aa \u0995\u09b0\u09c1\u09a8", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09cd\u09b0\u09ac\u09c7\u09b6 \u0995\u09b0\u09be URL\u099f\u09bf \u098f\u0995\u099f\u09bf \u0987\u09ae\u09c7\u09b2 \u09a0\u09bf\u0995\u09be\u09a8\u09be \u09ac\u09b2\u09c7 \u09ae\u09a8\u09c7 \u09b9\u099a\u09cd\u099b\u09c7\u0964 \u0986\u09aa\u09a8\u09bf \u09aa\u09cd\u09b0\u09af\u09bc\u09cb\u099c\u09a8\u09c0\u09af\u09bc \u09ae\u09c7\u0987\u09b2\u099f\u09cb \u09af\u09cb\u0997 \u0995\u09b0\u09a4\u09c7 \u099a\u09be\u09a8: \u0989\u09aa\u09b8\u09b0\u09cd\u0997?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09cd\u09b0\u09ac\u09c7\u09b6 \u0995\u09b0\u09be URL\u099f\u09bf \u098f\u0995\u099f\u09bf \u09ac\u09b9\u09bf\u09b0\u09be\u0997\u09a4 \u09b2\u09bf\u0999\u09cd\u0995 \u09ac\u09b2\u09c7 \u09ae\u09a8\u09c7 \u09b9\u099a\u09cd\u099b\u09c7\u0964 \u0986\u09aa\u09a8\u09bf \u0995\u09bf \u09aa\u09cd\u09b0\u09af\u09bc\u09cb\u099c\u09a8\u09c0\u09af\u09bc http:\/\/ \u09aa\u09cd\u09b0\u09bf\u09ab\u09bf\u0995\u09cd\u09b8 \u09af\u09cb\u0997 \u0995\u09b0\u09a4\u09c7 \u099a\u09be\u09a8?", +"Link list": "\u09b2\u09bf\u0999\u09cd\u0995 \u09a4\u09be\u09b2\u09bf\u0995\u09be", +"Insert video": "\u09ad\u09bf\u09a1\u09bf\u0993 \u09a2\u09cb\u0995\u09be\u09a8", +"Insert\/edit video": "\u09ad\u09bf\u09a1\u09bf\u0993 \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \/ \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8", +"Insert\/edit media": "\u09ae\u09bf\u09a1\u09bf\u09af\u09bc\u09be \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8 \/ \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8", +"Alternative source": "\u09ac\u09bf\u0995\u09b2\u09cd\u09aa \u0989\u09ce\u09b8", +"Poster": "\u09aa\u09cb\u09b8\u09cd\u099f\u09be\u09b0", +"Paste your embed code below:": "\u09a8\u09c0\u099a\u09c7\u09b0 \u0986\u09aa\u09a8\u09be\u09b0 \u098f\u09ae\u09cd\u09ac\u09c7\u09a1 \u0995\u09cb\u09a1 \u0986\u099f\u0995\u09be\u09a8:", +"Embed": "\u098f\u09ae\u09cd\u09ac\u09c7\u09a1", +"Media": "\u09ae\u09bf\u09a1\u09bf\u09af\u09bc\u09be", +"Nonbreaking space": "\u0985\u09ac\u09bf\u099a\u09cd\u099b\u09bf\u09a8\u09cd\u09a8 \u09b8\u09cd\u09a5\u09be\u09a8", +"Page break": "\u09aa\u09c3\u09b7\u09cd\u09a0\u09be \u09ac\u09bf\u09b0\u09a4\u09bf", +"Paste as text": "\u09aa\u09be\u09a0\u09cd\u09af \u09b9\u09bf\u09b8\u09be\u09ac\u09c7 \u09aa\u09c7\u09b8\u09cd\u099f \u0995\u09b0\u09c1\u09a8", +"Preview": "\u09aa\u09c2\u09b0\u09cd\u09ac\u09b0\u09c2\u09aa", +"Print": "\u099b\u09be\u09aa\u09be", +"Save": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3", +"Find": "\u0986\u09ac\u09bf\u09b7\u09cd\u0995\u09be\u09b0", +"Replace with": "\u09aa\u09cd\u09b0\u09a4\u09bf\u09b8\u09cd\u09a5\u09be\u09aa\u09a8", +"Replace": "\u09aa\u09cd\u09b0\u09a4\u09bf\u09b8\u09cd\u09a5\u09be\u09aa\u09a8 \u0995\u09b0\u09be", +"Replace all": "\u09b8\u09ae\u09b8\u09cd\u09a4 \u09aa\u09cd\u09b0\u09a4\u09bf\u09b8\u09cd\u09a5\u09be\u09aa\u09a8", +"Prev": "\u09aa\u09c2\u09b0\u09cd\u09ac\u09ac\u09b0\u09cd\u09a4\u09c0", +"Next": "\u09aa\u09b0\u09ac\u09b0\u09cd\u09a4\u09c0", +"Find and replace": "\u0996\u09c1\u0981\u099c\u09c1\u09a8 \u0993 \u09aa\u09cd\u09b0\u09a4\u09bf\u09b8\u09cd\u09a5\u09be\u09aa\u09a8 \u0995\u09b0\u09c1\u09a8", +"Could not find the specified string.": "\u09a8\u09bf\u09b0\u09cd\u09a6\u09bf\u09b7\u09cd\u099f \u09b8\u09cd\u099f\u09cd\u09b0\u09bf\u0982\u099f\u09bf \u0996\u09c1\u0981\u099c\u09c7 \u09aa\u09be\u0993\u09af\u09bc\u09be \u09af\u09be\u09af\u09bc\u09a8\u09bf\u0964", +"Match case": "\u09ae\u09cd\u09af\u09be\u099a \u0995\u09cd\u09b7\u09c7\u09a4\u09cd\u09b0\u09c7", +"Whole words": "\u09b8\u09ae\u09cd\u09aa\u09c2\u09b0\u09cd\u09a3 \u09b6\u09ac\u09cd\u09a6\u09c7\u09b0", +"Spellcheck": "\u09ac\u09be\u09a8\u09be\u09a8 \u09af\u09be\u099a\u09be\u0987", +"Ignore": "\u0989\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u09be", +"Ignore all": "\u09b8\u09ac\u0997\u09c1\u09b2\u09cb \u0989\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u09c1\u09a8", +"Finish": "\u09b6\u09c7\u09b7", +"Add to Dictionary": "\u0985\u09ad\u09bf\u09a7\u09be\u09a8 \u09af\u09cb\u0997 \u0995\u09b0\u09c1\u09a8", +"Insert table": "\u099f\u09c7\u09ac\u09bf\u09b2 \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8", +"Table properties": "\u099f\u09c7\u09ac\u09bf\u09b2 \u09ac\u09c8\u09b6\u09bf\u09b7\u09cd\u099f\u09cd\u09af", +"Delete table": "\u09b8\u09be\u09b0\u09a3\u09bf \u09ae\u09c1\u099b\u09c1\u09a8", +"Cell": "\u09b8\u09c7\u09b2", +"Row": "\u09b8\u09be\u09b0\u09bf", +"Column": "\u0995\u09b2\u09be\u09ae", +"Cell properties": "\u09b8\u09c7\u09b2 \u09ac\u09c8\u09b6\u09bf\u09b7\u09cd\u099f\u09cd\u09af", +"Merge cells": "\u09b8\u09c7\u09b2 \u09ae\u09be\u09b0\u09cd\u099c \u0995\u09b0\u09c1\u09a8", +"Split cell": "\u09b8\u09cd\u09aa\u09cd\u09b2\u09bf\u099f \u09b8\u09c7\u09b2", +"Insert row before": "\u0986\u0997\u09c7 \u09b8\u09be\u09b0\u09bf \u09a2\u09cb\u0995\u09be\u09a8", +"Insert row after": "\u09aa\u09b0\u09c7 \u09b8\u09be\u09b0\u09bf \u09a2\u09cb\u0995\u09be\u09a8", +"Delete row": "\u09b8\u09be\u09b0\u09bf \u09ae\u09c1\u099b\u09c1\u09a8", +"Row properties": "\u09b8\u09be\u09b0\u09bf \u09ac\u09c8\u09b6\u09bf\u09b7\u09cd\u099f\u09cd\u09af", +"Cut row": "\u09b8\u09be\u09b0\u09bf \u0995\u09be\u099f\u09c1\u09a8", +"Copy row": "\u09b8\u09be\u09b0\u09bf \u0985\u09a8\u09c1\u09b2\u09bf\u09aa\u09bf \u0995\u09b0\u09c1\u09a8", +"Paste row before": "\u0986\u0997\u09c7 \u09b8\u09be\u09b0\u09bf \u09aa\u09c7\u09b8\u09cd\u099f \u0995\u09b0\u09c1\u09a8", +"Paste row after": "\u09aa\u09b0\u09c7 \u09b8\u09be\u09b0\u09bf \u09aa\u09c7\u09b8\u09cd\u099f \u0995\u09b0\u09c1\u09a8", +"Insert column before": "\u0986\u0997\u09c7 \u0995\u09b2\u09be\u09ae \u09a2\u09cb\u0995\u09be\u09a8", +"Insert column after": "\u09aa\u09b0\u09c7 \u0995\u09b2\u09be\u09ae \u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6 \u0995\u09b0\u09c1\u09a8", +"Delete column": "\u0995\u09b2\u09be\u09ae \u09ae\u09c1\u099b\u09c1\u09a8", +"Cols": "\u0995\u09b2\u09be\u09ae \u0997\u09c1\u09b2\u09cb", +"Rows": "\u09b8\u09be\u09b0\u09bf\u0997\u09c1\u09b2\u09cb", +"Width": "\u09aa\u09cd\u09b0\u09b8\u09cd\u09a5", +"Height": "\u0989\u099a\u09cd\u099a\u09a4\u09be", +"Cell spacing": "\u09b8\u09c7\u09b2 \u09ab\u09be\u0981\u0995\u09be", +"Cell padding": "\u09b8\u09c7\u09b2 \u09aa\u09cd\u09af\u09be\u09a1\u09bf\u0982", +"Caption": "\u0995\u09cd\u09af\u09be\u09aa\u09b6\u09a8", +"Left": "\u09ac\u09be\u09ae", +"Center": "\u0995\u09c7\u09a8\u09cd\u09a6\u09cd\u09b0", +"Right": "\u09a1\u09be\u09a8", +"Cell type": "\u09b8\u09c7\u09b2 \u099f\u09be\u0987\u09aa", +"Scope": "\u09ac\u09cd\u09af\u09be\u09aa\u09cd\u09a4\u09bf", +"Alignment": "\u09b6\u09cd\u09b0\u09c7\u09a3\u09c0\u09ac\u09bf\u09a8\u09cd\u09af\u09be\u09b8", +"H Align": "H \u09b8\u09be\u09b0\u09bf\u09ac\u09a6\u09cd\u09a7", +"V Align": "V \u09b8\u09be\u09b0\u09bf\u09ac\u09a6\u09cd\u09a7", +"Top": "\u0989\u09aa\u09b0", +"Middle": "\u09ae\u09a7\u09cd\u09af\u09ae", +"Bottom": "\u09a8\u09bf\u099a\u09c7", +"Header cell": "\u09b9\u09c7\u09a1\u09be\u09b0 \u09b8\u09c7\u09b2", +"Row group": "\u09b8\u09be\u09b0\u09bf \u0997\u09cd\u09b0\u09c1\u09aa", +"Column group": "\u0995\u09b2\u09be\u09ae \u0997\u09cd\u09b0\u09c1\u09aa", +"Row type": "\u09b8\u09be\u09b0\u09bf\u09b0 \u09a7\u09b0\u09a8", +"Header": "\u09b9\u09c7\u09a1\u09be\u09b0", +"Body": "\u09ac\u09a1\u09bf", +"Footer": "\u09ab\u09c1\u099f\u09be\u09b0", +"Border color": "\u09b8\u09c0\u09ae\u09be\u09a8\u09cd\u09a4 \u09b0\u0999", +"Insert template": "\u099f\u09c7\u09ae\u09aa\u09cd\u09b2\u09c7\u099f \u09a2\u09cb\u0995\u09be\u09a8", +"Templates": "\u099f\u09c7\u09ae\u09aa\u09cd\u09b2\u09c7\u099f", +"Template": "\u099f\u09c7\u09ae\u09aa\u09cd\u09b2\u09c7\u099f", +"Text color": "\u09b2\u09c7\u0996\u09be\u09b0 \u09b0\u0999", +"Background color": "\u09aa\u09c7\u099b\u09a8\u09c7\u09b0 \u09b0\u0982", +"Custom...": "\u0995\u09be\u09b8\u09cd\u099f\u09ae...", +"Custom color": "\u0995\u09be\u09b8\u09cd\u099f\u09ae \u09b0\u0982", +"No color": "\u0995\u09cb\u09a8 \u09b0\u0982 \u09a8\u09c7\u0987", +"Table of Contents": "\u09b8\u09c1\u099a\u09bf\u09aa\u09a4\u09cd\u09b0", +"Show blocks": "\u09ac\u09cd\u09b2\u0995 \u09a6\u09c7\u0996\u09be\u09a8", +"Show invisible characters": "\u0985\u09a6\u09c3\u09b6\u09cd\u09af \u0985\u0995\u09cd\u09b7\u09b0 \u09a6\u09c7\u0996\u09be\u09a8", +"Words: {0}": "\u09b6\u09ac\u09cd\u09a6: {0}", +"{0} words": "{0} \u09b6\u09ac\u09cd\u09a6", +"File": "\u09ab\u09be\u0987\u09b2", +"Edit": "\u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8 \u0995\u09b0\u09be", +"Insert": "\u09b8\u09a8\u09cd\u09a8\u09bf\u09ac\u09c7\u09b6", +"View": "\u09a6\u09c3\u09b6\u09cd\u09af", +"Format": "\u09ac\u09bf\u09a8\u09cd\u09af\u09be\u09b8", +"Table": "\u099f\u09c7\u09ac\u09bf\u09b2", +"Tools": "\u09b8\u09b0\u099e\u09cd\u099c\u09be\u09ae\u09b8\u09ae\u09c2\u09b9", +"Powered by {0}": "{0} \u09a6\u09cd\u09ac\u09be\u09b0\u09be \u099a\u09be\u09b2\u09bf\u09a4", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u09b0\u09bf\u099a \u099f\u09c7\u0995\u09cd\u09b8\u099f \u098f\u09b0\u09bf\u09af\u09bc\u09be \u09ae\u09c7\u09a8\u09c1 \u099c\u09a8\u09cd\u09af ALT-F9 \u099a\u09be\u09aa\u09c1\u09a8 \u099f\u09c1\u09b2\u09ac\u09be\u09b0\u09c7\u09b0 \u099c\u09a8\u09cd\u09af ALT-F10 \u099f\u09bf\u09aa\u09c1\u09a8 \u09b8\u09be\u09b9\u09be\u09af\u09cd\u09af\u09c7\u09b0 \u099c\u09a8\u09cd\u09af ALT-0 \u099a\u09be\u09aa\u09c1\u09a8" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ca.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ca.js new file mode 100644 index 0000000000..671e875351 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ca.js @@ -0,0 +1,261 @@ +tinymce.addI18n('ca',{ +"Redo": "Refer", +"Undo": "Desfer", +"Cut": "Retalla", +"Copy": "Copia", +"Paste": "Enganxa", +"Select all": "Seleccionar-ho tot", +"New document": "Nou document", +"Ok": "Acceptar", +"Cancel": "Cancel\u00b7la", +"Visual aids": "Assist\u00e8ncia visual", +"Bold": "Negreta", +"Italic": "Cursiva", +"Underline": "Subratllat", +"Strikethrough": "Ratllat", +"Superscript": "Super\u00edndex", +"Subscript": "Sub\u00edndex", +"Clear formatting": "Eliminar format", +"Align left": "Aliniat a l'esquerra", +"Align center": "Centrat", +"Align right": "Aliniat a la dreta", +"Justify": "Justificat", +"Bullet list": "Llista no ordenada", +"Numbered list": "Llista enumerada", +"Decrease indent": "Disminuir sagnat", +"Increase indent": "Augmentar sagnat", +"Close": "Tanca", +"Formats": "Formats", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "El vostre navegador no suporta l'acc\u00e9s directe al portaobjectes. Si us plau, feu servir les dreceres de teclat Ctrl+X\/C\/V.", +"Headers": "Cap\u00e7aleres", +"Header 1": "Cap\u00e7alera 1", +"Header 2": "Cap\u00e7alera 2", +"Header 3": "Cap\u00e7alera 3", +"Header 4": "Cap\u00e7alera 4", +"Header 5": "Cap\u00e7alera 5", +"Header 6": "Cap\u00e7alera 6", +"Headings": "Encap\u00e7alaments", +"Heading 1": "Encap\u00e7alament 1", +"Heading 2": "Encap\u00e7alament 2", +"Heading 3": "Encap\u00e7alament 3", +"Heading 4": "Encap\u00e7alament 4", +"Heading 5": "Encap\u00e7alament 5", +"Heading 6": "Encap\u00e7alament 6", +"Preformatted": "Preformatted", +"Div": "Div", +"Pre": "Pre", +"Code": "Codi", +"Paragraph": "Par\u00e0graf", +"Blockquote": "Cita", +"Inline": "En l\u00ednia", +"Blocks": "Blocs", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Enganxar ara est\u00e0 en mode text pla. Els continguts s'enganxaran com a text pla fins que desactivis aquesta opci\u00f3. ", +"Font Family": "Fam\u00edlia de la font", +"Font Sizes": "Mides de la font", +"Class": "Classe", +"Browse for an image": "Explorar una imatge", +"OR": "O", +"Drop an image here": "Deixar anar una imatge aqu\u00ed", +"Upload": "Pujar", +"Block": "Bloc", +"Align": "Alinear", +"Default": "Per defecte", +"Circle": "Cercle", +"Disc": "Disc", +"Square": "Quadrat", +"Lower Alpha": "Alfa menor", +"Lower Greek": "Grec menor", +"Lower Roman": "Roman menor", +"Upper Alpha": "Alfa major", +"Upper Roman": "Roman major", +"Anchor": "\u00c0ncora", +"Name": "Nom", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "La Id ha de comen\u00e7ar amb una lletra, seguida d'altres lletres, n\u00fameros, punts, ratlles, comes, o guions baixos", +"You have unsaved changes are you sure you want to navigate away?": "Teniu canvis sense desar, esteu segur que voleu deixar-ho ara?", +"Restore last draft": "Restaurar l'\u00faltim esborrany", +"Special character": "Car\u00e0cter especial", +"Source code": "Codi font", +"Insert\/Edit code sample": "Inserir\/Editar tros de codi", +"Language": "Idioma", +"Code sample": "Mostra de codi", +"Color": "Color", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "D'esquerra a dreta", +"Right to left": "De dreta a esquerra", +"Emoticons": "Emoticones", +"Document properties": "Propietats del document", +"Title": "T\u00edtol", +"Keywords": "Paraules clau", +"Description": "Descripci\u00f3", +"Robots": "Robots", +"Author": "Autor", +"Encoding": "Codificaci\u00f3", +"Fullscreen": "Pantalla completa", +"Action": "Acci\u00f3", +"Shortcut": "Drecera", +"Help": "Ajuda", +"Address": "Adre\u00e7a", +"Focus to menubar": "Enfocar la barra de men\u00fa", +"Focus to toolbar": "Enfocar la barra d'eines", +"Focus to element path": "Enfocar la ruta d'elements", +"Focus to contextual toolbar": "Enfocar la barra d'eines contextual", +"Insert link (if link plugin activated)": "Inserir enlla\u00e7 (si el complement d'enlla\u00e7 est\u00e0 activat)", +"Save (if save plugin activated)": "Desar (si el complement desar est\u00e0 activat)", +"Find (if searchreplace plugin activated)": "Cercar (si el complement cercar-reempla\u00e7ar est\u00e0 activat)", +"Plugins installed ({0}):": "Complements instal\u00b7lats ({0}):", +"Premium plugins:": "Complements premium", +"Learn more...": "Apr\u00e8n m\u00e9s...", +"You are using {0}": "Est\u00e0s utilitzant {0}", +"Plugins": "Complements", +"Handy Shortcuts": "Dreceres a m\u00e0", +"Horizontal line": "L\u00ednia horitzontal", +"Insert\/edit image": "Inserir\/editar imatge", +"Image description": "Descripci\u00f3 de la imatge", +"Source": "Font", +"Dimensions": "Dimensions", +"Constrain proportions": "Mantenir proporcions", +"General": "General", +"Advanced": "Avan\u00e7at", +"Style": "Estil", +"Vertical space": "Espai vertical", +"Horizontal space": "Espai horitzontal", +"Border": "Vora", +"Insert image": "Inserir imatge", +"Image": "Imatge", +"Image list": "Llista d'imatges", +"Rotate counterclockwise": "Girar a l'esquerra", +"Rotate clockwise": "Girar a la dreta", +"Flip vertically": "Capgirar verticalment", +"Flip horizontally": "Capgirar horitzontalment", +"Edit image": "Editar imatge", +"Image options": "Opcions d'imatge", +"Zoom in": "Ampliar", +"Zoom out": "Empetitir", +"Crop": "Escap\u00e7ar", +"Resize": "Canviar mida", +"Orientation": "Orientaci\u00f3", +"Brightness": "Brillantor", +"Sharpen": "Remarcar vores", +"Contrast": "Contrast", +"Color levels": "Nivells de color", +"Gamma": "Gamma", +"Invert": "Invertir", +"Apply": "Aplicar", +"Back": "Tornar", +"Insert date\/time": "Inserir data\/hora", +"Date\/time": "Data\/hora", +"Insert link": "Inserir enlla\u00e7", +"Insert\/edit link": "Inserir\/editar enlla\u00e7", +"Text to display": "Text per mostrar", +"Url": "URL", +"Target": "Dest\u00ed", +"None": "Cap", +"New window": "Finestra nova", +"Remove link": "Treure enlla\u00e7", +"Anchors": "\u00c0ncores", +"Link": "Enlla\u00e7", +"Paste or type a link": "Enganxa o escriu un enlla\u00e7", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL que has escrit sembla una adre\u00e7a de correu electr\u00f2nic. Vols afegir-li el prefix obligatori mailto: ?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL que has escrit sembla un enlla\u00e7 extern. Vols afegir-li el prefix obligatori http:\/\/ ?", +"Link list": "Llista d'enlla\u00e7os", +"Insert video": "Inserir v\u00eddeo", +"Insert\/edit video": "Inserir\/editar v\u00eddeo", +"Insert\/edit media": "Inserir\/editar mitj\u00e0", +"Alternative source": "Font alternativa", +"Poster": "P\u00f3ster", +"Paste your embed code below:": "Enganxau el codi a sota:", +"Embed": "Incloure", +"Media": "Mitjans", +"Nonbreaking space": "Espai fixe", +"Page break": "Salt de p\u00e0gina", +"Paste as text": "Enganxar com a text", +"Preview": "Previsualitzaci\u00f3", +"Print": "Imprimir", +"Save": "Desa", +"Find": "Buscar", +"Replace with": "Rempla\u00e7ar amb", +"Replace": "Rempla\u00e7ar", +"Replace all": "Rempla\u00e7ar-ho tot", +"Prev": "Anterior", +"Next": "Seg\u00fcent", +"Find and replace": "Buscar i rempla\u00e7ar", +"Could not find the specified string.": "No es pot trobar el text especificat.", +"Match case": "Coincidir maj\u00fascules", +"Whole words": "Paraules senceres", +"Spellcheck": "Comprovar ortrografia", +"Ignore": "Ignorar", +"Ignore all": "Ignorar tots", +"Finish": "Finalitzar", +"Add to Dictionary": "Afegir al diccionari", +"Insert table": "Inserir taula", +"Table properties": "Propietats de taula", +"Delete table": "Esborrar taula", +"Cell": "Cel\u00b7la", +"Row": "Fila", +"Column": "Columna", +"Cell properties": "Propietats de cel\u00b7la", +"Merge cells": "Fusionar cel\u00b7les", +"Split cell": "Dividir cel\u00b7les", +"Insert row before": "Inserir fila a sobre", +"Insert row after": "Inserir fila a sota", +"Delete row": "Esborrar fila", +"Row properties": "Propietats de fila", +"Cut row": "Retallar fila", +"Copy row": "Copiar fila", +"Paste row before": "Enganxar fila a sobre", +"Paste row after": "Enganxar fila a sota", +"Insert column before": "Inserir columna abans", +"Insert column after": "Inserir columna despr\u00e9s", +"Delete column": "Esborrar columna", +"Cols": "Cols", +"Rows": "Files", +"Width": "Amplada", +"Height": "Al\u00e7ada", +"Cell spacing": "Espai entre cel\u00b7les", +"Cell padding": "Marge intern", +"Caption": "Encap\u00e7alament", +"Left": "A l'esquerra", +"Center": "Centrat", +"Right": "A la dreta", +"Cell type": "Tipus de cel\u00b7la", +"Scope": "\u00c0mbit", +"Alignment": "Aliniament", +"H Align": "Al\u00edniament H", +"V Align": "Al\u00edniament V", +"Top": "Superior", +"Middle": "Mitj\u00e0", +"Bottom": "Inferior", +"Header cell": "Cel\u00b7la de cap\u00e7alera", +"Row group": "Grup de fila", +"Column group": "Grup de columna", +"Row type": "Tipus de fila", +"Header": "Cap\u00e7alera", +"Body": "Cos", +"Footer": "Peu", +"Border color": "Color de vora", +"Insert template": "Inserir plantilla", +"Templates": "Plantilles", +"Template": "Plantilla", +"Text color": "Color del text", +"Background color": "Color del fons", +"Custom...": "Personalitzar...", +"Custom color": "Personalitzar el color", +"No color": "Sense color", +"Table of Contents": "Taula de continguts", +"Show blocks": "Mostrar blocs", +"Show invisible characters": "Mostrar car\u00e0cters invisibles", +"Words: {0}": "Paraules: {0}", +"{0} words": "{0} paraules", +"File": "Arxiu", +"Edit": "Edici\u00f3", +"Insert": "Inserir", +"View": "Veure", +"Format": "Format", +"Table": "Taula", +"Tools": "Eines", +"Powered by {0}": "Impulsat per {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u00c0rea de text amb format. Premeu ALT-F9 per mostrar el men\u00fa, ALT F10 per la barra d'eines i ALT-0 per ajuda." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs.js new file mode 100644 index 0000000000..264b32fcd0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs.js @@ -0,0 +1,261 @@ +tinymce.addI18n('cs',{ +"Redo": "Znovu", +"Undo": "Zp\u011bt", +"Cut": "Vyjmout", +"Copy": "Kop\u00edrovat", +"Paste": "Vlo\u017eit", +"Select all": "Vybrat v\u0161e", +"New document": "Nov\u00fd dokument", +"Ok": "OK", +"Cancel": "Zru\u0161it", +"Visual aids": "Vizu\u00e1ln\u00ed pom\u016fcky", +"Bold": "Tu\u010dn\u00e9", +"Italic": "Kurz\u00edva", +"Underline": "Podtr\u017een\u00e9", +"Strikethrough": "P\u0159e\u0161rktnut\u00e9", +"Superscript": "Horn\u00ed index", +"Subscript": "Doln\u00ed index", +"Clear formatting": "Vymazat form\u00e1tov\u00e1n\u00ed", +"Align left": "Zarovnat vlevo", +"Align center": "Zarovnat na st\u0159ed", +"Align right": "Zarovnat vpravo", +"Justify": "Zarovnat do bloku", +"Bullet list": "Odr\u00e1\u017eky", +"Numbered list": "\u010c\u00edslov\u00e1n\u00ed", +"Decrease indent": "Zmen\u0161it odsazen\u00ed", +"Increase indent": "Zv\u011bt\u0161it odsazen\u00ed", +"Close": "Zav\u0159\u00edt", +"Formats": "Form\u00e1ty", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "V\u00e1\u0161 prohl\u00ed\u017ee\u010d nepodporuje p\u0159\u00edm\u00fd p\u0159\u00edstup do schr\u00e1nky. Pou\u017eijte pros\u00edm kl\u00e1vesov\u00e9 zkratky Ctrl+X\/C\/V.", +"Headers": "Nadpisy", +"Header 1": "Nadpis 1", +"Header 2": "Nadpis 2", +"Header 3": "Nadpis 3", +"Header 4": "Nadpis 4", +"Header 5": "Nadpis 5", +"Header 6": "Nadpis 6", +"Headings": "Nadpisy", +"Heading 1": "Nadpis 1", +"Heading 2": "Nadpis 2", +"Heading 3": "Nadpis 3", +"Heading 4": "Nadpis 4", +"Heading 5": "Nadpis 5", +"Heading 6": "Nadpis 6", +"Preformatted": "P\u0159edform\u00e1tov\u00e1no", +"Div": "Div (blok)", +"Pre": "Pre (p\u0159edform\u00e1tov\u00e1no)", +"Code": "Code (k\u00f3d)", +"Paragraph": "Odstavec", +"Blockquote": "Citace", +"Inline": "\u0158\u00e1dkov\u00e9 zobrazen\u00ed (inline)", +"Blocks": "Blokov\u00e9 zobrazen\u00ed (block)", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Je zapnuto vkl\u00e1d\u00e1n\u00ed \u010dist\u00e9ho textu. Dokud nebude tato volba vypnuta, bude ve\u0161ker\u00fd obsah vlo\u017een jako \u010dist\u00fd text.", +"Font Family": "Typ p\u00edsma", +"Font Sizes": "Velikost p\u00edsma", +"Class": "T\u0159\u00edda", +"Browse for an image": "Vyhledat obr\u00e1zek", +"OR": "nebo", +"Drop an image here": "Nahr\u00e1t obr\u00e1zek", +"Upload": "Nahr\u00e1t", +"Block": "Blok", +"Align": "Zarovnat", +"Default": "V\u00fdchoz\u00ed", +"Circle": "Kole\u010dko", +"Disc": "Punt\u00edk", +"Square": "\u010ctvere\u010dek", +"Lower Alpha": "Norm\u00e1ln\u00ed \u010d\u00edslov\u00e1n\u00ed", +"Lower Greek": "Mal\u00e9 p\u00edsmenkov\u00e1n\u00ed", +"Lower Roman": "Mal\u00e9 \u0159\u00edmsk\u00e9 \u010d\u00edslice", +"Upper Alpha": "velk\u00e9 p\u00edsmenkov\u00e1n\u00ed", +"Upper Roman": "\u0158\u00edmsk\u00e9 \u010d\u00edslice", +"Anchor": "Kotva", +"Name": "N\u00e1zev", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id by m\u011blo za\u010d\u00ednat p\u00edsmenem a d\u00e1le obsahovat pouze p\u00edsmena, \u010d\u00edsla, poml\u010dky, te\u010dky, dvojte\u010dky, nebo podtr\u017e\u00edtka.", +"You have unsaved changes are you sure you want to navigate away?": "M\u00e1te neulo\u017een\u00e9 zm\u011bny. Opravdu chcete opustit str\u00e1nku?", +"Restore last draft": "Obnovit posledn\u00ed koncept", +"Special character": "Speci\u00e1ln\u00ed znak", +"Source code": "Zdrojov\u00fd k\u00f3d", +"Insert\/Edit code sample": "Vlo\u017eit \/ Upravit uk\u00e1zkov\u00fd k\u00f3d", +"Language": "Jazyk", +"Code sample": "Uk\u00e1zkov\u00fd k\u00f3d", +"Color": "Barva", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Zleva doprava", +"Right to left": "Zprava doleva", +"Emoticons": "Emotikony", +"Document properties": "Vlastnosti dokumentu", +"Title": "Titulek", +"Keywords": "Kl\u00ed\u010dov\u00e1 slova", +"Description": "Popis", +"Robots": "Roboti", +"Author": "Autor", +"Encoding": "K\u00f3dov\u00e1n\u00ed", +"Fullscreen": "Na celou obrazovku", +"Action": "Akce", +"Shortcut": "Kl\u00e1vesov\u00e1 zkratka", +"Help": "N\u00e1pov\u011bda", +"Address": "Blok s po\u0161tovn\u00ed adresou", +"Focus to menubar": "P\u0159ej\u00edt do menu", +"Focus to toolbar": "P\u0159ej\u00edt na panel n\u00e1stroj\u016f", +"Focus to element path": "P\u0159ej\u00edt na element path", +"Focus to contextual toolbar": "P\u0159ej\u00edt na kontextov\u00fd panel n\u00e1stroj\u016f", +"Insert link (if link plugin activated)": "Vlo\u017eit odkaz (pokud je aktivn\u00ed link plugin)", +"Save (if save plugin activated)": "Ulo\u017eit (pokud je aktivni save plugin)", +"Find (if searchreplace plugin activated)": "Hledat (pokud je aktivn\u00ed plugin searchreplace)", +"Plugins installed ({0}):": "Instalovan\u00e9 pluginy ({0}):", +"Premium plugins:": "Pr\u00e9miov\u00e9 pluginy:", +"Learn more...": "Zjistit v\u00edce...", +"You are using {0}": "Pou\u017e\u00edv\u00e1te {0}", +"Plugins": "Pluginy", +"Handy Shortcuts": "U\u017eite\u010dn\u00e9 kl\u00e1vesov\u00e9 zkratky", +"Horizontal line": "Vodorovn\u00e1 \u010d\u00e1ra", +"Insert\/edit image": "Vlo\u017eit \/ upravit obr\u00e1zek", +"Image description": "Popis obr\u00e1zku", +"Source": "URL", +"Dimensions": "Rozm\u011bry", +"Constrain proportions": "Zachovat proporce", +"General": "Obecn\u00e9", +"Advanced": "Pokro\u010dil\u00e9", +"Style": "Styl", +"Vertical space": "Vertik\u00e1ln\u00ed mezera", +"Horizontal space": "Horizont\u00e1ln\u00ed mezera", +"Border": "R\u00e1me\u010dek", +"Insert image": "Vlo\u017eit obr\u00e1zek", +"Image": "Obr\u00e1zek", +"Image list": "Seznam obr\u00e1zk\u016f", +"Rotate counterclockwise": "Oto\u010dit doleva", +"Rotate clockwise": "Oto\u010dit doprava", +"Flip vertically": "P\u0159evr\u00e1tit svisle", +"Flip horizontally": "P\u0159evr\u00e1tit vodorovn\u011b", +"Edit image": "Upravit obr\u00e1zek", +"Image options": "Vlastnosti obr\u00e1zku", +"Zoom in": "P\u0159ibl\u00ed\u017eit", +"Zoom out": "Odd\u00e1lit", +"Crop": "O\u0159\u00edznout", +"Resize": "Zm\u011bnit velikost", +"Orientation": "Transformovat", +"Brightness": "Jas", +"Sharpen": "Ostrost", +"Contrast": "Kontrast", +"Color levels": "\u00darovn\u011b barev", +"Gamma": "Gama", +"Invert": "Invertovat", +"Apply": "Pou\u017e\u00edt", +"Back": "Zp\u011bt", +"Insert date\/time": "Vlo\u017eit datum \/ \u010das", +"Date\/time": "Datum\/\u010das", +"Insert link": "Vlo\u017eit odkaz", +"Insert\/edit link": "Vlo\u017eit \/ upravit odkaz", +"Text to display": "Text k zobrazen\u00ed", +"Url": "URL", +"Target": "C\u00edl", +"None": "\u017d\u00e1dn\u00e9", +"New window": "Nov\u00e9 okno", +"Remove link": "Odstranit odkaz", +"Anchors": "Kotvy", +"Link": "Odkaz", +"Paste or type a link": "Vlo\u017eit nebo napsat odkaz", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Zadan\u00e9 URL vypad\u00e1 jako e-mailov\u00e1 adresa. Chcete doplnit povinn\u00fd prefix mailto:?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Zadan\u00e9 URL vypad\u00e1 jako odkaz na jin\u00fd web. Chcete doplnit povinn\u00fd prefix http:\/\/?", +"Link list": "Seznam odkaz\u016f", +"Insert video": "Vlo\u017eit video", +"Insert\/edit video": "Vlo\u017eit \/ upravit video", +"Insert\/edit media": "Vlo\u017eit \/ upravit m\u00e9dia", +"Alternative source": "Alternativn\u00ed zdroj", +"Poster": "N\u00e1hled", +"Paste your embed code below:": "Vlo\u017ete k\u00f3d pro vlo\u017een\u00ed n\u00ed\u017ee:", +"Embed": "Vlo\u017eit", +"Media": "M\u00e9dia", +"Nonbreaking space": "Pevn\u00e1 mezera", +"Page break": "Konec str\u00e1nky", +"Paste as text": "Vlo\u017eit jako \u010dist\u00fd text", +"Preview": "N\u00e1hled", +"Print": "Tisk", +"Save": "Ulo\u017eit", +"Find": "Naj\u00edt", +"Replace with": "Nahradit za", +"Replace": "Nahradit", +"Replace all": "Nahradit v\u0161e", +"Prev": "P\u0159edchoz\u00ed", +"Next": "Dal\u0161\u00ed", +"Find and replace": "Naj\u00edt a nahradit", +"Could not find the specified string.": "Zadan\u00fd \u0159et\u011bzec nebyl nalezen.", +"Match case": "Rozli\u0161ovat mal\u00e1 a velk\u00e1 p\u00edsmena", +"Whole words": "Pouze cel\u00e1 slova", +"Spellcheck": "Kontrola pravopisu", +"Ignore": "Ignorovat", +"Ignore all": "Ignorovat v\u0161e", +"Finish": "Ukon\u010dit", +"Add to Dictionary": "P\u0159idat do slovn\u00edku", +"Insert table": "Vlo\u017eit tabulku", +"Table properties": "Vlastnosti tabulky", +"Delete table": "Smazat tabulku", +"Cell": "Bu\u0148ka", +"Row": "\u0158\u00e1dek", +"Column": "Sloupec", +"Cell properties": "Vlastnosti bu\u0148ky", +"Merge cells": "Slou\u010dit bu\u0148ky", +"Split cell": "Rozd\u011blit bu\u0148ky", +"Insert row before": "Vlo\u017eit \u0159\u00e1dek nad", +"Insert row after": "Vlo\u017eit \u0159\u00e1dek pod", +"Delete row": "Smazat \u0159\u00e1dek", +"Row properties": "Vlastnosti \u0159\u00e1dku", +"Cut row": "Vyjmout \u0159\u00e1dek", +"Copy row": "Kop\u00edrovat \u0159\u00e1dek", +"Paste row before": "Vlo\u017eit \u0159\u00e1dek nad", +"Paste row after": "Vlo\u017eit \u0159\u00e1dek pod", +"Insert column before": "Vlo\u017eit sloupec vlevo", +"Insert column after": "Vlo\u017eit sloupec vpravo", +"Delete column": "Smazat sloupec", +"Cols": "Sloupc\u016f", +"Rows": "\u0158\u00e1dek", +"Width": "\u0160\u00ed\u0159ka", +"Height": "V\u00fd\u0161ka", +"Cell spacing": "Vn\u011bj\u0161\u00ed okraj bun\u011bk", +"Cell padding": "Vnit\u0159n\u00ed okraj bun\u011bk", +"Caption": "Nadpis", +"Left": "Vlevo", +"Center": "Na st\u0159ed", +"Right": "Vpravo", +"Cell type": "Typ bu\u0148ky", +"Scope": "Rozsah", +"Alignment": "Zarovn\u00e1n\u00ed", +"H Align": "Horizont\u00e1ln\u00ed zarovn\u00e1n\u00ed", +"V Align": "Vertik\u00e1ln\u00ed zarovn\u00e1n\u00ed", +"Top": "Nahoru", +"Middle": "Uprost\u0159ed", +"Bottom": "Dol\u016f", +"Header cell": "Hlavi\u010dkov\u00e1 bu\u0148ka", +"Row group": "Skupina \u0159\u00e1dk\u016f", +"Column group": "Skupina sloupc\u016f", +"Row type": "Typ \u0159\u00e1dku", +"Header": "Hlavi\u010dka", +"Body": "T\u011blo", +"Footer": "Pati\u010dka", +"Border color": "Barva r\u00e1me\u010dku", +"Insert template": "Vlo\u017eit \u0161ablonu", +"Templates": "\u0160ablony", +"Template": "\u0160ablona", +"Text color": "Barva p\u00edsma", +"Background color": "Barva pozad\u00ed", +"Custom...": "Vlastn\u00ed...", +"Custom color": "Vlastn\u00ed barva", +"No color": "Bez barvy", +"Table of Contents": "Obsah", +"Show blocks": "Uk\u00e1zat bloky", +"Show invisible characters": "Zobrazit speci\u00e1ln\u00ed znaky", +"Words: {0}": "Po\u010det slov: {0}", +"{0} words": "Po\u010det slov: {0}", +"File": "Soubor", +"Edit": "\u00dapravy", +"Insert": "Vlo\u017eit", +"View": "Zobrazit", +"Format": "Form\u00e1t", +"Table": "Tabulka", +"Tools": "N\u00e1stroje", +"Powered by {0}": "Vytvo\u0159il {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Editor. Stiskn\u011bte ALT-F9 pro menu, ALT-F10 pro n\u00e1strojovou li\u0161tu a ALT-0 pro n\u00e1pov\u011bdu." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs_CZ.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs_CZ.js new file mode 100644 index 0000000000..b5b47394c0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cs_CZ.js @@ -0,0 +1,260 @@ +tinymce.addI18n('cs_CZ',{ +"Redo": "Znovu", +"Undo": "Zp\u011bt", +"Cut": "Vyjmout", +"Copy": "Kop\u00edrovat", +"Paste": "Vlo\u017eit", +"Select all": "Vybrat v\u0161e", +"New document": "Nov\u00fd dokument", +"Ok": "Ok", +"Cancel": "Zru\u0161it", +"Visual aids": "Vizu\u00e1ln\u00ed pom\u016fcky", +"Bold": "Tu\u010dn\u011b", +"Italic": "Kurz\u00edva", +"Underline": "Podtr\u017een\u00e9", +"Strikethrough": "P\u0159e\u0161krtnut\u00e9", +"Superscript": "Horn\u00ed index", +"Subscript": "Doln\u00ed index", +"Clear formatting": "Vymazat form\u00e1tov\u00e1n\u00ed", +"Align left": "Vlevo", +"Align center": "Na st\u0159ed", +"Align right": "Vpravo", +"Justify": "Zarovnat do bloku", +"Bullet list": "Odr\u00e1\u017eky", +"Numbered list": "\u010c\u00edslov\u00e1n\u00ed", +"Decrease indent": "Zmen\u0161it odsazen\u00ed", +"Increase indent": "Zv\u011b\u0161it odsazen\u00ed", +"Close": "Zav\u0159\u00edt", +"Formats": "Form\u00e1ty", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "V\u00e1\u0161 prohl\u00ed\u017ee\u010d nepodporuje p\u0159\u00edm\u00fd p\u0159\u00edstup do schr\u00e1nky. Pou\u017eijte pros\u00edm kl\u00e1vesov\u00e9 zkratky Ctrl+X\/C\/V.", +"Headers": "Nadpisy", +"Header 1": "Nadpis 1", +"Header 2": "Nadpis 2", +"Header 3": "Nadpis 3", +"Header 4": "Nadpis 4", +"Header 5": "Nadpis 5", +"Header 6": "Nadpis 6", +"Headings": "Nadpisy", +"Heading 1": "Nadpis 1", +"Heading 2": "Nadpis 2", +"Heading 3": "Nadpis 3", +"Heading 4": "Nadpis 4", +"Heading 5": "Nadpis 5", +"Heading 6": "Nadpis 6", +"Div": "Div (blok)", +"Pre": "Pre (p\u0159edform\u00e1tov\u00e1no)", +"Code": "Code (k\u00f3d)", +"Paragraph": "Odstavec", +"Blockquote": "Citace", +"Inline": "\u0158\u00e1dkov\u00e9 zobrazen\u00ed (inline)", +"Blocks": "Blokov\u00e9 zobrazen\u00ed (block)", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Je zapnuto vkl\u00e1d\u00e1n\u00ed \u010dist\u00e9ho textu. Dokud nebude tato volba vypnuta, bude ve\u0161ker\u00fd obsah vlo\u017een jako \u010dist\u00fd text.", +"Font Family": "Rodina p\u00edsma", +"Font Sizes": "Velikost p\u00edsma", +"Class": "T\u0159\u00edda", +"Browse for an image": "Vybrat obr\u00e1zek", +"OR": "NEBO", +"Drop an image here": "P\u0159et\u00e1hn\u011bte obr\u00e1zek sem", +"Upload": "Nahr\u00e1t", +"Block": "Blok", +"Align": "Zarovnat", +"Default": "V\u00fdchoz\u00ed", +"Circle": "Kole\u010dko", +"Disc": "Punt\u00edk", +"Square": "\u010ctvere\u010dek", +"Lower Alpha": "Mal\u00e1 p\u00edsmena", +"Lower Greek": "\u0158eck\u00e1 p\u00edsmena", +"Lower Roman": "Mal\u00e9 \u0159\u00edmsl\u00e9 \u010d\u00edslice", +"Upper Alpha": "Velk\u00e1 p\u00edsmena", +"Upper Roman": "\u0158\u00edmsk\u00e9 \u010d\u00edslice", +"Anchor": "Kotva", +"Name": "N\u00e1zev", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID by m\u011blo za\u010d\u00ednat p\u00edsmenem, n\u00e1sledovan\u00fdm pouze p\u00edsmeny, \u010d\u00edsly, poml\u010dkami, te\u010dkami, \u010d\u00e1rkami a nebo podtr\u017e\u00edtky.", +"You have unsaved changes are you sure you want to navigate away?": "M\u00e1te neulo\u017een\u00e9 zm\u011bny. Opravdu chcete opustit str\u00e1nku?", +"Restore last draft": "Obnovit posledn\u00ed koncept.", +"Special character": "Speci\u00e1ln\u00ed znak", +"Source code": "Zdrojov\u00fd k\u00f3d", +"Insert\/Edit code sample": "Vlo\u017eit\/Upravit uk\u00e1zku k\u00f3du", +"Language": "Jazyk", +"Code sample": "Uk\u00e1zka k\u00f3du", +"Color": "Barva", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Zleva doprava", +"Right to left": "Zprava doleva", +"Emoticons": "Emotikony", +"Document properties": "Vlastnosti dokumentu", +"Title": "Titulek", +"Keywords": "Kl\u00ed\u010dov\u00e1 slova", +"Description": "Popis", +"Robots": "Roboti", +"Author": "Autor", +"Encoding": "K\u00f3dov\u00e1n\u00ed", +"Fullscreen": "Celk\u00e1 obrazovka", +"Action": "Akce", +"Shortcut": "Kl\u00e1vesov\u00e1 zkratka", +"Help": "N\u00e1pov\u011bda", +"Address": "Blok s po\u0161tovn\u00ed adresou", +"Focus to menubar": "P\u0159ej\u00edt do menu", +"Focus to toolbar": "P\u0159ej\u00edt na panel n\u00e1stroj\u016f", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "P\u0159ej\u00edt na kontextov\u00fd panel n\u00e1stroj\u016f", +"Insert link (if link plugin activated)": "Vlo\u017eit odkaz (pokud je aktivn\u00ed link plugin)", +"Save (if save plugin activated)": "Ulo\u017eit (pokud je aktivni save plugin)", +"Find (if searchreplace plugin activated)": "Hledat (pokud je aktivn\u00ed plugin searchreplace)", +"Plugins installed ({0}):": "Instalovan\u00e9 pluginy ({0}):", +"Premium plugins:": "Pr\u00e9miov\u00e9 pluginy:", +"Learn more...": "Zjistit v\u00edce...", +"You are using {0}": "Pou\u017e\u00edv\u00e1te {0}", +"Plugins": "Pluginy", +"Handy Shortcuts": "U\u017eite\u010dn\u00e9 kl\u00e1vesov\u00e9 zkratky", +"Horizontal line": "Vodorovn\u00e1 linka", +"Insert\/edit image": "Vlo\u017eit \/ upravit obr\u00e1zek", +"Image description": "Popis obr\u00e1zku", +"Source": "URL", +"Dimensions": "Rozm\u011bry", +"Constrain proportions": "Zachovat proporce", +"General": "Obecn\u00e9", +"Advanced": "Pokro\u010dil\u00e9", +"Style": "Styl", +"Vertical space": "Vertik\u00e1ln\u00ed mezera", +"Horizontal space": "Horizont\u00e1ln\u00ed mezera", +"Border": "R\u00e1me\u010dek", +"Insert image": "Vlo\u017eit obr\u00e1zek", +"Image": "Obr\u00e1zek", +"Image list": "Seznam obr\u00e1zk\u016f", +"Rotate counterclockwise": "Oto\u010dit doleva", +"Rotate clockwise": "Oto\u010dit doprava", +"Flip vertically": "P\u0159evr\u00e1tit svisle", +"Flip horizontally": "P\u0159evr\u00e1tit vodorovn\u011b", +"Edit image": "Upravit obr\u00e1zek", +"Image options": "Vlastnosti obr\u00e1zku", +"Zoom in": "P\u0159ibl\u00ed\u017eit", +"Zoom out": "Odd\u00e1lit", +"Crop": "O\u0159\u00edznout", +"Resize": "Zm\u011bnit velikost", +"Orientation": "Orientace", +"Brightness": "Jas", +"Sharpen": "Ostrost", +"Contrast": "Kontrast", +"Color levels": "\u00darovn\u011b barev", +"Gamma": "Gama", +"Invert": "Invertovat", +"Apply": "Pou\u017e\u00edt", +"Back": "Zp\u011bt", +"Insert date\/time": "Vlo\u017eit datum \/ \u010das", +"Date\/time": "Datum\/\u010das", +"Insert link": "Vlo\u017eit odkaz", +"Insert\/edit link": "Vlo\u017eit \/ upravit odkaz", +"Text to display": "Text odkazu", +"Url": "URL", +"Target": "C\u00edl", +"None": "\u017d\u00e1dn\u00fd", +"New window": "Nov\u00e9 okno", +"Remove link": "Odstranit odkaz", +"Anchors": "Kotvy", +"Link": "Odkaz", +"Paste or type a link": "Vlo\u017ete nebo napi\u0161te adresu odkazu", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Zadan\u00e9 URL vypad\u00e1 jako e-mailov\u00e1 adresa. Chcete doplnit povinn\u00fd prefix mailto:?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Zadan\u00e9 URL vypad\u00e1 jako odkaz na jin\u00fd web. Chcete doplnit povinn\u00fd prefix http:\/\/?", +"Link list": "Seznam odkaz\u016f", +"Insert video": "Vlo\u017eit video", +"Insert\/edit video": "Vlo\u017eit \/ upravit video", +"Insert\/edit media": "Vlo\u017eit\/upravit m\u00e9dia", +"Alternative source": "Alternativn\u00ed zdroj", +"Poster": "Poster", +"Paste your embed code below:": "Vlo\u017ete k\u00f3d pro vlo\u017een\u00ed", +"Embed": "Vlo\u017een\u00fd", +"Media": "M\u00e9dia", +"Nonbreaking space": "Pevn\u00e1 mezera", +"Page break": "Konec str\u00e1nky", +"Paste as text": "Vlo\u017eit jako \u010dist\u00fd text", +"Preview": "N\u00e1hled", +"Print": "Tisk", +"Save": "Ulo\u017eit", +"Find": "Naj\u00edt", +"Replace with": "Nahradit za", +"Replace": "Nahradit", +"Replace all": "Nahradit v\u0161e", +"Prev": "P\u0159edchoz\u00ed", +"Next": "Dal\u0161\u00ed", +"Find and replace": "Naj\u00edt a nahradit", +"Could not find the specified string.": "Zadan\u00fd \u0159et\u011bzec nebyl nalezen.", +"Match case": "Rozli\u0161ovat mal\u00e1 a velk\u00e1 p\u00edsmena", +"Whole words": "Pouze cel\u00e1 slova", +"Spellcheck": "Kontrola pravopisu", +"Ignore": "Ignorovat", +"Ignore all": "Ignorovat v\u0161e", +"Finish": "Dokon\u010dit", +"Add to Dictionary": "P\u0159idat do slovn\u00edku", +"Insert table": "Vlo\u017eit tabulku", +"Table properties": "Vlastnosti tabulky", +"Delete table": "Smazat tabulku", +"Cell": "Bu\u0148ka", +"Row": "\u0158\u00e1dek", +"Column": "Sloupec", +"Cell properties": "Vlastnosti bu\u0148ky", +"Merge cells": "Slou\u010dit bu\u0148ky", +"Split cell": "Rozd\u011blit bu\u0148ku", +"Insert row before": "Vlo\u017eit \u0159\u00e1dek p\u0159ed", +"Insert row after": "Vlo\u017eit \u0159\u00e1dek za", +"Delete row": "Smazat \u0159\u00e1dek", +"Row properties": "Vlastnosti \u0159\u00e1dku", +"Cut row": "Vyjmout \u0159\u00e1dek", +"Copy row": "Kop\u00edrovat \u0159\u00e1dek", +"Paste row before": "Vlo\u017eit \u0159\u00e1dek nad", +"Paste row after": "Vlo\u017eit \u0159\u00e1dek pod", +"Insert column before": "Vlo\u017eit sloupec vlevo", +"Insert column after": "Vlo\u017eit sloupec vpravo", +"Delete column": "Smazat sloupec", +"Cols": "Sloupce", +"Rows": "\u0158\u00e1dky", +"Width": "\u0160\u00ed\u0159ka", +"Height": "V\u00fd\u0161ka", +"Cell spacing": "Vn\u011bj\u0161\u00ed okraj bun\u011bk", +"Cell padding": "Vnit\u0159n\u00ed okraj bun\u011bk", +"Caption": "Titulek", +"Left": "Vlevo", +"Center": "Na st\u0159ed", +"Right": "Vpravo", +"Cell type": "Typ bu\u0148ky", +"Scope": "Rozsah", +"Alignment": "Zarovn\u00e1n\u00ed", +"H Align": "Horizont\u00e1ln\u00ed zarovn\u00e1n\u00ed", +"V Align": "Vertik\u00e1ln\u00ed zarovn\u00e1n\u00ed", +"Top": "Nahoru", +"Middle": "Na st\u0159ed", +"Bottom": "Dol\u016f", +"Header cell": "Hlavi\u010dkov\u00e1 bu\u0148ka", +"Row group": "Skupina \u0159\u00e1dk\u016f", +"Column group": "Skupina sloupc\u016f", +"Row type": "Typ \u0159\u00e1dku", +"Header": "Hlavi\u010dka", +"Body": "T\u011blo", +"Footer": "Pati\u010dka", +"Border color": "Barva r\u00e1me\u010dku", +"Insert template": "Vlo\u017eit ze \u0161ablony", +"Templates": "\u0160ablony", +"Template": "\u0160ablona", +"Text color": "Barva p\u00edsma", +"Background color": "Barva pozad\u00ed", +"Custom...": "Vlastn\u00ed", +"Custom color": "Vlastn\u00ed barva", +"No color": "Bez barvy", +"Table of Contents": "Generovat obsah", +"Show blocks": "Uk\u00e1zat bloky", +"Show invisible characters": "Uk\u00e1zat skryt\u00e9 znaky", +"Words: {0}": "Slova: {0}", +"{0} words": "{0} slov", +"File": "Soubor", +"Edit": "\u00dapravy", +"Insert": "Vlo\u017eit", +"View": "Zobrazit", +"Format": "Form\u00e1t", +"Table": "Tabulka", +"Tools": "N\u00e1stroje", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "RTF dokument. Stikn\u011bte ALT-F9 pro zobrazen\u00ed menu, ALT-F10 pro zobrazen\u00ed n\u00e1strojov\u00e9 li\u0161ty, ALT-0 pro n\u00e1pov\u011bdu." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js new file mode 100644 index 0000000000..d2fdd077f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js @@ -0,0 +1,230 @@ +tinymce.addI18n('cy',{ +"Cut": "Torri", +"Heading 5": "Pennawd 5", +"Header 2": "Pennawd 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Dyw eich porwr ddim yn cynnal mynediad uniongyrchol i'r clipfwrdd. Yn hytrach defnyddiwch y bysellau llwybrau byr Ctrl+X\/C\/V.", +"Heading 4": "Pennawd 4", +"Div": "Div", +"Heading 2": "Pennawd 2", +"Paste": "Gludo", +"Close": "Cau", +"Font Family": "Teulu Ffont", +"Pre": "Pre", +"Align right": "Aliniad de", +"New document": "Dogfen newydd", +"Blockquote": "Dyfyniad bloc", +"Numbered list": "Rhestr rifol", +"Heading 1": "Pennawd 1", +"Headings": "Penawdau", +"Increase indent": "Cynyddu mewnoliad", +"Formats": "Fformatau", +"Headers": "Penawdau", +"Select all": "Dewis popeth", +"Header 3": "Pennawd 3", +"Blocks": "Blociau", +"Undo": "Dadwneud", +"Strikethrough": "Llinell drwodd", +"Bullet list": "Rhestr fwled", +"Header 1": "Pennawd 1", +"Superscript": "Uwchsgript", +"Clear formatting": "Clirio pob fformat", +"Font Sizes": "Meintiau Ffont", +"Subscript": "Is-sgript", +"Header 6": "Pennawd 6", +"Redo": "Ailwneud", +"Paragraph": "Paragraff", +"Ok": "Iawn", +"Bold": "Bras", +"Code": "Cod", +"Italic": "Italig", +"Align center": "Aliniad canol", +"Header 5": "Pennawd 5", +"Heading 6": "Pennawd 6", +"Heading 3": "Pennawd 3", +"Decrease indent": "Lleihau mewnoliad", +"Header 4": "Pennawd 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Mae gludo nawr yn gweithio yn y modd testun plaen. Caiff testun plaen ei ludo nawr tan gaiff yr opsiwn ei doglo i'w ddiffodd.", +"Underline": "Tanlinellu", +"Cancel": "Canslo", +"Justify": "Unioni", +"Inline": "Mewnlin", +"Copy": "Cop\u00efo", +"Align left": "Aliniad chwith", +"Visual aids": "Cymorth gweledol", +"Lower Greek": "Groeg Is", +"Square": "Sgw\u00e2r", +"Default": "Diofyn", +"Lower Alpha": "Alffa Is", +"Circle": "Cylch", +"Disc": "Disg", +"Upper Alpha": "Alffa Uwch", +"Upper Roman": "Rhufeinig Uwch", +"Lower Roman": "Rhufeinig Is", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Dylai Id gychwyn gyda llythyren ac yna dim ond llythrennau, rhifau, llinellau toriad,dotiau, colonau neu danlinellau.", +"Name": "Enw", +"Anchor": "Angor", +"Id": "Id", +"You have unsaved changes are you sure you want to navigate away?": "Mae newidiadau heb eu cadw - ydych chi wir am symud i ffwrdd?", +"Restore last draft": "Adfer y drafft olaf", +"Special character": "Nod arbennig", +"Source code": "Cod gwreiddiol", +"Language": "Iaith", +"Insert\/Edit code sample": "Mewnosod\/golygu sampl cod", +"B": "Gl", +"R": "C", +"G": "Gw", +"Color": "Lliw", +"Right to left": "De i'r chwith", +"Left to right": "Chwith i'r dde", +"Emoticons": "Gwenogluniau", +"Robots": "Robotiaid", +"Document properties": "Priodweddau'r ddogfen", +"Title": "Teitl", +"Keywords": "Allweddeiriau", +"Encoding": "Amgodiad", +"Description": "Disgrifiad", +"Author": "Awdur", +"Fullscreen": "Sgrin llawn", +"Horizontal line": "Llinell lorweddol", +"Horizontal space": "Gofod llorweddol", +"Insert\/edit image": "Mewnosod\/golygu delwedd", +"General": "Cyffredinol", +"Advanced": "Uwch", +"Source": "Ffynhonnell", +"Border": "Border", +"Constrain proportions": "Gorfodi cyfrannedd", +"Vertical space": "Gofod fertigol", +"Image description": "Disgrifiad y ddelwedd", +"Style": "Arddull", +"Dimensions": "Dimensiynau", +"Insert image": "Mewnosod delwedd", +"Image": "Delwedd", +"Zoom in": "Chwyddo mewn", +"Contrast": "Cyferbynnedd", +"Back": "Nol", +"Gamma": "Gamma", +"Flip horizontally": "Fflipio llorweddol", +"Resize": "Ailfeintio", +"Sharpen": "Hogi", +"Zoom out": "Chwyddo allan", +"Image options": "Dewisiadau delwedd", +"Apply": "Rhoi ar waith", +"Brightness": "Disgleirdeb", +"Rotate clockwise": "Troi clocwedd", +"Rotate counterclockwise": "Troi gwrthgloc", +"Edit image": "Golygu delwedd", +"Color levels": "Lefelau Lliw", +"Crop": "Tocio", +"Orientation": "Cyfeiriadaeth", +"Flip vertically": "Fflipio fertigol", +"Invert": "Gwrthdroi", +"Date\/time": "Dyddiad\/amser", +"Insert date\/time": "Mewnosod dyddiad\/amser", +"Remove link": "Tynnu dolen", +"Url": "Url", +"Text to display": "Testun i'w ddangos", +"Anchors": "Angorau", +"Insert link": "Mewnosod dolen", +"Link": "Dolen", +"New window": "Ffenest newydd", +"None": "Dim", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Mae'n debyg mai dolen allanol yw'r URL hwn. Ydych chi am ychwanegu'r rhagddodiad http:\/\/ ?", +"Paste or type a link": "Pastio neu deipio dolen", +"Target": "Targed", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Mae'n debyg mai cyfeiriad e-bost yw'r URL hwn. Ydych chi am ychwanegu'r rhagddoddiad mailto:?", +"Insert\/edit link": "Mewnosod\/golygu dolen", +"Insert\/edit video": "Mewnosod\/golygu fideo", +"Media": "Cyfrwng", +"Alternative source": "Ffynhonnell amgen", +"Paste your embed code below:": "Gludwch eich cod mewnosod isod:", +"Insert video": "Mewnosod fideo", +"Poster": "Poster", +"Insert\/edit media": "Mewnosod\/golygu cyfrwng", +"Embed": "Mewnosod", +"Nonbreaking space": "Bwlch heb dorri", +"Page break": "Toriad tudalen", +"Paste as text": "Gludo fel testun", +"Preview": "Rhagolwg", +"Print": "Argraffu", +"Save": "Cadw", +"Could not find the specified string.": "Methu ffeindio'r llinyn hwnnw.", +"Replace": "Amnewid", +"Next": "Nesaf", +"Whole words": "Geiriau cyfan", +"Find and replace": "Chwilio ac amnewid", +"Replace with": "Amnewid gyda", +"Find": "Chwilio", +"Replace all": "Amnewid y cwbl", +"Match case": "Cas yn cyfateb", +"Prev": "Blaenorol", +"Spellcheck": "Sillafydd", +"Finish": "Gorffen", +"Ignore all": "Amwybyddu pob", +"Ignore": "Anwybyddu", +"Add to Dictionary": "Adio i'r Geiriadur", +"Insert row before": "Mewnosod rhes cyn", +"Rows": "Rhesi", +"Height": "Uchder", +"Paste row after": "Gludo rhes ar \u00f4l", +"Alignment": "Aliniad", +"Border color": "Lliw Border", +"Column group": "Gr\u0175p colofn", +"Row": "Rhes", +"Insert column before": "Mewnosod colofn cyn", +"Split cell": "Hollti celloedd", +"Cell padding": "Padio celloedd", +"Cell spacing": "Bylchiad celloedd", +"Row type": "Math y rhes", +"Insert table": "Mewnosod tabl", +"Body": "Corff", +"Caption": "Pennawd", +"Footer": "Troedyn", +"Delete row": "Dileu rhes", +"Paste row before": "Gludo rhes cyn", +"Scope": "Cwmpas", +"Delete table": "Dileu'r tabl", +"H Align": "Aliniad Ll", +"Top": "Brig", +"Header cell": "Cell bennawd", +"Column": "Colofn", +"Row group": "Gr\u0175p rhes", +"Cell": "Cell", +"Middle": "Canol", +"Cell type": "Math y gell", +"Copy row": "Cop\u00efo rhes", +"Row properties": "Priodweddau rhes", +"Table properties": "Priodweddau tabl", +"Bottom": "Gwaelod", +"V Align": "Aliniad F", +"Header": "Pennyn", +"Right": "De", +"Insert column after": "Mewnosod colofn ar \u00f4l", +"Cols": "Colofnau", +"Insert row after": "Mewnosod rhes ar \u00f4l", +"Width": "Lled", +"Cell properties": "Priodweddau'r gell", +"Left": "Chwith", +"Cut row": "Torri rhes", +"Delete column": "Dileu colofn", +"Center": "Canol", +"Merge cells": "Cyfuno celloedd", +"Insert template": "Mewnosod templed", +"Templates": "Templedi", +"Background color": "Lliw cefndir", +"Custom...": "Personol...", +"Custom color": "Lliw personol", +"No color": "Dim Lliw", +"Text color": "Lliw testun", +"Table of Contents": "Tabl Cynnwys", +"Show blocks": "Dangos blociau", +"Show invisible characters": "Dangos nodau anweledig", +"Words: {0}": "Geiriau: {0}", +"Insert": "Mewnosod", +"File": "Ffeil", +"Edit": "Golygu", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Ardal Testun Uwch. Pwyswch ALT-F9 ar gyfer y ddewislen, Pwyswch ALT-F10 ar gyfer y bar offer. Pwyswch ALT-0 am gymorth", +"Tools": "Offer", +"View": "Dangos", +"Table": "Tabl", +"Format": "Fformat" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js index d295bf5750..90e4009d92 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js @@ -1,219 +1,261 @@ tinymce.addI18n('da',{ -"Cut": "Klip", -"Heading 5": "Overskrift 5", -"Header 2": "Overskrift 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Din browser underst\u00f8tter ikke direkte adgang til clipboard. Benyt Ctrl+X\/C\/ keybord shortcuts i stedet for.", -"Heading 4": "Overskrift 4", -"Div": "Div", -"Heading 2": "Overskrift 2", -"Paste": "Inds\u00e6t", -"Close": "Luk", -"Font Family": "Skrifttype", -"Pre": "Pre", -"Align right": "H\u00f8jrejusteret", -"New document": "Nyt dokument", -"Blockquote": "Indrykning", -"Numbered list": "Nummerering", -"Heading 1": "Overskrift 1", -"Headings": "Overskrifter", -"Increase indent": "For\u00f8g indrykning", -"Formats": "Formater", -"Headers": "Overskrifter", -"Select all": "V\u00e6lg alle", -"Header 3": "Overskrift 3", -"Blocks": "Blokke", -"Undo": "Fortryd", -"Strikethrough": "Gennemstreg", -"Bullet list": "Punkt tegn", -"Header 1": "Overskrift 1", -"Superscript": "H\u00e6vet", -"Clear formatting": "Nulstil formattering", -"Font Sizes": "Skriftst\u00f8rrelse", -"Subscript": "S\u00e6nket", -"Header 6": "Overskrift 6", "Redo": "Genopret", -"Paragraph": "S\u00e6tning", -"Ok": "Ok", -"Bold": "Fed", -"Code": "Code", -"Italic": "Kursiv", -"Align center": "Centreret", -"Header 5": "Overskrift 5", -"Heading 6": "Overskrift 6", -"Heading 3": "Overskrift 3", -"Decrease indent": "Formindsk indrykning", -"Header 4": "Overskrift 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "S\u00e6t ind er indstillet til at inds\u00e6tte som ren tekst. Indhold bliver nu indsat uden formatering indtil du \u00e6ndrer indstillingen.", -"Underline": "Understreg", -"Cancel": "Fortryd", -"Justify": "Justering", -"Inline": "Inline", +"Undo": "Fortryd", +"Cut": "Klip", "Copy": "Kopier", -"Align left": "Venstrejusteret", +"Paste": "Inds\u00e6t", +"Select all": "V\u00e6lg alle", +"New document": "Nyt dokument", +"Ok": "Ok", +"Cancel": "Fortryd", "Visual aids": "Visuel hj\u00e6lp", -"Lower Greek": "Lower Gr\u00e6sk", -"Square": "Kvadrat", +"Bold": "Fed", +"Italic": "Kursiv", +"Underline": "Understreg", +"Strikethrough": "Gennemstreg", +"Superscript": "H\u00e6vet", +"Subscript": "S\u00e6nket", +"Clear formatting": "Nulstil formattering", +"Align left": "Venstrejusteret", +"Align center": "Centreret", +"Align right": "H\u00f8jrejusteret", +"Justify": "Justering", +"Bullet list": "Punkt tegn", +"Numbered list": "Nummerering", +"Decrease indent": "Formindsk indrykning", +"Increase indent": "For\u00f8g indrykning", +"Close": "Luk", +"Formats": "Formater", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Din browser underst\u00f8tter ikke direkte adgang til clipboard. Benyt Ctrl+X\/C\/ keybord shortcuts i stedet for.", +"Headers": "Overskrifter", +"Header 1": "Overskrift 1", +"Header 2": "Overskrift 2", +"Header 3": "Overskrift 3", +"Header 4": "Overskrift 4", +"Header 5": "Overskrift 5", +"Header 6": "Overskrift 6", +"Headings": "Overskrifter", +"Heading 1": "Overskrift 1", +"Heading 2": "Overskrift 2", +"Heading 3": "Overskrift 3", +"Heading 4": "Overskrift 4", +"Heading 5": "Overskrift 5", +"Heading 6": "Overskrift 6", +"Preformatted": "Pr\u00e6formateret", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "S\u00e6tning", +"Blockquote": "Indrykning", +"Inline": "Inline", +"Blocks": "Blokke", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "S\u00e6t ind er indstillet til at inds\u00e6tte som ren tekst. Indhold bliver nu indsat uden formatering indtil du \u00e6ndrer indstillingen.", +"Font Family": "Skrifttype", +"Font Sizes": "Skriftst\u00f8rrelse", +"Class": "Klasse", +"Browse for an image": "S\u00f8g efter et billede", +"OR": "ELLER", +"Drop an image here": "Slip et billede her", +"Upload": "Opload", +"Block": "Blok", +"Align": "Tilpas", "Default": "Standard", -"Lower Alpha": "Lower Alpha", "Circle": "Cirkel", "Disc": "Disk", +"Square": "Kvadrat", +"Lower Alpha": "Lower Alpha", +"Lower Greek": "Lower Gr\u00e6sk", +"Lower Roman": "Lower Roman", "Upper Alpha": "Upper Alpha", "Upper Roman": "Upper Roman", -"Lower Roman": "Lower Roman", -"Name": "Navn", "Anchor": "Anchor", +"Name": "Navn", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id b\u00f8r starte med et bogstav, efterfulgt af bogstaver, tal, bindestreger, punktummer, koloner eller underscores.", "You have unsaved changes are you sure you want to navigate away?": "Du har ikke gemte \u00e6ndringer. Er du sikker p\u00e5 at du vil forts\u00e6tte?", "Restore last draft": "Genopret sidste kladde", "Special character": "Specielle tegn", "Source code": "Kildekode", -"B": "B", +"Insert\/Edit code sample": "Inds\u00e6t\/Ret kodeeksempel", +"Language": "Sprog", +"Code sample": "Kodepr\u00f8ve", +"Color": "Farve", "R": "R", "G": "G", -"Color": "Farve", -"Right to left": "H\u00f8jre til venstre", +"B": "B", "Left to right": "Venstre til h\u00f8jre", +"Right to left": "H\u00f8jre til venstre", "Emoticons": "Emot-ikoner", -"Robots": "Robotter", "Document properties": "Dokument egenskaber", "Title": "Titel", "Keywords": "S\u00f8geord", -"Encoding": "Kodning", "Description": "Beskrivelse", +"Robots": "Robotter", "Author": "Forfatter", +"Encoding": "Kodning", "Fullscreen": "Fuldsk\u00e6rm", +"Action": "Handling", +"Shortcut": "Genvej", +"Help": "Hj\u00e6lp", +"Address": "Adresse", +"Focus to menubar": "Fokus p\u00e5 menulinjen", +"Focus to toolbar": "Fokus p\u00e5 v\u00e6rkt\u00f8jslinjen", +"Focus to element path": "Fokuser p\u00e5 elementvej", +"Focus to contextual toolbar": "Fokuser p\u00e5 kontekstuelle v\u00e6rkt\u00f8jslinje", +"Insert link (if link plugin activated)": "Inds\u00e6t link (hvis link plugin er aktiveret)", +"Save (if save plugin activated)": "Gem (hvis save plugin er aktiveret)", +"Find (if searchreplace plugin activated)": "Find (hvis searchreplace plugin er aktiveret)", +"Plugins installed ({0}):": "Installerede plugins ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "L\u00e6r mere...", +"You are using {0}": "Du benytter {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Praktiske Genveje", "Horizontal line": "Vandret linie", -"Horizontal space": "Vandret afstand", "Insert\/edit image": "Inds\u00e6t\/ret billede", +"Image description": "Billede beskrivelse", +"Source": "Kilde", +"Dimensions": "Dimensioner", +"Constrain proportions": "Behold propertioner", "General": "Generet", "Advanced": "Avanceret", -"Source": "Kilde", -"Border": "Kant", -"Constrain proportions": "Behold propertioner", -"Vertical space": "Lodret afstand", -"Image description": "Billede beskrivelse", "Style": "Stil", -"Dimensions": "Dimensioner", +"Vertical space": "Lodret afstand", +"Horizontal space": "Vandret afstand", +"Border": "Kant", "Insert image": "Inds\u00e6t billede", -"Zoom in": "Zoom ind", -"Contrast": "Kontrast", -"Back": "Tilbage", -"Gamma": "Gamma", -"Flip horizontally": "Flip horisontalt", -"Resize": "Skaler", -"Sharpen": "G\u00f8r skarpere", -"Zoom out": "Zoom ud", -"Image options": "Billede indstillinger", -"Apply": "Anvend", -"Brightness": "Lysstyrke", -"Rotate clockwise": "Drej med urets retning", +"Image": "Billede", +"Image list": "Billede liste", "Rotate counterclockwise": "Drej modsat urets retning", -"Edit image": "Rediger billede", -"Color levels": "Farve niveauer", -"Crop": "Besk\u00e6r", -"Orientation": "Retning", +"Rotate clockwise": "Drej med urets retning", "Flip vertically": "Flip vertikalt", +"Flip horizontally": "Flip horisontalt", +"Edit image": "Rediger billede", +"Image options": "Billede indstillinger", +"Zoom in": "Zoom ind", +"Zoom out": "Zoom ud", +"Crop": "Besk\u00e6r", +"Resize": "Skaler", +"Orientation": "Retning", +"Brightness": "Lysstyrke", +"Sharpen": "G\u00f8r skarpere", +"Contrast": "Kontrast", +"Color levels": "Farve niveauer", +"Gamma": "Gamma", "Invert": "Inverter", +"Apply": "Anvend", +"Back": "Tilbage", "Insert date\/time": "Inds\u00e6t dato\/klokkeslet", -"Remove link": "Fjern link", -"Url": "Url", -"Text to display": "Vis tekst", -"Anchors": "Ankre", +"Date\/time": "Dato\/klokkeslet", "Insert link": "Inds\u00e6t link", -"New window": "Nyt vindue", -"None": "Ingen", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URLen som du angav ser ud til at v\u00e6re et eksternt link. \u00d8nsker du at tilf\u00f8je det kr\u00e6vede prefiks http:\/\/ ?", -"Target": "Target", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URLen som du angav ser ud til at v\u00e6re en email adresse. \u00d8nsker du at tilf\u00f8je det kr\u00e6vede prefiks mailto: ?", "Insert\/edit link": "Inds\u00e6t\/ret link", -"Insert\/edit video": "Inds\u00e6t\/ret video", -"Poster": "Poster", -"Alternative source": "Alternativ kilde", -"Paste your embed code below:": "Inds\u00e6t din embed kode herunder:", +"Text to display": "Vis tekst", +"Url": "Url", +"Target": "Target", +"None": "Ingen", +"New window": "Nyt vindue", +"Remove link": "Fjern link", +"Anchors": "Ankre", +"Link": "Link", +"Paste or type a link": "Inds\u00e6t eller skriv et link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URLen som du angav ser ud til at v\u00e6re en email adresse. \u00d8nsker du at tilf\u00f8je det kr\u00e6vede prefiks mailto: ?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URLen som du angav ser ud til at v\u00e6re et eksternt link. \u00d8nsker du at tilf\u00f8je det kr\u00e6vede prefiks http:\/\/ ?", +"Link list": "Link liste", "Insert video": "Inds\u00e6t video", +"Insert\/edit video": "Inds\u00e6t\/ret video", +"Insert\/edit media": "Inds\u00e6t\/ret medier", +"Alternative source": "Alternativ kilde", +"Poster": "Poster", +"Paste your embed code below:": "Inds\u00e6t din embed kode herunder:", "Embed": "Integrer", +"Media": "Medier", "Nonbreaking space": "H\u00e5rdt mellemrum", "Page break": "Sideskift", "Paste as text": "Inds\u00e6t som ren tekst", "Preview": "Forh\u00e5ndsvisning", "Print": "Udskriv", "Save": "Gem", -"Could not find the specified string.": "Kunne ikke finde s\u00f8getekst", -"Replace": "Erstat", -"Next": "N\u00e6ste", -"Whole words": "Hele ord", -"Find and replace": "Find og erstat", -"Replace with": "Erstat med", "Find": "Find", +"Replace with": "Erstat med", +"Replace": "Erstat", "Replace all": "Erstat alt", -"Match case": "STORE og sm\u00e5 bogstaver", "Prev": "Forrige", +"Next": "N\u00e6ste", +"Find and replace": "Find og erstat", +"Could not find the specified string.": "Kunne ikke finde s\u00f8getekst", +"Match case": "STORE og sm\u00e5 bogstaver", +"Whole words": "Hele ord", "Spellcheck": "Stavekontrol", -"Finish": "F\u00e6rdig", -"Ignore all": "Ignorer alt", "Ignore": "Ignorer", +"Ignore all": "Ignorer alt", +"Finish": "F\u00e6rdig", "Add to Dictionary": "Tilf\u00f8j til ordbog", -"Insert row before": "Inds\u00e6t r\u00e6kke f\u00f8r", -"Rows": "R\u00e6kker", -"Height": "H\u00f8jde", -"Paste row after": "Inds\u00e6t r\u00e6kke efter", -"Alignment": "Tilpasning", -"Border color": "Kant farve", -"Column group": "Kolonne gruppe", -"Row": "R\u00e6kke", -"Insert column before": "Inds\u00e6t kolonne f\u00f8r", -"Split cell": "Split celle", -"Cell padding": "Celle padding", -"Cell spacing": "Celle afstand", -"Row type": "R\u00e6kke type", "Insert table": "Inds\u00e6t tabel", -"Body": "Krop", -"Caption": "Tekst", -"Footer": "Sidefod", -"Delete row": "Slet r\u00e6kke", -"Paste row before": "Inds\u00e6t r\u00e6kke f\u00f8r", -"Scope": "Anvendelsesomr\u00e5de", -"Delete table": "Slet tabel", -"H Align": "H juster", -"Top": "Top", -"Header cell": "Sidehoved celle", -"Column": "Kolonne", -"Row group": "R\u00e6kke gruppe", -"Cell": "Celle", -"Middle": "Midt", -"Cell type": "Celle type", -"Copy row": "Kopier r\u00e6kke", -"Row properties": "R\u00e6kke egenskaber", "Table properties": "Tabel egenskaber", -"Bottom": "Bund", -"V Align": "V juster", -"Header": "Sidehoved", -"Right": "H\u00f8jre", -"Insert column after": "Inds\u00e6t kolonne efter", -"Cols": "Kolonne", -"Insert row after": "Inds\u00e6t r\u00e6kke efter", -"Width": "Bredde", +"Delete table": "Slet tabel", +"Cell": "Celle", +"Row": "R\u00e6kke", +"Column": "Kolonne", "Cell properties": "Celle egenskaber", -"Left": "Venstre", -"Cut row": "Klip r\u00e6kke", -"Delete column": "Slet kolonne", -"Center": "Centrering", "Merge cells": "Flet celler", +"Split cell": "Split celle", +"Insert row before": "Inds\u00e6t r\u00e6kke f\u00f8r", +"Insert row after": "Inds\u00e6t r\u00e6kke efter", +"Delete row": "Slet r\u00e6kke", +"Row properties": "R\u00e6kke egenskaber", +"Cut row": "Klip r\u00e6kke", +"Copy row": "Kopier r\u00e6kke", +"Paste row before": "Inds\u00e6t r\u00e6kke f\u00f8r", +"Paste row after": "Inds\u00e6t r\u00e6kke efter", +"Insert column before": "Inds\u00e6t kolonne f\u00f8r", +"Insert column after": "Inds\u00e6t kolonne efter", +"Delete column": "Slet kolonne", +"Cols": "Kolonne", +"Rows": "R\u00e6kker", +"Width": "Bredde", +"Height": "H\u00f8jde", +"Cell spacing": "Celle afstand", +"Cell padding": "Celle padding", +"Caption": "Tekst", +"Left": "Venstre", +"Center": "Centrering", +"Right": "H\u00f8jre", +"Cell type": "Celle type", +"Scope": "Anvendelsesomr\u00e5de", +"Alignment": "Tilpasning", +"H Align": "H juster", +"V Align": "V juster", +"Top": "Top", +"Middle": "Midt", +"Bottom": "Bund", +"Header cell": "Sidehoved celle", +"Row group": "R\u00e6kke gruppe", +"Column group": "Kolonne gruppe", +"Row type": "R\u00e6kke type", +"Header": "Sidehoved", +"Body": "Krop", +"Footer": "Sidefod", +"Border color": "Kant farve", "Insert template": "Inds\u00e6t skabelon", "Templates": "Skabeloner", +"Template": "Skabelon", +"Text color": "Tekst farve", "Background color": "Baggrunds farve", "Custom...": "Brugerdefineret...", "Custom color": "Brugerdefineret farve", "No color": "Ingen farve", -"Text color": "Tekst farve", +"Table of Contents": "Indholdsfortegnelse", "Show blocks": "Vis klokke", "Show invisible characters": "Vis usynlige tegn", "Words: {0}": "Ord: {0}", -"Insert": "Inds\u00e6t", +"{0} words": "{0} ord", "File": "Fil", "Edit": "Rediger", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text omr\u00e5de. Tryk ALT-F9 for menu. Tryk ALT-F10 for toolbar. Tryk ALT-0 for hj\u00e6lp", -"Tools": "V\u00e6rkt\u00f8j", +"Insert": "Inds\u00e6t", "View": "Vis", +"Format": "Format", "Table": "Tabel", -"Format": "Format" +"Tools": "V\u00e6rkt\u00f8j", +"Powered by {0}": "Drevet af {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text omr\u00e5de. Tryk ALT-F9 for menu. Tryk ALT-F10 for toolbar. Tryk ALT-0 for hj\u00e6lp" }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de.js index 9a31056850..32a45747bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de.js @@ -1,219 +1,261 @@ tinymce.addI18n('de',{ -"Cut": "Ausschneiden", -"Heading 5": "\u00dcberschrift 5", -"Header 2": "\u00dcberschrift 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Ihr Browser unterst\u00fctzt leider keinen direkten Zugriff auf die Zwischenablage. Bitte benutzen Sie die Strg + X \/ C \/ V Tastenkombinationen.", -"Heading 4": "\u00dcberschrift 4", -"Div": "Textblock", -"Heading 2": "\u00dcberschrift 2", -"Paste": "Einf\u00fcgen", -"Close": "Schlie\u00dfen", -"Font Family": "Schriftart", -"Pre": "Vorformatierter Text", -"Align right": "Rechtsb\u00fcndig ausrichten", -"New document": "Neues Dokument", -"Blockquote": "Zitat", -"Numbered list": "Nummerierte Liste", -"Heading 1": "\u00dcberschrift 1", -"Headings": "\u00dcberschriften", -"Increase indent": "Einzug vergr\u00f6\u00dfern", -"Formats": "Formate", -"Headers": "\u00dcberschriften", -"Select all": "Alles ausw\u00e4hlen", -"Header 3": "\u00dcberschrift 3", -"Blocks": "Absatzformate", -"Undo": "R\u00fcckg\u00e4ngig", -"Strikethrough": "Durchgestrichen", -"Bullet list": "Aufz\u00e4hlung", -"Header 1": "\u00dcberschrift 1", -"Superscript": "Hochgestellt", -"Clear formatting": "Formatierung entfernen", -"Font Sizes": "Schriftgr\u00f6\u00dfe", -"Subscript": "Tiefgestellt", -"Header 6": "\u00dcberschrift 6", "Redo": "Wiederholen", -"Paragraph": "Absatz", -"Ok": "Ok", -"Bold": "Fett", -"Code": "Quelltext", -"Italic": "Kursiv", -"Align center": "Zentriert ausrichten", -"Header 5": "\u00dcberschrift 5", -"Heading 6": "\u00dcberschrift 6", -"Heading 3": "\u00dcberschrift 3", -"Decrease indent": "Einzug verkleinern", -"Header 4": "\u00dcberschrift 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Einf\u00fcgen ist nun im einfachen Textmodus. Inhalte werden ab jetzt als unformatierter Text eingef\u00fcgt, bis Sie diese Einstellung wieder ausschalten!", -"Underline": "Unterstrichen", -"Cancel": "Abbrechen", -"Justify": "Blocksatz", -"Inline": "Zeichenformate", +"Undo": "R\u00fcckg\u00e4ngig", +"Cut": "Ausschneiden", "Copy": "Kopieren", -"Align left": "Linksb\u00fcndig ausrichten", +"Paste": "Einf\u00fcgen", +"Select all": "Alles ausw\u00e4hlen", +"New document": "Neues Dokument", +"Ok": "Ok", +"Cancel": "Abbrechen", "Visual aids": "Visuelle Hilfen", -"Lower Greek": "Griechische Kleinbuchstaben", -"Square": "Quadrat", +"Bold": "Fett", +"Italic": "Kursiv", +"Underline": "Unterstrichen", +"Strikethrough": "Durchgestrichen", +"Superscript": "Hochgestellt", +"Subscript": "Tiefgestellt", +"Clear formatting": "Formatierung entfernen", +"Align left": "Linksb\u00fcndig ausrichten", +"Align center": "Zentriert ausrichten", +"Align right": "Rechtsb\u00fcndig ausrichten", +"Justify": "Blocksatz", +"Bullet list": "Aufz\u00e4hlung", +"Numbered list": "Nummerierte Liste", +"Decrease indent": "Einzug verkleinern", +"Increase indent": "Einzug vergr\u00f6\u00dfern", +"Close": "Schlie\u00dfen", +"Formats": "Formate", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Ihr Browser unterst\u00fctzt leider keinen direkten Zugriff auf die Zwischenablage. Bitte benutzen Sie die Strg + X \/ C \/ V Tastenkombinationen.", +"Headers": "\u00dcberschriften", +"Header 1": "\u00dcberschrift 1", +"Header 2": "\u00dcberschrift 2", +"Header 3": "\u00dcberschrift 3", +"Header 4": "\u00dcberschrift 4", +"Header 5": "\u00dcberschrift 5", +"Header 6": "\u00dcberschrift 6", +"Headings": "\u00dcberschriften", +"Heading 1": "\u00dcberschrift 1", +"Heading 2": "\u00dcberschrift 2", +"Heading 3": "\u00dcberschrift 3", +"Heading 4": "\u00dcberschrift 4", +"Heading 5": "\u00dcberschrift 5", +"Heading 6": "\u00dcberschrift 6", +"Preformatted": "Preformatted", +"Div": "Textblock", +"Pre": "Vorformatierter Text", +"Code": "Quelltext", +"Paragraph": "Absatz", +"Blockquote": "Zitat", +"Inline": "Zeichenformate", +"Blocks": "Absatzformate", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Einf\u00fcgen ist nun im einfachen Textmodus. Inhalte werden ab jetzt als unformatierter Text eingef\u00fcgt, bis Sie diese Einstellung wieder ausschalten!", +"Font Family": "Schriftart", +"Font Sizes": "Schriftgr\u00f6\u00dfe", +"Class": "Klasse", +"Browse for an image": "Bild...", +"OR": "ODER", +"Drop an image here": "Bild hier ablegen", +"Upload": "Hochladen", +"Block": "Blocksatz", +"Align": "Ausrichtung", "Default": "Standard", -"Lower Alpha": "Kleinbuchstaben", "Circle": "Kreis", "Disc": "Punkt", +"Square": "Quadrat", +"Lower Alpha": "Kleinbuchstaben", +"Lower Greek": "Griechische Kleinbuchstaben", +"Lower Roman": "R\u00f6mische Zahlen (Kleinbuchstaben)", "Upper Alpha": "Gro\u00dfbuchstaben", "Upper Roman": "R\u00f6mische Zahlen (Gro\u00dfbuchstaben)", -"Lower Roman": "R\u00f6mische Zahlen (Kleinbuchstaben)", -"Name": "Name", "Anchor": "Textmarke", +"Name": "Name", +"Id": "Kennung", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Die Kennung sollte mit einem Buchstaben anfangen. Nachfolgend nur Buchstaben, Zahlen, Striche (Minus), Punkte, Kommas und Unterstriche.", "You have unsaved changes are you sure you want to navigate away?": "Die \u00c4nderungen wurden noch nicht gespeichert, sind Sie sicher, dass Sie diese Seite verlassen wollen?", "Restore last draft": "Letzten Entwurf wiederherstellen", "Special character": "Sonderzeichen", "Source code": "Quelltext", -"B": "B", +"Insert\/Edit code sample": "Codebeispiel einf\u00fcgen\/bearbeiten", +"Language": "Sprache", +"Code sample": "Codebeispiel", +"Color": "Farbe", "R": "R", "G": "G", -"Color": "Farbe", -"Right to left": "Von rechts nach links", +"B": "B", "Left to right": "Von links nach rechts", +"Right to left": "Von rechts nach links", "Emoticons": "Emoticons", -"Robots": "Robots", "Document properties": "Dokumenteigenschaften", "Title": "Titel", "Keywords": "Sch\u00fcsselw\u00f6rter", -"Encoding": "Zeichenkodierung", "Description": "Beschreibung", +"Robots": "Robots", "Author": "Verfasser", +"Encoding": "Zeichenkodierung", "Fullscreen": "Vollbild", +"Action": "Aktion", +"Shortcut": "Shortcut", +"Help": "Hilfe", +"Address": "Adresse", +"Focus to menubar": "Fokus auf Men\u00fcleiste", +"Focus to toolbar": "Fokus auf Werkzeugleiste", +"Focus to element path": "Fokus auf Elementpfad", +"Focus to contextual toolbar": "Fokus auf kontextbezogene Werkzeugleiste", +"Insert link (if link plugin activated)": "Link einf\u00fcgen (wenn Link-Plugin aktiviert ist)", +"Save (if save plugin activated)": "Speichern (wenn Save-Plugin aktiviert ist)", +"Find (if searchreplace plugin activated)": "Suchen einf\u00fcgen (wenn Suchen\/Ersetzen-Plugin aktiviert ist)", +"Plugins installed ({0}):": "installierte Plugins ({0}):", +"Premium plugins:": "Premium Plugins:", +"Learn more...": "Erfahren Sie mehr dazu...", +"You are using {0}": "Sie verwenden {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Praktische Tastenkombinationen", "Horizontal line": "Horizontale Linie", -"Horizontal space": "Horizontaler Abstand", "Insert\/edit image": "Bild einf\u00fcgen\/bearbeiten", +"Image description": "Bildbeschreibung", +"Source": "Quelle", +"Dimensions": "Abmessungen", +"Constrain proportions": "Seitenverh\u00e4ltnis beibehalten", "General": "Allgemein", "Advanced": "Erweitert", -"Source": "Quelle", -"Border": "Rahmen", -"Constrain proportions": "Seitenverh\u00e4ltnis beibehalten", -"Vertical space": "Vertikaler Abstand", -"Image description": "Bildbeschreibung", "Style": "Stil", -"Dimensions": "Abmessungen", +"Vertical space": "Vertikaler Abstand", +"Horizontal space": "Horizontaler Abstand", +"Border": "Rahmen", "Insert image": "Bild einf\u00fcgen", -"Zoom in": "Ansicht vergr\u00f6\u00dfern", -"Contrast": "Kontrast", -"Back": "Zur\u00fcck", -"Gamma": "Gamma", -"Flip horizontally": "Horizontal spiegeln", -"Resize": "Skalieren", -"Sharpen": "Sch\u00e4rfen", -"Zoom out": "Ansicht verkleinern", -"Image options": "Bildeigenschaften", -"Apply": "Anwenden", -"Brightness": "Helligkeit", -"Rotate clockwise": "Im Uhrzeigersinn drehen", +"Image": "Bild", +"Image list": "Bildliste", "Rotate counterclockwise": "Gegen den Uhrzeigersinn drehen", -"Edit image": "Bild bearbeiten", -"Color levels": "Farbwerte", -"Crop": "Bescheiden", -"Orientation": "Ausrichtung", +"Rotate clockwise": "Im Uhrzeigersinn drehen", "Flip vertically": "Vertikal spiegeln", +"Flip horizontally": "Horizontal spiegeln", +"Edit image": "Bild bearbeiten", +"Image options": "Bildeigenschaften", +"Zoom in": "Ansicht vergr\u00f6\u00dfern", +"Zoom out": "Ansicht verkleinern", +"Crop": "Bescheiden", +"Resize": "Skalieren", +"Orientation": "Ausrichtung", +"Brightness": "Helligkeit", +"Sharpen": "Sch\u00e4rfen", +"Contrast": "Kontrast", +"Color levels": "Farbwerte", +"Gamma": "Gamma", "Invert": "Invertieren", +"Apply": "Anwenden", +"Back": "Zur\u00fcck", "Insert date\/time": "Datum\/Uhrzeit einf\u00fcgen ", -"Remove link": "Link entfernen", -"Url": "URL", -"Text to display": "Anzuzeigender Text", -"Anchors": "Textmarken", +"Date\/time": "Datum\/Uhrzeit", "Insert link": "Link einf\u00fcgen", -"New window": "Neues Fenster", -"None": "Keine", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Diese Adresse scheint ein externer Link zu sein. M\u00f6chten Sie das dazu ben\u00f6tigte \"http:\/\/\" voranstellen?", -"Target": "Ziel", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Diese Adresse scheint eine E-Mail-Adresse zu sein. M\u00f6chten Sie das dazu ben\u00f6tigte \"mailto:\" voranstellen?", "Insert\/edit link": "Link einf\u00fcgen\/bearbeiten", -"Insert\/edit video": "Video einf\u00fcgen\/bearbeiten", -"Poster": "Poster", -"Alternative source": "Alternative Quelle", -"Paste your embed code below:": "F\u00fcgen Sie Ihren Einbettungscode hier ein:", +"Text to display": "Anzuzeigender Text", +"Url": "URL", +"Target": "Ziel", +"None": "Keine", +"New window": "Neues Fenster", +"Remove link": "Link entfernen", +"Anchors": "Textmarken", +"Link": "Link", +"Paste or type a link": "Link einf\u00fcgen oder eintippen", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Diese Adresse scheint eine E-Mail-Adresse zu sein. M\u00f6chten Sie das dazu ben\u00f6tigte \"mailto:\" voranstellen?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Diese Adresse scheint ein externer Link zu sein. M\u00f6chten Sie das dazu ben\u00f6tigte \"http:\/\/\" voranstellen?", +"Link list": "Linkliste", "Insert video": "Video einf\u00fcgen", +"Insert\/edit video": "Video einf\u00fcgen\/bearbeiten", +"Insert\/edit media": "Medien einf\u00fcgen\/bearbeiten", +"Alternative source": "Alternative Quelle", +"Poster": "Poster", +"Paste your embed code below:": "F\u00fcgen Sie Ihren Einbettungscode hier ein:", "Embed": "Einbetten", +"Media": "Medium", "Nonbreaking space": "Gesch\u00fctztes Leerzeichen", "Page break": "Seitenumbruch", "Paste as text": "Als Text einf\u00fcgen", "Preview": "Vorschau", "Print": "Drucken", "Save": "Speichern", -"Could not find the specified string.": "Die Zeichenfolge wurde nicht gefunden.", -"Replace": "Ersetzen", -"Next": "Weiter", -"Whole words": "Nur ganze W\u00f6rter", -"Find and replace": "Suchen und ersetzen", -"Replace with": "Ersetzen durch", "Find": "Suchen", +"Replace with": "Ersetzen durch", +"Replace": "Ersetzen", "Replace all": "Alles ersetzen", -"Match case": "Gro\u00df-\/Kleinschreibung beachten", "Prev": "Zur\u00fcck", +"Next": "Weiter", +"Find and replace": "Suchen und ersetzen", +"Could not find the specified string.": "Die Zeichenfolge wurde nicht gefunden.", +"Match case": "Gro\u00df-\/Kleinschreibung beachten", +"Whole words": "Nur ganze W\u00f6rter", "Spellcheck": "Rechtschreibpr\u00fcfung", -"Finish": "Ende", -"Ignore all": "Alles Ignorieren", "Ignore": "Ignorieren", +"Ignore all": "Alles Ignorieren", +"Finish": "Ende", "Add to Dictionary": "Zum W\u00f6rterbuch hinzuf\u00fcgen", -"Insert row before": "Neue Zeile davor einf\u00fcgen ", -"Rows": "Zeilen", -"Height": "H\u00f6he", -"Paste row after": "Zeile danach einf\u00fcgen", -"Alignment": "Ausrichtung", -"Border color": "Rahmenfarbe", -"Column group": "Spaltengruppe", -"Row": "Zeile", -"Insert column before": "Neue Spalte davor einf\u00fcgen", -"Split cell": "Zelle aufteilen", -"Cell padding": "Zelleninnenabstand", -"Cell spacing": "Zellenabstand", -"Row type": "Zeilentyp", "Insert table": "Tabelle einf\u00fcgen", -"Body": "Inhalt", -"Caption": "Beschriftung", -"Footer": "Fu\u00dfzeile", -"Delete row": "Zeile l\u00f6schen", -"Paste row before": "Zeile davor einf\u00fcgen", -"Scope": "G\u00fcltigkeitsbereich", -"Delete table": "Tabelle l\u00f6schen", -"H Align": "Horizontale Ausrichtung", -"Top": "Oben", -"Header cell": "Kopfzelle", -"Column": "Spalte", -"Row group": "Zeilengruppe", -"Cell": "Zelle", -"Middle": "Mitte", -"Cell type": "Zellentyp", -"Copy row": "Zeile kopieren", -"Row properties": "Zeileneigenschaften", "Table properties": "Tabelleneigenschaften", -"Bottom": "Unten", -"V Align": "Vertikale Ausrichtung", -"Header": "Kopfzeile", -"Right": "Rechtsb\u00fcndig", -"Insert column after": "Neue Spalte danach einf\u00fcgen", -"Cols": "Spalten", -"Insert row after": "Neue Zeile danach einf\u00fcgen", -"Width": "Breite", +"Delete table": "Tabelle l\u00f6schen", +"Cell": "Zelle", +"Row": "Zeile", +"Column": "Spalte", "Cell properties": "Zelleneigenschaften", -"Left": "Linksb\u00fcndig", -"Cut row": "Zeile ausschneiden", -"Delete column": "Spalte l\u00f6schen", -"Center": "Zentriert", "Merge cells": "Zellen verbinden", +"Split cell": "Zelle aufteilen", +"Insert row before": "Neue Zeile davor einf\u00fcgen ", +"Insert row after": "Neue Zeile danach einf\u00fcgen", +"Delete row": "Zeile l\u00f6schen", +"Row properties": "Zeileneigenschaften", +"Cut row": "Zeile ausschneiden", +"Copy row": "Zeile kopieren", +"Paste row before": "Zeile davor einf\u00fcgen", +"Paste row after": "Zeile danach einf\u00fcgen", +"Insert column before": "Neue Spalte davor einf\u00fcgen", +"Insert column after": "Neue Spalte danach einf\u00fcgen", +"Delete column": "Spalte l\u00f6schen", +"Cols": "Spalten", +"Rows": "Zeilen", +"Width": "Breite", +"Height": "H\u00f6he", +"Cell spacing": "Zellenabstand", +"Cell padding": "Zelleninnenabstand", +"Caption": "Beschriftung", +"Left": "Linksb\u00fcndig", +"Center": "Zentriert", +"Right": "Rechtsb\u00fcndig", +"Cell type": "Zellentyp", +"Scope": "G\u00fcltigkeitsbereich", +"Alignment": "Ausrichtung", +"H Align": "Horizontale Ausrichtung", +"V Align": "Vertikale Ausrichtung", +"Top": "Oben", +"Middle": "Mitte", +"Bottom": "Unten", +"Header cell": "Kopfzelle", +"Row group": "Zeilengruppe", +"Column group": "Spaltengruppe", +"Row type": "Zeilentyp", +"Header": "Kopfzeile", +"Body": "Inhalt", +"Footer": "Fu\u00dfzeile", +"Border color": "Rahmenfarbe", "Insert template": "Vorlage einf\u00fcgen ", "Templates": "Vorlagen", +"Template": "Vorlage", +"Text color": "Textfarbe", "Background color": "Hintergrundfarbe", "Custom...": "Benutzerdefiniert...", "Custom color": "Benutzerdefinierte Farbe", "No color": "Keine Farbe", -"Text color": "Textfarbe", -"Show blocks": " Bl\u00f6cke anzeigen", +"Table of Contents": "Inhaltsverzeichnis", +"Show blocks": "Bl\u00f6cke anzeigen", "Show invisible characters": "Unsichtbare Zeichen anzeigen", "Words: {0}": "W\u00f6rter: {0}", -"Insert": "Einf\u00fcgen", +"{0} words": "{0} W\u00f6rter", "File": "Datei", "Edit": "Bearbeiten", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich-Text- Area. Dr\u00fccken Sie ALT-F9 f\u00fcr das Men\u00fc. Dr\u00fccken Sie ALT-F10 f\u00fcr Symbolleiste. Dr\u00fccken Sie ALT-0 f\u00fcr Hilfe", -"Tools": "Werkzeuge", +"Insert": "Einf\u00fcgen", "View": "Ansicht", +"Format": "Format", "Table": "Tabelle", -"Format": "Format" +"Tools": "Werkzeuge", +"Powered by {0}": "Betrieben von {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich-Text- Area. Dr\u00fccken Sie ALT-F9 f\u00fcr das Men\u00fc. Dr\u00fccken Sie ALT-F10 f\u00fcr Symbolleiste. Dr\u00fccken Sie ALT-0 f\u00fcr Hilfe" }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js new file mode 100644 index 0000000000..2af071f5ce --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js @@ -0,0 +1,261 @@ +tinymce.addI18n('de_AT',{ +"Redo": "Wiederholen", +"Undo": "R\u00fcckg\u00e4ngig", +"Cut": "Ausschneiden", +"Copy": "Kopieren", +"Paste": "Einf\u00fcgen", +"Select all": "Alles ausw\u00e4hlen", +"New document": "Neues Dokument", +"Ok": "Ok", +"Cancel": "Abbrechen", +"Visual aids": "Hilfslinien und unsichtbare Elemente einblenden", +"Bold": "Fett", +"Italic": "Kursiv", +"Underline": "Unterstrichen", +"Strikethrough": "Durchgestrichen", +"Superscript": "Hochgestellt", +"Subscript": "Tiefgestellt", +"Clear formatting": "Formatierungen zur\u00fccksetzen", +"Align left": "Linksb\u00fcndig", +"Align center": "Zentriert", +"Align right": "Rechtsb\u00fcndig", +"Justify": "Blocksatz", +"Bullet list": "Unsortierte Liste", +"Numbered list": "Sortierte Liste", +"Decrease indent": "Ausr\u00fccken", +"Increase indent": "Einr\u00fccken", +"Close": "Schlie\u00dfen", +"Formats": "Formate", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Ihr Browser unterst\u00fctzt keinen direkten Zugriff auf die Zwischenablage. Bitte nutzen Sie die Tastaturk\u00fcrzel Strg+X\/C\/V stattdessen.", +"Headers": "\u00dcberschriften", +"Header 1": "\u00dcberschrift 1", +"Header 2": "\u00dcberschrift 2", +"Header 3": "\u00dcberschrift 3", +"Header 4": "\u00dcberschrift 4", +"Header 5": "\u00dcberschrift 5", +"Header 6": "\u00dcberschrift 6", +"Headings": "\u00dcberschriften", +"Heading 1": "\u00dcberschrift 1", +"Heading 2": "\u00dcberschrift 2", +"Heading 3": "\u00dcberschrift 3", +"Heading 4": "\u00dcberschrift 4", +"Heading 5": "\u00dcberschrift 5", +"Heading 6": "\u00dcberschrift 6", +"Preformatted": "Vorformatiert", +"Div": "Block (div)", +"Pre": "Vorformatierter Text (pre)", +"Code": "Code (code)", +"Paragraph": "Absatz (p)", +"Blockquote": "Zitat (blockquote)", +"Inline": "Inline", +"Blocks": "Bl\u00f6cke", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Alle Texte werden nun ohne Formatierung eingef\u00fcgt, bis diese Einstellung wieder ge\u00e4ndert wird.", +"Font Family": "Schriftart", +"Font Sizes": "Schriftgr\u00f6\u00dfen", +"Class": "Klasse", +"Browse for an image": "Bild...", +"OR": "oder", +"Drop an image here": "Bild hierher ziehen", +"Upload": "Hochladen", +"Block": "Block", +"Align": "Ausrichtung", +"Default": "Standard", +"Circle": "Kreis", +"Disc": "Gef\u00fcllter Kreis", +"Square": "Quadrat", +"Lower Alpha": "Kleinbuchstaben", +"Lower Greek": "Griechische Kleinbuchstaben", +"Lower Roman": "R\u00f6mische Zahlen (Kleinbuchstaben)", +"Upper Alpha": "Gro\u00dfbuchstaben", +"Upper Roman": "R\u00f6mische Zahlen (Gro\u00dfbuchstaben)", +"Anchor": "Anker", +"Name": "Name", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Eine ID f\u00e4ngt mit einem Buchstaben an, gefolgt von Buchstaben, Ziffern, Bindestrichen, Punkten, Doppelpunkten oder Unterstrichen.", +"You have unsaved changes are you sure you want to navigate away?": "Sie haben ungespeicherte \u00c4nderungen. Sind Sie sicher, dass Sie die Seite verlassen wollen?", +"Restore last draft": "Letzten Entwurf wiederherstellen.", +"Special character": "Sonderzeichen", +"Source code": "Quelltext", +"Insert\/Edit code sample": "Beispielcode einf\u00fcgen\/bearbeiten", +"Language": "Sprache", +"Code sample": "Code Beispiel", +"Color": "Farbe", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Links nach rechts", +"Right to left": "Rechts nach links", +"Emoticons": "Emoticons", +"Document properties": "Dokumenteigenschaften", +"Title": "Titel", +"Keywords": "Schl\u00fcsselw\u00f6rter", +"Description": "Beschreibung", +"Robots": "Suchmaschinen", +"Author": "Autor", +"Encoding": "Enkodierung", +"Fullscreen": "Vollbild", +"Action": "Aktion", +"Shortcut": "Tastenkombination", +"Help": "Hilfe", +"Address": "Adresse", +"Focus to menubar": "Fokus auf Men\u00fcleiste", +"Focus to toolbar": "Fokus auf Werkzeugleiste", +"Focus to element path": "Fokus auf Elementpfad", +"Focus to contextual toolbar": "Fokus auf kontextbezogene Werkzeugleiste", +"Insert link (if link plugin activated)": "Link einf\u00fcgen (wenn Plugin aktiv ist)", +"Save (if save plugin activated)": "Speichern (wenn Plugin aktiv ist)", +"Find (if searchreplace plugin activated)": "Suchen (wenn Plugin aktiv ist)", +"Plugins installed ({0}):": "Installierte Plugins ({0}):", +"Premium plugins:": "Premium Plugins:", +"Learn more...": "Mehr Informationen...", +"You are using {0}": "Sie verwenden {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Praktische Abk\u00fcrzungen", +"Horizontal line": "Horizontale Trennlinie", +"Insert\/edit image": "Bild einf\u00fcgen\/bearbeiten", +"Image description": "Bildbeschreibung", +"Source": "Adresse", +"Dimensions": "Ausma\u00dfe", +"Constrain proportions": "Seitenverh\u00e4ltnis beibehalten", +"General": "Allgemein", +"Advanced": "Erweitert", +"Style": "Format", +"Vertical space": "Vertikaler Abstand", +"Horizontal space": "Horizontaler Abstand", +"Border": "Rahmen", +"Insert image": "Bild einf\u00fcgen", +"Image": "Bild", +"Image list": "Bilderliste", +"Rotate counterclockwise": "Gegen den Uhrzeigersinn drehen", +"Rotate clockwise": "Im Uhrzeigersinn drehen", +"Flip vertically": "Vertikal kippen", +"Flip horizontally": "Horizontal kippen", +"Edit image": "Bild bearbeiten", +"Image options": "Bildeinstellungen", +"Zoom in": "Einzoomen", +"Zoom out": "Auszoomen", +"Crop": "Zuschneiden", +"Resize": "Gr\u00f6\u00dfe \u00e4ndern", +"Orientation": "Orientierung", +"Brightness": "Helligkeit", +"Sharpen": "Sch\u00e4rfen", +"Contrast": "Kontrast", +"Color levels": "Farbwerte", +"Gamma": "Gamma", +"Invert": "Invertieren", +"Apply": "Anwenden", +"Back": "Zur\u00fcck", +"Insert date\/time": "Zeit\/Datum einf\u00fcgen", +"Date\/time": "Zeit\/Datum", +"Insert link": "Link einf\u00fcgen", +"Insert\/edit link": "Link einf\u00fcgen\/bearbeiten", +"Text to display": "Angezeigter Text", +"Url": "Url", +"Target": "Ziel", +"None": "Keine", +"New window": "Neues Fenster", +"Remove link": "Link entfernen", +"Anchors": "Anker", +"Link": "Link", +"Paste or type a link": "Link einf\u00fcgen oder eintippen", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Die eingegebene URL scheint eine E-Mail-Adresse zu sein. Soll das notwendige \"mailto:\"-Pr\u00e4fix hinzugef\u00fcgt werden?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Die eingegebene URL scheint eine externe Web-Adresse zu sein. Soll das notwendige \"http:\/\/\"-Pr\u00e4fix hinzugef\u00fcgt werden?", +"Link list": "Linkliste", +"Insert video": "Video einf\u00fcgen", +"Insert\/edit video": "Video einf\u00fcgen\/bearbeiten", +"Insert\/edit media": "Medien einf\u00fcgen\/bearbeiten", +"Alternative source": "Alternative Quelle", +"Poster": "Poster", +"Paste your embed code below:": "F\u00fcgen unten Sie Ihren Quellcode zum einbetten ein", +"Embed": "Einbetten", +"Media": "Medien", +"Nonbreaking space": "gesch\u00fctztes Leerzeichen", +"Page break": "Seitenumbruch", +"Paste as text": "Als Text einf\u00fcgen", +"Preview": "Vorschau", +"Print": "Drucken", +"Save": "Speichern", +"Find": "Suchen", +"Replace with": "Ersetzen durch", +"Replace": "Ersetzen", +"Replace all": "Alle ersetzen", +"Prev": "Vorheriges", +"Next": "N\u00e4chstes", +"Find and replace": "Suchen und ersetzen", +"Could not find the specified string.": "Keine \u00dcbereinstimmung gefunden", +"Match case": "Gro\u00df-\/Kleinschreibung beachten", +"Whole words": "Vollst\u00e4ndige W\u00f6rter", +"Spellcheck": "Rechtschreibung \u00fcberpr\u00fcfen", +"Ignore": "Ignorieren", +"Ignore all": "Alle ignorieren", +"Finish": "Fertig", +"Add to Dictionary": "Zum W\u00f6rterbuch hinzuf\u00fcgen", +"Insert table": "Tabelle einf\u00fcgen", +"Table properties": "Tabelleneigenschaften", +"Delete table": "Tabelle l\u00f6schen", +"Cell": "Zelle", +"Row": "Zeile", +"Column": "Spalte", +"Cell properties": "Zelleneigenschaften", +"Merge cells": "Zellen vereinen", +"Split cell": "Verbundene Zellen trennen", +"Insert row before": "Neue Zeile oberhalb einf\u00fcgen", +"Insert row after": "Neue Zeile unterhalb einf\u00fcgen", +"Delete row": "Zeile l\u00f6schen", +"Row properties": "Zeileneigenschaften", +"Cut row": "Zeile ausschneiden", +"Copy row": "Zeile kopieren", +"Paste row before": "Zeile oberhalb einf\u00fcgen", +"Paste row after": "Zeile unterhalb einf\u00fcgen", +"Insert column before": "Neue Spalte links einf\u00fcgen", +"Insert column after": "Neue Spalte rechts einf\u00fcgen", +"Delete column": "Spalte l\u00f6schen", +"Cols": "Spalten", +"Rows": "Zeilen", +"Width": "Breite", +"Height": "H\u00f6he", +"Cell spacing": "Zellenabstand", +"Cell padding": "Abstand innerhalb der Zellen", +"Caption": "Beschriftung der Tabelle", +"Left": "Links", +"Center": "Zentriert", +"Right": "Rechts", +"Cell type": "Zellentyp", +"Scope": "Geltungsbereich", +"Alignment": "Ausrichtung", +"H Align": "Ausrichtung H", +"V Align": "Ausrichtung V", +"Top": "Oben", +"Middle": "Mitte", +"Bottom": "Unten", +"Header cell": "\u00dcberschrift", +"Row group": "Zeilengruppe", +"Column group": "Spaltengruppe", +"Row type": "Zeilentyp", +"Header": "Tabellen\u00fcberschrift", +"Body": "Tabellenk\u00f6rper", +"Footer": "Tabellenfu\u00df", +"Border color": "Rahmenfarbe", +"Insert template": "Vorlage einf\u00fcgen", +"Templates": "Vorlagen", +"Template": "Vorlage", +"Text color": "Textfarbe", +"Background color": "Hintergrundfarbe", +"Custom...": "Benutzerdefiniert...", +"Custom color": "Benutzerdefinierte Farbe", +"No color": "Keine Farbe", +"Table of Contents": "Inhaltsverzeichnis", +"Show blocks": "Blockelemente einblenden", +"Show invisible characters": "Unsichtbare Zeichen einblenden", +"Words: {0}": "W\u00f6rter: {0}", +"{0} words": "{0} W\u00f6rter", +"File": "Datei", +"Edit": "Bearbeiten", +"Insert": "Einf\u00fcgen", +"View": "Ansicht", +"Format": "Format", +"Table": "Tabelle", +"Tools": "Extras", +"Powered by {0}": "Betrieben von {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Dr\u00fccken Sie ALT-F9 f\u00fcr das Men\u00fc. Dr\u00fccken Sie ALT-F10 f\u00fcr die Werkzeugleiste. Dr\u00fccken Sie ALT-0 f\u00fcr Hilfe" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/dv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/dv.js new file mode 100644 index 0000000000..3de3a6d9ac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/dv.js @@ -0,0 +1,230 @@ +tinymce.addI18n('dv',{ +"Cut": "\u0786\u07a6\u0793\u07b0", +"Heading 5": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 5", +"Header 2": "\u0780\u07ac\u0791\u07a7 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0786\u07b0\u078d\u07a8\u0795\u07b0\u0784\u07af\u0791\u07b0 \u0784\u07ad\u0782\u07aa\u0782\u07b0 \u0786\u07aa\u0783\u07aa\u0789\u07aa\u078e\u07ac \u0780\u07aa\u0787\u07b0\u078b\u07a6\u060c \u0784\u07b0\u0783\u07af\u0792\u07a6\u0783\u0787\u07a6\u0786\u07aa\u0782\u07b0 \u0782\u07aa\u078b\u07ad! Ctrl+X\/C\/V \u0784\u07ad\u0782\u07aa\u0782\u07b0 \u0786\u07aa\u0783\u07ad!", +"Heading 4": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 4", +"Div": "\u0791\u07a6\u0787\u07a8\u0788\u07b0", +"Heading 2": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 2", +"Paste": "\u0795\u07ad\u0790\u07b0\u0793\u07b0", +"Close": "\u0782\u07a8\u0787\u07b0\u0788\u07a7", +"Font Family": "\u078a\u07ae\u0782\u07b0\u0793\u07b0", +"Pre": "\u0795\u07b0\u0783\u07a9", +"Align right": "\u0786\u07a6\u0782\u07a7\u078c\u07a6\u0781\u07b0 \u0796\u07a6\u0787\u07b0\u0790\u07a7", +"New document": "\u0787\u07a7 \u0791\u07ae\u0786\u07a8\u0787\u07aa\u0789\u07ac\u0782\u07b0\u0793\u07b0", +"Blockquote": "\u0784\u07b0\u078d\u07ae\u0786\u07b0-\u0786\u07af\u0793\u07b0", +"Numbered list": "\u0782\u07a6\u0782\u07b0\u0784\u07a6\u0783\u07aa \u078d\u07a8\u0790\u07b0\u0793\u07b0", +"Heading 1": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 1", +"Headings": "\u0780\u07ac\u0791\u07a8\u0782\u07b0", +"Increase indent": "\u078b\u07aa\u0783\u07aa\u0789\u07a8\u0782\u07b0 \u0784\u07ae\u0791\u07aa\u0786\u07aa\u0783\u07ad", +"Formats": "\u078a\u07af\u0789\u07ac\u0793\u07b0\u078c\u07a6\u0787\u07b0", +"Headers": "\u0780\u07ac\u0791\u07a7\u078c\u07a6\u0787\u07b0", +"Select all": "\u0790\u07ac\u078d\u07ac\u0786\u07b0\u0793\u07b0 \u0787\u07af\u078d\u07b0", +"Header 3": "\u0780\u07ac\u0791\u07a7 3", +"Blocks": "\u0784\u07b0\u078d\u07ae\u0786\u07b0\u078c\u07a6\u0787\u07b0", +"Undo": "\u0787\u07a6\u0782\u07b0\u0791\u07ab", +"Strikethrough": "\u0789\u07ac\u078b\u07aa \u0783\u07ae\u0782\u078e\u07ae", +"Bullet list": "\u0784\u07aa\u078d\u07ac\u0793\u07b0 \u078d\u07a8\u0790\u07b0\u0793\u07b0", +"Header 1": "\u0780\u07ac\u0791\u07a7 1", +"Superscript": "\u0789\u07a6\u078c\u07a9\u0787\u07a6\u0786\u07aa\u0783\u07aa", +"Clear formatting": "\u078a\u07af\u0789\u07ac\u0793\u07b0\u078c\u07a6\u0787\u07b0 \u078a\u07ae\u0780\u07ad", +"Font Sizes": "\u078a\u07ae\u0782\u07b0\u0793\u07b0 \u0790\u07a6\u0787\u07a8\u0792\u07b0", +"Subscript": "\u078c\u07a8\u0783\u07a9\u0787\u07a6\u0786\u07aa\u0783\u07aa", +"Header 6": "\u0780\u07ac\u0791\u07a7 6", +"Redo": "\u0783\u07a9\u0791\u07ab", +"Paragraph": "\u0795\u07ac\u0783\u07ac\u078e\u07b0\u0783\u07a7\u078a\u07b0", +"Ok": "\u0787\u07af\u0786\u07ad", +"Bold": "\u0784\u07af\u078d\u07b0\u0791\u07b0", +"Code": "\u0786\u07af\u0791\u07b0", +"Italic": "\u0787\u07a8\u0793\u07a6\u078d\u07a8\u0786\u07b0", +"Align center": "\u0789\u07ac\u078b\u07a6\u0781\u07b0 \u0796\u07a6\u0787\u07b0\u0790\u07a7", +"Header 5": "\u0780\u07ac\u0791\u07a7 5", +"Heading 6": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 6", +"Heading 3": "\u0780\u07ac\u0791\u07a8\u0782\u07b0 3", +"Decrease indent": "\u078b\u07aa\u0783\u07aa\u0789\u07a8\u0782\u07b0 \u0786\u07aa\u0791\u07a6\u0786\u07aa\u0783\u07ad", +"Header 4": "\u0780\u07ac\u0791\u07a7 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ac\u0788\u07ad\u0782\u07a9 \u0795\u07b0\u078d\u07ac\u0787\u07a8\u0782\u07b0\u0786\u07ae\u0781\u07b0! \u0784\u07a6\u078b\u07a6\u078d\u07aa \u0786\u07aa\u0783\u07ac\u0787\u07b0\u0788\u07aa\u0789\u07a6\u0781\u07b0 \u0789\u07a8 \u0787\u07ae\u0795\u07b0\u079d\u07a6\u0782\u07b0 \u0787\u07ae\u078a\u07b0 \u0786\u07ae\u0781\u07b0\u078d\u07a6\u0787\u07b0\u0788\u07a7!", +"Underline": "\u078b\u07a6\u0781\u07aa\u0783\u07ae\u0782\u078e\u07aa", +"Cancel": "\u0786\u07ac\u0782\u07b0\u0790\u07a6\u078d\u07b0", +"Justify": "\u0787\u07ac\u0787\u07b0\u0788\u07a6\u0783\u07aa \u0786\u07aa\u0783\u07ad", +"Inline": "\u0787\u07a8\u0782\u07b0\u078d\u07a6\u0787\u07a8\u0782\u07b0", +"Copy": "\u0786\u07ae\u0795\u07a9", +"Align left": "\u0788\u07a7\u078c\u07a6\u0781\u07b0 \u0796\u07a6\u0787\u07b0\u0790\u07a7", +"Visual aids": "\u0788\u07a8\u079d\u07aa\u0787\u07a6\u078d\u07b0 \u0787\u07ac\u0787\u07a8\u0791\u07b0\u0790\u07b0", +"Lower Greek": "\u078d\u07af\u0788\u07a6\u0783 \u078e\u07b0\u0783\u07a9\u0786\u07b0", +"Square": "\u078e\u07ae\u0785\u07a8", +"Default": "\u0791\u07a8\u078a\u07af\u078d\u07b0\u0793\u07b0", +"Lower Alpha": "\u078d\u07af\u0788\u07a6\u0783 \u0787\u07a6\u078d\u07b0\u078a\u07a7", +"Circle": "\u0784\u07ae\u0785\u07aa", +"Disc": "\u0788\u07a6\u0781\u07b0\u0784\u07aa\u0783\u07aa", +"Upper Alpha": "\u0787\u07a6\u0795\u07a7 \u0787\u07a6\u078d\u07b0\u078a\u07a7", +"Upper Roman": "\u0787\u07a6\u0795\u07a7 \u0783\u07af\u0789\u07a6\u0782\u07b0", +"Lower Roman": "\u078d\u07af\u0788\u07a6\u0783 \u0783\u07af\u0789\u07a6\u0782\u07b0", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0787\u07a6\u0787\u07a8\u0791\u07a9 \u078a\u07ac\u0781\u07ac\u0782\u07b0\u0788\u07a7\u0782\u07a9 \u0787\u07a6\u0786\u07aa\u0783\u07a6\u0786\u07aa\u0782\u07b0\u060c \u0787\u07ad\u078e\u07ac \u078a\u07a6\u0780\u07aa\u078e\u07a6\u0787\u07a8 \u0787\u07a6\u0786\u07aa\u0783\u07aa\u078c\u07a6\u0786\u07ac\u0787\u07b0\u060c \u0782\u07a6\u0782\u07b0\u0784\u07a6\u0783\u07aa\u078c\u07a6\u0787\u07b0\u060c \u0791\u07ad\u079d\u07b0\u078c\u07a6\u0787\u07b0\u060c \u078c\u07a8\u0786\u07a8\u078c\u07a6\u0787\u07b0\u060c \u0786\u07ae\u078d\u07ae\u0782\u07b0\u078c\u07a6\u0787\u07b0 \u0782\u07aa\u0788\u07a6\u078c\u07a6 \u078b\u07a6\u0781\u07aa \u0783\u07ae\u0782\u078e\u07aa\u078c\u07a6\u0787\u07b0", +"Name": "\u0782\u07a6\u0782\u07b0", +"Anchor": "\u0787\u07ac\u0782\u07b0\u0786\u07a6\u0783", +"Id": "\u0787\u07a6\u0787\u07a8\u0791\u07a9", +"You have unsaved changes are you sure you want to navigate away?": "\u0784\u07a6\u078b\u07a6\u078d\u07aa\u078c\u07a6\u0787\u07b0 \u0790\u07ad\u0788\u07b0 \u0782\u07aa\u0786\u07ae\u0781\u07b0 \u078b\u07ab\u0786\u07ae\u0781\u07b0\u078d\u07a6\u0782\u07b0\u0788\u07a9\u078c\u07a6\u061f", +"Restore last draft": "\u078a\u07a6\u0780\u07aa\u078e\u07ac \u0791\u07b0\u0783\u07a7\u078a\u07b0\u0793\u07b0 \u0783\u07ac\u0790\u07b0\u0793\u07af \u0786\u07aa\u0783\u07ad", +"Special character": "\u079a\u07a7\u0787\u07b0\u0790\u07a6 \u0787\u07a6\u0786\u07aa\u0783\u07aa\u078c\u07a6\u0787\u07b0", +"Source code": "\u0789\u07a6\u0790\u07b0\u078b\u07a6\u0783\u07aa", +"Language": "\u0784\u07a6\u0790\u07b0", +"Insert\/Edit code sample": "\u0786\u07af\u0791\u07aa \u0789\u07a8\u0790\u07a7\u078d\u07aa \u0787\u07a8\u0782\u07b0\u0790\u07a7\u0793\u07aa\/\u0787\u07ac\u0791\u07a8\u0793\u07b0 \u0786\u07aa\u0783\u07aa\u0782\u07b0", +"B": "\u0784\u07a9", +"R": "\u0787\u07a7\u0783\u07aa", +"G": "\u0796\u07a9", +"Color": "\u0786\u07aa\u078d\u07a6", +"Right to left": "\u0786\u07a6\u0782\u07a7\u078c\u07aa\u0782\u07b0 \u0788\u07a7\u078c\u07a6\u0781\u07b0", +"Left to right": "\u0788\u07a7\u078c\u07aa\u0782\u07b0 \u0786\u07a6\u0782\u07a7\u078c\u07a6\u0781\u07b0", +"Emoticons": "\u079d\u07aa\u0787\u07ab\u0783\u07aa \u078a\u07ae\u0793\u07af", +"Robots": "\u0783\u07af\u0784\u07ae\u0793\u07b0\u0790\u07b0", +"Document properties": "\u0791\u07ae\u0786\u07a8\u0787\u07aa\u0789\u07ac\u0782\u07b0\u0793\u07b0\u078e\u07ac \u0790\u07a8\u078a\u07a6\u078c\u07a6\u0787\u07b0", +"Title": "\u0793\u07a6\u0787\u07a8\u0793\u07a6\u078d\u07b0", +"Keywords": "\u0786\u07a9\u0788\u07af\u0791\u07b0\u078c\u07a6\u0787\u07b0", +"Encoding": "\u0787\u07ac\u0782\u07b0\u0786\u07af\u0791\u07a8\u0782\u07b0", +"Description": "\u078c\u07a6\u078a\u07b0\u0790\u07a9\u078d\u07aa", +"Author": "\u0788\u07ac\u0783\u07a8\u078a\u07a6\u0783\u07a7\u078c\u07b0", +"Fullscreen": "\u078a\u07aa\u078d\u07b0\u0790\u07b0\u0786\u07b0\u0783\u07a9\u0782\u07b0", +"Horizontal line": "\u0780\u07aa\u0783\u07a6\u0790\u07b0 \u0783\u07ae\u0782\u078e\u07aa", +"Horizontal space": "\u0780\u07ae\u0783\u07a8\u0792\u07af\u0782\u07b0\u0793\u07a6\u078d\u07b0 \u0790\u07b0\u0795\u07ad\u0790\u07b0", +"Insert\/edit image": "\u078a\u07ae\u0793\u07af\u078d\u07aa\u0782\u07b0\/\u0784\u07a6\u078b\u07a6\u078d\u07aa\u0786\u07aa\u0783\u07aa\u0782\u07b0", +"General": "\u0787\u07a7\u0782\u07b0\u0789\u07aa", +"Advanced": "\u0787\u07ac\u0791\u07b0\u0788\u07a7\u0782\u07b0\u0790\u07b0\u0791\u07b0", +"Source": "\u0789\u07a6\u0790\u07b0\u078b\u07a6\u0783\u07aa", +"Border": "\u0784\u07af\u0791\u07a6\u0783\u07aa", +"Constrain proportions": "\u0788\u07a6\u0792\u07a6\u0782\u07b0 \u0780\u07a8\u078a\u07a6\u0780\u07a6\u0787\u07b0\u0793\u07a7", +"Vertical space": "\u0788\u07a7\u0793\u07a8\u0786\u07a6\u078d\u07b0 \u0790\u07b0\u0795\u07ad\u0790\u07b0", +"Image description": "\u078a\u07ae\u0793\u07af\u078e\u07ac \u078c\u07a6\u078a\u07b0\u0790\u07a9\u078d\u07aa", +"Style": "\u0790\u07b0\u0793\u07a6\u0787\u07a8\u078d\u07b0", +"Dimensions": "\u0789\u07a8\u0782\u07b0\u078c\u07a6\u0787\u07b0", +"Insert image": "\u078a\u07ae\u0793\u07af \u0787\u07a8\u0782\u07b0\u0790\u07a7\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Image": "\u078a\u07ae\u0793\u07af", +"Zoom in": "\u0784\u07ae\u0791\u07aa\u0786\u07aa\u0783\u07ad", +"Contrast": "\u078c\u07a6\u078a\u07a7\u078c\u07aa\u0786\u07a6\u0782\u07b0", +"Back": "\u078a\u07a6\u0780\u07a6\u078c\u07a6\u0781\u07b0", +"Gamma": "\u078e\u07ad\u0789\u07a7", +"Flip horizontally": "\u0780\u07aa\u0783\u07a6\u0780\u07a6\u0781\u07b0\u0788\u07a7\u078e\u07ae\u078c\u07a6\u0781\u07b0 \u078a\u07aa\u0781\u07aa\u0782\u07b0\u0796\u07a6\u0780\u07a7", +"Resize": "\u0790\u07a6\u0787\u07a8\u0792\u07aa\u0784\u07a6\u078b\u07a6\u078d\u07aa\u0786\u07aa\u0783\u07aa\u0782\u07b0", +"Sharpen": "\u078c\u07ab\u0782\u07aa\u0786\u07a6\u0782\u07b0", +"Zoom out": "\u0786\u07aa\u0791\u07a6\u0786\u07aa\u0783\u07ad", +"Image options": "\u078a\u07ae\u0793\u07af \u0787\u07ae\u0795\u07b0\u079d\u07a6\u0782\u07b0\u078c\u07a6\u0787\u07b0", +"Apply": "\u0787\u07ac\u0795\u07b0\u078d\u07a6\u0787\u07a8\u0786\u07aa\u0783\u07ad", +"Brightness": "\u0787\u07a6\u078d\u07a8\u0789\u07a8\u0782\u07b0", +"Rotate clockwise": "\u0786\u07a6\u0782\u07a7\u078c\u07a6\u0781\u07b0 \u0787\u07a6\u0782\u0784\u07aa\u0783\u07a7", +"Rotate counterclockwise": "\u0788\u07a7\u078c\u07a6\u0781\u07b0 \u0787\u07a6\u0782\u0784\u07aa\u0783\u07a7", +"Edit image": "\u078a\u07ae\u0793\u07af \u0787\u07ac\u0791\u07a8\u0793\u07b0\u0786\u07aa\u07aa\u0783\u07aa\u0782\u07b0", +"Color levels": "\u0786\u07aa\u078d\u07a6\u0787\u07a8\u078e\u07ac \u078d\u07ac\u0788\u07ac\u078d\u07b0\u078c\u07a6\u0787\u07b0", +"Crop": "\u0786\u07b0\u0783\u07ae\u0795\u07b0\u0786\u07aa\u0783\u07aa\u0782\u07b0", +"Orientation": "\u0787\u07ae\u0783\u07a8\u0787\u07ac\u0782\u07b0\u0793\u07ad\u079d\u07a6\u0782\u07b0", +"Flip vertically": "\u0789\u07a6\u078c\u07a8\u0782\u07b0\u078c\u07a8\u0783\u07a8\u0787\u07a6\u0781\u07b0\u0788\u07a7\u078e\u07ae\u078c\u07a6\u0781\u07b0 \u078a\u07aa\u0781\u07aa\u0782\u07b0\u0796\u07a6\u0780\u07a7", +"Invert": "\u0787\u07a8\u0782\u07b0\u0788\u07a7\u0793\u07aa", +"Date\/time": "\u078c\u07a7\u0783\u07a9\u079a\u07b0\/\u0788\u07a6\u078e\u07aa\u078c\u07aa", +"Insert date\/time": "\u0788\u07a6\u078e\u07aa\u078c\u07aa\/\u078c\u07a7\u0783\u07a9\u079a\u07b0 \u078d\u07aa\u0782\u07b0", +"Remove link": "\u078d\u07a8\u0782\u07b0\u0786\u07b0 \u078a\u07ae\u0780\u07ad", +"Url": "\u0794\u07ab.\u0787\u07a7\u0783\u07b0.\u0787\u07ac\u078d\u07b0", +"Text to display": "\u078b\u07a6\u0787\u07b0\u0786\u07a6\u0782\u07b0\u0788\u07a9 \u0787\u07a8\u0784\u07a7\u0783\u07a7\u078c\u07b0", +"Anchors": "\u0787\u07ac\u0782\u07b0\u0786\u07a6\u0783\u078c\u07a6\u0787\u07b0", +"Insert link": "\u078d\u07a8\u0782\u07b0\u0786\u07b0 \u078d\u07aa\u0782\u07b0", +"Link": "\u078d\u07a8\u0782\u07b0\u0786\u07aa", +"New window": "\u0787\u07a7 \u0788\u07a8\u0782\u07b0\u0791\u07af\u0787\u07a6\u0786\u07a6\u0781\u07b0", +"None": "\u0782\u07ae\u0782\u07b0", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u078c\u07a8\u0794\u07a6 \u078d\u07a8\u0794\u07aa\u0787\u07b0\u0788\u07a9 \u0787\u07ac\u0780\u07ac\u0782\u07b0 \u0790\u07a6\u0787\u07a8\u0793\u07ac\u0787\u07b0\u078e\u07ac \u078d\u07a8\u0782\u07b0\u0786\u07ac\u0787\u07b0\u0786\u07a6\u0789\u07aa\u0782\u07b0 \u0787\u07ac\u0797\u07b0.\u0793\u07a9.\u0793\u07a9.\u0795\u07a9 \u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa \u0786\u07aa\u0783\u07a6\u0782\u07b0\u078c\u07af\u061f", +"Paste or type a link": "\u078d\u07a8\u0782\u07b0\u0786\u07aa \u078d\u07a8\u0794\u07aa\u0787\u07b0\u0788\u07a7 \u0782\u07aa\u0788\u07a6\u078c\u07a6 \u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07a6\u0787\u07b0\u0788\u07a7", +"Target": "\u0793\u07a7\u078e\u07ac\u0793\u07b0", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0789\u07ac\u0787\u07a8\u078d\u07b0\u0793\u07ab - \u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa\u0786\u07aa\u0783\u07a6\u0787\u07b0\u0788\u07a6\u0782\u07b0 \u0784\u07ad\u0782\u07aa\u0782\u07b0\u078a\u07aa\u0785\u07aa\u078c\u07af\u061f", +"Insert\/edit link": "\u078d\u07a8\u0782\u07b0\u0786\u07b0 \u078d\u07aa\u0782\u07b0\/\u0784\u07a6\u078b\u07a6\u078d\u07aa \u078e\u07ac\u0782\u07a6\u0787\u07aa\u0782\u07b0", +"Insert\/edit video": "\u0788\u07a9\u0791\u07a8\u0787\u07af \u078d\u07aa\u0782\u07b0\/\u0784\u07a6\u078b\u07a6\u078d\u07aa \u078e\u07ac\u0782\u07a6\u0787\u07aa\u0782\u07b0", +"Media": "\u0789\u07a9\u0791\u07a8\u0787\u07a7", +"Alternative source": "\u0787\u07a6\u078d\u07b0\u0793\u07a6\u0782\u07ad\u0793\u07a8\u0788\u07b0 \u0790\u07af\u0790\u07b0", +"Paste your embed code below:": "\u0787\u07ac\u0789\u07b0\u0784\u07ac\u0791\u07b0 \u0786\u07af\u0791\u07b0 \u078c\u07a8\u0783\u07a9\u078e\u07a6\u0787\u07a8 \u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Insert video": "\u0788\u07a9\u0791\u07a8\u0787\u07af \u078d\u07aa\u0782\u07b0", +"Poster": "\u0795\u07af\u0790\u07b0\u0793\u07a6\u0783", +"Insert\/edit media": "\u0787\u07a8\u0782\u07b0\u0790\u07a7\u0793\u07b0\/\u0787\u07ac\u0791\u07a8\u0793\u07b0 \u0789\u07a9\u0791\u07a8\u0787\u07a7", +"Embed": "\u0787\u07ac\u0789\u07b0\u0784\u07ac\u0791\u07b0", +"Nonbreaking space": "\u0782\u07ae\u0782\u07b0 \u0784\u07b0\u0783\u07ad\u0786\u07a8\u0782\u07b0 \u0790\u07b0\u0795\u07ad\u0790\u07b0", +"Page break": "\u0795\u07ad\u0796\u07b0 \u0784\u07b0\u0783\u07ad\u0786\u07b0", +"Paste as text": "\u0793\u07ac\u0786\u07b0\u0790\u07b0\u0793\u07b0 \u078e\u07ae\u078c\u07a6\u0781\u07b0 \u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Preview": "\u0795\u07b0\u0783\u07a9\u0788\u07a8\u0787\u07aa", +"Print": "\u0795\u07b0\u0783\u07a8\u0782\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Save": "\u0790\u07ad\u0788\u07b0 \u0786\u07aa\u0783\u07ad", +"Could not find the specified string.": "\u078c\u07a8\u0794\u07a6 \u0780\u07af\u0787\u07b0\u078b\u07a6\u0788\u07a7 \u078d\u07a6\u078a\u07aa\u0792\u07ac\u0787\u07b0 \u0782\u07aa\u078a\u07ac\u0782\u07aa\u0782\u07aa", +"Replace": "\u0784\u07a6\u078b\u07a6\u078d\u07aa \u0786\u07aa\u0783\u07ad", +"Next": "\u078a\u07a6\u0780\u07a6\u078c\u07a6\u0781\u07b0", +"Whole words": "\u0784\u07a6\u0790\u07b0\u078c\u07a6\u0787\u07b0 \u0787\u07ac\u0787\u07b0\u0786\u07ae\u0781\u07b0", +"Find and replace": "\u0780\u07af\u078b\u07aa\u0789\u07a6\u0781\u07b0\u078a\u07a6\u0780\u07aa \u0784\u07a6\u078b\u07a6\u078d\u07aa \u0786\u07aa\u0783\u07aa\u0782\u07b0", +"Replace with": "\u0784\u07a6\u078b\u07a6\u078d\u07aa\u078e\u07a6\u0787\u07a8 \u0784\u07ad\u0782\u07aa\u0782\u07b0 \u0786\u07aa\u0783\u07a7\u0782\u07a9", +"Find": "\u0780\u07af\u078b\u07a7", +"Replace all": "\u0780\u07aa\u0783\u07a8\u0780\u07a7 \u0787\u07ac\u0787\u07b0\u0797\u07ac\u0787\u07b0 \u0784\u07a6\u078b\u07a6\u078d\u07aa \u0786\u07aa\u0783\u07ad", +"Match case": "\u0786\u07ad\u0790\u07b0 \u0787\u07a6\u0781\u07b0 \u0784\u07a6\u078d\u07a7", +"Prev": "\u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0", +"Spellcheck": "\u0786\u07aa\u0781\u07b0 \u0780\u07af\u078b\u07a7", +"Finish": "\u0782\u07a8\u0782\u07b0\u0789\u07a7", +"Ignore all": "\u0780\u07aa\u0783\u07a8\u0780\u07a7 \u0787\u07ac\u0787\u07b0\u0797\u07ac\u0787\u07b0 \u078b\u07ab\u0786\u07ae\u0781\u07b0\u078d\u07a7", +"Ignore": "\u078b\u07ab\u0786\u07ae\u0781\u07b0\u078d\u07a7", +"Add to Dictionary": "\u0783\u07a6\u078b\u07a9\u078a\u07a6\u0781\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa\u0786\u07aa\u0783\u07ad", +"Insert row before": "\u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0 \u0783\u07af\u0787\u07ac\u0787\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa \u0786\u07aa\u0783\u07ad", +"Rows": "\u0783\u07af", +"Height": "\u078b\u07a8\u078e\u07aa\u0789\u07a8\u0782\u07b0", +"Paste row after": "\u078a\u07a6\u0780\u07a6\u078c\u07a6\u0781\u07b0 \u0783\u07af \u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Alignment": "\u0787\u07ac\u078d\u07a6\u0787\u07a8\u0782\u07b0\u0789\u07ac\u0782\u07b0\u0793\u07b0", +"Border color": "\u0784\u07af\u0791\u07a6\u0783\u07aa \u0786\u07aa\u078d\u07a6", +"Column group": "\u0786\u07ae\u078d\u07a6\u0789\u07b0 \u078e\u07b0\u0783\u07ab\u0795\u07b0", +"Row": "\u0783\u07af", +"Insert column before": "\u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0 \u0786\u07ae\u078d\u07a6\u0789\u07ac\u0787\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa \u0786\u07aa\u0783\u07ad", +"Split cell": "\u0790\u07ac\u078d\u07b0 \u0788\u07a6\u0786\u07a8\u0786\u07aa\u0783\u07ad", +"Cell padding": "\u0790\u07ac\u078d\u07b0 \u0795\u07ac\u0791\u07a8\u0782\u07b0", +"Cell spacing": "\u0790\u07ac\u078d\u07b0 \u0790\u07b0\u0795\u07ad\u0790\u07a8\u0782\u07b0\u078e", +"Row type": "\u0783\u07af\u078e\u07ac \u0788\u07a6\u0787\u07b0\u078c\u07a6\u0783\u07aa", +"Insert table": "\u0793\u07ad\u0784\u07a6\u078d\u07b0 \u078d\u07aa\u0782\u07b0", +"Body": "\u0784\u07ae\u0791\u07a9", +"Caption": "\u0786\u07ac\u0795\u07b0\u079d\u07a6\u0782\u07b0", +"Footer": "\u078a\u07ab\u0793\u07a6\u0783", +"Delete row": "\u0783\u07af \u078a\u07ae\u0780\u07ad", +"Paste row before": "\u0786\u07aa\u0783\u07a8\u0787\u07a6\u0781\u07b0 \u0783\u07af \u0795\u07ad\u0790\u07b0\u0793\u07b0 \u0786\u07aa\u0783\u07ad", +"Scope": "\u0790\u07b0\u0786\u07af\u0795\u07b0", +"Delete table": "\u0793\u07ad\u0784\u07a6\u078d\u07b0 \u078a\u07ae\u0780\u07ad", +"H Align": "\u0780\u07aa\u0783\u07a6\u0790\u07b0 \u0787\u07ac\u078d\u07a6\u0787\u07a8\u0782\u07b0", +"Top": "\u0789\u07a6\u078c\u07a8", +"Header cell": "\u0780\u07ac\u0791\u07a7 \u0790\u07ac\u078d\u07b0", +"Column": "\u0786\u07ae\u078d\u07a6\u0789\u07b0", +"Row group": "\u0783\u07af \u078e\u07b0\u0783\u07ab\u0795\u07b0", +"Cell": "\u0790\u07ac\u078d\u07b0", +"Middle": "\u0789\u07ac\u078b\u07aa", +"Cell type": "\u0790\u07ac\u078d\u07b0\u078e\u07ac \u0788\u07a6\u0787\u07b0\u078c\u07a6\u0783\u07aa", +"Copy row": "\u0783\u07af \u0786\u07ae\u0795\u07a9\u0786\u07aa\u0783\u07ad", +"Row properties": "\u0783\u07af\u078e\u07ac \u0790\u07a8\u078a\u07a6\u078c\u07a6\u0787\u07b0", +"Table properties": "\u0793\u07ad\u0784\u07a6\u078d\u07b0\u078e\u07ac \u0790\u07a8\u078a\u07a6\u078c\u07a6\u0787\u07b0", +"Bottom": "\u078c\u07a8\u0783\u07a8", +"V Align": "\u078b\u07a8\u078e\u07a6\u0781\u07b0 \u0787\u07ac\u078d\u07a6\u0787\u07a8\u0782\u07b0", +"Header": "\u0780\u07ac\u0791\u07a7", +"Right": "\u0786\u07a6\u0782\u07a7\u078c\u07a6\u0781\u07b0", +"Insert column after": "\u078a\u07a6\u0780\u07a6\u078c\u07a6\u0781\u07b0 \u0786\u07ae\u078d\u07a6\u0789\u07ac\u0787\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa \u0786\u07aa\u0783\u07ad", +"Cols": "\u0786\u07ae\u078d\u07a6\u0789\u07b0", +"Insert row after": "\u078a\u07a6\u0780\u07a6\u078c\u07a6\u0781\u07b0 \u0783\u07af\u0787\u07ac\u0787\u07b0 \u0787\u07a8\u078c\u07aa\u0783\u07aa \u0786\u07aa\u0783\u07ad", +"Width": "\u078a\u07aa\u0785\u07a7\u0789\u07a8\u0782\u07b0", +"Cell properties": "\u0790\u07ac\u078d\u07b0\u078e\u07ac \u0790\u07a8\u078a\u07a6\u078c\u07a6\u0787\u07b0", +"Left": "\u0788\u07a7\u078c\u07a6\u0781\u07b0", +"Cut row": "\u0783\u07af \u0786\u07a6\u0793\u07b0\u0786\u07aa\u0783\u07ad", +"Delete column": "\u0786\u07ae\u078d\u07a6\u0789\u07b0 \u078a\u07ae\u0780\u07ad", +"Center": "\u0789\u07ac\u078b\u07a6\u0781\u07b0", +"Merge cells": "\u0790\u07ac\u078d\u07b0 \u0787\u07ac\u0787\u07b0\u0786\u07aa\u0783\u07ad", +"Insert template": "\u0793\u07ac\u0789\u07b0\u0795\u07b0\u078d\u07ad\u0793\u07b0 \u0787\u07a8\u0782\u07b0\u0790\u07a7\u0793\u07b0 \u0786\u07aa\u0783\u07aa\u0782\u07b0", +"Templates": "\u0793\u07ac\u0789\u07b0\u0795\u07b0\u078d\u07ad\u0793\u07b0\u078c\u07a6\u0787\u07b0", +"Background color": "\u0784\u07ac\u0786\u07b0\u078e\u07b0\u0783\u07a6\u0787\u07aa\u0782\u07b0\u0791\u07b0\u078e\u07ac \u0786\u07aa\u078d\u07a6", +"Custom...": "\u0787\u07a6\u0789\u07a8\u0787\u07b0\u078d\u07a6", +"Custom color": "\u0787\u07a6\u0789\u07a8\u0787\u07b0\u078d\u07a6 \u0786\u07aa\u078d\u07a6", +"No color": "\u0786\u07aa\u078d\u07a6 \u0782\u07aa\u0796\u07a6\u0787\u07b0\u0790\u07a7", +"Text color": "\u0787\u07a6\u0786\u07aa\u0783\u07aa\u078e\u07ac \u0786\u07aa\u078d\u07a6", +"Table of Contents": "\u0780\u07a8\u0789\u07ac\u0782\u07ad \u0784\u07a6\u0787\u07a8\u078c\u07a6\u0787\u07b0", +"Show blocks": "\u0784\u07b0\u078d\u07ae\u0786\u07b0\u078c\u07a6\u0787\u07b0 \u078b\u07a6\u0787\u07b0\u0786\u07a7", +"Show invisible characters": "\u0782\u07aa\u078a\u07ac\u0782\u07b0\u0782\u07a6 \u0787\u07a6\u0786\u07aa\u0783\u07aa\u078c\u07a6\u0787\u07b0 \u078b\u07a6\u0787\u07b0\u0786\u07a7", +"Words: {0}": "\u0784\u07a6\u0790\u07b0: {0}", +"Insert": "\u0787\u07a8\u0782\u07b0\u0790\u07a7\u0793\u07b0", +"File": "\u078a\u07a6\u0787\u07a8\u078d\u07b0", +"Edit": "\u0784\u07a6\u078b\u07a6\u078d\u07aa \u078e\u07ac\u0782\u07a6\u0787\u07aa\u0782\u07b0", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0783\u07a8\u0797\u07b0 \u0793\u07ac\u0786\u07b0\u0790\u07b0\u0793\u07b0 \u0787\u07ad\u0783\u07a8\u0787\u07a7. \u0789\u07ac\u0782\u07ab \u0780\u07af\u078b\u07aa\u0789\u07a6\u0781\u07b0 ALT-F9. \u0793\u07ab\u078d\u07b0\u0784\u07a6\u0783 \u0780\u07af\u078b\u07aa\u0789\u07a6\u0781\u07b0 ALT-F10. \u0787\u07ac\u0780\u07a9 \u0780\u07af\u078b\u07aa\u0789\u07a6\u0781\u07b0 ALT-0", +"Tools": "\u0793\u07ab\u078d\u07b0\u078c\u07a6\u0787\u07b0", +"View": "\u0788\u07a8\u0787\u07aa", +"Table": "\u0793\u07ad\u0784\u07a6\u078d\u07b0", +"Format": "\u078a\u07af\u0789\u07ac\u0793\u07b0" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/el.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/el.js new file mode 100644 index 0000000000..b5f840da87 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/el.js @@ -0,0 +1,261 @@ +tinymce.addI18n('el',{ +"Redo": "\u0395\u03c0\u03b1\u03bd\u03ac\u03bb\u03b7\u03c8\u03b7", +"Undo": "\u0391\u03bd\u03b1\u03af\u03c1\u03b5\u03c3\u03b7", +"Cut": "\u0391\u03c0\u03bf\u03ba\u03bf\u03c0\u03ae", +"Copy": "\u0391\u03bd\u03c4\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae", +"Paste": "\u0395\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7", +"Select all": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03cc\u03bb\u03c9\u03bd", +"New document": "\u039d\u03ad\u03bf \u03ad\u03b3\u03b3\u03c1\u03b1\u03c6\u03bf", +"Ok": "\u0395\u03bd\u03c4\u03ac\u03be\u03b5\u03b9", +"Cancel": "\u0391\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7", +"Visual aids": "O\u03c0\u03c4\u03b9\u03ba\u03ac \u03b2\u03bf\u03b7\u03b8\u03ae\u03bc\u03b1\u03c4\u03b1 ", +"Bold": "\u0388\u03bd\u03c4\u03bf\u03bd\u03b7", +"Italic": "\u03a0\u03bb\u03ac\u03b3\u03b9\u03b1", +"Underline": "\u03a5\u03c0\u03bf\u03b3\u03c1\u03ac\u03bc\u03bc\u03b9\u03c3\u03b7", +"Strikethrough": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03ae \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae", +"Superscript": "\u0395\u03ba\u03b8\u03ad\u03c4\u03b7\u03c2", +"Subscript": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", +"Clear formatting": "\u0391\u03c0\u03b1\u03bb\u03bf\u03b9\u03c6\u03ae \u03bc\u03bf\u03c1\u03c6\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2", +"Align left": "\u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7 \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", +"Align center": "\u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7 \u03c3\u03c4\u03bf \u03ba\u03ad\u03bd\u03c4\u03c1\u03bf", +"Align right": "\u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7 \u03b4\u03b5\u03be\u03b9\u03ac", +"Justify": "\u03a0\u03bb\u03ae\u03c1\u03b7\u03c2 \u03c3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7", +"Bullet list": "\u039b\u03af\u03c3\u03c4\u03b1 \u03bc\u03b5 \u03ba\u03bf\u03c5\u03ba\u03ba\u03af\u03b4\u03b5\u03c2", +"Numbered list": "\u0391\u03c1\u03b9\u03b8\u03bc\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1", +"Decrease indent": "\u039c\u03b5\u03af\u03c9\u03c3\u03b7 \u03b5\u03c3\u03bf\u03c7\u03ae\u03c2", +"Increase indent": "\u0391\u03cd\u03be\u03b7\u03c3\u03b7 \u03b5\u03c3\u03bf\u03c7\u03ae\u03c2", +"Close": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf", +"Formats": "\u039c\u03bf\u03c1\u03c6\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u039f \u03c0\u03b5\u03c1\u03b9\u03b7\u03b3\u03b7\u03c4\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03b9 \u03ac\u03bc\u03b5\u03c3\u03b7 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf \u03c0\u03c1\u03cc\u03c7\u03b5\u03b9\u03c1\u03bf. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03bd\u03c4\u03bf\u03bc\u03b5\u03cd\u03c3\u03b5\u03b9\u03c2 \u03c0\u03bb\u03b7\u03ba\u03c4\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5 Ctrl+X\/C\/V.", +"Headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", +"Header 1": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 1", +"Header 2": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 2", +"Header 3": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 3", +"Header 4": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 4", +"Header 5": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 5", +"Header 6": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 6", +"Headings": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", +"Heading 1": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 1", +"Heading 2": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 2", +"Heading 3": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 3", +"Heading 4": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 4", +"Heading 5": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 5", +"Heading 6": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1 6", +"Preformatted": "\u03a0\u03c1\u03bf\u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03bf", +"Div": "Div", +"Pre": "Pre", +"Code": "\u039a\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2", +"Paragraph": "\u03a0\u03b1\u03c1\u03ac\u03b3\u03c1\u03b1\u03c6\u03bf\u03c2", +"Blockquote": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae \u03c0\u03b1\u03c1\u03ac\u03b8\u03b5\u03c3\u03b7\u03c2", +"Inline": "\u0395\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03c9\u03bc\u03ad\u03bd\u03b7", +"Blocks": "\u03a4\u03bc\u03ae\u03bc\u03b1\u03c4\u03b1", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0397 \u03b5\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03ce\u03c1\u03b1 \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03c0\u03bb\u03bf\u03cd \u03ba\u03b5\u03b9\u03bc\u03ad\u03bd\u03bf\u03c5. \u03a4\u03b1 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03bc\u03b9\u03b1\u03c2 \u03b5\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7\u03c2 \u03b8\u03b1 \u03b5\u03c0\u03b9\u03ba\u03bf\u03bb\u03bb\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03c9\u03c2 \u03b1\u03c0\u03bb\u03cc \u03ba\u03b5\u03af\u03bc\u03b5\u03bd\u03bf \u03cc\u03c3\u03bf \u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03c5\u03c4\u03ae \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03bd\u03b5\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03ae.", +"Font Family": "\u0393\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03bf\u03c3\u03b5\u03b9\u03c1\u03ac", +"Font Sizes": "\u039c\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2", +"Class": "\u039a\u03bb\u03ac\u03c3\u03b7", +"Browse for an image": "\u0391\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1", +"OR": "\u0389", +"Drop an image here": "\u03a1\u03af\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03b5\u03b4\u03ce", +"Upload": "\u039c\u03b5\u03c4\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7", +"Block": "\u03a4\u03bc\u03ae\u03bc\u03b1", +"Align": "\u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7", +"Default": "\u03a0\u03c1\u03bf\u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf", +"Circle": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2", +"Disc": "\u0394\u03af\u03c3\u03ba\u03bf\u03c2", +"Square": "\u03a4\u03b5\u03c4\u03c1\u03ac\u03b3\u03c9\u03bd\u03bf", +"Lower Alpha": "\u03a0\u03b5\u03b6\u03ac \u03bb\u03b1\u03c4\u03b9\u03bd\u03b9\u03ba\u03ac", +"Lower Greek": "\u03a0\u03b5\u03b6\u03ac \u03b5\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac", +"Lower Roman": "\u03a0\u03b5\u03b6\u03ac \u03c1\u03c9\u03bc\u03b1\u03ca\u03ba\u03ac", +"Upper Alpha": "\u039a\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03b1 \u03bb\u03b1\u03c4\u03b9\u03bd\u03b9\u03ba\u03ac", +"Upper Roman": "\u039a\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03b1 \u03c1\u03c9\u03bc\u03b1\u03ca\u03ba\u03ac", +"Anchor": "\u0391\u03b3\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7", +"Name": "\u038c\u03bd\u03bf\u03bc\u03b1", +"Id": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u039f \u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03c1\u03c7\u03af\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03ad\u03bd\u03b1 \u03b3\u03c1\u03ac\u03bc\u03bc\u03b1, \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf \u03bc\u03cc\u03bd\u03bf \u03b1\u03c0\u03cc \u03b3\u03c1\u03ac\u03bc\u03bc\u03b1\u03c4\u03b1, \u03b1\u03c1\u03b9\u03b8\u03bc\u03bf\u03cd\u03c2, \u03c0\u03b1\u03cd\u03bb\u03b5\u03c2, \u03c4\u03b5\u03bb\u03b5\u03af\u03b5\u03c2, \u03ac\u03bd\u03c9 \u03c4\u03b5\u03bb\u03b5\u03af\u03b1 \u03ae \u03c5\u03c0\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03af\u03c3\u03b5\u03b9\u03c2.", +"You have unsaved changes are you sure you want to navigate away?": "\u0388\u03c7\u03b5\u03c4\u03b5 \u03bc\u03b7 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2. \u0395\u03af\u03c3\u03c4\u03b5 \u03b2\u03ad\u03b2\u03b1\u03b9\u03bf\u03b9 \u03cc\u03c4\u03b9 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c6\u03cd\u03b3\u03b5\u03c4\u03b5 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03c3\u03b5\u03bb\u03af\u03b4\u03b1;", +"Restore last draft": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03bf\u03c5 \u03c3\u03c7\u03b5\u03b4\u03af\u03bf\u03c5", +"Special character": "\u0395\u03b9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03b1\u03c2", +"Source code": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bf\u03c2 \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2", +"Insert\/Edit code sample": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 \u03b4\u03b5\u03af\u03b3\u03bc\u03b1\u03c4\u03bf\u03c2 \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1", +"Language": "\u0393\u03bb\u03ce\u03c3\u03c3\u03b1", +"Code sample": "\u0394\u03b5\u03af\u03b3\u03bc\u03b1 \u039a\u03ce\u03b4\u03b9\u03ba\u03b1", +"Color": "\u03a7\u03c1\u03ce\u03bc\u03b1", +"R": "\u03ba", +"G": "\u03a0", +"B": "\u039c", +"Left to right": "\u0391\u03c0\u03cc \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac \u03c0\u03c1\u03bf\u03c2 \u03c4\u03b1 \u03b4\u03b5\u03be\u03b9\u03ac", +"Right to left": "\u0391\u03c0\u03cc \u03b4\u03b5\u03be\u03b9\u03ac \u03c0\u03c1\u03bf\u03c2 \u03c4\u03b1 \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", +"Emoticons": "\u03a6\u03b1\u03c4\u03c3\u03bf\u03cd\u03bb\u03b5\u03c2", +"Document properties": "\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b3\u03b3\u03c1\u03ac\u03c6\u03bf\u03c5", +"Title": "\u03a4\u03af\u03c4\u03bb\u03bf\u03c2", +"Keywords": "\u039b\u03ad\u03be\u03b5\u03b9\u03c2 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac", +"Description": "\u03a0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae", +"Robots": "\u03a1\u03bf\u03bc\u03c0\u03cc\u03c4", +"Author": "\u03a3\u03c5\u03bd\u03c4\u03ac\u03ba\u03c4\u03b7\u03c2", +"Encoding": "\u039a\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", +"Fullscreen": "\u03a0\u03bb\u03ae\u03c1\u03b7\u03c2 \u03bf\u03b8\u03cc\u03bd\u03b7", +"Action": "\u0395\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1", +"Shortcut": "\u03a3\u03c5\u03bd\u03c4\u03cc\u03bc\u03b5\u03c5\u03c3\u03b7", +"Help": "\u0392\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1", +"Address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", +"Focus to menubar": "\u0395\u03c3\u03c4\u03af\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf \u03bc\u03b5\u03bd\u03bf\u03cd", +"Focus to toolbar": "\u0395\u03c3\u03c4\u03af\u03b1\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae \u03b5\u03c1\u03b3\u03b1\u03bb\u03b5\u03af\u03c9\u03bd", +"Focus to element path": "\u0395\u03c3\u03c4\u03af\u03b1\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf\u03c5", +"Focus to contextual toolbar": "\u0395\u03c3\u03c4\u03af\u03b1\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03b1\u03c6\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae \u03b5\u03c1\u03b3\u03b1\u03bb\u03b5\u03af\u03c9\u03bd", +"Insert link (if link plugin activated)": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5 (\u03b5\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03c4\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5)", +"Save (if save plugin activated)": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 (\u03b5\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7\u03c2)", +"Find (if searchreplace plugin activated)": "\u0395\u03cd\u03c1\u03b5\u03c3\u03b7 (\u03b5\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03c4\u03b7\u03c2 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7\u03c2)", +"Plugins installed ({0}):": "\u0395\u03b3\u03ba\u03b1\u03c4\u03b5\u03c3\u03c4\u03b7\u03bc\u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1 ({0}):", +"Premium plugins:": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1 \u03c5\u03c8\u03b7\u03bb\u03ae\u03c2 \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2:", +"Learn more...": "\u039c\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1...", +"You are using {0}": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 {0}", +"Plugins": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1", +"Handy Shortcuts": "\u03a7\u03c1\u03ae\u03c3\u03b9\u03bc\u03b5\u03c2 \u03c3\u03c5\u03bd\u03c4\u03bf\u03bc\u03b5\u03cd\u03c3\u03b5\u03b9\u03c2", +"Horizontal line": "\u039f\u03c1\u03b9\u03b6\u03cc\u03bd\u03c4\u03b9\u03b1 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae", +"Insert\/edit image": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2", +"Image description": "\u03a0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2", +"Source": "\u03a0\u03b7\u03b3\u03ae", +"Dimensions": "\u0394\u03b9\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2", +"Constrain proportions": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b1\u03bd\u03b1\u03bb\u03bf\u03b3\u03b9\u03ce\u03bd", +"General": "\u0393\u03b5\u03bd\u03b9\u03ba\u03ac", +"Advanced": "\u0393\u03b9\u03b1 \u03a0\u03c1\u03bf\u03c7\u03c9\u03c1\u03b7\u03bc\u03ad\u03bd\u03bf\u03c5\u03c2", +"Style": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7", +"Vertical space": "\u039a\u03ac\u03b8\u03b5\u03c4\u03bf \u03b4\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1", +"Horizontal space": "\u039f\u03c1\u03b9\u03b6\u03cc\u03bd\u03c4\u03b9\u03bf \u03b4\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1", +"Border": "\u03a0\u03bb\u03b1\u03af\u03c3\u03b9\u03bf", +"Insert image": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2", +"Image": "\u0395\u03b9\u03ba\u03cc\u03bd\u03b1", +"Image list": "\u039b\u03af\u03c3\u03c4\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03c9\u03bd", +"Rotate counterclockwise": "\u03a0\u03b5\u03c1\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03cc\u03c3\u03c4\u03c1\u03bf\u03c6\u03b1", +"Rotate clockwise": "\u03a0\u03b5\u03c1\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03b4\u03b5\u03be\u03b9\u03cc\u03c3\u03c4\u03c1\u03bf\u03c6\u03b1", +"Flip vertically": "\u0391\u03bd\u03b1\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03ba\u03b1\u03b8\u03ad\u03c4\u03c9\u03c2", +"Flip horizontally": "\u0391\u03bd\u03b1\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03bf\u03c1\u03b9\u03b6\u03bf\u03bd\u03c4\u03af\u03c9\u03c2", +"Edit image": "\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2", +"Image options": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2", +"Zoom in": "\u039c\u03b5\u03b3\u03ad\u03b8\u03c5\u03bd\u03c3\u03b7", +"Zoom out": "\u03a3\u03bc\u03af\u03ba\u03c1\u03c5\u03bd\u03c3\u03b7", +"Crop": "\u03a0\u03b5\u03c1\u03b9\u03ba\u03bf\u03c0\u03ae", +"Resize": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03bc\u03b5\u03b3\u03ad\u03b8\u03bf\u03c5\u03c2", +"Orientation": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03bd\u03b1\u03c4\u03bf\u03bb\u03b9\u03c3\u03bc\u03cc\u03c2", +"Brightness": "\u03a6\u03c9\u03c4\u03b5\u03b9\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1", +"Sharpen": "\u038c\u03be\u03c5\u03bd\u03c3\u03b7", +"Contrast": "\u0391\u03bd\u03c4\u03af\u03b8\u03b5\u03c3\u03b7", +"Color levels": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03b1 \u03c7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2", +"Gamma": "\u0393\u03ac\u03bc\u03bc\u03b1", +"Invert": "\u0391\u03bd\u03c4\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae", +"Apply": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae", +"Back": "\u03a0\u03af\u03c3\u03c9", +"Insert date\/time": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03b7\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1\u03c2\/\u03ce\u03c1\u03b1\u03c2", +"Date\/time": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1\/\u03ce\u03c1\u03b1", +"Insert link": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5", +"Insert\/edit link": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5", +"Text to display": "\u039a\u03b5\u03af\u03bc\u03b5\u03bd\u03bf \u03b3\u03b9\u03b1 \u03b5\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7", +"Url": "URL", +"Target": "\u03a0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2", +"None": "\u039a\u03b1\u03bc\u03af\u03b1", +"New window": "\u039d\u03ad\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf", +"Remove link": "\u0391\u03c6\u03b1\u03af\u03c1\u03b5\u03c3\u03b7 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5", +"Anchors": "\u0386\u03b3\u03ba\u03c5\u03c1\u03b5\u03c2", +"Link": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf\u03c2", +"Paste or type a link": "\u0395\u03c0\u03b9\u03ba\u03bf\u03bb\u03bb\u03ae\u03c3\u03c4\u03b5 \u03ae \u03c0\u03bb\u03b7\u03ba\u03c4\u03c1\u03bf\u03bb\u03bf\u03b3\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c0\u03bf\u03c5 \u03b5\u03b9\u03c3\u03ac\u03c7\u03b8\u03b7\u03ba\u03b5 \u03c0\u03b9\u03b8\u03b1\u03bd\u03ce\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 email. \u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf \u03c0\u03c1\u03cc\u03b8\u03b7\u03bc\u03b1 mailto:;", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c0\u03bf\u03c5 \u03b5\u03b9\u03c3\u03ac\u03c7\u03b8\u03b7\u03ba\u03b5 \u03c0\u03b9\u03b8\u03b1\u03bd\u03ce\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf\u03c2. \u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf \u03c0\u03c1\u03cc\u03b8\u03b7\u03bc\u03b1 http:\/\/;", +"Link list": "\u039b\u03af\u03c3\u03c4\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03c9\u03bd", +"Insert video": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", +"Insert\/edit video": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", +"Insert\/edit media": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1 media", +"Alternative source": "\u0395\u03bd\u03b1\u03bb\u03bb\u03b1\u03ba\u03c4\u03b9\u03ba\u03ae \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7", +"Poster": "\u0391\u03c6\u03af\u03c3\u03b1", +"Paste your embed code below:": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03c9\u03bc\u03ad\u03bd\u03bf \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9:", +"Embed": "\u0395\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7", +"Media": "\u039c\u03ad\u03c3\u03b1 (\u03bc\u03af\u03bd\u03c4\u03b9\u03b1)", +"Nonbreaking space": "\u039a\u03b5\u03bd\u03cc \u03c7\u03c9\u03c1\u03af\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae", +"Page break": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03c3\u03b5\u03bb\u03af\u03b4\u03b1\u03c2", +"Paste as text": "\u0395\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7 \u03c9\u03c2 \u03ba\u03b5\u03af\u03bc\u03b5\u03bd\u03bf", +"Preview": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7", +"Print": "\u0395\u03ba\u03c4\u03cd\u03c0\u03c9\u03c3\u03b7", +"Save": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7", +"Find": "\u0395\u03cd\u03c1\u03b5\u03c3\u03b7", +"Replace with": "\u0391\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03bc\u03b5", +"Replace": "\u0391\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", +"Replace all": "\u0391\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03cc\u03bb\u03c9\u03bd", +"Prev": "\u03a0\u03c1\u03bf\u03b7\u03b3.", +"Next": "\u0395\u03c0\u03cc\u03bc.", +"Find and replace": "\u0395\u03cd\u03c1\u03b5\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", +"Could not find the specified string.": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b1\u03bb\u03c6\u03b1\u03c1\u03b9\u03b8\u03bc\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd.", +"Match case": "\u03a4\u03b1\u03af\u03c1\u03b9\u03b1\u03c3\u03bc\u03b1 \u03c0\u03b5\u03b6\u03ce\u03bd\/\u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03c9\u03bd", +"Whole words": "\u039f\u03bb\u03cc\u03ba\u03bb\u03b7\u03c1\u03b5\u03c2 \u03bb\u03ad\u03be\u03b5\u03b9\u03c2", +"Spellcheck": "\u039f\u03c1\u03b8\u03bf\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 ", +"Ignore": "\u03a0\u03b1\u03c1\u03ac\u03b2\u03bb\u03b5\u03c8\u03b7", +"Ignore all": "\u03a0\u03b1\u03c1\u03ac\u03b2\u03bb\u03b5\u03c8\u03b7 \u03cc\u03bb\u03c9\u03bd", +"Finish": "\u03a4\u03ad\u03bb\u03bf\u03c2", +"Add to Dictionary": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03c3\u03c4\u03bf \u039b\u03b5\u03be\u03b9\u03ba\u03cc", +"Insert table": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c0\u03af\u03bd\u03b1\u03ba\u03b1", +"Table properties": "\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c0\u03af\u03bd\u03b1\u03ba\u03b1", +"Delete table": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03c0\u03af\u03bd\u03b1\u03ba\u03b1", +"Cell": "\u039a\u03b5\u03bb\u03af", +"Row": "\u0393\u03c1\u03b1\u03bc\u03bc\u03ae", +"Column": "\u03a3\u03c4\u03ae\u03bb\u03b7", +"Cell properties": "\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03ba\u03b5\u03bb\u03b9\u03bf\u03cd", +"Merge cells": "\u03a3\u03c5\u03b3\u03c7\u03ce\u03bd\u03b5\u03c5\u03c3\u03b7 \u03ba\u03b5\u03bb\u03b9\u03ce\u03bd", +"Split cell": "\u0394\u03b9\u03b1\u03af\u03c1\u03b5\u03c3\u03b7 \u03ba\u03b5\u03bb\u03b9\u03bf\u03cd", +"Insert row before": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2 \u03b5\u03c0\u03ac\u03bd\u03c9", +"Insert row after": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2 \u03ba\u03ac\u03c4\u03c9", +"Delete row": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2", +"Row properties": "\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2", +"Cut row": "\u0391\u03c0\u03bf\u03ba\u03bf\u03c0\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2", +"Copy row": "\u0391\u03bd\u03c4\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2", +"Paste row before": "\u0395\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2 \u03b5\u03c0\u03ac\u03bd\u03c9", +"Paste row after": "\u0395\u03c0\u03b9\u03ba\u03cc\u03bb\u03bb\u03b7\u03c3\u03b7 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2 \u03ba\u03ac\u03c4\u03c9", +"Insert column before": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c3\u03c4\u03ae\u03bb\u03b7\u03c2 \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", +"Insert column after": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c3\u03c4\u03ae\u03bb\u03b7\u03c2 \u03b4\u03b5\u03be\u03b9\u03ac", +"Delete column": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03c3\u03c4\u03ae\u03bb\u03b7\u03c2", +"Cols": "\u03a3\u03c4\u03ae\u03bb\u03b5\u03c2", +"Rows": "\u0393\u03c1\u03b1\u03bc\u03bc\u03ad\u03c2", +"Width": "\u03a0\u03bb\u03ac\u03c4\u03bf\u03c2", +"Height": "\u038e\u03c8\u03bf\u03c2", +"Cell spacing": "\u0391\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7 \u03ba\u03b5\u03bb\u03b9\u03ce\u03bd", +"Cell padding": "\u0391\u03bd\u03b1\u03c0\u03bb\u03ae\u03c1\u03c9\u03c3\u03b7 \u03ba\u03b5\u03bb\u03b9\u03ce\u03bd", +"Caption": "\u039b\u03b5\u03b6\u03ac\u03bd\u03c4\u03b1", +"Left": "\u0391\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", +"Center": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b1\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7", +"Right": "\u0394\u03b5\u03be\u03b9\u03ac", +"Cell type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03ba\u03b5\u03bb\u03b9\u03bf\u03cd", +"Scope": "\u0388\u03ba\u03c4\u03b1\u03c3\u03b7", +"Alignment": "\u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7", +"H Align": "\u039f\u03c1. \u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7", +"V Align": "\u039a. \u03a3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7", +"Top": "\u039a\u03bf\u03c1\u03c5\u03c6\u03ae", +"Middle": "\u039c\u03ad\u03c3\u03b7", +"Bottom": "\u039a\u03ac\u03c4\u03c9", +"Header cell": "\u039a\u03b5\u03bb\u03af-\u03ba\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1", +"Row group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ce\u03bd", +"Column group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c3\u03c4\u03b7\u03bb\u03ce\u03bd", +"Row type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae\u03c2", +"Header": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b1", +"Body": "\u03a3\u03ce\u03bc\u03b1", +"Footer": "\u03a5\u03c0\u03bf\u03c3\u03ad\u03bb\u03b9\u03b4\u03bf", +"Border color": "\u03a7\u03c1\u03ce\u03bc\u03b1 \u03c0\u03bb\u03b1\u03b9\u03c3\u03af\u03bf\u03c5", +"Insert template": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c0\u03c1\u03bf\u03c4\u03cd\u03c0\u03bf\u03c5 ", +"Templates": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03b1", +"Template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf", +"Text color": "\u03a7\u03c1\u03ce\u03bc\u03b1 \u03ba\u03b5\u03b9\u03bc\u03ad\u03bd\u03bf\u03c5 ", +"Background color": "\u03a7\u03c1\u03ce\u03bc\u03b1 \u03c6\u03cc\u03bd\u03c4\u03bf\u03c5", +"Custom...": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae...", +"Custom color": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf \u03c7\u03c1\u03ce\u03bc\u03b1", +"No color": "\u03a7\u03c9\u03c1\u03af\u03c2 \u03c7\u03c1\u03ce\u03bc\u03b1", +"Table of Contents": "\u03a0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03a0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", +"Show blocks": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c4\u03bc\u03b7\u03bc\u03ac\u03c4\u03c9\u03bd", +"Show invisible characters": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03ba\u03c1\u03c5\u03c6\u03ce\u03bd \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd", +"Words: {0}": "\u039b\u03ad\u03be\u03b5\u03b9\u03c2: {0}", +"{0} words": "{0} \u03bb\u03ad\u03be\u03b5\u03b9\u03c2", +"File": "\u0391\u03c1\u03c7\u03b5\u03af\u03bf", +"Edit": "\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1", +"Insert": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae", +"View": "\u03a0\u03c1\u03bf\u03b2\u03bf\u03bb\u03ae", +"Format": "\u039c\u03bf\u03c1\u03c6\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", +"Table": "\u03a0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2", +"Tools": "\u0395\u03c1\u03b3\u03b1\u03bb\u03b5\u03af\u03b1", +"Powered by {0}": "\u03a4\u03c1\u03bf\u03c6\u03bf\u03b4\u03bf\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae \u0395\u03bc\u03c0\u03bb\u03bf\u03c5\u03c4\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u039a\u03b5\u03b9\u03bc\u03ad\u03bd\u03bf\u03c5. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 ALT-F9 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03bc\u03b5\u03bd\u03bf\u03cd. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 ALT-F10 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b3\u03c1\u03b1\u03bc\u03bc\u03ae \u03b5\u03c1\u03b3\u03b1\u03bb\u03b5\u03af\u03c9\u03bd. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 ALT-0 \u03b3\u03b9\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en.js deleted file mode 100644 index 19324f74cd..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({en:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode_stick":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words:"},visualblocks:{desc:'Show/hide block elements'}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js new file mode 100644 index 0000000000..f32de01724 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js @@ -0,0 +1,261 @@ +tinymce.addI18n('en_CA',{ +"Redo": "Redo", +"Undo": "Undo", +"Cut": "Cut", +"Copy": "Copy", +"Paste": "Paste", +"Select all": "Select all", +"New document": "New document", +"Ok": "Ok", +"Cancel": "Cancel", +"Visual aids": "Visual aids", +"Bold": "Bold", +"Italic": "Italic", +"Underline": "Underline", +"Strikethrough": "Strikethrough", +"Superscript": "Superscript", +"Subscript": "Subscript", +"Clear formatting": "Clear formatting", +"Align left": "Align left", +"Align center": "Align center", +"Align right": "Align right", +"Justify": "Justify", +"Bullet list": "Bullet list", +"Numbered list": "Numbered list", +"Decrease indent": "Decrease indent", +"Increase indent": "Increase indent", +"Close": "Close", +"Formats": "Formats", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.", +"Headers": "Headers", +"Header 1": "Header 1", +"Header 2": "Header 2", +"Header 3": "Header 3", +"Header 4": "Header 4", +"Header 5": "Header 5", +"Header 6": "Header 6", +"Headings": "Headings", +"Heading 1": "Heading 1", +"Heading 2": "Heading 2", +"Heading 3": "Heading 3", +"Heading 4": "Heading 4", +"Heading 5": "Heading 5", +"Heading 6": "Heading 6", +"Preformatted": "Preformatted", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "Paragraph", +"Blockquote": "Blockquote", +"Inline": "Inline", +"Blocks": "Blocks", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.", +"Font Family": "Font Family", +"Font Sizes": "Font Sizes", +"Class": "Class", +"Browse for an image": "Browse for an image", +"OR": "OR", +"Drop an image here": "Drop an image here", +"Upload": "Upload", +"Block": "Blocks", +"Align": "Align", +"Default": "Default", +"Circle": "Circle", +"Disc": "Disc", +"Square": "Square", +"Lower Alpha": "Lower Alpha", +"Lower Greek": "Lower Greek", +"Lower Roman": "Lower Roman", +"Upper Alpha": "Upper Alpha", +"Upper Roman": "Upper Roman", +"Anchor": "Anchor", +"Name": "Name", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID should start with a letter, followed only by letters, numbers, dashes, dots, colons, or underscores.", +"You have unsaved changes are you sure you want to navigate away?": "You have unsaved changes are you sure you want to navigate away?", +"Restore last draft": "Restore last draft", +"Special character": "Special character", +"Source code": "Source code", +"Insert\/Edit code sample": "Insert\/Edit code sample", +"Language": "Language", +"Code sample": "Code sample", +"Color": "Colour", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Left to right", +"Right to left": "Right to left", +"Emoticons": "Emoticons", +"Document properties": "Document properties", +"Title": "Title", +"Keywords": "Keywords", +"Description": "Description", +"Robots": "Robots", +"Author": "Author", +"Encoding": "Encoding", +"Fullscreen": "Fullscreen", +"Action": "Action", +"Shortcut": "Shortcut", +"Help": "Help", +"Address": "Address", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "Insert link (if link plugin activated)", +"Save (if save plugin activated)": "Save (if save plugin activated)", +"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)", +"Plugins installed ({0}):": "Plugins installed ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "Learn more...", +"You are using {0}": "You are using {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Handy Shortcuts", +"Horizontal line": "Horizontal line", +"Insert\/edit image": "Insert\/edit image", +"Image description": "Image description", +"Source": "Source", +"Dimensions": "Dimensions", +"Constrain proportions": "Constrain proportions", +"General": "General", +"Advanced": "Advanced", +"Style": "Style", +"Vertical space": "Vertical space", +"Horizontal space": "Horizontal space", +"Border": "Border", +"Insert image": "Insert image", +"Image": "Image", +"Image list": "Image list", +"Rotate counterclockwise": "Rotate counterclockwise", +"Rotate clockwise": "Rotate clockwise", +"Flip vertically": "Flip vertically", +"Flip horizontally": "Flip horizontally", +"Edit image": "Edit image", +"Image options": "Image options", +"Zoom in": "Zoom in", +"Zoom out": "Zoom out", +"Crop": "Crop", +"Resize": "Resize", +"Orientation": "Orientation", +"Brightness": "Brightness", +"Sharpen": "Sharpen", +"Contrast": "Contrast", +"Color levels": "Colour levels", +"Gamma": "Gamma", +"Invert": "Invert", +"Apply": "Apply", +"Back": "Back", +"Insert date\/time": "Insert date\/time", +"Date\/time": "Date\/time", +"Insert link": "Insert link", +"Insert\/edit link": "Insert\/edit link", +"Text to display": "Text to display", +"Url": "Url", +"Target": "Target", +"None": "None", +"New window": "New window", +"Remove link": "Remove link", +"Anchors": "Anchors", +"Link": "Link", +"Paste or type a link": "Paste or type a link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?", +"Link list": "Link list", +"Insert video": "Insert video", +"Insert\/edit video": "Insert\/edit video", +"Insert\/edit media": "Insert\/edit media", +"Alternative source": "Alternative source", +"Poster": "Poster", +"Paste your embed code below:": "Paste your embed code below:", +"Embed": "Embed", +"Media": "Media", +"Nonbreaking space": "Nonbreaking space", +"Page break": "Page break", +"Paste as text": "Paste as text", +"Preview": "Preview", +"Print": "Print", +"Save": "Save", +"Find": "Find", +"Replace with": "Replace with", +"Replace": "Replace", +"Replace all": "Replace all", +"Prev": "Prev", +"Next": "Next", +"Find and replace": "Find and replace", +"Could not find the specified string.": "Could not find the specified string.", +"Match case": "Match case", +"Whole words": "Whole words", +"Spellcheck": "Spellcheck", +"Ignore": "Ignore", +"Ignore all": "Ignore all", +"Finish": "Finish", +"Add to Dictionary": "Add to Dictionary", +"Insert table": "Insert table", +"Table properties": "Table properties", +"Delete table": "Delete table", +"Cell": "Cell", +"Row": "Row", +"Column": "Column", +"Cell properties": "Cell properties", +"Merge cells": "Merge cells", +"Split cell": "Split cell", +"Insert row before": "Insert row before", +"Insert row after": "Insert row after", +"Delete row": "Delete row", +"Row properties": "Row properties", +"Cut row": "Cut row", +"Copy row": "Copy row", +"Paste row before": "Paste row before", +"Paste row after": "Paste row after", +"Insert column before": "Insert column before", +"Insert column after": "Insert column after", +"Delete column": "Delete column", +"Cols": "Cols", +"Rows": "Rows", +"Width": "Width", +"Height": "Height", +"Cell spacing": "Cell spacing", +"Cell padding": "Cell padding", +"Caption": "Caption", +"Left": "Left", +"Center": "Center", +"Right": "Right", +"Cell type": "Cell type", +"Scope": "Scope", +"Alignment": "Alignment", +"H Align": "H Align", +"V Align": "V Align", +"Top": "Top", +"Middle": "Middle", +"Bottom": "Bottom", +"Header cell": "Header cell", +"Row group": "Row group", +"Column group": "Column group", +"Row type": "Row type", +"Header": "Header", +"Body": "Body", +"Footer": "Footer", +"Border color": "Border colour", +"Insert template": "Insert template", +"Templates": "Templates", +"Template": "Template", +"Text color": "Text colour", +"Background color": "Background colour", +"Custom...": "Custom...", +"Custom color": "Custom colour", +"No color": "No colour", +"Table of Contents": "Table of Contents", +"Show blocks": "Show blocks", +"Show invisible characters": "Show invisible characters", +"Words: {0}": "Words: {0}", +"{0} words": "{0} words", +"File": "File", +"Edit": "Edit", +"Insert": "Insert", +"View": "View", +"Format": "Format", +"Table": "Table", +"Tools": "Tools", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_GB.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_GB.js new file mode 100644 index 0000000000..312698a93b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_GB.js @@ -0,0 +1,261 @@ +tinymce.addI18n('en_GB',{ +"Redo": "Redo", +"Undo": "Undo", +"Cut": "Cut", +"Copy": "Copy", +"Paste": "Paste", +"Select all": "Select all", +"New document": "New document", +"Ok": "Ok", +"Cancel": "Cancel", +"Visual aids": "Visual aids", +"Bold": "Bold", +"Italic": "Italic", +"Underline": "Underline", +"Strikethrough": "Strike-through", +"Superscript": "Superscript", +"Subscript": "Subscript", +"Clear formatting": "Clear formatting", +"Align left": "Align left", +"Align center": "Align centre", +"Align right": "Align right", +"Justify": "Justify", +"Bullet list": "Bullet list", +"Numbered list": "Numbered list", +"Decrease indent": "Decrease indent", +"Increase indent": "Increase indent", +"Close": "Close", +"Formats": "Formats", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.", +"Headers": "Headers", +"Header 1": "Header 1", +"Header 2": "Header 2", +"Header 3": "Header 3", +"Header 4": "Header 4", +"Header 5": "Header 5", +"Header 6": "Header 6", +"Headings": "Headings", +"Heading 1": "Heading 1", +"Heading 2": "Heading 2", +"Heading 3": "Heading 3", +"Heading 4": "Heading 4", +"Heading 5": "Heading 5", +"Heading 6": "Heading 6", +"Preformatted": "Preformatted", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "Paragraph", +"Blockquote": "Blockquote", +"Inline": "Inline", +"Blocks": "Blocks", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.", +"Font Family": "Font Family", +"Font Sizes": "Font Sizes", +"Class": "Class", +"Browse for an image": "Browse for an image", +"OR": "OR", +"Drop an image here": "Drop an image here", +"Upload": "Upload", +"Block": "Block", +"Align": "Align", +"Default": "Default", +"Circle": "Circle", +"Disc": "Disc", +"Square": "Square", +"Lower Alpha": "Lower Alpha", +"Lower Greek": "Lower Greek", +"Lower Roman": "Lower Roman", +"Upper Alpha": "Upper Alpha", +"Upper Roman": "Upper Roman", +"Anchor": "Anchor", +"Name": "Name", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.", +"You have unsaved changes are you sure you want to navigate away?": "You have unsaved changes are you sure you want to navigate away?", +"Restore last draft": "Restore last draft", +"Special character": "Special character", +"Source code": "Source code", +"Insert\/Edit code sample": "Insert\/Edit code sample", +"Language": "Language", +"Code sample": "Code sample", +"Color": "Colour", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Left to right", +"Right to left": "Right to left", +"Emoticons": "Emoticons", +"Document properties": "Document properties", +"Title": "Title", +"Keywords": "Keywords", +"Description": "Description", +"Robots": "Robots", +"Author": "Author", +"Encoding": "Encoding", +"Fullscreen": "Full-screen", +"Action": "Action", +"Shortcut": "Shortcut", +"Help": "Help", +"Address": "Address", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "Insert link (if link plugin activated)", +"Save (if save plugin activated)": "Save (if save plugin activated)", +"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)", +"Plugins installed ({0}):": "Plugins installed ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "Learn more...", +"You are using {0}": "You are using {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Handy Shortcuts", +"Horizontal line": "Horizontal line", +"Insert\/edit image": "Insert\/edit image", +"Image description": "Image description", +"Source": "Source", +"Dimensions": "Dimensions", +"Constrain proportions": "Constrain proportions", +"General": "General", +"Advanced": "Advanced", +"Style": "Style", +"Vertical space": "Vertical space", +"Horizontal space": "Horizontal space", +"Border": "Border", +"Insert image": "Insert image", +"Image": "Image", +"Image list": "Image list", +"Rotate counterclockwise": "Rotate counterclockwise", +"Rotate clockwise": "Rotate clockwise", +"Flip vertically": "Flip vertically", +"Flip horizontally": "Flip horizontally", +"Edit image": "Edit image", +"Image options": "Image options", +"Zoom in": "Zoom in", +"Zoom out": "Zoom out", +"Crop": "Crop", +"Resize": "Resize", +"Orientation": "Orientation", +"Brightness": "Brightness", +"Sharpen": "Sharpen", +"Contrast": "Contrast", +"Color levels": "Colour levels", +"Gamma": "Gamma", +"Invert": "Invert", +"Apply": "Apply", +"Back": "Back", +"Insert date\/time": "Insert date\/time", +"Date\/time": "Date\/time", +"Insert link": "Insert link", +"Insert\/edit link": "Insert\/edit link", +"Text to display": "Text to display", +"Url": "URL", +"Target": "Target", +"None": "None", +"New window": "New window", +"Remove link": "Remove link", +"Anchors": "Anchors", +"Link": "Link", +"Paste or type a link": "Paste or type a link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?", +"Link list": "Link list", +"Insert video": "Insert video", +"Insert\/edit video": "Insert\/edit video", +"Insert\/edit media": "Insert\/edit media", +"Alternative source": "Alternative source", +"Poster": "Poster", +"Paste your embed code below:": "Paste your embed code below:", +"Embed": "Embed", +"Media": "Media", +"Nonbreaking space": "Non-breaking space", +"Page break": "Page break", +"Paste as text": "Paste as text", +"Preview": "Preview", +"Print": "Print", +"Save": "Save", +"Find": "Find", +"Replace with": "Replace with", +"Replace": "Replace", +"Replace all": "Replace all", +"Prev": "Prev", +"Next": "Next", +"Find and replace": "Find and replace", +"Could not find the specified string.": "Could not find the specified string.", +"Match case": "Match case", +"Whole words": "Whole words", +"Spellcheck": "Spell-check", +"Ignore": "Ignore", +"Ignore all": "Ignore all", +"Finish": "Finish", +"Add to Dictionary": "Add to Dictionary", +"Insert table": "Insert table", +"Table properties": "Table properties", +"Delete table": "Delete table", +"Cell": "Cell", +"Row": "Row", +"Column": "Column", +"Cell properties": "Cell properties", +"Merge cells": "Merge cells", +"Split cell": "Split cell", +"Insert row before": "Insert row before", +"Insert row after": "Insert row after", +"Delete row": "Delete row", +"Row properties": "Row properties", +"Cut row": "Cut row", +"Copy row": "Copy row", +"Paste row before": "Paste row before", +"Paste row after": "Paste row after", +"Insert column before": "Insert column before", +"Insert column after": "Insert column after", +"Delete column": "Delete column", +"Cols": "Cols", +"Rows": "Rows", +"Width": "Width", +"Height": "Height", +"Cell spacing": "Cell spacing", +"Cell padding": "Cell padding", +"Caption": "Caption", +"Left": "Left", +"Center": "Centre", +"Right": "Right", +"Cell type": "Cell type", +"Scope": "Scope", +"Alignment": "Alignment", +"H Align": "H Align", +"V Align": "V Align", +"Top": "Top", +"Middle": "Middle", +"Bottom": "Bottom", +"Header cell": "Header cell", +"Row group": "Row group", +"Column group": "Column group", +"Row type": "Row type", +"Header": "Header", +"Body": "Body", +"Footer": "Footer", +"Border color": "Border colour", +"Insert template": "Insert template", +"Templates": "Templates", +"Template": "Template", +"Text color": "Text colour", +"Background color": "Background colour", +"Custom...": "Custom...", +"Custom color": "Custom colour", +"No color": "No colour", +"Table of Contents": "Table of Contents", +"Show blocks": "Show blocks", +"Show invisible characters": "Show invisible characters", +"Words: {0}": "Words: {0}", +"{0} words": "{0} words", +"File": "File", +"Edit": "Edit", +"Insert": "Insert", +"View": "View", +"Format": "Format", +"Table": "Table", +"Tools": "Tools", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_us.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_us.js deleted file mode 100644 index 4e94198e5a..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_us.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({en_us:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode_stick":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words:"},visualblocks:{desc:'Show/hide block elements'}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es.js new file mode 100644 index 0000000000..9cb0e9d53a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es.js @@ -0,0 +1,261 @@ +tinymce.addI18n('es',{ +"Redo": "Rehacer", +"Undo": "Deshacer", +"Cut": "Cortar", +"Copy": "Copiar", +"Paste": "Pegar", +"Select all": "Seleccionar todo", +"New document": "Nuevo documento", +"Ok": "Ok", +"Cancel": "Cancelar", +"Visual aids": "Ayudas visuales", +"Bold": "Negrita", +"Italic": "It\u00e1lica", +"Underline": "Subrayado", +"Strikethrough": "Tachado", +"Superscript": "Super\u00edndice", +"Subscript": "Sub\u00edndice", +"Clear formatting": "Limpiar formato", +"Align left": "Alinear a la izquierda", +"Align center": "Alinear al centro", +"Align right": "Alinear a la derecha", +"Justify": "Justificar", +"Bullet list": "Lista de vi\u00f1etas", +"Numbered list": "Lista numerada", +"Decrease indent": "Disminuir sangr\u00eda", +"Increase indent": "Incrementar sangr\u00eda", +"Close": "Cerrar", +"Formats": "Formatos", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Tu navegador no soporta acceso directo al portapapeles. Por favor usa las teclas Crtl+X\/C\/V de tu teclado", +"Headers": "Encabezados", +"Header 1": "Encabezado 1", +"Header 2": "Encabezado 2 ", +"Header 3": "Encabezado 3", +"Header 4": "Encabezado 4", +"Header 5": "Encabezado 5 ", +"Header 6": "Encabezado 6", +"Headings": "Encabezados", +"Heading 1": "Encabezado 1", +"Heading 2": "Encabezado 2", +"Heading 3": "Encabezado 3", +"Heading 4": "Encabezado 4", +"Heading 5": "Encabezado 5", +"Heading 6": "Encabezado 6", +"Preformatted": "Preformateado", +"Div": "Capa", +"Pre": "Pre", +"Code": "C\u00f3digo", +"Paragraph": "P\u00e1rrafo", +"Blockquote": "Bloque de cita", +"Inline": "en l\u00ednea", +"Blocks": "Bloques", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Pegar est\u00e1 ahora en modo de texto plano. El contenido se pegar\u00e1 como texto plano hasta que desactive esta opci\u00f3n.", +"Font Family": "Familia de fuentes", +"Font Sizes": "Tama\u00f1os de fuente", +"Class": "Clase", +"Browse for an image": "Exporador de imagenes", +"OR": "O", +"Drop an image here": "Arrastre una imagen aqu\u00ed", +"Upload": "Subir", +"Block": "Bloque", +"Align": "Alinear", +"Default": "Por defecto", +"Circle": "C\u00edrculo", +"Disc": "Disco", +"Square": "Cuadrado", +"Lower Alpha": "Inferior Alfa", +"Lower Greek": "Inferior Griega", +"Lower Roman": "Inferior Romana", +"Upper Alpha": "Superior Alfa", +"Upper Roman": "Superior Romana", +"Anchor": "Ancla", +"Name": "Nombre", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Deber\u00eda comenzar por una letra, seguida solo de letras, n\u00fameros, guiones, puntos, dos puntos o guiones bajos.", +"You have unsaved changes are you sure you want to navigate away?": "Tiene cambios sin guardar. \u00bfEst\u00e1 seguro de que quiere salir?", +"Restore last draft": "Restaurar el \u00faltimo borrador", +"Special character": "Car\u00e1cter especial", +"Source code": "C\u00f3digo fuente", +"Insert\/Edit code sample": "Insertar\/editar c\u00f3digo de prueba", +"Language": "Idioma", +"Code sample": "Ejemplo de c\u00f3digo", +"Color": "Color", +"R": "R", +"G": "V", +"B": "A", +"Left to right": "De izquierda a derecha", +"Right to left": "De derecha a izquierda", +"Emoticons": "Emoticonos", +"Document properties": "Propiedades del documento", +"Title": "T\u00edtulo", +"Keywords": "Palabras clave", +"Description": "Descripci\u00f3n", +"Robots": "Robots", +"Author": "Autor", +"Encoding": "Codificaci\u00f3n", +"Fullscreen": "Pantalla completa", +"Action": "Acci\u00f3n", +"Shortcut": "Atajo", +"Help": "Ayuda", +"Address": "Direcci\u00f3n", +"Focus to menubar": "Enfocar la barra del men\u00fa", +"Focus to toolbar": "Enfocar la barra de herramientas", +"Focus to element path": "Enfocar la ruta del elemento", +"Focus to contextual toolbar": "Enfocar la barra de herramientas contextual", +"Insert link (if link plugin activated)": "Insertar enlace (si el complemento de enlace est\u00e1 activado)", +"Save (if save plugin activated)": "Guardar (si el componente de salvar est\u00e1 activado)", +"Find (if searchreplace plugin activated)": "Buscar (si el complemento buscar-remplazar est\u00e1 activado)", +"Plugins installed ({0}):": "Plugins instalados ({0}):", +"Premium plugins:": "Complementos premium:", +"Learn more...": "Aprende m\u00e1s...", +"You are using {0}": "Estas usando {0}", +"Plugins": "Complementos", +"Handy Shortcuts": "Accesos directos", +"Horizontal line": "L\u00ednea horizontal", +"Insert\/edit image": "Insertar\/editar imagen", +"Image description": "Descripci\u00f3n de la imagen", +"Source": "Enlace", +"Dimensions": "Dimensiones", +"Constrain proportions": "Restringir proporciones", +"General": "General", +"Advanced": "Avanzado", +"Style": "Estilo", +"Vertical space": "Espacio vertical", +"Horizontal space": "Espacio horizontal", +"Border": "Borde", +"Insert image": "Insertar imagen", +"Image": "Imagen", +"Image list": "Lista de im\u00e1genes", +"Rotate counterclockwise": "Girar a la izquierda", +"Rotate clockwise": "Girar a la derecha", +"Flip vertically": "Invertir verticalmente", +"Flip horizontally": "Invertir horizontalmente", +"Edit image": "Editar imagen", +"Image options": "Opciones de imagen", +"Zoom in": "Acercar", +"Zoom out": "Alejar", +"Crop": "Recortar", +"Resize": "Redimensionar", +"Orientation": "Orientaci\u00f3n", +"Brightness": "Brillo", +"Sharpen": "Forma", +"Contrast": "Contraste", +"Color levels": "Niveles de color", +"Gamma": "Gamma", +"Invert": "Invertir", +"Apply": "Aplicar", +"Back": "Atr\u00e1s", +"Insert date\/time": "Insertar fecha\/hora", +"Date\/time": "Fecha\/hora", +"Insert link": "Insertar enlace", +"Insert\/edit link": "Insertar\/editar enlace", +"Text to display": "Texto para mostrar", +"Url": "URL", +"Target": "Destino", +"None": "Ninguno", +"New window": "Nueva ventana", +"Remove link": "Quitar enlace", +"Anchors": "Anclas", +"Link": "Enlace", +"Paste or type a link": "Pega o introduce un enlace", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "El enlace que has introducido no parece ser una direcci\u00f3n de correo electr\u00f3nico. Quieres a\u00f1adir el prefijo necesario mailto: ?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "El enlace que has introducido no parece ser una enlace externo. Quieres a\u00f1adir el prefijo necesario http:\/\/ ?", +"Link list": "Lista de enlaces", +"Insert video": "Insertar video", +"Insert\/edit video": "Insertar\/editar video", +"Insert\/edit media": "Insertar\/editar medio", +"Alternative source": "Enlace alternativo", +"Poster": "Miniatura", +"Paste your embed code below:": "Pega tu c\u00f3digo embebido debajo", +"Embed": "Incrustado", +"Media": "Media", +"Nonbreaking space": "Espacio fijo", +"Page break": "Salto de p\u00e1gina", +"Paste as text": "Pegar como texto", +"Preview": "Previsualizar", +"Print": "Imprimir", +"Save": "Guardar", +"Find": "Buscar", +"Replace with": "Reemplazar con", +"Replace": "Reemplazar", +"Replace all": "Reemplazar todo", +"Prev": "Anterior", +"Next": "Siguiente", +"Find and replace": "Buscar y reemplazar", +"Could not find the specified string.": "No se encuentra la cadena de texto especificada", +"Match case": "Coincidencia exacta", +"Whole words": "Palabras completas", +"Spellcheck": "Corrector ortogr\u00e1fico", +"Ignore": "Ignorar", +"Ignore all": "Ignorar todos", +"Finish": "Finalizar", +"Add to Dictionary": "A\u00f1adir al Diccionario", +"Insert table": "Insertar tabla", +"Table properties": "Propiedades de la tabla", +"Delete table": "Eliminar tabla", +"Cell": "Celda", +"Row": "Fila", +"Column": "Columna", +"Cell properties": "Propiedades de la celda", +"Merge cells": "Combinar celdas", +"Split cell": "Dividir celdas", +"Insert row before": "Insertar fila antes", +"Insert row after": "Insertar fila despu\u00e9s ", +"Delete row": "Eliminar fila", +"Row properties": "Propiedades de la fila", +"Cut row": "Cortar fila", +"Copy row": "Copiar fila", +"Paste row before": "Pegar la fila antes", +"Paste row after": "Pegar la fila despu\u00e9s", +"Insert column before": "Insertar columna antes", +"Insert column after": "Insertar columna despu\u00e9s", +"Delete column": "Eliminar columna", +"Cols": "Columnas", +"Rows": "Filas", +"Width": "Ancho", +"Height": "Alto", +"Cell spacing": "Espacio entre celdas", +"Cell padding": "Relleno de celda", +"Caption": "Subt\u00edtulo", +"Left": "Izquierda", +"Center": "Centrado", +"Right": "Derecha", +"Cell type": "Tipo de celda", +"Scope": "\u00c1mbito", +"Alignment": "Alineaci\u00f3n", +"H Align": "Alineamiento Horizontal", +"V Align": "Alineamiento Vertical", +"Top": "Arriba", +"Middle": "Centro", +"Bottom": "Abajo", +"Header cell": "Celda de la cebecera", +"Row group": "Grupo de filas", +"Column group": "Grupo de columnas", +"Row type": "Tipo de fila", +"Header": "Cabecera", +"Body": "Cuerpo", +"Footer": "Pie de p\u00e1gina", +"Border color": "Color del borde", +"Insert template": "Insertar plantilla", +"Templates": "Plantillas", +"Template": "Plantilla", +"Text color": "Color del texto", +"Background color": "Color de fondo", +"Custom...": "Personalizar...", +"Custom color": "Color personalizado", +"No color": "Sin color", +"Table of Contents": "Tabla de contenidos", +"Show blocks": "Mostrar bloques", +"Show invisible characters": "Mostrar caracteres invisibles", +"Words: {0}": "Palabras: {0}", +"{0} words": "{0} palabras", +"File": "Archivo", +"Edit": "Editar", +"Insert": "Insertar", +"View": "Ver", +"Format": "Formato", +"Table": "Tabla", +"Tools": "Herramientas", +"Powered by {0}": "Desarrollado por {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u00c1rea de texto enriquecido. Pulse ALT-F9 para el menu. Pulse ALT-F10 para la barra de herramientas. Pulse ALT-0 para ayuda" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js new file mode 100644 index 0000000000..b0f2019ffe --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js @@ -0,0 +1,261 @@ +tinymce.addI18n('es_MX',{ +"Redo": "Deshacer", +"Undo": "Rehacer", +"Cut": "Cortar", +"Copy": "Copiar", +"Paste": "Pegar", +"Select all": "Seleccionar todo", +"New document": "Nuevo documento", +"Ok": "Aceptar", +"Cancel": "Cancelar", +"Visual aids": "Ayuda visual", +"Bold": "Negrita", +"Italic": "Cursiva", +"Underline": "Subrayado", +"Strikethrough": "Tachado", +"Superscript": "\u00cdndice", +"Subscript": "Sub\u00edndice", +"Clear formatting": "Limpiar formato", +"Align left": "Alinear a la izquierda", +"Align center": "Centrar", +"Align right": "Alinear a la derecha", +"Justify": "Justificar", +"Bullet list": "Lista de vi\u00f1eta", +"Numbered list": "Lista numerada", +"Decrease indent": "Decrementar identado", +"Increase indent": "Incrementar identado", +"Close": "Cerrar", +"Formats": "Formato", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Su navegador no soporta acceso directo al portapapeles. Por favor haga uso de la combinaci\u00f3n de teclas Ctrl+X para cortar, Ctrl+C para copiar y Ctrl+V para pegar con el teclado. ", +"Headers": "Encabezado", +"Header 1": "Encabezado 1", +"Header 2": "Encabezado 2", +"Header 3": "Encabezado 3", +"Header 4": "Encabezado 4", +"Header 5": "Encabezado 5", +"Header 6": "Encabezado 6", +"Headings": "Encabezados", +"Heading 1": "Encabezados 1", +"Heading 2": "Encabezados 2", +"Heading 3": "Encabezados 3", +"Heading 4": "Encabezados 4", +"Heading 5": "Encabezados 5", +"Heading 6": "Encabezados 6", +"Preformatted": "Pre-formateado", +"Div": "Div", +"Pre": "Pre", +"Code": "C\u00f3digo", +"Paragraph": "P\u00e1rrafo", +"Blockquote": "Blockquote", +"Inline": "En l\u00ednea", +"Blocks": "Bloque", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Se pegar\u00e1 en texto plano. El contenido se pegar\u00e1 como texto plano hasta que desactive esta opci\u00f3n.", +"Font Family": "Tipo de letra", +"Font Sizes": "Tama\u00f1o de letra", +"Class": "Clase", +"Browse for an image": "Ver por imagen", +"OR": "OR", +"Drop an image here": "Arrastra una imagen aqu\u00ed", +"Upload": "Subir", +"Block": "Bloque", +"Align": "Alineaci\u00f3n", +"Default": "Por defecto", +"Circle": "Circulo", +"Disc": "Disco", +"Square": "Cuadro", +"Lower Alpha": "Alfa min\u00fascula", +"Lower Greek": "Griega min\u00fascula", +"Lower Roman": "Romano min\u00fascula", +"Upper Alpha": "Alfa may\u00fascula", +"Upper Roman": "May\u00fascula Romana", +"Anchor": "Anclar", +"Name": "Nombre", +"Id": "Identificador", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "El Identificador debe comenzar con una letra, seguido solo por letras, n\u00fameros, puntos, guiones medios o guiones bajos. ", +"You have unsaved changes are you sure you want to navigate away?": "No se han guardado los cambios. \u00bfSeguro que desea abandonar la p\u00e1gina?", +"Restore last draft": "Restaurar el \u00faltimo borrador", +"Special character": "Caracter especial", +"Source code": "C\u00f3digo fuente", +"Insert\/Edit code sample": "Insertar\/Editar c\u00f3digo muestra", +"Language": "idioma", +"Code sample": "C\u00f3digo muestra", +"Color": "Color", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Izquierda a derecha", +"Right to left": "Derecha a Izquierda", +"Emoticons": "Emoticones", +"Document properties": "Propiedades del documento", +"Title": "T\u00edtulo", +"Keywords": "Palabras clave", +"Description": "Descripci\u00f3n ", +"Robots": "Robots", +"Author": "Autor", +"Encoding": "Codificaci\u00f3n", +"Fullscreen": "Pantalla completa", +"Action": "Acci\u00f3n", +"Shortcut": "Atajo", +"Help": "Ayuda", +"Address": "Direcci\u00f3n", +"Focus to menubar": "Enfocar en barra de menu", +"Focus to toolbar": "Enfocar en barra de herramientas", +"Focus to element path": "Enfocar ruta del elemento", +"Focus to contextual toolbar": "Enfocar en barra de herramientas contextual", +"Insert link (if link plugin activated)": "Insertar enlace (si enlace del plugin est\u00e1 activo)", +"Save (if save plugin activated)": "Guardar (si el plugin guardar est\u00e1 activo)", +"Find (if searchreplace plugin activated)": "Buscar (si el plugin buscar\/reemplazar est\u00e1 activo)", +"Plugins installed ({0}):": "Plugins instalados ({0}):", +"Premium plugins:": "Plugins premium:", +"Learn more...": "Aprende m\u00e1s...", +"You are using {0}": "est\u00e1s usando {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Atajos \u00fatiles", +"Horizontal line": "L\u00ednea Horizontal", +"Insert\/edit image": "Insertar\/editar imagen", +"Image description": "Descripci\u00f3n de imagen", +"Source": "Origen", +"Dimensions": "Dimensiones", +"Constrain proportions": "Restringir proporciones", +"General": "General", +"Advanced": "Avanzado", +"Style": "Estilo", +"Vertical space": "Espacio vertical", +"Horizontal space": "Espacio horizontal", +"Border": "Borde", +"Insert image": "Insertar imagen", +"Image": "Imagen", +"Image list": "Lista de im\u00e1genes", +"Rotate counterclockwise": "Rotar en sentido contrario a las manecillas", +"Rotate clockwise": "Rotar en sentido de las manecillas", +"Flip vertically": "Voltear verticalmente", +"Flip horizontally": "Volter horizontalmente", +"Edit image": "Editar imagen", +"Image options": "Opciones de la imagen", +"Zoom in": "Acercar", +"Zoom out": "Alejar", +"Crop": "Recortar", +"Resize": "Cambiar tama\u00f1o", +"Orientation": "Orientaci\u00f3n", +"Brightness": "Brillo", +"Sharpen": "Nitidez", +"Contrast": "Contraste", +"Color levels": "Niveles de Color", +"Gamma": "Gamma", +"Invert": "Invertir", +"Apply": "Aplicar", +"Back": "Regresar", +"Insert date\/time": "Insertar fecha\/hora", +"Date\/time": "Fecha\/hora", +"Insert link": "Insertar enlace", +"Insert\/edit link": "Inserta\/editar enlace", +"Text to display": "Texto a mostrar", +"Url": "Url", +"Target": "Objetivo", +"None": "Ninguno", +"New window": "Nueva ventana", +"Remove link": "Eliminar elnace", +"Anchors": "Anclas", +"Link": "Enlace", +"Paste or type a link": "Pega o escribe un enlace", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "El URL que ha insertado tiene formato de correo electr\u00f3nico. \u00bfDesea agregar con prefijo \"mailto:\"?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "El URL que ha ingresado es un enlace externo. \u00bfDesea agregar el prefijo \"http:\/\/\"?", +"Link list": "Lista de enlaces", +"Insert video": "Insertar video", +"Insert\/edit video": "Insertar\/editar video", +"Insert\/edit media": "Insertar\/editar multimedia", +"Alternative source": "Fuente alternativa", +"Poster": "Cartel", +"Paste your embed code below:": "Pegue su c\u00f3digo de inserci\u00f3n abajo:", +"Embed": "Incrustar", +"Media": "Multimedia", +"Nonbreaking space": "Espacio de no separaci\u00f3n", +"Page break": "Salto de p\u00e1gina ", +"Paste as text": "Copiar como texto", +"Preview": "Vista previa ", +"Print": "Imprimir", +"Save": "Guardar", +"Find": "Buscar", +"Replace with": "Remplazar con", +"Replace": "Remplazar", +"Replace all": "Remplazar todo", +"Prev": "Anterior", +"Next": "Siguiente", +"Find and replace": "Buscar y reemplazar", +"Could not find the specified string.": "No se ha encontrado la cadena especificada.", +"Match case": "Coincidencia", +"Whole words": "Palabras completas", +"Spellcheck": "Revisi\u00f3n ortogr\u00e1fica", +"Ignore": "Ignorar", +"Ignore all": "Ignorar todo", +"Finish": "Terminar", +"Add to Dictionary": "Agregar al diccionario ", +"Insert table": "Insertar tabla", +"Table properties": "Propiedades de tabla", +"Delete table": "Eliminar tabla", +"Cell": "Celda", +"Row": "Rengl\u00f3n ", +"Column": "Columna", +"Cell properties": "Propiedades de celda", +"Merge cells": "Unir celdas", +"Split cell": "Dividir celdas", +"Insert row before": "Insertar rengl\u00f3n antes", +"Insert row after": "Insertar rengl\u00f3n despu\u00e9s", +"Delete row": "Eliminar rengl\u00f3n ", +"Row properties": "Propiedades del rengl\u00f3n ", +"Cut row": "Cortar renglon", +"Copy row": "Copiar rengl\u00f3n ", +"Paste row before": "Pegar rengl\u00f3n antes", +"Paste row after": "Pegar rengl\u00f3n despu\u00e9s", +"Insert column before": "Insertar columna antes", +"Insert column after": "Insertar columna despu\u00e9s", +"Delete column": "Eliminar columna", +"Cols": "Columnas", +"Rows": "Renglones ", +"Width": "Ancho", +"Height": "Alto", +"Cell spacing": "Espacio entre celdas", +"Cell padding": "Relleno de la celda", +"Caption": "Subt\u00edtulo", +"Left": "Izquierda", +"Center": "Centro", +"Right": "Derecha", +"Cell type": "Tipo de celda", +"Scope": "Alcance", +"Alignment": "Alineaci\u00f3n ", +"H Align": "Alineaci\u00f3n Horizontal", +"V Align": "Alineaci\u00f3n Vertical", +"Top": "Arriba", +"Middle": "Centrado", +"Bottom": "Abajo", +"Header cell": "Celda de encabezado", +"Row group": "Grupo de renglones", +"Column group": "Grupo de columnas", +"Row type": "Tipo de rengl\u00f3n ", +"Header": "Encabezado", +"Body": "Cuerpo", +"Footer": "Pie", +"Border color": "Color del borde", +"Insert template": "Insertar plantilla", +"Templates": "Plantilla", +"Template": "Plantilla", +"Text color": "Color de letra", +"Background color": "Color de fondo", +"Custom...": "Personalizar", +"Custom color": "Perzonalizar color", +"No color": "Sin color", +"Table of Contents": "Tabla de Contenidos", +"Show blocks": "Mostrar bloques", +"Show invisible characters": "Mostrar caracteres invisibles", +"Words: {0}": "Palabras:{0}", +"{0} words": "{0} palabras", +"File": "Archivo", +"Edit": "Editar", +"Insert": "Insertar", +"View": "Vistas", +"Format": "Formato", +"Table": "Tabla", +"Tools": "Herramientas", +"Powered by {0}": "Creado con {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Presione dentro del \u00e1rea de texto ALT-F9 para invocar el men\u00fa, ALT-F10 para la barra de herramientas y ALT-0 para la ayuda." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/et.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/et.js new file mode 100644 index 0000000000..c6beeeb510 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/et.js @@ -0,0 +1,261 @@ +tinymce.addI18n('et',{ +"Redo": "Tee uuesti", +"Undo": "V\u00f5ta tagasi", +"Cut": "L\u00f5ika", +"Copy": "Kopeeri", +"Paste": "Kleebi", +"Select all": "Vali k\u00f5ik", +"New document": "Uus dokument", +"Ok": "Ok", +"Cancel": "Katkesta", +"Visual aids": "N\u00e4itevahendid", +"Bold": "Rasvane", +"Italic": "Kaldkiri", +"Underline": "Allakriipsutatud", +"Strikethrough": "L\u00e4bikriipsutatud", +"Superscript": "\u00dclaindeks", +"Subscript": "Alaindeks", +"Clear formatting": "Puhasta vorming", +"Align left": "Joonda vasakule", +"Align center": "Joonda keskele", +"Align right": "Joonda paremale", +"Justify": "Joonda r\u00f6\u00f6pselt", +"Bullet list": "J\u00e4rjestamata loend", +"Numbered list": "J\u00e4rjestatud loend", +"Decrease indent": "V\u00e4henda taanet", +"Increase indent": "Suurenda taanet", +"Close": "Sulge", +"Formats": "Vormingud", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Sinu veebilehitseja ei toeta otsest ligip\u00e4\u00e4su l\u00f5ikelauale. Palun kasuta selle asemel klaviatuuri kiirk\u00e4sklusi Ctrl+X\/C\/V.", +"Headers": "P\u00e4ised", +"Header 1": "Pealkiri 1", +"Header 2": "Pealkiri 2", +"Header 3": "Pealkiri 3", +"Header 4": "Pealkiri 4", +"Header 5": "Pealkiri 5", +"Header 6": "Pealkiri 6", +"Headings": "Pealkirjad", +"Heading 1": "Pealkiri 1", +"Heading 2": "Pealkiri 2", +"Heading 3": "Pealkiri 3", +"Heading 4": "Pealkiri 4", +"Heading 5": "Pealkiri 5", +"Heading 6": "Pealkiri 6", +"Preformatted": "Eelvormindaud", +"Div": "Sektsioon", +"Pre": "Eelvormindatud", +"Code": "Kood", +"Paragraph": "L\u00f5ik", +"Blockquote": "Plokktsitaat", +"Inline": "Reasisene", +"Blocks": "Plokid", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Asetamine on n\u00fc\u00fcd tekstire\u017eiimis. Sisu asetatakse n\u00fc\u00fcd lihttekstina, kuni sa l\u00fclitad selle valiku v\u00e4lja.", +"Font Family": "Kirjastiilid", +"Font Sizes": "Kirja suurused", +"Class": "Klass", +"Browse for an image": "Sirvi pilte", +"OR": "V\u00d5I", +"Drop an image here": "Kukuta pilt siia", +"Upload": "\u00dcles laadimine", +"Block": "Plokk", +"Align": "Joonda", +"Default": "Vaikimisi", +"Circle": "Ring", +"Disc": "Ketas", +"Square": "Ruut", +"Lower Alpha": "V\u00e4iket\u00e4hed (a, b, c)", +"Lower Greek": "Kreeka v\u00e4iket\u00e4hed (\u03b1, \u03b2, \u03b3)", +"Lower Roman": "Rooma v\u00e4iket\u00e4hed (i, ii, iii)", +"Upper Alpha": "Suurt\u00e4hed (A, B, C)", +"Upper Roman": "Rooma suurt\u00e4hed (I, II, III)", +"Anchor": "Ankur", +"Name": "Nimi", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID peaks algama t\u00e4hega ning sellele peaks j\u00e4rgnema ainult t\u00e4hed, arvud, sidekriipsud, punktid, koolonid v\u00f5i alakriipsud.", +"You have unsaved changes are you sure you want to navigate away?": "Sul on salvestamata muudatusi. Oled Sa kindel, et soovid mujale navigeeruda?", +"Restore last draft": "Taasta viimane mustand", +"Special character": "Erim\u00e4rk", +"Source code": "L\u00e4htekood", +"Insert\/Edit code sample": "Sisesta\/muuda koodin\u00e4idis", +"Language": "Keel", +"Code sample": "Koodi n\u00e4idis", +"Color": "V\u00e4rv", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Vasakult paremale", +"Right to left": "Paremalt vasakule", +"Emoticons": "Emotikonid", +"Document properties": "Dokumendi omadused", +"Title": "Pealkiri", +"Keywords": "M\u00e4rks\u00f5nad", +"Description": "Kirjeldus", +"Robots": "Robotid", +"Author": "Autor", +"Encoding": "M\u00e4rgistik", +"Fullscreen": "T\u00e4isekraan", +"Action": "Tegevus", +"Shortcut": "Otsetee", +"Help": "Abiinfo", +"Address": "Aadress", +"Focus to menubar": "Fookus men\u00fc\u00fcribale", +"Focus to toolbar": "Fookus t\u00f6\u00f6riistaribale", +"Focus to element path": "Fookus elemendi asukohale", +"Focus to contextual toolbar": "Fookus kontekstimen\u00fc\u00fcle", +"Insert link (if link plugin activated)": "Sisesta link (kui lingi plugin on aktiveeritud)", +"Save (if save plugin activated)": "Salvesta (kui salvestamise plugin on aktiveeritud)", +"Find (if searchreplace plugin activated)": "Otsi (kui plugin searchreplace on aktiveeritud)", +"Plugins installed ({0}):": "Pluginad on paigaldatud ({0}):", +"Premium plugins:": "Tasulised pluginad:", +"Learn more...": "Vaata lisainfot...", +"You are using {0}": "Sa kasutad {0}", +"Plugins": "Pluginad", +"Handy Shortcuts": "Mugavad otseteed", +"Horizontal line": "Horisontaaljoon", +"Insert\/edit image": "Lisa\/muuda pilt", +"Image description": "Pildi kirjeldus", +"Source": "Allikas", +"Dimensions": "M\u00f5\u00f5tmed", +"Constrain proportions": "S\u00e4ilita kuvasuhe", +"General": "\u00dcldine", +"Advanced": "T\u00e4iendavad seaded", +"Style": "Stiil", +"Vertical space": "P\u00fcstine vahe", +"Horizontal space": "Reavahe", +"Border": "\u00c4\u00e4ris", +"Insert image": "Lisa pilt", +"Image": "Pilt", +"Image list": "Piltide nimekiri", +"Rotate counterclockwise": "P\u00f6\u00f6ra vastup\u00e4eva", +"Rotate clockwise": "P\u00f6\u00f6ra p\u00e4rip\u00e4eva", +"Flip vertically": "Peegelda vertikaalselt", +"Flip horizontally": "Peegelda horisontaalselt", +"Edit image": "Muuda pilti", +"Image options": "Pildi valikud", +"Zoom in": "Suumi sisse", +"Zoom out": "Suumi v\u00e4lja", +"Crop": "L\u00f5ika", +"Resize": "Muuda suurust", +"Orientation": "Suund", +"Brightness": "Heledus", +"Sharpen": "Teravamaks", +"Contrast": "Kontrast", +"Color levels": "V\u00e4rvi tasemed", +"Gamma": "Gamma", +"Invert": "P\u00f6\u00f6ra v\u00e4rvid", +"Apply": "Rakenda", +"Back": "Tagasi", +"Insert date\/time": "Lisa kuup\u00e4ev\/kellaaeg", +"Date\/time": "Kuup\u00e4ev\/kellaaeg", +"Insert link": "Lisa link", +"Insert\/edit link": "Lisa\/muuda link", +"Text to display": "Kuvatav tekst", +"Url": "Viide (url)", +"Target": "Sihtm\u00e4rk", +"None": "Puudub", +"New window": "Uus aken", +"Remove link": "Eemalda link", +"Anchors": "Ankrud", +"Link": "Link", +"Paste or type a link": "Aseta v\u00f5i sisesta link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL, mille sa sisestasid, n\u00e4ib olevat e-posti aadress. Kas sa soovid lisada sellele eesliite mailto: ?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL, mille sa sisestasid, n\u00e4ib olevat v\u00e4line link. Kas sa soovid lisada sellele eesliite http:\/\/ ?", +"Link list": "Linkide nimekiri", +"Insert video": "Lisa video", +"Insert\/edit video": "Lisa\/muuda video", +"Insert\/edit media": "Lisa\/muuda multimeediat", +"Alternative source": "Teine allikas", +"Poster": "Lisaja", +"Paste your embed code below:": "Kleebi oma manustamiskood siia alla:", +"Embed": "Manusta", +"Media": "Multimeedia", +"Nonbreaking space": "T\u00fchim\u00e4rk (nbsp)", +"Page break": "Lehevahetus", +"Paste as text": "Aseta tekstina", +"Preview": "Eelvaade", +"Print": "Tr\u00fcki", +"Save": "Salvesta", +"Find": "Otsi", +"Replace with": "Asendus", +"Replace": "Asenda", +"Replace all": "Asenda k\u00f5ik", +"Prev": "Eelm", +"Next": "J\u00e4rg", +"Find and replace": "Otsi ja asenda", +"Could not find the specified string.": "Ei suutnud leida etteantud s\u00f5net.", +"Match case": "Erista suur- ja v\u00e4iket\u00e4hti", +"Whole words": "Terviks\u00f5nad", +"Spellcheck": "\u00d5igekirja kontroll", +"Ignore": "Eira", +"Ignore all": "Eira k\u00f5iki", +"Finish": "L\u00f5peta", +"Add to Dictionary": "Lisa s\u00f5naraamatusse", +"Insert table": "Lisa tabel", +"Table properties": "Tabeli omadused", +"Delete table": "Kustuta tabel", +"Cell": "Lahter", +"Row": "Rida", +"Column": "Tulp", +"Cell properties": "Lahtri omadused", +"Merge cells": "\u00dchenda lahtrid", +"Split cell": "T\u00fckelda lahter", +"Insert row before": "Lisa rida enne", +"Insert row after": "Lisa rida j\u00e4rele", +"Delete row": "Kustuta rida", +"Row properties": "Rea omadused", +"Cut row": "L\u00f5ika rida", +"Copy row": "Kopeeri rida", +"Paste row before": "Kleebi rida enne", +"Paste row after": "Kleebi rida j\u00e4rele", +"Insert column before": "Lisa tulp enne", +"Insert column after": "Lisa tulp j\u00e4rele", +"Delete column": "Kustuta tulp", +"Cols": "Veerud", +"Rows": "Read", +"Width": "Laius", +"Height": "K\u00f5rgus", +"Cell spacing": "Lahtrivahe", +"Cell padding": "Lahtri sisu ja tabeli \u00e4\u00e4rise vahe", +"Caption": "Alapealkiri", +"Left": "Vasakul", +"Center": "Keskel", +"Right": "Paremal", +"Cell type": "Lahtri t\u00fc\u00fcp", +"Scope": "Ulatus", +"Alignment": "Joondus", +"H Align": "H Joondus", +"V Align": "V Joondus", +"Top": "\u00dcleval", +"Middle": "Keskel", +"Bottom": "All", +"Header cell": "P\u00e4islahter", +"Row group": "Ridade r\u00fchm", +"Column group": "Veergude r\u00fchm", +"Row type": "Rea t\u00fc\u00fcp", +"Header": "P\u00e4is", +"Body": "P\u00f5hiosa", +"Footer": "Jalus", +"Border color": "Piirjoone v\u00e4rv", +"Insert template": "Lisa mall", +"Templates": "Mallid", +"Template": "Mall", +"Text color": "Teksti v\u00e4rv", +"Background color": "Tausta v\u00e4rv", +"Custom...": "Kohandatud...", +"Custom color": "Kohandatud v\u00e4rv", +"No color": "V\u00e4rvi pole", +"Table of Contents": "Sisukord", +"Show blocks": "N\u00e4ita plokke", +"Show invisible characters": "N\u00e4ita peidetud m\u00e4rke", +"Words: {0}": "S\u00f5nu: {0}", +"{0} words": "{0} s\u00f5na", +"File": "Fail", +"Edit": "Muuda", +"Insert": "Sisesta", +"View": "Vaade", +"Format": "Vorming", +"Table": "Tabel", +"Tools": "T\u00f6\u00f6riistad", +"Powered by {0}": "Kasutatud tarkvara {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rikastatud teksti ala. Men\u00fc\u00fc jaoks vajuta ALT-F9. T\u00f6\u00f6riistariba jaoks vajuta ALT-F10. Abi saamiseks vajuta ALT-0." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/eu.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/eu.js new file mode 100644 index 0000000000..c4374394d4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/eu.js @@ -0,0 +1,261 @@ +tinymce.addI18n('eu',{ +"Redo": "Berregin", +"Undo": "Desegin", +"Cut": "Ebaki", +"Copy": "Kopiatu", +"Paste": "Itsatsi", +"Select all": "Hautatu dena", +"New document": "Dokumentu berria", +"Ok": "Ondo", +"Cancel": "Ezeztatu", +"Visual aids": "Laguntza bisualak", +"Bold": "Lodia", +"Italic": "Etzana", +"Underline": "Azpimarratua", +"Strikethrough": "Marratua", +"Superscript": "Goi-indize", +"Subscript": "Azpiindize", +"Clear formatting": "Garbitu formatua", +"Align left": "Lerrokatu ezkerrean", +"Align center": "Lerrokatu erdian", +"Align right": "Lerrokatu eskuinean", +"Justify": "Justifikatuta", +"Bullet list": "Bulet zerrenda", +"Numbered list": "Zerrenda zenbatua", +"Decrease indent": "Txikitu koska", +"Increase indent": "Handitu koska", +"Close": "Itxi", +"Formats": "Formatuak", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Zure nabigatzaileak ez du arbela zuzenean erabiltzeko euskarririk. Mesedez erabili CTRL+X\/C\/V teklatuko lasterbideak.", +"Headers": "Goiburuak", +"Header 1": "1 Goiburua", +"Header 2": "2 Goiburua", +"Header 3": "3 Goiburua", +"Header 4": "4 Goiburua", +"Header 5": "5 Goiburua", +"Header 6": "6 Goiburua", +"Headings": "Izenburuak", +"Heading 1": "1. izenburua", +"Heading 2": "2. izenburua", +"Heading 3": "3. izenburua", +"Heading 4": "4. izenburua", +"Heading 5": "5. izenburua", +"Heading 6": "6. izenburua", +"Preformatted": "Aurreformateatuta", +"Div": "Div", +"Pre": "Pre", +"Code": "Kodea", +"Paragraph": "Paragrafoa", +"Blockquote": "Blockquote", +"Inline": "Lerroan", +"Blocks": "Blokeak", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Itsatsi testu arrunt moduan dago orain. Edukiak testu arruntak bezala itsatsiko dira aukera hau itzaltzen duzunera arte.", +"Font Family": "Letra-tipo familia", +"Font Sizes": "Letra-tamainak", +"Class": "Klasea", +"Browse for an image": "Irudia arakatu", +"OR": "EDO", +"Drop an image here": "Irudia hona ekarri", +"Upload": "Kargatu", +"Block": "Blokea", +"Align": "Alineatu", +"Default": "Lehenetstia", +"Circle": "Zirkulua", +"Disc": "Diskoa", +"Square": "Karratua", +"Lower Alpha": "Behe alfa", +"Lower Greek": "Behe grekoa", +"Lower Roman": "Behe erromatarra", +"Upper Alpha": "Goi alfa", +"Upper Roman": "Goi erromatarra", +"Anchor": "Esteka", +"Name": "Izena", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Ida hizki batekin hasi behar da, jarraian hizkiak, zenbakiak, gidoiak, puntuak, bi-puntu edo azpiko marrak bakarrik izan ditzake.", +"You have unsaved changes are you sure you want to navigate away?": "Gorde gabeko aldaketak dituzu, zihur zaude hemendik irten nahi duzula?", +"Restore last draft": "Leheneratu azken zirriborroa", +"Special character": "Karaktere bereziak", +"Source code": "Iturburu-kodea", +"Insert\/Edit code sample": "Txertatu\/editatu kode adibidea", +"Language": "Hizkuntza", +"Code sample": "Kode adibidea", +"Color": "Kolorea", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Ezkerretik eskuinera", +"Right to left": "Eskuinetik ezkerrera", +"Emoticons": "Irrifartxoak", +"Document properties": "Dokumentuaren propietateak", +"Title": "Titulua", +"Keywords": "Hitz gakoak", +"Description": "Deskribapena", +"Robots": "Robotak", +"Author": "Egilea", +"Encoding": "Encoding", +"Fullscreen": "Pantaila osoa", +"Action": "Akzioa", +"Shortcut": "Laster tekla", +"Help": "Laguntza", +"Address": "Helbidea", +"Focus to menubar": "Fokoa menu-barrara eraman", +"Focus to toolbar": "Fokoa tresna-barrara eraman", +"Focus to element path": "Fokoa elementuaren bidera eraman", +"Focus to contextual toolbar": "Fokoa kontestuko tresna-barrara eraman", +"Insert link (if link plugin activated)": "Lotura txertatu (lotura plugina aktibatuta badago)", +"Save (if save plugin activated)": "Gorde (gordetzeko plugina aktibatuta badago)", +"Find (if searchreplace plugin activated)": "Bilatu (bilatuordezkatu plugina instalatuta badago)", +"Plugins installed ({0}):": "Instalatutako pluginak ({0}):", +"Premium plugins:": "Premium pluginak:", +"Learn more...": "Gehiago ikasi...", +"You are using {0}": "{0} erabiltzen ari zara", +"Plugins": "Pluginak", +"Handy Shortcuts": "Laster-tekla erabilgarriak", +"Horizontal line": "Marra horizontala", +"Insert\/edit image": "Irudia txertatu\/editatu", +"Image description": "Irudiaren deskribapena", +"Source": "Iturburua", +"Dimensions": "Neurriak", +"Constrain proportions": "Zerraditu proportzioak", +"General": "Orokorra", +"Advanced": "Aurreratua", +"Style": "Estiloa", +"Vertical space": "Hutsune bertikala", +"Horizontal space": "Hutsune horizontala", +"Border": "Ertza", +"Insert image": "Irudia txertatu", +"Image": "Irudia", +"Image list": "Irudi zerrenda", +"Rotate counterclockwise": "Erlojuaren aurkako eran biratu", +"Rotate clockwise": "Erlojuaren eran biratu", +"Flip vertically": "Bertikalki irauli", +"Flip horizontally": "Horizontalki irauli", +"Edit image": "Irudia editatu", +"Image options": "Irudiaren aukerak", +"Zoom in": "Zooma handiagotu", +"Zoom out": "Zooma txikiagotu", +"Crop": "Moztu", +"Resize": "Tamaina aldatu", +"Orientation": "Orientazioa", +"Brightness": "Distira", +"Sharpen": "Zorroztu", +"Contrast": "Kontrastatu", +"Color levels": "Kolore mailak", +"Gamma": "Gamma", +"Invert": "Biratu", +"Apply": "Gorde", +"Back": "Atzera", +"Insert date\/time": "Data\/ordua txertatu", +"Date\/time": "Data\/ordua", +"Insert link": "Esteka txertatu", +"Insert\/edit link": "Esteka txertatu\/editatu", +"Text to display": "Bistaratzeko testua", +"Url": "Url", +"Target": "Target", +"None": "Bat ere ez", +"New window": "Lehio berria", +"Remove link": "Kendu esteka", +"Anchors": "Estekak", +"Link": "Lotura", +"Paste or type a link": "Itsatsu edo idatzi lotura", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Sartu duzun URL-ak e-posta helbidea dela dirudi. Nahi duzu dagokion mailto: aurrizkia gehitzea?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Sartu duzun URL-ak kanpoko esteka dela dirudi. Nahi duzu dagokion http:\/\/ aurrizkia gehitzea?", +"Link list": "Loturen zerrenda", +"Insert video": "Bideoa txertatu", +"Insert\/edit video": "Bideoa txertatu\/editatu", +"Insert\/edit media": "Media txertatu\/editatu", +"Alternative source": "Iturburu alternatiboa", +"Poster": "Poster-a", +"Paste your embed code below:": "Itsatsi hemen zure enkapsulatzeko kodea:", +"Embed": "Kapsulatu", +"Media": "Media", +"Nonbreaking space": "Zuriune zatiezina", +"Page break": "Orrialde-jauzia", +"Paste as text": "Itsatsi testu bezala", +"Preview": "Aurrebista", +"Print": "Inprimatu", +"Save": "Gorde", +"Find": "Bilatu", +"Replace with": "Honekin ordeztu", +"Replace": "Ordeztu", +"Replace all": "Ordeztu dena", +"Prev": "Aurrekoa", +"Next": "Hurrengoa", +"Find and replace": "Bilatu eta ordeztu", +"Could not find the specified string.": "Ezin izan da zehaztutako katea aurkitu.", +"Match case": "Maiuskula\/minuskula", +"Whole words": "hitz osoak", +"Spellcheck": "Egiaztapenak", +"Ignore": "Ez ikusi", +"Ignore all": "Ez ikusi guztia", +"Finish": "Amaitu", +"Add to Dictionary": "Hiztegira gehitu", +"Insert table": "Txertatu taula", +"Table properties": "Taularen propietateak", +"Delete table": "Taula ezabatu", +"Cell": "Gelaxka", +"Row": "Errenkada", +"Column": "Zutabea", +"Cell properties": "Gelaxkaren propietateak", +"Merge cells": "Batu gelaxkak", +"Split cell": "Banatu gelaxkak", +"Insert row before": "Txertatu errenkada aurretik", +"Insert row after": "Txertatu errenkada ostean", +"Delete row": "Ezabatu errenkada", +"Row properties": "Errenkadaren propietateak", +"Cut row": "Ebaki errenkada", +"Copy row": "Kopiatu errenkada", +"Paste row before": "Itsatsi errenkada aurretik", +"Paste row after": "Itsatsi errenkada ostean", +"Insert column before": "Txertatu zutabe aurretik", +"Insert column after": "Txertatu zutabea ostean", +"Delete column": "Ezabatu zutabea", +"Cols": "Zutabeak", +"Rows": "Errenkadak", +"Width": "Zabalera", +"Height": "Altuera", +"Cell spacing": "Gelaxka arteko tartea", +"Cell padding": "Gelaxken betegarria", +"Caption": "Epigrafea", +"Left": "Ezkerra", +"Center": "Erdia", +"Right": "Eskuina", +"Cell type": "Gelaxka mota", +"Scope": "Esparrua", +"Alignment": "Lerrokatzea", +"H Align": "Lerrokatze horizontala", +"V Align": "Lerrokatze bertikala", +"Top": "Goian", +"Middle": "Erdian", +"Bottom": "Behean", +"Header cell": "Goiburuko gelaxka", +"Row group": "Lerro taldea", +"Column group": "Zutabe taldea", +"Row type": "Lerro mota", +"Header": "Goiburua", +"Body": "Gorputza", +"Footer": "Oina", +"Border color": "Inguruko marraren kolorea", +"Insert template": "Txertatu txantiloia", +"Templates": "Txantiloiak", +"Template": "Txantiloia", +"Text color": "Testuaren kolorea", +"Background color": "Atzeko kolorea", +"Custom...": "Pertsonalizatu", +"Custom color": "Pertsonalizatutako kolorea", +"No color": "Kolorerik ez", +"Table of Contents": "Edukien taula", +"Show blocks": "Erakutsi blokeak", +"Show invisible characters": "Erakutsi karaktere izkutuak", +"Words: {0}": "Hitzak: {0}", +"{0} words": "{0} hitz", +"File": "Fitxategia", +"Edit": "Editatu", +"Insert": "Sartu", +"View": "Ikusi", +"Format": "Formatua", +"Table": "Taula", +"Tools": "Tresnak", +"Powered by {0}": "{0}rekin egina", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Testu aberastuko area. Sakatu ALT-F9 menurako. Sakatu ALT-F10 tresna-barrarako. Sakatu ALT-0 laguntzarako" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fa_IR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fa_IR.js new file mode 100644 index 0000000000..7ac42b6831 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fa_IR.js @@ -0,0 +1,262 @@ +tinymce.addI18n('fa_IR',{ +"Redo": "\u0628\u0627\u0632 \u0646\u0634\u0627\u0646", +"Undo": "\u0628\u0627\u0632 \u06af\u0631\u062f\u0627\u0646", +"Cut": "\u0628\u0631\u0634", +"Copy": "\u0631\u0648\u0646\u0648\u06cc\u0633\u06cc", +"Paste": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646", +"Select all": "\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647", +"New document": "\u0633\u0646\u062f \u062c\u062f\u06cc\u062f", +"Ok": "\u062a\u0627\u06cc\u06cc\u062f", +"Cancel": "\u0627\u0646\u0635\u0631\u0627\u0641", +"Visual aids": "\u06a9\u0645\u06a9 \u0628\u0635\u0631\u06cc", +"Bold": "\u062f\u0631\u0634\u062a", +"Italic": "\u06a9\u062c", +"Underline": "\u0632\u06cc\u0631 \u062e\u0637", +"Strikethrough": "\u062e\u0637 \u062e\u0648\u0631\u062f\u0647", +"Superscript": "\u0646\u0645\u0627", +"Subscript": "\u067e\u0627\u06cc\u0647", +"Clear formatting": "\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0642\u0627\u0644\u0628 \u0628\u0646\u062f\u06cc", +"Align left": "\u0686\u067e \u0686\u06cc\u0646", +"Align center": "\u0648\u0633\u0637 \u0686\u06cc\u0646", +"Align right": "\u0631\u0627\u0633\u062a \u0686\u06cc\u0646", +"Justify": "\u062a\u0631\u0627\u0632 \u062f\u0648 \u0637\u0631\u0641\u0647", +"Bullet list": "\u0641\u0647\u0631\u0633\u062a \u0646\u0634\u0627\u0646\u0647 \u062f\u0627\u0631", +"Numbered list": "\u0641\u0647\u0631\u0633\u062a \u0634\u0645\u0627\u0631\u0647 \u062f\u0627\u0631", +"Decrease indent": "\u06a9\u0627\u0647\u0634 \u062a\u0648\u0631\u0641\u062a\u06af\u06cc", +"Increase indent": "\u0627\u0641\u0632\u0627\u06cc\u0634 \u062a\u0648\u0631\u0641\u062a\u06af\u06cc", +"Close": "\u0628\u0633\u062a\u0646", +"Formats": "\u0642\u0627\u0644\u0628 \u0647\u0627", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0645\u0631\u0648\u0631\u06af\u0631 \u0634\u0645\u0627 \u062f\u0633\u062a\u0631\u0633\u06cc \u0645\u0633\u062a\u0642\u06cc\u0645 \u0628\u0647 \u06a9\u0644\u06cc\u067e \u0628\u0648\u0631\u062f \u0631\u0627 \u067e\u0634\u062a\u06cc\u0628\u0627\u0646\u06cc \u0646\u0645\u06cc \u06a9\u0646\u062f\u060c \u0644\u0637\u0641\u0627 \u0627\u0632 \u0645\u06cc\u0627\u0646\u0628\u0631\u0647\u0627\u06cc Ctrl+X\/C\/V \u0635\u0641\u062d\u0647 \u06a9\u0644\u06cc\u062f \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0646\u0645\u0627\u06cc\u06cc\u062f . ", +"Headers": "\u0633\u0631 \u0622\u0645\u062f\u0647\u0627", +"Header 1": "\u0633\u0631 \u0622\u0645\u062f 1", +"Header 2": "\u0633\u0631 \u0622\u0645\u062f 2", +"Header 3": "\u0633\u0631 \u0622\u0645\u062f 3", +"Header 4": "\u0633\u0631 \u0622\u0645\u062f 4", +"Header 5": "\u0633\u0631 \u0622\u0645\u062f 5", +"Header 6": "\u0633\u0631 \u0622\u0645\u062f 6", +"Headings": "\u0639\u0646\u0627\u0648\u06cc\u0646", +"Heading 1": "\u0639\u0646\u0648\u0627\u0646 1", +"Heading 2": "\u0639\u0646\u0648\u0627\u0646 2", +"Heading 3": "\u0639\u0646\u0648\u0627\u0646 3", +"Heading 4": "\u0639\u0646\u0648\u0627\u0646 4", +"Heading 5": "\u0639\u0646\u0648\u0627\u0646 5", +"Heading 6": "\u0639\u0646\u0648\u0627\u0646 6", +"Preformatted": "\u0627\u0632 \u067e\u06cc\u0634 \u0642\u0627\u0644\u0628 \u0628\u0646\u062f\u06cc \u0634\u062f\u0647", +"Div": "\u0628\u0644\u0648\u06a9 \u062c\u062f\u0627 \u0633\u0627\u0632 (\u062a\u06af Div)", +"Pre": "\u0628\u0644\u0648\u06a9 \u0645\u062a\u0646 \u0642\u0627\u0644\u0628 \u062f\u0627\u0631 (\u062a\u06af Pre)", +"Code": "\u0628\u0644\u0648\u06a9 \u06a9\u062f\u0646\u0648\u06cc\u0633\u06cc (\u062a\u06a9 Code)", +"Paragraph": "\u067e\u0627\u0631\u0627\u06af\u0631\u0627\u0641 (\u062a\u06af P)", +"Blockquote": "\u0628\u0644\u0648\u06a9 \u0646\u0642\u0644 \u0642\u0648\u0644 (\u062a\u06af BlockQuote)", +"Inline": "\u0631\u0648 \u062e\u0637", +"Blocks": "\u0628\u0644\u0648\u06a9 \u0647\u0627", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0627\u0645\u06a9\u0627\u0646 \u0686\u0633\u0628\u0627\u0646\u062f\u0646\u060c \u062f\u0631 \u062d\u0627\u0644\u062a \u0645\u062a\u0646 \u062e\u0627\u0644\u0635 \u062a\u0646\u0638\u06cc\u0645 \u06af\u0634\u062a\u0647. \u062a\u0627 \u0632\u0645\u0627\u0646 \u062a\u063a\u06cc\u06cc\u0631 \u0627\u06cc\u0646 \u062d\u0627\u0644\u062a\u060c \u0645\u062d\u062a\u0648\u0627\u06cc \u0645\u0648\u0631\u062f \u0686\u0633\u0628\u0627\u0646\u062f\u0646\u060c \u0628\u0647 \u0635\u0648\u0631\u062a \u0645\u062a\u0646 \u062e\u0627\u0644\u0635 \u062e\u0648\u0627\u0647\u062f \u0686\u0633\u0628\u06cc\u062f.", +"Font Family": "\u0646\u0648\u0639 \u0642\u0644\u0645", +"Font Sizes": "\u0627\u0646\u062f\u0627\u0632\u0647\u0621 \u0642\u0644\u0645", +"Class": "\u0631\u062f\u0647", +"Browse for an image": "\u06cc\u0627\u0641\u062a\u0646 \u06cc\u06a9 \u062a\u0635\u0648\u06cc\u0631", +"OR": "\u00ab\u06cc\u0627\u00bb", +"Drop an image here": "\u06cc\u06a9 \u062a\u0635\u0648\u06cc\u0631 \u0627\u06cc\u0646\u062c\u0627 \u0631\u0647\u0627 \u06a9\u0646\u06cc\u062f", +"Upload": "\u0628\u0627\u0631\u06af\u0630\u0627\u0631\u06cc", +"Block": "\u0628\u0644\u0648\u06a9", +"Align": "\u0686\u06cc\u062f\u0645\u0627\u0646", +"Default": "\u067e\u06cc\u0634 \u0641\u0631\u0636", +"Circle": "\u062f\u0627\u06cc\u0631\u0647", +"Disc": "\u062f\u0627\u06cc\u0631\u0647\u0621 \u062a\u0648\u067e\u0631", +"Square": "\u0686\u0647\u0627\u0631 \u06af\u0648\u0634", +"Lower Alpha": "\u062d\u0631\u0648\u0641 \u06a9\u0648\u0686\u06a9", +"Lower Greek": "\u062d\u0631\u0648\u0641 \u06a9\u0648\u0686\u06a9 \u06cc\u0648\u0646\u0627\u0646\u06cc", +"Lower Roman": "\u0627\u0631\u0642\u0627\u0645 \u06a9\u0648\u0686\u06a9 \u0631\u0648\u0645\u06cc", +"Upper Alpha": "\u062d\u0631\u0648\u0641 \u0628\u0632\u0631\u06af", +"Upper Roman": "\u0627\u0631\u0642\u0627\u0645 \u0628\u0632\u0631\u06af \u0631\u0648\u0645\u06cc", +"Anchor": "\u0642\u0644\u0627\u0628", +"Name": "\u0646\u0627\u0645", +"Id": "\u0634\u0646\u0627\u0633\u0647", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u0634\u0646\u0627\u0633\u0647 \u0645\u06cc \u0628\u0627\u06cc\u0633\u062a \u0628\u0627 \u06cc\u06a9 \u062d\u0631\u0641 \u0627\u0644\u0641\u0628\u0627 \u0622\u063a\u0627\u0632 \u0648 \u0628\u0627 \u062f\u0646\u0628\u0627\u0644\u0647 \u0627\u06cc \u0627\u0632 \u062d\u0631\u0648\u0641\u060c \u0627\u0639\u062f\u0627\u062f\u060c \u0639\u0644\u0627\u0645\u062a \u0645\u0650\u0646\u0647\u0627\u060c \u0646\u0642\u0637\u0647\u060c \u062f\u0648 \u0646\u0642\u0637\u0647 \u06cc\u0627 \u062e\u0637 \u062a\u06cc\u0631\u0647 \u0627\u062f\u0627\u0645\u0647 \u06cc\u0627\u0628\u062f.", +"You have unsaved changes are you sure you want to navigate away?": "\u062a\u063a\u06cc\u06cc\u0631\u0627\u062a \u0634\u0645\u0627 \u0630\u062e\u06cc\u0631\u0647 \u0646\u0634\u062f\u0647 \u0627\u0646\u062f\u060c \u0622\u06cc\u0627 \u062c\u0647\u062a \u062e\u0631\u0648\u062c \u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u062f\u0627\u0631\u06cc\u062f\u061f", +"Restore last draft": "\u0628\u0627\u0632\u06cc\u0627\u0628\u06cc \u0622\u062e\u0631\u06cc\u0646 \u067e\u06cc\u0634 \u0646\u0648\u06cc\u0633", +"Special character": "\u0646\u0648\u06cc\u0633\u0647 \u0647\u0627\u06cc \u062e\u0627\u0635", +"Source code": "\u0645\u062a\u0646 \u06a9\u062f \u0645\u0646\u0628\u0639", +"Insert\/Edit code sample": "\u062f\u0631\u062c\/\u0648\u06cc\u0631\u0627\u06cc\u0634 \u0646\u0645\u0648\u0646\u0647\u0621 \u06a9\u062f", +"Language": "\u0632\u0628\u0627\u0646", +"Code sample": "\u0646\u0645\u0648\u0646\u0647 \u06a9\u064f\u062f", +"Color": "\u0631\u0646\u06af", +"R": "\u0642\u0631\u0645\u0632", +"G": "\u0633\u0628\u0632", +"B": "\u0622\u0628\u06cc", +"Left to right": "\u0686\u067e \u0628\u0647 \u0631\u0627\u0633\u062a", +"Right to left": "\u0631\u0627\u0633\u062a \u0628\u0647 \u0686\u067e", +"Emoticons": "\u0635\u0648\u0631\u062a\u06a9 \u0647\u0627", +"Document properties": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0633\u0646\u062f", +"Title": "\u0639\u0646\u0648\u0627\u0646", +"Keywords": "\u0648\u0627\u0698\u06af\u0627\u0646 \u06a9\u0644\u06cc\u062f\u06cc", +"Description": "\u062a\u0648\u0636\u06cc\u062d", +"Robots": "\u0631\u0648\u0628\u0627\u062a\u0647\u0627", +"Author": "\u0645\u0648\u0644\u0641", +"Encoding": "\u06a9\u062f\u06af\u0632\u0627\u0631\u06cc \u0645\u062a\u0646", +"Fullscreen": "\u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647", +"Action": "\u0639\u0645\u0644", +"Shortcut": "\u0645\u06cc\u0627\u0646\u0628\u064f\u0631", +"Help": "\u0631\u0627\u0647\u0646\u0645\u0627", +"Address": "\u0646\u0634\u0627\u0646\u06cc", +"Focus to menubar": "\u062a\u0645\u0631\u06a9\u0632 \u0628\u0631 \u0646\u0648\u0627\u0631 \u0645\u0646\u0648", +"Focus to toolbar": "\u062a\u0645\u0631\u06a9\u0632 \u0628\u0631 \u0646\u0648\u0627\u0631 \u0627\u0628\u0632\u0627\u0631", +"Focus to element path": "\u062a\u0645\u0631\u06a9\u0632 \u0628\u0631 \u0645\u0633\u06cc\u0631 \u0627\u0650\u0644\u0650\u0645\u0627\u0646", +"Focus to contextual toolbar": "\u062a\u0645\u0631\u06a9\u0632 \u0628\u0631 \u0646\u0648\u0627\u0631 \u0627\u0628\u0632\u0627\u0631 \u0645\u062a\u0646\u06cc", +"Insert link (if link plugin activated)": "\u062f\u0631\u062c \u067e\u06cc\u0648\u0646\u062f (\u0627\u06af\u0631 \u0627\u0641\u0632\u0648\u0646\u0647\u0621 \u067e\u06cc\u0648\u0646\u062f \u0641\u0639\u0627\u0644 \u0634\u062f)", +"Save (if save plugin activated)": "\u062b\u0628\u062a\u00a0(\u0627\u06af\u0631 \u0627\u0641\u0632\u0648\u0646\u0647\u0621 \u0630\u062e\u06cc\u0631\u0647 \u0633\u0627\u0632\u06cc \u0641\u0639\u0627\u0644 \u0634\u062f)", +"Find (if searchreplace plugin activated)": "\u06cc\u0627\u0641\u062a\u0646 (\u0627\u06af\u0631 \u0627\u0641\u0632\u0648\u0646\u0647\u0621 \u062c\u0633\u062a\u062c\u0648\/\u062c\u0627\u06cc\u06af\u0632\u06cc\u0646\u06cc \u0641\u0639\u0627\u0644 \u0634\u062f)", +"Plugins installed ({0}):": "\u0627\u0641\u0632\u0648\u0646\u0647 \u0647\u0627\u06cc\u06cc \u06a9\u0647 \u0646\u0635\u0628 \u0634\u062f\u0646\u062f ({0}):", +"Premium plugins:": "\u0627\u0641\u0632\u0648\u0646\u0647 \u0647\u0627\u06cc \u0645\u062e\u0635\u0648\u0635:", +"Learn more...": "\u06cc\u0627\u062f\u06af\u06cc\u0631\u06cc \u0628\u06cc\u0634\u062a\u0631...", +"You are using {0}": "\u0634\u0645\u0627 \u062f\u0631 \u062d\u0627\u0644 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 {0} \u0645\u06cc \u0628\u0627\u0634\u06cc\u062f", +"Plugins": "\u0627\u0641\u0632\u0648\u0646\u0647 \u0647\u0627", +"Handy Shortcuts": "\u0645\u06cc\u0627\u0646\u0628\u064f\u0631\u0647\u0627\u06cc \u0633\u0648\u062f\u0645\u0646\u062f", +"Horizontal line": "\u062e\u0637 \u0627\u0641\u0642\u06cc", +"Insert\/edit image": "\u062f\u0631\u062c\/\u0648\u06cc\u0631\u0627\u06cc\u0634 \u062a\u0635\u0648\u06cc\u0631", +"Image description": "\u062a\u0648\u0635\u06cc\u0641 \u062a\u0635\u0648\u06cc\u0631", +"Source": "\u0645\u0646\u0628\u0639", +"Dimensions": "\u0627\u0628\u0639\u0627\u062f", +"Constrain proportions": "\u062d\u0641\u0638 \u062a\u0646\u0627\u0633\u0628", +"General": "\u0639\u0645\u0648\u0645\u06cc", +"Advanced": "\u067e\u06cc\u0634\u0631\u0641\u062a\u0647", +"Style": "\u0633\u0628\u06a9", +"Vertical space": "\u0641\u0636\u0627\u06cc \u0639\u0645\u0648\u062f\u06cc", +"Horizontal space": "\u0641\u0636\u0627\u06cc \u0627\u0641\u0642\u06cc", +"Border": "\u0644\u0628\u0647", +"Insert image": "\u062f\u0631\u062c \u062a\u0635\u0648\u06cc\u0631", +"Image": "\u062a\u0635\u0648\u06cc\u0631", +"Image list": "\u0641\u0647\u0631\u0633\u062a \u062a\u0635\u0648\u06cc\u0631\u06cc", +"Rotate counterclockwise": "\u062f\u064e\u0648\u064e\u0631\u0627\u0646 \u067e\u0627\u062f \u0633\u0627\u0639\u062a \u06af\u0631\u062f", +"Rotate clockwise": "\u062f\u064e\u0648\u064e\u0631\u0627\u0646 \u0633\u0627\u0639\u062a \u06af\u0631\u062f", +"Flip vertically": "\u0642\u0631\u06cc\u0646\u0647 \u0639\u0645\u0648\u062f\u06cc", +"Flip horizontally": "\u0642\u0631\u06cc\u0646\u0647 \u0627\u0641\u0642\u06cc", +"Edit image": "\u0648\u06cc\u0631\u0627\u0633\u062a \u062a\u0635\u0648\u06cc\u0631", +"Image options": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u062a\u0635\u0648\u06cc\u0631", +"Zoom in": "\u0628\u0632\u0631\u06af \u0646\u0645\u0627\u06cc\u06cc", +"Zoom out": "\u06a9\u0648\u0686\u06a9 \u0646\u0645\u0627\u06cc\u06cc", +"Crop": "\u0628\u064f\u0631\u0634 \u062f\u064f\u0648\u0631", +"Resize": "\u062a\u063a\u06cc\u06cc\u0631 \u0627\u0646\u062f\u0627\u0632\u0647", +"Orientation": "\u06af\u0650\u0631\u0627", +"Brightness": "\u0631\u0648\u0634\u0646\u0627\u06cc\u06cc", +"Sharpen": "\u0628\u0647\u0628\u0648\u062f \u0644\u0628\u0647", +"Contrast": "\u062a\u0636\u0627\u062f \u0631\u0646\u06af", +"Color levels": "\u0633\u0637\u0648\u062d \u0631\u0646\u06af", +"Gamma": "\u06af\u0627\u0645\u0627", +"Invert": "\u0628\u0631\u06af\u0634\u062a \u0631\u0646\u06af", +"Apply": "\u0627\u0650\u0639\u0645\u0627\u0644", +"Back": "\u0628\u0627\u0632\u06af\u0634\u062a", +"Insert date\/time": "\u062f\u0631\u062c \u062a\u0627\u0631\u06cc\u062e\/\u0632\u0645\u0627\u0646", +"Date\/time": "\u062a\u0627\u0631\u06cc\u062e\/\u0632\u0645\u0627\u0646", +"Insert link": "\u062f\u0631\u062c \u067e\u06cc\u0648\u0646\u062f", +"Insert\/edit link": "\u062f\u0631\u062c\/\u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0648\u0646\u062f", +"Text to display": "\u0645\u062a\u0646 \u0646\u0645\u0627\u06cc\u0634\u06cc", +"Url": "\u0622\u062f\u0631\u0633", +"Target": "\u0645\u0642\u0635\u062f", +"None": "\u0647\u06cc\u0686", +"New window": "\u067e\u0646\u062c\u0631\u0647\u0621 \u062c\u062f\u06cc\u062f", +"Remove link": "\u062d\u0630\u0641 \u067e\u06cc\u0648\u0646\u062f", +"Anchors": "\u0642\u0644\u0627\u0628 \u0647\u0627", +"Link": "\u067e\u06cc\u0648\u0646\u062f", +"Paste or type a link": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646 \u06cc\u0627 \u062a\u0627\u06cc\u067e \u067e\u06cc\u0648\u0646\u062f", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0628\u0647 \u0646\u0638\u0631 \u0645\u06cc \u0631\u0633\u062f \u0622\u062f\u0631\u0633 \u0648\u0631\u0648\u062f\u06cc \u06cc\u06a9 \u0631\u0627\u06cc\u0627\u0646\u0627\u0645\u0647 \u0628\u0627\u0634\u062f. \u0622\u06cc\u0627 \u062a\u0645\u0627\u06cc\u0644 \u0628\u0647 \u0627\u0641\u0632\u0648\u0631\u062f\u0646 \u067e\u06cc\u0634\u0648\u0646\u062f mailto: \u062f\u0627\u0631\u06cc\u062f\u061f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0628\u0647 \u0646\u0638\u0631 \u0645\u06cc \u0631\u0633\u062f \u0622\u062f\u0631\u0633 \u0648\u0631\u0648\u062f\u06cc \u0627\u0631\u062c\u0627\u0639\u06cc \u0628\u0647 \u062e\u0627\u0631\u062c \u0627\u0632 \u0627\u06cc\u0646 \u0633\u0627\u06cc\u062a \u0645\u06cc \u0628\u0627\u0634\u062f. \u0622\u06cc\u0627 \u062a\u0645\u0627\u06cc\u0644 \u0628\u0647 \u0627\u0641\u0632\u0648\u0631\u062f\u0646 \u067e\u06cc\u0634\u0648\u0646\u062f http:\/\/ \u062f\u0627\u0631\u06cc\u062f\u061f", +"Link list": "\u0641\u0647\u0631\u0633\u062a \u067e\u06cc\u0648\u0646\u062f", +"Insert video": "\u062f\u0631\u062c \u0648\u06cc\u062f\u06cc\u0648", +"Insert\/edit video": "\u062f\u0631\u062c\/\u0648\u06cc\u0631\u0627\u06cc\u0634 \u0648\u06cc\u062f\u06cc\u0648", +"Insert\/edit media": "\u062f\u0631\u062c\/\u0648\u06cc\u0631\u0627\u06cc\u0634 \u0631\u0633\u0627\u0646\u0647", +"Alternative source": "\u0645\u0646\u0628\u0639 \u062c\u0627\u06cc\u06af\u0632\u06cc\u0646", +"Poster": "\u067e\u0648\u0633\u062a\u0631", +"Paste your embed code below:": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646 \u06a9\u062f \u062c\u0627\u0633\u0627\u0632\u06cc \u0634\u0645\u0627 \u062f\u0631 \u0632\u06cc\u0631: ", +"Embed": "\u062c\u0627\u0633\u0627\u0632\u06cc", +"Media": "\u0631\u0633\u0627\u0646\u0647", +"Nonbreaking space": "\u0641\u0636\u0627\u06cc \u062e\u0627\u0644\u06cc \u0628\u0631\u0634 \u0646\u0627\u067e\u0630\u06cc\u0631", +"Page break": "\u0628\u0631\u0634 \u0635\u0641\u062d\u0647", +"Paste as text": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646 \u0645\u062a\u0646", +"Preview": "\u067e\u06cc\u0634 \u0646\u0645\u0627\u06cc\u0634", +"Print": "\u0686\u0627\u067e", +"Save": "\u0630\u062e\u06cc\u0631\u0647", +"Find": "\u062c\u0633\u062a\u062c\u0648", +"Replace with": "\u062c\u0627\u06cc\u06af\u0632\u06cc\u0646\u06cc \u0628\u0627", +"Replace": "\u062c\u0627\u06cc\u06af\u0632\u06cc\u0646\u06cc", +"Replace all": "\u062c\u0627\u06cc\u06af\u0632\u06cc\u0646 \u0647\u0645\u0647", +"Prev": "\u0642\u0628\u0644\u06cc", +"Next": "\u0628\u0639\u062f\u06cc", +"Find and replace": "\u062c\u0633\u062a\u062c\u0648 \u0648 \u062c\u0627\u06cc\u06af\u0632\u06cc\u0646\u06cc", +"Could not find the specified string.": "\u0631\u0634\u062a\u0647\u0621 \u0645\u0648\u0631\u062f \u0646\u0638\u0631 \u06cc\u0627\u0641\u062a \u0646\u06af\u0631\u062f\u06cc\u062f.", +"Match case": "\u062a\u0637\u0627\u0628\u0642 \u062d\u0631\u0648\u0641", +"Whole words": "\u062a\u0645\u0627\u0645 \u0648\u0627\u0698\u06af\u0627\u0646", +"Spellcheck": "\u0628\u0631\u0631\u0633\u06cc \u0627\u0645\u0644\u0627\u0621", +"Ignore": "\u0628\u06cc \u062e\u06cc\u0627\u0644", +"Ignore all": "\u0628\u06cc \u062e\u06cc\u0627\u0644 \u0647\u0645\u0647", +"Finish": "\u0627\u062a\u0645\u0627\u0645", +"Add to Dictionary": "\u0628\u0647 \u0648\u0627\u0698\u0647 \u0646\u0627\u0645\u0647 \u0628\u06cc \u0627\u0641\u0632\u0627", +"Insert table": "\u062f\u0631\u062c \u062c\u062f\u0648\u0644", +"Table properties": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u062c\u062f\u0648\u0644", +"Delete table": "\u062d\u0630\u0641 \u062c\u062f\u0648\u0644", +"Cell": "\u0633\u0644\u0648\u0644", +"Row": "\u0633\u0637\u0631", +"Column": "\u0633\u062a\u0648\u0646", +"Cell properties": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0633\u0644\u0648\u0644", +"Merge cells": "\u067e\u06cc\u0648\u0646\u062f \u0633\u0644\u0648\u0644 \u0647\u0627", +"Split cell": "\u062c\u062f\u0627 \u0633\u0627\u0632\u06cc \u0633\u0644\u0648\u0644", +"Insert row before": "\u062f\u0631\u062c \u0633\u0637\u0631 \u062f\u0631 \u0628\u0627\u0644\u0627", +"Insert row after": "\u062f\u0631\u062c \u0633\u0637\u0631 \u062f\u0631 \u067e\u0627\u06cc\u06cc\u0646", +"Delete row": "\u062d\u0630\u0641 \u0633\u0637\u0631", +"Row properties": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0633\u0637\u0631", +"Cut row": "\u0628\u0631\u0634 \u0633\u0637\u0631", +"Copy row": "\u0631\u0648\u0646\u0648\u06cc\u0633\u06cc \u0633\u0637\u0631", +"Paste row before": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646 \u0633\u0637\u0631 \u062f\u0631 \u0628\u0627\u0644\u0627", +"Paste row after": "\u0686\u0633\u0628\u0627\u0646\u062f\u0646 \u0633\u0637\u0631 \u062f\u0631 \u067e\u0627\u06cc\u06cc\u0646", +"Insert column before": "\u062f\u0631\u062c \u0633\u062a\u0648\u0646 \u0642\u0628\u0644", +"Insert column after": "\u062f\u0631\u062c \u0633\u062a\u0648\u0646 \u0628\u0639\u062f", +"Delete column": "\u062d\u0630\u0641 \u0633\u062a\u0648\u0646", +"Cols": "\u0633\u062a\u0648\u0646 \u0647\u0627", +"Rows": "\u0633\u0637\u0631 \u0647\u0627", +"Width": "\u0639\u0631\u0636", +"Height": "\u0627\u0631\u062a\u0641\u0627\u0639", +"Cell spacing": "\u0641\u0627\u0635\u0644\u0647 \u0645\u06cc\u0627\u0646 \u0633\u0644\u0648\u0644\u06cc", +"Cell padding": "\u062d\u0627\u0634\u06cc\u0647 \u062f\u0631\u0648\u0646 \u0633\u0644\u0648\u0644\u06cc", +"Caption": "\u0639\u0646\u0648\u0627\u0646", +"Left": "\u0686\u067e", +"Center": "\u0645\u06cc\u0627\u0646\u0647", +"Right": "\u0631\u0627\u0633\u062a", +"Cell type": "\u0646\u0648\u0639 \u0633\u0644\u0648\u0644", +"Scope": "\u062d\u0648\u0632\u0647", +"Alignment": "\u0647\u0645 \u062a\u0631\u0627\u0632\u06cc", +"H Align": "\u062a\u0631\u0627\u0632 \u0627\u0641\u0642\u06cc", +"V Align": "\u062a\u0631\u0627\u0632 \u0639\u0645\u0648\u062f\u06cc", +"Top": "\u0628\u0627\u0644\u0627", +"Middle": "\u0645\u06cc\u0627\u0646\u0647", +"Bottom": "\u067e\u0627\u06cc\u06cc\u0646", +"Header cell": "\u0633\u0644\u0648\u0644 \u0633\u0631 \u0633\u062a\u0648\u0646", +"Row group": "\u06af\u0631\u0648\u0647 \u0633\u0637\u0631\u06cc", +"Column group": "\u06af\u0631\u0648\u0647 \u0633\u062a\u0648\u0646\u06cc", +"Row type": "\u0646\u0648\u0639 \u0633\u0637\u0631", +"Header": "\u0633\u0631 \u0622\u0645\u062f", +"Body": "\u0628\u062f\u0646\u0647", +"Footer": "\u067e\u0627 \u0646\u0648\u0634\u062a", +"Border color": "\u0631\u0646\u06af \u0644\u0628\u0647", +"Insert template": "\u062f\u0631\u062c \u0627\u0644\u06af\u0648", +"Templates": "\u0627\u0644\u06af\u0648\u0647\u0627", +"Template": "\u0627\u0644\u06af\u0648", +"Text color": "\u0631\u0646\u06af \u0645\u062a\u0646", +"Background color": "\u0631\u0646\u06af \u067e\u0633 \u0632\u0645\u06cc\u0646\u0647", +"Custom...": "\u062f\u0644\u062e\u0648\u0627\u0647...", +"Custom color": "\u0631\u0646\u06af \u062f\u0644\u062e\u0648\u0627\u0647", +"No color": "\u0628\u062f\u0648\u0646 \u0631\u0646\u06af", +"Table of Contents": "\u0641\u0647\u0631\u0633\u062a \u0639\u0646\u0627\u0648\u06cc\u0646", +"Show blocks": "\u0646\u0645\u0627\u06cc\u0634 \u0628\u0644\u0648\u06a9 \u0647\u0627", +"Show invisible characters": "\u0646\u0645\u0627\u06cc\u0634 \u0646\u0648\u06cc\u0633\u0647 \u0647\u0627\u06cc \u0646\u0627\u067e\u06cc\u062f\u0627", +"Words: {0}": "\u0648\u0627\u0698\u0647 \u0647\u0627: {0}", +"{0} words": "{0} \u0648\u0627\u0698\u0647", +"File": "\u067e\u0631\u0648\u0646\u062f\u0647", +"Edit": "\u0648\u06cc\u0631\u0627\u06cc\u0634", +"Insert": "\u062f\u0631\u062c", +"View": "\u0646\u0645\u0627\u06cc\u0634", +"Format": "\u0642\u0627\u0644\u0628", +"Table": "\u062c\u062f\u0648\u0644", +"Tools": "\u0627\u0628\u0632\u0627\u0631\u0647\u0627", +"Powered by {0}": "\u062a\u0648\u0627\u0646 \u06af\u0631\u0641\u062a\u0647 \u0627\u0632 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0646\u0627\u062d\u06cc\u0647 \u0645\u062a\u0646 \u063a\u0646\u06cc.\n\u062c\u0647\u062a \u0645\u0634\u0627\u0647\u062f\u0647 \u0645\u0646\u0648 \u0627\u0632 \u06a9\u0644\u06cc\u062f\u0647\u0627\u06cc \u062a\u0631\u06a9\u06cc\u0628\u06cc ALT + F9 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0646\u0645\u0627\u06cc\u06cc\u062f.\n\u062c\u0647\u062a \u0645\u0634\u0627\u0647\u062f\u0647 \u0646\u0648\u0627\u0631 \u0627\u0628\u0632\u0627\u0631 \u0627\u0632 \u06a9\u0644\u06cc\u062f\u0647\u0627\u06cc \u062a\u0631\u06a9\u06cc\u0628\u06cc ALT + F10 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0646\u0645\u0627\u06cc\u06cc\u062f.\n\u062c\u0647\u062a \u0645\u0634\u0627\u0647\u062f\u0647 \u0631\u0627\u0647\u0646\u0645\u0627 \u0627\u0632 \u06a9\u0644\u06cc\u062f\u0647\u0627\u06cc \u062a\u0631\u06a9\u06cc\u0628\u06cc ALT + 0 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0646\u0645\u0627\u06cc\u06cc\u062f.", +"_dir": "rtl" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fi.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fi.js index 87677bd3ff..db521d6dec 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fi.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fi.js @@ -1,219 +1,261 @@ tinymce.addI18n('fi',{ -"Cut": "Leikkaa", -"Heading 5": "Otsikko 5", -"Header 2": "Otsikko 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Selaimesi ei tue leikep\u00f6yd\u00e4n suoraa k\u00e4ytt\u00e4mist\u00e4. Ole hyv\u00e4 ja k\u00e4yt\u00e4 n\u00e4pp\u00e4imist\u00f6n Ctrl+X\/C\/V n\u00e4pp\u00e4inyhdistelmi\u00e4.", -"Heading 4": "Otsikko 4", -"Div": "Div", -"Heading 2": "Otsikko 2", -"Paste": "Liit\u00e4", -"Close": "Sulje", -"Font Family": "Fontti", -"Pre": "Esimuotoiltu", -"Align right": "Tasaa oikealle", -"New document": "Uusi dokumentti", -"Blockquote": "Lainauslohko", -"Numbered list": "J\u00e4rjestetty lista", -"Heading 1": "Otsikko 1", -"Headings": "Otsikot", -"Increase indent": "Loitonna", -"Formats": "Muotoilut", -"Headers": "Otsikot", -"Select all": "Valitse kaikki", -"Header 3": "Otsikko 3", -"Blocks": "Lohkot", -"Undo": "Peru", -"Strikethrough": "Yliviivaus", -"Bullet list": "J\u00e4rjest\u00e4m\u00e4t\u00f6n lista", -"Header 1": "Otsikko 1", -"Superscript": "Yl\u00e4indeksi", -"Clear formatting": "Poista muotoilu", -"Font Sizes": "Fonttikoko", -"Subscript": "Alaindeksi", -"Header 6": "Otsikko 6", "Redo": "Tee uudelleen", -"Paragraph": "Kappale", -"Ok": "Ok", -"Bold": "Lihavointi", -"Code": "Koodi", -"Italic": "Kursivointi", -"Align center": "Keskit\u00e4", -"Header 5": "Otsikko 5", -"Heading 6": "Otsikko 6", -"Heading 3": "Otsikko 3", -"Decrease indent": "Sisenn\u00e4", -"Header 4": "Otsikko 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Liitt\u00e4minen on nyt pelk\u00e4n tekstin -tilassa. Sis\u00e4ll\u00f6t liitet\u00e4\u00e4n nyt pelkk\u00e4n\u00e4 tekstin\u00e4, kunnes otat vaihtoehdon pois k\u00e4yt\u00f6st\u00e4.", -"Underline": "Alleviivaus", -"Cancel": "Peruuta", -"Justify": "Tasaa", -"Inline": "Samalla rivill\u00e4", +"Undo": "Peru", +"Cut": "Leikkaa", "Copy": "Kopioi", -"Align left": "Tasaa vasemmalle", +"Paste": "Liit\u00e4", +"Select all": "Valitse kaikki", +"New document": "Uusi dokumentti", +"Ok": "Ok", +"Cancel": "Peruuta", "Visual aids": "Visuaaliset neuvot", -"Lower Greek": "pienet kirjaimet: \u03b1, \u03b2, \u03b3", -"Square": "Neli\u00f6", +"Bold": "Lihavointi", +"Italic": "Kursivointi", +"Underline": "Alleviivaus", +"Strikethrough": "Yliviivaus", +"Superscript": "Yl\u00e4indeksi", +"Subscript": "Alaindeksi", +"Clear formatting": "Poista muotoilu", +"Align left": "Tasaa vasemmalle", +"Align center": "Keskit\u00e4", +"Align right": "Tasaa oikealle", +"Justify": "Tasaa", +"Bullet list": "J\u00e4rjest\u00e4m\u00e4t\u00f6n lista", +"Numbered list": "J\u00e4rjestetty lista", +"Decrease indent": "Sisenn\u00e4", +"Increase indent": "Loitonna", +"Close": "Sulje", +"Formats": "Muotoilut", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Selaimesi ei tue leikep\u00f6yd\u00e4n suoraa k\u00e4ytt\u00e4mist\u00e4. Ole hyv\u00e4 ja k\u00e4yt\u00e4 n\u00e4pp\u00e4imist\u00f6n Ctrl+X\/C\/V n\u00e4pp\u00e4inyhdistelmi\u00e4.", +"Headers": "Otsikot", +"Header 1": "Otsikko 1", +"Header 2": "Otsikko 2", +"Header 3": "Otsikko 3", +"Header 4": "Otsikko 4", +"Header 5": "Otsikko 5", +"Header 6": "Otsikko 6", +"Headings": "Otsikot", +"Heading 1": "Otsikko 1", +"Heading 2": "Otsikko 2", +"Heading 3": "Otsikko 3", +"Heading 4": "Otsikko 4", +"Heading 5": "Otsikko 5", +"Heading 6": "Otsikko 6", +"Preformatted": "Preformatted", +"Div": "Div", +"Pre": "Esimuotoiltu", +"Code": "Koodi", +"Paragraph": "Kappale", +"Blockquote": "Lainauslohko", +"Inline": "Samalla rivill\u00e4", +"Blocks": "Lohkot", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Liitt\u00e4minen on nyt pelk\u00e4n tekstin -tilassa. Sis\u00e4ll\u00f6t liitet\u00e4\u00e4n nyt pelkk\u00e4n\u00e4 tekstin\u00e4, kunnes otat vaihtoehdon pois k\u00e4yt\u00f6st\u00e4.", +"Font Family": "Fontti", +"Font Sizes": "Fonttikoko", +"Class": "Luokka", +"Browse for an image": "Selaa kuvia", +"OR": "TAI", +"Drop an image here": "Pudota kuva t\u00e4h\u00e4n", +"Upload": "Vie", +"Block": "Lohko", +"Align": "Tasaa", "Default": "Oletus", -"Lower Alpha": "pienet kirjaimet: a, b, c", "Circle": "Pallo", "Disc": "Ympyr\u00e4", +"Square": "Neli\u00f6", +"Lower Alpha": "pienet kirjaimet: a, b, c", +"Lower Greek": "pienet kirjaimet: \u03b1, \u03b2, \u03b3", +"Lower Roman": "pienet kirjaimet: i, ii, iii", "Upper Alpha": "isot kirjaimet: A, B, C", "Upper Roman": "isot kirjaimet: I, II, III", -"Lower Roman": "pienet kirjaimet: i, ii, iii", -"Name": "Nimi", "Anchor": "Ankkuri", +"Name": "Nimi", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id voi alkaa kirjaimella, sen j\u00e4lkeen voi k\u00e4ytt\u00e4\u00e4 kirjaimia, numeroja, viivoja, pisteit\u00e4, kaksoispistett\u00e4 ja alaviivausta", "You have unsaved changes are you sure you want to navigate away?": "Sinulla on tallentamattomia muutoksia, haluatko varmasti siirty\u00e4 toiselle sivulle?", "Restore last draft": "Palauta aiempi luonnos", "Special character": "Erikoismerkki", "Source code": "L\u00e4hdekoodi", -"B": "B", +"Insert\/Edit code sample": "Lis\u00e4\u00e4\/muokkaa koodiesimerkki", +"Language": "Kieli", +"Code sample": "Koodiesimerkki", +"Color": "V\u00e4ri", "R": "R", "G": "G", -"Color": "V\u00e4ri", -"Right to left": "Oikealta vasemmalle", +"B": "B", "Left to right": "Vasemmalta oikealle", +"Right to left": "Oikealta vasemmalle", "Emoticons": "Hymi\u00f6t", -"Robots": "Robotit", "Document properties": "Dokumentin ominaisuudet", "Title": "Otsikko", "Keywords": "Avainsanat", -"Encoding": "Merkist\u00f6", "Description": "Kuvaus", +"Robots": "Robotit", "Author": "Tekij\u00e4", +"Encoding": "Merkist\u00f6", "Fullscreen": "Koko ruutu", +"Action": "Toiminto", +"Shortcut": "Oikotie", +"Help": "Ohje", +"Address": "Osoite", +"Focus to menubar": "Kohdistus valikkoon", +"Focus to toolbar": "Kohdistus ty\u00f6kalupalkkiin", +"Focus to element path": "Kohdistus elementtiin", +"Focus to contextual toolbar": "Kohdistus kontekstuaaliseen ty\u00f6kalupalkkiin", +"Insert link (if link plugin activated)": "Lis\u00e4\u00e4 linkki (jos linkki-liit\u00e4nn\u00e4inen aktiivinen)", +"Save (if save plugin activated)": "Tallenna (jos tallenna-liit\u00e4nn\u00e4inen aktiivinen)", +"Find (if searchreplace plugin activated)": "Etsi (jos etsikorvaa-liit\u00e4nn\u00e4inen aktiivinen)", +"Plugins installed ({0}):": "Asennetut liit\u00e4nn\u00e4iset ({0}):", +"Premium plugins:": "Premium liit\u00e4nn\u00e4iset:", +"Learn more...": "Lis\u00e4tietoja...", +"You are using {0}": "K\u00e4yt\u00e4t {0}", +"Plugins": "Liit\u00e4nn\u00e4iset", +"Handy Shortcuts": "K\u00e4tev\u00e4t pikan\u00e4pp\u00e4imet", "Horizontal line": "Vaakasuora viiva", -"Horizontal space": "Horisontaalinen tila", "Insert\/edit image": "Lis\u00e4\u00e4\/muokkaa kuva", +"Image description": "Kuvaus", +"Source": "L\u00e4hde", +"Dimensions": "Mittasuhteet", +"Constrain proportions": "S\u00e4ilyt\u00e4 mittasuhteet", "General": "Yleiset", "Advanced": "Lis\u00e4asetukset", -"Source": "L\u00e4hde", -"Border": "Reunus", -"Constrain proportions": "S\u00e4ilyt\u00e4 mittasuhteet", -"Vertical space": "Vertikaalinen tila", -"Image description": "Kuvaus", "Style": "Tyyli", -"Dimensions": "Mittasuhteet", +"Vertical space": "Vertikaalinen tila", +"Horizontal space": "Horisontaalinen tila", +"Border": "Reunus", "Insert image": "Lis\u00e4\u00e4 kuva", -"Zoom in": "L\u00e4henn\u00e4", -"Contrast": "Kontrasti", -"Back": "Takaisin", -"Gamma": "Gamma", -"Flip horizontally": "K\u00e4\u00e4nn\u00e4 vaakasuunnassa", -"Resize": "Kuvan koon muutos", -"Sharpen": "Ter\u00e4vyys", -"Zoom out": "Loitonna", -"Image options": "Kuvan asetukset", -"Apply": "Aseta", -"Brightness": "Kirkkaus", -"Rotate clockwise": "Kierr\u00e4 my\u00f6t\u00e4p\u00e4iv\u00e4\u00e4n", +"Image": "Kuva", +"Image list": "Kuvalista", "Rotate counterclockwise": "Kierr\u00e4 vastap\u00e4iv\u00e4\u00e4n", -"Edit image": "Muokkaa kuvaa", -"Color levels": "V\u00e4ritasot", -"Crop": "Rajaa valintaan", -"Orientation": "Suunta", +"Rotate clockwise": "Kierr\u00e4 my\u00f6t\u00e4p\u00e4iv\u00e4\u00e4n", "Flip vertically": "K\u00e4\u00e4nn\u00e4 pystysuunnassa", +"Flip horizontally": "K\u00e4\u00e4nn\u00e4 vaakasuunnassa", +"Edit image": "Muokkaa kuvaa", +"Image options": "Kuvan asetukset", +"Zoom in": "L\u00e4henn\u00e4", +"Zoom out": "Loitonna", +"Crop": "Rajaa valintaan", +"Resize": "Kuvan koon muutos", +"Orientation": "Suunta", +"Brightness": "Kirkkaus", +"Sharpen": "Ter\u00e4vyys", +"Contrast": "Kontrasti", +"Color levels": "V\u00e4ritasot", +"Gamma": "Gamma", "Invert": "K\u00e4\u00e4nteinen", +"Apply": "Aseta", +"Back": "Takaisin", "Insert date\/time": "Lis\u00e4\u00e4 p\u00e4iv\u00e4m\u00e4\u00e4r\u00e4 tai aika", -"Remove link": "Poista linkki", -"Url": "Osoite", -"Text to display": "N\u00e4ytett\u00e4v\u00e4 teksti", -"Anchors": "Ankkurit", +"Date\/time": "P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4\/aika", "Insert link": "Lis\u00e4\u00e4 linkki", -"New window": "Uusi ikkuna", -"None": "Ei mit\u00e4\u00e4n", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Antamasi osoite n\u00e4ytt\u00e4\u00e4 olevan ulkoinen linkki. Haluatko lis\u00e4t\u00e4 osoitteeseen vaaditun http:\/\/ -etuliitteen?", -"Target": "Kohde", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Antamasi osoite n\u00e4ytt\u00e4\u00e4 olevan s\u00e4hk\u00f6postiosoite. Haluatko lis\u00e4t\u00e4 osoitteeseen vaaditun mailto: -etuliitteen?", "Insert\/edit link": "Lis\u00e4\u00e4\/muokkaa linkki", -"Insert\/edit video": "Lis\u00e4\u00e4\/muokkaa video", -"Poster": "L\u00e4hett\u00e4j\u00e4", -"Alternative source": "Vaihtoehtoinen l\u00e4hde", -"Paste your embed code below:": "Liit\u00e4 upotuskoodisi alapuolelle:", +"Text to display": "N\u00e4ytett\u00e4v\u00e4 teksti", +"Url": "Osoite", +"Target": "Kohde", +"None": "Ei mit\u00e4\u00e4n", +"New window": "Uusi ikkuna", +"Remove link": "Poista linkki", +"Anchors": "Ankkurit", +"Link": "Linkki", +"Paste or type a link": "Sijoita tai kirjoita linkki", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Antamasi osoite n\u00e4ytt\u00e4\u00e4 olevan s\u00e4hk\u00f6postiosoite. Haluatko lis\u00e4t\u00e4 osoitteeseen vaaditun mailto: -etuliitteen?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Antamasi osoite n\u00e4ytt\u00e4\u00e4 olevan ulkoinen linkki. Haluatko lis\u00e4t\u00e4 osoitteeseen vaaditun http:\/\/ -etuliitteen?", +"Link list": "Linkkilista", "Insert video": "Lis\u00e4\u00e4 video", +"Insert\/edit video": "Lis\u00e4\u00e4\/muokkaa video", +"Insert\/edit media": "Lis\u00e4\u00e4\/muokkaa media", +"Alternative source": "Vaihtoehtoinen l\u00e4hde", +"Poster": "L\u00e4hett\u00e4j\u00e4", +"Paste your embed code below:": "Liit\u00e4 upotuskoodisi alapuolelle:", "Embed": "Upota", +"Media": "Media", "Nonbreaking space": "Sitova v\u00e4lily\u00f6nti", "Page break": "Sivunvaihto", "Paste as text": "Liit\u00e4 tekstin\u00e4", "Preview": "Esikatselu", "Print": "Tulosta", "Save": "Tallenna", -"Could not find the specified string.": "Haettua merkkijonoa ei l\u00f6ytynyt.", -"Replace": "Korvaa", -"Next": "Seur.", -"Whole words": "Koko sanat", -"Find and replace": "Etsi ja korvaa", -"Replace with": "Korvaa", "Find": "Etsi", +"Replace with": "Korvaa", +"Replace": "Korvaa", "Replace all": "Korvaa kaikki", -"Match case": "Erota isot ja pienet kirjaimet", "Prev": "Edel.", +"Next": "Seur.", +"Find and replace": "Etsi ja korvaa", +"Could not find the specified string.": "Haettua merkkijonoa ei l\u00f6ytynyt.", +"Match case": "Erota isot ja pienet kirjaimet", +"Whole words": "Koko sanat", "Spellcheck": "Oikolue", -"Finish": "Lopeta", -"Ignore all": "\u00c4l\u00e4 huomioi mit\u00e4\u00e4n", "Ignore": "\u00c4l\u00e4 huomioi", +"Ignore all": "\u00c4l\u00e4 huomioi mit\u00e4\u00e4n", +"Finish": "Lopeta", "Add to Dictionary": "Lis\u00e4\u00e4 sanakirjaan", -"Insert row before": "Lis\u00e4\u00e4 rivi ennen", -"Rows": "Rivit", -"Height": "Korkeus", -"Paste row after": "Liit\u00e4 rivi j\u00e4lkeen", -"Alignment": "Tasaus", -"Border color": "Reunuksen v\u00e4ri", -"Column group": "Sarakeryhm\u00e4", -"Row": "Rivi", -"Insert column before": "Lis\u00e4\u00e4 rivi ennen", -"Split cell": "Jaa solu", -"Cell padding": "Solun tyhj\u00e4 tila", -"Cell spacing": "Solun v\u00e4li", -"Row type": "Rivityyppi", "Insert table": "Lis\u00e4\u00e4 taulukko", -"Body": "Runko", -"Caption": "Seloste", -"Footer": "Alaosa", -"Delete row": "Poista rivi", -"Paste row before": "Liit\u00e4 rivi ennen", -"Scope": "Laajuus", -"Delete table": "Poista taulukko", -"H Align": "H tasaus", -"Top": "Yl\u00e4reuna", -"Header cell": "Otsikkosolu", -"Column": "Sarake", -"Row group": "Riviryhm\u00e4", -"Cell": "Solu", -"Middle": "Keskikohta", -"Cell type": "Solun tyyppi", -"Copy row": "Kopioi rivi", -"Row properties": "Rivin ominaisuudet", "Table properties": "Taulukon ominaisuudet", -"Bottom": "Alareuna", -"V Align": "V tasaus", -"Header": "Otsikko", -"Right": "Oikea", -"Insert column after": "Lis\u00e4\u00e4 rivi j\u00e4lkeen", -"Cols": "Sarakkeet", -"Insert row after": "Lis\u00e4\u00e4 rivi j\u00e4lkeen", -"Width": "Leveys", +"Delete table": "Poista taulukko", +"Cell": "Solu", +"Row": "Rivi", +"Column": "Sarake", "Cell properties": "Solun ominaisuudet", -"Left": "Vasen", -"Cut row": "Leikkaa rivi", -"Delete column": "Poista sarake", -"Center": "Keskell\u00e4", "Merge cells": "Yhdist\u00e4 solut", +"Split cell": "Jaa solu", +"Insert row before": "Lis\u00e4\u00e4 rivi ennen", +"Insert row after": "Lis\u00e4\u00e4 rivi j\u00e4lkeen", +"Delete row": "Poista rivi", +"Row properties": "Rivin ominaisuudet", +"Cut row": "Leikkaa rivi", +"Copy row": "Kopioi rivi", +"Paste row before": "Liit\u00e4 rivi ennen", +"Paste row after": "Liit\u00e4 rivi j\u00e4lkeen", +"Insert column before": "Lis\u00e4\u00e4 rivi ennen", +"Insert column after": "Lis\u00e4\u00e4 rivi j\u00e4lkeen", +"Delete column": "Poista sarake", +"Cols": "Sarakkeet", +"Rows": "Rivit", +"Width": "Leveys", +"Height": "Korkeus", +"Cell spacing": "Solun v\u00e4li", +"Cell padding": "Solun tyhj\u00e4 tila", +"Caption": "Seloste", +"Left": "Vasen", +"Center": "Keskell\u00e4", +"Right": "Oikea", +"Cell type": "Solun tyyppi", +"Scope": "Laajuus", +"Alignment": "Tasaus", +"H Align": "H tasaus", +"V Align": "V tasaus", +"Top": "Yl\u00e4reuna", +"Middle": "Keskikohta", +"Bottom": "Alareuna", +"Header cell": "Otsikkosolu", +"Row group": "Riviryhm\u00e4", +"Column group": "Sarakeryhm\u00e4", +"Row type": "Rivityyppi", +"Header": "Otsikko", +"Body": "Runko", +"Footer": "Alaosa", +"Border color": "Reunuksen v\u00e4ri", "Insert template": "Lis\u00e4\u00e4 pohja", "Templates": "Pohjat", +"Template": "Pohja", +"Text color": "Tekstin v\u00e4ri", "Background color": "Taustan v\u00e4ri", "Custom...": "Mukauta...", "Custom color": "Mukautettu v\u00e4ri", "No color": "Ei v\u00e4ri\u00e4", -"Text color": "Tekstin v\u00e4ri", +"Table of Contents": "Sis\u00e4llysluettelo", "Show blocks": "N\u00e4yt\u00e4 lohkot", "Show invisible characters": "N\u00e4yt\u00e4 n\u00e4kym\u00e4tt\u00f6m\u00e4t merkit", "Words: {0}": "Sanat: {0}", -"Insert": "Lis\u00e4\u00e4", +"{0} words": "{0} sanaa", "File": "Tiedosto", "Edit": "Muokkaa", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rikastetun tekstin alue. Paina ALT-F9 valikkoon. Paina ALT-F10 ty\u00f6kaluriviin. Paina ALT-0 ohjeeseen.", -"Tools": "Ty\u00f6kalut", +"Insert": "Lis\u00e4\u00e4", "View": "N\u00e4yt\u00e4", +"Format": "Muotoilu", "Table": "Taulukko", -"Format": "Muotoilu" +"Tools": "Ty\u00f6kalut", +"Powered by {0}": "Tehty {0}:ll\u00e4", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rikastetun tekstin alue. Paina ALT-F9 valikkoon. Paina ALT-F10 ty\u00f6kaluriviin. Paina ALT-0 ohjeeseen." }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js index b9cfd8b515..2d074f8c6e 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js @@ -1 +1,389 @@ -tinyMCE.addI18n({fr:{common:{"more_colors":"Plus de couleurs","invalid_data":"Erreur : saisie de valeurs incorrectes. Elles sont mises en \u00e9vidence en rouge.","popup_blocked":"D\u00e9sol\u00e9, nous avons d\u00e9tect\u00e9 que votre bloqueur de popup a bloqu\u00e9 une fen\u00eatre dont l\'application a besoin. Vous devez d\u00e9sactiver votre bloqueur de popup pour pouvoir utiliser cet outil.","clipboard_no_support":"Actuellement non support\u00e9 par votre navigateur.\n Veuillez utiliser les raccourcis clavier \u00e0 la place.","clipboard_msg":"Les fonctions Copier/Couper/Coller ne sont pas valables sur Mozilla et Firefox.\nSouhaitez-vous avoir plus d\'informations sur ce sujet ?","not_set":"-- non d\u00e9fini --","class_name":"Classe",browse:"parcourir",close:"Fermer",cancel:"Annuler",update:"Mettre \u00e0 jour",insert:"Ins\u00e9rer",apply:"Appliquer","edit_confirm":"Souhaitez-vous utiliser le mode WYSIWYG pour cette zone de texte ?","invalid_data_number":"{#field} doit \u00eatre un nombre","invalid_data_min":"{#field} doit \u00eatre un nombre plus grand que {#min}","invalid_data_size":"{#field} doit \u00eatre un nombre ou un pourcentage",value:"(valeur)"},contextmenu:{full:"Justifi\u00e9",right:"Droite",center:"Centr\u00e9",left:"Gauche",align:"Alignement"},insertdatetime:{"day_short":"Dim,Lun,Mar,Mer,Jeu,Ven,Sam,Dim","day_long":"Dimanche,Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche","months_short":"Jan,F\u00e9v,Mar,Avr,Mai,Juin,Juil,Ao\u00fbt,Sep,Oct,Nov,D\u00e9c","months_long":"Janvier,F\u00e9vrier,Mars,Avril,Mai,Juin,Juillet,Ao\u00fbt,Septembre,Octobre,Novembre,D\u00e9cembre","inserttime_desc":"Ins\u00e9rer l\'heure","insertdate_desc":"Ins\u00e9rer la date","time_fmt":"%H:%M:%S","date_fmt":"%d-%m-%Y"},print:{"print_desc":"Imprimer"},preview:{"preview_desc":"Pr\u00e9visualiser"},directionality:{"rtl_desc":"\u00c9criture de droite \u00e0 gauche","ltr_desc":"\u00c9criture de gauche \u00e0 droite"},layer:{content:"Nouvelle couche\u2026","absolute_desc":"Activer le positionnement absolu","backward_desc":"D\u00e9placer vers l\'arri\u00e8re","forward_desc":"D\u00e9placer vers l\'avant","insertlayer_desc":"Ins\u00e9rer une nouvelle couche"},save:{"save_desc":"Enregistrer","cancel_desc":"Annuler toutes les modifications"},nonbreaking:{"nonbreaking_desc":"Ins\u00e9rer une espace ins\u00e9cable"},iespell:{download:"ieSpell n\'est pas install\u00e9. Souhaitez-vous l\'installer maintenant ?","iespell_desc":"Lancer le v\u00e9rificateur d\'orthographe"},advhr:{"delta_height":"Ecart de hauteur","delta_width":"Ecart de largeur","advhr_desc":"Ins\u00e9rer un trait horizontal"},emotions:{"delta_height":"delta_height","delta_width":"delta_width","emotions_desc":"\u00c9motic\u00f4nes"},searchreplace:{"replace_desc":"Rechercher / remplacer","search_desc":"Rechercher","delta_width":"","delta_height":""},advimage:{"image_desc":"Ins\u00e9rer / \u00e9diter une image","delta_width":"","delta_height":""},advlink:{"link_desc":"Ins\u00e9rer / \u00e9diter un lien","delta_height":"","delta_width":""},xhtmlxtras:{"attribs_desc":"Ins\u00e9rer / \u00e9diter les attributs","ins_desc":"Ins\u00e9r\u00e9","del_desc":"Barr\u00e9","acronym_desc":"Acronyme","abbr_desc":"Abr\u00e9viation","cite_desc":"Citation","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"\u00c9diter la feuille de style (CSS)","delta_height":"","delta_width":""},paste:{"plaintext_mode":"Le collage est actuellement en mode texte non format\u00e9. Cliquez \u00e0 nouveau pour revenir en mode de collage ordinaire.","plaintext_mode_sticky":"Le collage est actuellement en mode texte non format\u00e9. Cliquez \u00e0 nouveau pour revenir en mode de collage ordinaire. Apr\u00e8s avoir coll\u00e9 quelque chose, vous retournerez en mode de collage ordinaire.","selectall_desc":"Tout s\u00e9lectionner","paste_word_desc":"Coller un texte cr\u00e9\u00e9 sous Word","paste_text_desc":"Coller comme texte brut"},"paste_dlg":{"word_title":"Utilisez CTRL+V sur votre clavier pour coller le texte dans la fen\u00eatre.","text_linebreaks":"Conserver les retours \u00e0 la ligne","text_title":"Utilisez CTRL+V sur votre clavier pour coller le texte dans la fen\u00eatre."},table:{cell:"Cellule",col:"Colonne",row:"Ligne",del:"Effacer le tableau","copy_row_desc":"Copier la ligne","cut_row_desc":"Couper la ligne","paste_row_after_desc":"Coller la ligne apr\u00e8s","paste_row_before_desc":"Coller la ligne avant","props_desc":"Propri\u00e9t\u00e9s du tableau","cell_desc":"Propri\u00e9t\u00e9s de la cellule","row_desc":"Propri\u00e9t\u00e9s de la ligne","merge_cells_desc":"Fusionner les cellules","split_cells_desc":"Scinder les cellules fusionn\u00e9es","delete_col_desc":"Effacer la colonne","col_after_desc":"Ins\u00e9rer une colonne apr\u00e8s","col_before_desc":"Ins\u00e9rer une colonne avant","delete_row_desc":"Effacer la ligne","row_after_desc":"Ins\u00e9rer une ligne apr\u00e8s","row_before_desc":"Ins\u00e9rer une ligne avant",desc:"Ins\u00e9rer un nouveau tableau","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"Si vous restaurez le contenu sauv\u00e9, vous perdrez le contenu qui est actuellement dans l\'\u00e9diteur.\n\n\u00cates-vous s\u00fbr de vouloir restaurer le contenu sauv\u00e9 ?","restore_content":"Restaurer le contenu auto-sauvegard\u00e9.","unload_msg":"Les modifications apport\u00e9es seront perdues si vous quittez cette page."},fullscreen:{desc:"Passer en mode plein \u00e9cran"},media:{edit:"\u00c9diter un m\u00e9dia incorpor\u00e9",desc:"Ins\u00e9rer / \u00e9diter un m\u00e9dia incorpor\u00e9","delta_height":"","delta_width":""},fullpage:{desc:"Propri\u00e9t\u00e9s du document","delta_width":"","delta_height":""},template:{desc:"Ins\u00e9rer un mod\u00e8le pr\u00e9d\u00e9fini."},visualchars:{desc:"Activer les caract\u00e8res de mise en page."},spellchecker:{desc:"Activer le v\u00e9rificateur d\'orthographe",menu:"Param\u00e8tres du v\u00e9rificateur d\'orthographe","ignore_word":"Ignorer le mot","ignore_words":"Tout ignorer",langs:"Langues",wait:"Veuillez patienter\u2026",sug:"Suggestions","no_sug":"Aucune suggestion","no_mpell":"Aucune erreur trouv\u00e9e.","learn_word":"Apprendre le mot"},pagebreak:{desc:"Ins\u00e9rer un saut de page."},advlist:{types:"Types",def:"D\u00e9faut","lower_alpha":"Alpha minuscule","lower_greek":"Grec minuscule","lower_roman":"Romain minuscule","upper_alpha":"Alpha majuscule","upper_roman":"Romain majuscule",circle:"Cercle",disc:"Disque",square:"Carr\u00e9"},colors:{"333300":"Olive fonc\u00e9","993300":"Orange br\u00fbl\u00e9","000000":"Noir","003300":"Vert fonc\u00e9","003366":"Azur fonc\u00e9","000080":"Bleu marine","333399":"Indigo","333333":"Gris tr\u00e8s fonc\u00e9","800000":"Bordeaux",FF6600:"Orange","808000":"Olive","008000":"Vert","008080":"Sarcelle","0000FF":"Bleu","666699":"Bleu gris\u00e2tre","808080":"Gris",FF0000:"Rouge",FF9900:"Ambre","99CC00":"Jaune vert","339966":"Mer verte","33CCCC":"Turquoise","3366FF":"Bleu royal","800080":"Violet","999999":"Gris moyen",FF00FF:"Magenta",FFCC00:"Or",FFFF00:"Jaune","00FF00":"Lime","00FFFF":"Bleu vert","00CCFF":"Bleu ciel","993366":"Brun",C0C0C0:"Argent",FF99CC:"Rose",FFCC99:"P\u00eache",FFFF99:"Jaune clair",CCFFCC:"Vert p\u00e2le",CCFFFF:"Cyan p\u00e2le","99CCFF":"Bleu ciel clair",CC99FF:"Prune",FFFFFF:"Blanc"},aria:{"rich_text_area":"Texte riche"},wordcount:{words:"Mots:"}}}); \ No newline at end of file +tinymce.addI18n('fr_FR',{ +"Redo": "R\u00e9tablir", +"Undo": "Annuler", +"Cut": "Couper", +"Copy": "Copier", +"Paste": "Coller", +"Select all": "S\u00e9lectionner tout", +"New document": "Nouveau document", +"Ok": "OK", +"Cancel": "Annuler", +"Visual aids": "Aides visuelles", +"Bold": "Gras", +"Italic": "Italique", +"Underline": "Soulign\u00e9", +"Strikethrough": "Barr\u00e9", +"Superscript": "Exposant", +"Subscript": "Indice", +"Clear formatting": "Effacer la mise en forme", +"Align left": "Aligner \u00e0 gauche", +"Align center": "Centrer", +"Align right": "Aligner \u00e0 droite", +"Justify": "Justifier", +"Bullet list": "Liste \u00e0 puces", +"Numbered list": "Liste num\u00e9rot\u00e9e", +"Decrease indent": "R\u00e9duire le retrait", +"Increase indent": "Augmenter le retrait", +"Close": "Fermer", +"Formats": "Formats", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Votre navigateur ne supporte pas l\u2019acc\u00e8s direct au presse-papiers. Merci d'utiliser les raccourcis clavier Ctrl+X\/C\/V.", +"Headers": "En-t\u00eates", +"Header 1": "En-t\u00eate 1", +"Header 2": "En-t\u00eate 2", +"Header 3": "En-t\u00eate 3", +"Header 4": "En-t\u00eate 4", +"Header 5": "En-t\u00eate 5", +"Header 6": "En-t\u00eate 6", +"Headings": "Titres", +"Heading 1": "Titre\u00a01", +"Heading 2": "Titre\u00a02", +"Heading 3": "Titre\u00a03", +"Heading 4": "Titre\u00a04", +"Heading 5": "Titre\u00a05", +"Heading 6": "Titre\u00a06", +"Preformatted": "Pr\u00e9format\u00e9", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "Paragraphe", +"Blockquote": "Blockquote", +"Inline": "En ligne", +"Blocks": "Blocs", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Le presse-papiers est maintenant en mode \"texte plein\". Les contenus seront coll\u00e9s sans retenir les formatages jusqu'\u00e0 ce que vous d\u00e9sactiviez cette option.", +"Fonts": "Polices", +"Font Sizes": "Tailles de police", +"Class": "Classe", +"Browse for an image": "Rechercher une image", +"OR": "OU", +"Drop an image here": "D\u00e9poser une image ici", +"Upload": "T\u00e9l\u00e9charger", +"Block": "Bloc", +"Align": "Aligner", +"Default": "Par d\u00e9faut", +"Circle": "Cercle", +"Disc": "Disque", +"Square": "Carr\u00e9", +"Lower Alpha": "Alpha minuscule", +"Lower Greek": "Grec minuscule", +"Lower Roman": "Romain minuscule", +"Upper Alpha": "Alpha majuscule", +"Upper Roman": "Romain majuscule", +"Anchor...": "Ancre...", +"Name": "Nom", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "L'Id doit commencer par une lettre suivi par des lettres, nombres, tirets, points, deux-points ou underscores", +"You have unsaved changes are you sure you want to navigate away?": "Vous avez des modifications non enregistr\u00e9es, \u00eates-vous s\u00fbr de quitter la page?", +"Restore last draft": "Restaurer le dernier brouillon", +"Special characters...": "Caract\u00e8res sp\u00e9ciaux...", +"Source code": "Code source", +"Insert\/Edit code sample": "Ins\u00e9rer \/ modifier une exemple de code", +"Language": "Langue", +"Code sample...": "Exemple de code...", +"Color Picker": "S\u00e9lecteur de couleurs", +"R": "R", +"G": "V", +"B": "B", +"Left to right": "Gauche \u00e0 droite", +"Right to left": "Droite \u00e0 gauche", +"Emoticons...": "\u00c9motic\u00f4nes...", +"Metadata and Document Properties": "M\u00e9tadonn\u00e9es et propri\u00e9t\u00e9s du document", +"Title": "Titre", +"Keywords": "Mots-cl\u00e9s", +"Description": "Description", +"Robots": "Robots", +"Author": "Auteur", +"Encoding": "Encodage", +"Fullscreen": "Plein \u00e9cran", +"Action": "Action", +"Shortcut": "Raccourci", +"Help": "Aide", +"Address": "Adresse", +"Focus to menubar": "Cibler la barre de menu", +"Focus to toolbar": "Cibler la barre d'outils", +"Focus to element path": "Cibler le chemin vers l'\u00e9l\u00e9ment", +"Focus to contextual toolbar": "Cibler la barre d'outils contextuelle", +"Insert link (if link plugin activated)": "Ins\u00e9rer un lien (si le module link est activ\u00e9)", +"Save (if save plugin activated)": "Enregistrer (si le module save est activ\u00e9)", +"Find (if searchreplace plugin activated)": "Rechercher (si le module searchreplace est activ\u00e9)", +"Plugins installed ({0}):": "Modules install\u00e9s ({0}) : ", +"Premium plugins:": "Modules premium :", +"Learn more...": "En savoir plus...", +"You are using {0}": "Vous utilisez {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Raccourcis utiles", +"Horizontal line": "Ligne horizontale", +"Insert\/edit image": "Ins\u00e9rer\/modifier une image", +"Image description": "Description de l'image", +"Source": "Source", +"Dimensions": "Dimensions", +"Constrain proportions": "Conserver les proportions", +"General": "G\u00e9n\u00e9ral", +"Advanced": "Avanc\u00e9", +"Style": "Style", +"Vertical space": "Espacement vertical", +"Horizontal space": "Espacement horizontal", +"Border": "Bordure", +"Insert image": "Ins\u00e9rer une image", +"Image...": "Image...", +"Image list": "Liste d'images", +"Rotate counterclockwise": "Rotation anti-horaire", +"Rotate clockwise": "Rotation horaire", +"Flip vertically": "Retournement vertical", +"Flip horizontally": "Retournement horizontal", +"Edit image": "Modifier l'image", +"Image options": "Options de l'image", +"Zoom in": "Zoomer", +"Zoom out": "D\u00e9zoomer", +"Crop": "Rogner", +"Resize": "Redimensionner", +"Orientation": "Orientation", +"Brightness": "Luminosit\u00e9", +"Sharpen": "Affiner", +"Contrast": "Contraste", +"Color levels": "Niveaux de couleur", +"Gamma": "Gamma", +"Invert": "Inverser", +"Apply": "Appliquer", +"Back": "Retour", +"Insert date\/time": "Ins\u00e9rer date\/heure", +"Date\/time": "Date\/heure", +"Insert\/Edit Link": "Ins\u00e9rer\/Modifier lien", +"Insert\/edit link": "Ins\u00e9rer\/modifier un lien", +"Text to display": "Texte \u00e0 afficher", +"Url": "Url", +"Open link in...": "Ouvrir le lien dans...", +"Current window": "Fen\u00eatre active", +"None": "n\/a", +"New window": "Nouvelle fen\u00eatre", +"Remove link": "Enlever le lien", +"Anchors": "Ancres", +"Link...": "Lien...", +"Paste or type a link": "Coller ou taper un lien", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre une adresse e-mail. Voulez-vous ajouter le pr\u00e9fixe mailto: n\u00e9cessaire?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre un lien externe. Voulez-vous ajouter le pr\u00e9fixe http:\/\/ n\u00e9cessaire?", +"Link list": "Liste de liens", +"Insert video": "Ins\u00e9rer une vid\u00e9o", +"Insert\/edit video": "Ins\u00e9rer\/modifier une vid\u00e9o", +"Insert\/edit media": "Ins\u00e9rer\/modifier un m\u00e9dia", +"Alternative source": "Source alternative", +"Alternative source URL": "URL de la source alternative", +"Media poster (Image URL)": "Affiche de m\u00e9dia (URL de l'image)", +"Paste your embed code below:": "Collez votre code d'int\u00e9gration ci-dessous :", +"Embed": "Int\u00e9grer", +"Media...": "M\u00e9dia...", +"Nonbreaking space": "Espace ins\u00e9cable", +"Page break": "Saut de page", +"Paste as text": "Coller comme texte", +"Preview": "Pr\u00e9visualiser", +"Print...": "Imprimer...", +"Save": "Enregistrer", +"Find": "Chercher", +"Replace with": "Remplacer par", +"Replace": "Remplacer", +"Replace all": "Tout remplacer", +"Previous": "Pr\u00e9c\u00e9dente", +"Next": "Suiv", +"Find and replace...": "Trouver et remplacer...", +"Could not find the specified string.": "Impossible de trouver la cha\u00eene sp\u00e9cifi\u00e9e.", +"Match case": "Respecter la casse", +"Find whole words only": "Mot entier", +"Spell check": "V\u00e9rification de l'orthographe", +"Ignore": "Ignorer", +"Ignore all": "Tout ignorer", +"Finish": "Finie", +"Add to Dictionary": "Ajouter au dictionnaire", +"Insert table": "Ins\u00e9rer un tableau", +"Table properties": "Propri\u00e9t\u00e9s du tableau", +"Delete table": "Supprimer le tableau", +"Cell": "Cellule", +"Row": "Ligne", +"Column": "Colonne", +"Cell properties": "Propri\u00e9t\u00e9s de la cellule", +"Merge cells": "Fusionner les cellules", +"Split cell": "Diviser la cellule", +"Insert row before": "Ins\u00e9rer une ligne avant", +"Insert row after": "Ins\u00e9rer une ligne apr\u00e8s", +"Delete row": "Effacer la ligne", +"Row properties": "Propri\u00e9t\u00e9s de la ligne", +"Cut row": "Couper la ligne", +"Copy row": "Copier la ligne", +"Paste row before": "Coller la ligne avant", +"Paste row after": "Coller la ligne apr\u00e8s", +"Insert column before": "Ins\u00e9rer une colonne avant", +"Insert column after": "Ins\u00e9rer une colonne apr\u00e8s", +"Delete column": "Effacer la colonne", +"Cols": "Colonnes", +"Rows": "Lignes", +"Width": "Largeur", +"Height": "Hauteur", +"Cell spacing": "Espacement inter-cellulles", +"Cell padding": "Espacement interne cellule", +"Show caption": "Afficher le sous-titrage", +"Left": "Gauche", +"Center": "Centr\u00e9", +"Right": "Droite", +"Cell type": "Type de cellule", +"Scope": "Etendue", +"Alignment": "Alignement", +"H Align": "Alignement H", +"V Align": "Alignement V", +"Top": "Haut", +"Middle": "Milieu", +"Bottom": "Bas", +"Header cell": "Cellule d'en-t\u00eate", +"Row group": "Groupe de lignes", +"Column group": "Groupe de colonnes", +"Row type": "Type de ligne", +"Header": "En-t\u00eate", +"Body": "Corps", +"Footer": "Pied", +"Border color": "Couleur de la bordure", +"Insert template...": "Ins\u00e9rer un mod\u00e8le...", +"Templates": "Th\u00e8mes", +"Template": "Mod\u00e8le", +"Text color": "Couleur du texte", +"Background color": "Couleur d'arri\u00e8re-plan", +"Custom...": "Personnalis\u00e9...", +"Custom color": "Couleur personnalis\u00e9e", +"No color": "Aucune couleur", +"Remove color": "Supprimer la couleur", +"Table of Contents": "Table des mati\u00e8res", +"Show blocks": "Afficher les blocs", +"Show invisible characters": "Afficher les caract\u00e8res invisibles", +"Word count": "Nombre de mots", +"Words: {0}": "Mots : {0}", +"{0} words": "{0} mots", +"File": "Fichier", +"Edit": "Editer", +"Insert": "Ins\u00e9rer", +"View": "Voir", +"Format": "Format", +"Table": "Tableau", +"Tools": "Outils", +"Powered by {0}": "Propuls\u00e9 par {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zone Texte Riche. Appuyer sur ALT-F9 pour le menu. Appuyer sur ALT-F10 pour la barre d'outils. Appuyer sur ALT-0 pour de l'aide.", +"Image title": "Titre d'image", +"Border width": "\u00c9paisseur de la bordure", +"Border style": "Style de la bordure", +"Error": "Erreur", +"Warn": "Avertir", +"Valid": "Valide", +"To open the popup, press Shift+Enter": "Pour ouvrir la popup, appuyez sur Maj+Entr\u00e9e", +"Rich Text Area. Press ALT-0 for help.": "Zone de texte riche. Appuyez sur ALT-0 pour l'aide.", +"System Font": "Police syst\u00e8me", +"Failed to upload image: {0}": "\u00c9chec d'envoi de l'image\u00a0: {0}", +"Failed to load plugin: {0} from url {1}": "\u00c9chec de chargement du plug-in\u00a0: {0} \u00e0 partir de l\u2019URL {1} ", +"Failed to load plugin url: {0}": "\u00c9chec de chargement de l'URL du plug-in\u00a0: {0}", +"Failed to initialize plugin: {0}": "\u00c9chec d'initialisation du plug-in\u00a0: {0}", +"example": "exemple", +"Search": "Rechercher", +"All": "Tout", +"Currency": "Devise", +"Text": "Texte", +"Quotations": "Citations", +"Mathematical": "Op\u00e9rateurs math\u00e9matiques", +"Extended Latin": "Latin \u00e9tendu", +"Symbols": "Symboles", +"Arrows": "Fl\u00e8ches", +"User Defined": "D\u00e9fini par l'utilisateur", +"dollar sign": "Symbole dollar", +"currency sign": "Symbole devise", +"euro-currency sign": "Symbole euro", +"colon sign": "Symbole col\u00f3n", +"cruzeiro sign": "Symbole cruzeiro", +"french franc sign": "Symbole franc fran\u00e7ais", +"lira sign": "Symbole lire", +"mill sign": "Symbole milli\u00e8me", +"naira sign": "Symbole naira", +"peseta sign": "Symbole peseta", +"rupee sign": "Symbole roupie", +"won sign": "Symbole won", +"new sheqel sign": "Symbole nouveau ch\u00e9kel", +"dong sign": "Symbole dong", +"kip sign": "Symbole kip", +"tugrik sign": "Symbole tougrik", +"drachma sign": "Symbole drachme", +"german penny symbol": "Symbole pfennig", +"peso sign": "Symbole peso", +"guarani sign": "Symbole guarani", +"austral sign": "Symbole austral", +"hryvnia sign": "Symbole hryvnia", +"cedi sign": "Symbole cedi", +"livre tournois sign": "Symbole livre tournois", +"spesmilo sign": "Symbole spesmilo", +"tenge sign": "Symbole tenge", +"indian rupee sign": "Symbole roupie indienne", +"turkish lira sign": "Symbole lire turque", +"nordic mark sign": "Symbole du mark nordique", +"manat sign": "Symbole manat", +"ruble sign": "Symbole rouble", +"yen character": "Sinogramme Yen", +"yuan character": "Sinogramme Yuan", +"yuan character, in hong kong and taiwan": "Sinogramme Yuan, Hong Kong et Taiwan", +"yen\/yuan character variant one": "Sinogramme Yen\/Yuan, premi\u00e8re variante", +"Loading emoticons...": "Chargement des \u00e9motic\u00f4nes en cours...", +"Could not load emoticons": "\u00c9chec de chargement des \u00e9motic\u00f4nes", +"People": "Personnes", +"Animals and Nature": "Animaux & nature", +"Food and Drink": "Nourriture & boissons", +"Activity": "Activit\u00e9", +"Travel and Places": "Voyages & lieux", +"Objects": "Objets", +"Flags": "Drapeaux", +"Characters": "Caract\u00e8res", +"Characters (no spaces)": "Caract\u00e8res (espaces non compris)", +"Error: Form submit field collision.": "Erreur : conflit de champs lors de la soumission du formulaire", +"Error: No form element found.": "Erreur : aucun \u00e9l\u00e9ment de formulaire trouv\u00e9.", +"Update": "Mettre \u00e0 jour", +"Color swatch": "\u00c9chantillon de couleurs", +"Turquoise": "Turquoise", +"Green": "Vert", +"Blue": "Bleu", +"Purple": "Violet", +"Navy Blue": "Bleu marine", +"Dark Turquoise": "Turquoise fonc\u00e9", +"Dark Green": "Vert fonc\u00e9", +"Medium Blue": "Bleu moyen", +"Medium Purple": "Violet moyen", +"Midnight Blue": "Bleu de minuit", +"Yellow": "Jaune", +"Orange": "Orange", +"Red": "Rouge", +"Light Gray": "Gris clair", +"Gray": "Gris", +"Dark Yellow": "Jaune fonc\u00e9", +"Dark Orange": "Orange fonc\u00e9", +"Dark Red": "Rouge fonc\u00e9", +"Medium Gray": "Gris moyen", +"Dark Gray": "Gris fonc\u00e9", +"Black": "Noir", +"White": "Blanc", +"Switch to or from fullscreen mode": "Passer en ou quitter le mode plein \u00e9cran", +"Open help dialog": "Ouvrir la bo\u00eete de dialogue d'aide", +"history": "historique", +"styles": "styles", +"formatting": "mise en forme", +"alignment": "alignement", +"indentation": "retrait", +"permanent pen": "feutre ind\u00e9l\u00e9bile", +"comments": "commentaires", +"Anchor": "Ancre", +"Special character": "Caract\u00e8res sp\u00e9ciaux", +"Code sample": "Extrait de code", +"Color": "Couleur", +"Emoticons": "Emotic\u00f4nes", +"Document properties": "Propri\u00e9t\u00e9 du document", +"Image": "Image", +"Insert link": "Ins\u00e9rer un lien", +"Target": "Cible", +"Link": "Lien", +"Poster": "Publier", +"Media": "M\u00e9dia", +"Print": "Imprimer", +"Prev": "Pr\u00e9c ", +"Find and replace": "Trouver et remplacer", +"Whole words": "Mots entiers", +"Spellcheck": "V\u00e9rification orthographique", +"Caption": "Titre", +"Insert template": "Ajouter un th\u00e8me" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr_FR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr_FR.js new file mode 100644 index 0000000000..5c37164b2c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr_FR.js @@ -0,0 +1,261 @@ +tinymce.addI18n('fr_FR',{ +"Redo": "R\u00e9tablir", +"Undo": "Annuler", +"Cut": "Couper", +"Copy": "Copier", +"Paste": "Coller", +"Select all": "Tout s\u00e9lectionner", +"New document": "Nouveau document", +"Ok": "Ok", +"Cancel": "Annuler", +"Visual aids": "Aides visuelle", +"Bold": "Gras", +"Italic": "Italique", +"Underline": "Soulign\u00e9", +"Strikethrough": "Barr\u00e9", +"Superscript": "Exposant", +"Subscript": "Indice", +"Clear formatting": "Effacer la mise en forme", +"Align left": "Aligner \u00e0 gauche", +"Align center": "Centrer", +"Align right": "Aligner \u00e0 droite", +"Justify": "Justifier", +"Bullet list": "Puces", +"Numbered list": "Num\u00e9rotation", +"Decrease indent": "Diminuer le retrait", +"Increase indent": "Augmenter le retrait", +"Close": "Fermer", +"Formats": "Formats", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Votre navigateur ne supporte pas la copie directe. Merci d'utiliser les touches Ctrl+X\/C\/V.", +"Headers": "Titres", +"Header 1": "Titre 1", +"Header 2": "Titre 2", +"Header 3": "Titre 3", +"Header 4": "Titre 4", +"Header 5": "Titre 5", +"Header 6": "Titre 6", +"Headings": "En-t\u00eates", +"Heading 1": "En-t\u00eate 1", +"Heading 2": "En-t\u00eate 2", +"Heading 3": "En-t\u00eate 3", +"Heading 4": "En-t\u00eate 4", +"Heading 5": "En-t\u00eate 5", +"Heading 6": "En-t\u00eate 6", +"Preformatted": "Pr\u00e9-formatt\u00e9", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "Paragraphe", +"Blockquote": "Citation", +"Inline": "En ligne", +"Blocks": "Blocs", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Le presse-papiers est maintenant en mode \"texte plein\". Les contenus seront coll\u00e9s sans retenir les formatages jusqu'\u00e0 ce que vous d\u00e9sactiviez cette option.", +"Font Family": "Police", +"Font Sizes": "Taille de police", +"Class": "Classe", +"Browse for an image": "Parcourir pour s\u00e9lectionner une image", +"OR": "OU", +"Drop an image here": "Glisser une image ici", +"Upload": "D\u00e9poser", +"Block": "Bloquer", +"Align": "Aligner", +"Default": "Par d\u00e9faut", +"Circle": "Cercle", +"Disc": "Disque", +"Square": "Carr\u00e9", +"Lower Alpha": "Alpha minuscule", +"Lower Greek": "Grec minuscule", +"Lower Roman": "Romain minuscule", +"Upper Alpha": "Alpha majuscule", +"Upper Roman": "Romain majuscule", +"Anchor": "Ancre", +"Name": "Nom", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "L'Id doit commencer par une lettre suivi par des lettres, nombres, tirets, points, deux-points ou underscores", +"You have unsaved changes are you sure you want to navigate away?": "Vous avez des modifications non enregistr\u00e9es, \u00eates-vous s\u00fbr de quitter la page?", +"Restore last draft": "Restaurer le dernier brouillon", +"Special character": "Caract\u00e8res sp\u00e9ciaux", +"Source code": "Code source", +"Insert\/Edit code sample": "Ins\u00e9rer \/ modifier une exemple de code", +"Language": "Langue", +"Code sample": "Extrait de code", +"Color": "Couleur", +"R": "R", +"G": "V", +"B": "B", +"Left to right": "Gauche \u00e0 droite", +"Right to left": "Droite \u00e0 gauche", +"Emoticons": "Emotic\u00f4nes", +"Document properties": "Propri\u00e9t\u00e9 du document", +"Title": "Titre", +"Keywords": "Mots-cl\u00e9s", +"Description": "Description", +"Robots": "Robots", +"Author": "Auteur", +"Encoding": "Encodage", +"Fullscreen": "Plein \u00e9cran", +"Action": "Action", +"Shortcut": "Raccourci", +"Help": "Aide", +"Address": "Adresse", +"Focus to menubar": "Cibler la barre de menu", +"Focus to toolbar": "Cibler la barre d'outils", +"Focus to element path": "Cibler le chemin vers l'\u00e9l\u00e9ment", +"Focus to contextual toolbar": "Cibler la barre d'outils contextuelle", +"Insert link (if link plugin activated)": "Ins\u00e9rer un lien (si le module link est activ\u00e9)", +"Save (if save plugin activated)": "Enregistrer (si le module save est activ\u00e9)", +"Find (if searchreplace plugin activated)": "Rechercher (si le module searchreplace est activ\u00e9)", +"Plugins installed ({0}):": "Modules install\u00e9s ({0}) : ", +"Premium plugins:": "Modules premium :", +"Learn more...": "En savoir plus...", +"You are using {0}": "Vous utilisez {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Raccourcis utiles", +"Horizontal line": "Ligne horizontale", +"Insert\/edit image": "Ins\u00e9rer\/modifier une image", +"Image description": "Description de l'image", +"Source": "Source", +"Dimensions": "Dimensions", +"Constrain proportions": "Conserver les proportions", +"General": "G\u00e9n\u00e9ral", +"Advanced": "Avanc\u00e9", +"Style": "Style", +"Vertical space": "Espacement vertical", +"Horizontal space": "Espacement horizontal", +"Border": "Bordure", +"Insert image": "Ins\u00e9rer une image", +"Image": "Image", +"Image list": "Liste d'images", +"Rotate counterclockwise": "Rotation anti-horaire", +"Rotate clockwise": "Rotation horaire", +"Flip vertically": "Retournement vertical", +"Flip horizontally": "Retournement horizontal", +"Edit image": "Modifier l'image", +"Image options": "Options de l'image", +"Zoom in": "Zoomer", +"Zoom out": "D\u00e9zoomer", +"Crop": "Rogner", +"Resize": "Redimensionner", +"Orientation": "Orientation", +"Brightness": "Luminosit\u00e9", +"Sharpen": "Affiner", +"Contrast": "Contraste", +"Color levels": "Niveaux de couleur", +"Gamma": "Gamma", +"Invert": "Inverser", +"Apply": "Appliquer", +"Back": "Retour", +"Insert date\/time": "Ins\u00e9rer date\/heure", +"Date\/time": "Date\/heure", +"Insert link": "Ins\u00e9rer un lien", +"Insert\/edit link": "Ins\u00e9rer\/modifier un lien", +"Text to display": "Texte \u00e0 afficher", +"Url": "Url", +"Target": "Cible", +"None": "n\/a", +"New window": "Nouvelle fen\u00eatre", +"Remove link": "Enlever le lien", +"Anchors": "Ancres", +"Link": "Lien", +"Paste or type a link": "Coller ou taper un lien", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre une adresse e-mail. Voulez-vous ajouter le pr\u00e9fixe mailto: n\u00e9cessaire?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre un lien externe. Voulez-vous ajouter le pr\u00e9fixe http:\/\/ n\u00e9cessaire?", +"Link list": "Liste de liens", +"Insert video": "Ins\u00e9rer une vid\u00e9o", +"Insert\/edit video": "Ins\u00e9rer\/modifier une vid\u00e9o", +"Insert\/edit media": "Ins\u00e9rer\/modifier un m\u00e9dia", +"Alternative source": "Source alternative", +"Poster": "Publier", +"Paste your embed code below:": "Collez votre code d'int\u00e9gration ci-dessous :", +"Embed": "Int\u00e9grer", +"Media": "M\u00e9dia", +"Nonbreaking space": "Espace ins\u00e9cable", +"Page break": "Saut de page", +"Paste as text": "Coller comme texte", +"Preview": "Pr\u00e9visualiser", +"Print": "Imprimer", +"Save": "Enregistrer", +"Find": "Chercher", +"Replace with": "Remplacer par", +"Replace": "Remplacer", +"Replace all": "Tout remplacer", +"Prev": "Pr\u00e9c ", +"Next": "Suiv", +"Find and replace": "Trouver et remplacer", +"Could not find the specified string.": "Impossible de trouver la cha\u00eene sp\u00e9cifi\u00e9e.", +"Match case": "Respecter la casse", +"Whole words": "Mots entiers", +"Spellcheck": "V\u00e9rification orthographique", +"Ignore": "Ignorer", +"Ignore all": "Tout ignorer", +"Finish": "Finie", +"Add to Dictionary": "Ajouter au dictionnaire", +"Insert table": "Ins\u00e9rer un tableau", +"Table properties": "Propri\u00e9t\u00e9s du tableau", +"Delete table": "Supprimer le tableau", +"Cell": "Cellule", +"Row": "Ligne", +"Column": "Colonne", +"Cell properties": "Propri\u00e9t\u00e9s de la cellule", +"Merge cells": "Fusionner les cellules", +"Split cell": "Diviser la cellule", +"Insert row before": "Ins\u00e9rer une ligne avant", +"Insert row after": "Ins\u00e9rer une ligne apr\u00e8s", +"Delete row": "Effacer la ligne", +"Row properties": "Propri\u00e9t\u00e9s de la ligne", +"Cut row": "Couper la ligne", +"Copy row": "Copier la ligne", +"Paste row before": "Coller la ligne avant", +"Paste row after": "Coller la ligne apr\u00e8s", +"Insert column before": "Ins\u00e9rer une colonne avant", +"Insert column after": "Ins\u00e9rer une colonne apr\u00e8s", +"Delete column": "Effacer la colonne", +"Cols": "Colonnes", +"Rows": "Lignes", +"Width": "Largeur", +"Height": "Hauteur", +"Cell spacing": "Espacement inter-cellulles", +"Cell padding": "Espacement interne cellule", +"Caption": "Titre", +"Left": "Gauche", +"Center": "Centr\u00e9", +"Right": "Droite", +"Cell type": "Type de cellule", +"Scope": "Etendue", +"Alignment": "Alignement", +"H Align": "Alignement H", +"V Align": "Alignement V", +"Top": "Haut", +"Middle": "Milieu", +"Bottom": "Bas", +"Header cell": "Cellule d'en-t\u00eate", +"Row group": "Groupe de lignes", +"Column group": "Groupe de colonnes", +"Row type": "Type de ligne", +"Header": "En-t\u00eate", +"Body": "Corps", +"Footer": "Pied", +"Border color": "Couleur de la bordure", +"Insert template": "Ajouter un th\u00e8me", +"Templates": "Th\u00e8mes", +"Template": "Mod\u00e8le", +"Text color": "Couleur du texte", +"Background color": "Couleur d'arri\u00e8re-plan", +"Custom...": "Personnalis\u00e9...", +"Custom color": "Couleur personnalis\u00e9e", +"No color": "Aucune couleur", +"Table of Contents": "Table des mati\u00e8res", +"Show blocks": "Afficher les blocs", +"Show invisible characters": "Afficher les caract\u00e8res invisibles", +"Words: {0}": "Mots : {0}", +"{0} words": "{0} mots", +"File": "Fichier", +"Edit": "Editer", +"Insert": "Ins\u00e9rer", +"View": "Voir", +"Format": "Format", +"Table": "Tableau", +"Tools": "Outils", +"Powered by {0}": "Propuls\u00e9 par {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zone Texte Riche. Appuyer sur ALT-F9 pour le menu. Appuyer sur ALT-F10 pour la barre d'outils. Appuyer sur ALT-0 pour de l'aide." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ga.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ga.js new file mode 100644 index 0000000000..c2a942c121 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ga.js @@ -0,0 +1,261 @@ +tinymce.addI18n('ga',{ +"Redo": "Athdh\u00e9an", +"Undo": "Cealaigh", +"Cut": "Gearr", +"Copy": "C\u00f3ipe\u00e1il", +"Paste": "Greamaigh", +"Select all": "Roghnaigh uile", +"New document": "C\u00e1ip\u00e9is nua", +"Ok": "OK", +"Cancel": "Cealaigh", +"Visual aids": "\u00c1iseanna amhairc", +"Bold": "Trom", +"Italic": "Iod\u00e1lach", +"Underline": "Fol\u00edne", +"Strikethrough": "L\u00edne tr\u00edd", +"Superscript": "Forscript", +"Subscript": "Foscript", +"Clear formatting": "Glan form\u00e1idi\u00fa", +"Align left": "Ail\u00ednigh ar chl\u00e9", +"Align center": "Ail\u00ednigh sa l\u00e1r", +"Align right": "Ail\u00ednigh ar dheis", +"Justify": "Comhfhadaigh", +"Bullet list": "Liosta Urchar", +"Numbered list": "Liosta Uimhrithe", +"Decrease indent": "Laghdaigh eang", +"Increase indent": "M\u00e9adaigh eang", +"Close": "D\u00fan", +"Formats": "Form\u00e1id\u00ed", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "N\u00ed f\u00e9idir le do bhrabhs\u00e1la\u00ed teacht go d\u00edreach ar an ngearrthaisce. Bain \u00fas\u00e1id as na haicearra\u00ed Ctrl+X\/C\/V. ", +"Headers": "Ceannt\u00e1sca", +"Header 1": "Ceannt\u00e1sc 1", +"Header 2": "Ceannt\u00e1sc 2", +"Header 3": "Ceannt\u00e1sc 3", +"Header 4": "Ceannt\u00e1sc 4", +"Header 5": "Ceannt\u00e1sc 5", +"Header 6": "Ceannt\u00e1sc 6", +"Headings": "Ceannteidil", +"Heading 1": "Ceannteideal 1", +"Heading 2": "Ceannteideal 2", +"Heading 3": "Ceannteideal 3", +"Heading 4": "Ceannteideal 4", +"Heading 5": "Ceannteideal 5", +"Heading 6": "Ceannteideal 6", +"Preformatted": "R\u00e9amhfhorm\u00e1idithe", +"Div": "Deighilt", +"Pre": "R\u00e9amh", +"Code": "C\u00f3d", +"Paragraph": "Alt", +"Blockquote": "Athfhriotal", +"Inline": "Inl\u00edne", +"Blocks": "Blocanna", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Sa m\u00f3d gn\u00e1th-th\u00e9acs anois. Gream\u00f3far \u00e1bhar mar ghn\u00e1th-th\u00e9acs go dt\u00ed go m\u00fachfaidh t\u00fa an rogha seo.", +"Font Family": "Cl\u00f3fhoireann", +"Font Sizes": "Cl\u00f3mh\u00e9ideanna", +"Class": "Aicme", +"Browse for an image": "Brabhs\u00e1il le haghaidh \u00edomh\u00e1", +"OR": "N\u00d3", +"Drop an image here": "Scaoil \u00edomh\u00e1 anseo", +"Upload": "Uasl\u00f3d\u00e1il", +"Block": "Bloc", +"Align": "Ail\u00ednigh", +"Default": "R\u00e9amhshocr\u00fa", +"Circle": "Ciorcal", +"Disc": "Diosca", +"Square": "Cearn\u00f3g", +"Lower Alpha": "Alfa Beag", +"Lower Greek": "Litir Bheag Ghr\u00e9agach", +"Lower Roman": "Litir Bheag R\u00f3mh\u00e1nach", +"Upper Alpha": "Alfa M\u00f3r", +"Upper Roman": "Litir Mh\u00f3r R\u00f3mh\u00e1nach", +"Anchor": "Ancaire", +"Name": "Ainm", +"Id": "Aitheantas", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "N\u00ed m\u00f3r don aitheantas tos\u00fa le litir, agus gan ach litreacha, uimhreacha, daiseanna, poncanna, idirstadanna, n\u00f3 fostr\u00edoca ina dhiaidh sin.", +"You have unsaved changes are you sure you want to navigate away?": "T\u00e1 athruithe gan s\u00e1bh\u00e1il ann. An bhfuil t\u00fa cinnte gur mhaith leat imeacht amach as seo?", +"Restore last draft": "Oscail an dr\u00e9acht is d\u00e9ana\u00ed", +"Special character": "Carachtar speisialta", +"Source code": "C\u00f3d foinseach", +"Insert\/Edit code sample": "Cuir sampla c\u00f3id isteach\/in eagar", +"Language": "Teanga", +"Code sample": "Sampla c\u00f3id", +"Color": "Dath", +"R": "D", +"G": "U", +"B": "G", +"Left to right": "Cl\u00e9-go-deas", +"Right to left": "Deas-go-cl\u00e9", +"Emoticons": "Straoiseoga", +"Document properties": "Air\u00edonna na C\u00e1ip\u00e9ise", +"Title": "Teideal", +"Keywords": "Lorgfhocail", +"Description": "Cur S\u00edos", +"Robots": "R\u00f3bait", +"Author": "\u00dadar", +"Encoding": "Ionch\u00f3d\u00fa", +"Fullscreen": "L\u00e1nsc\u00e1ile\u00e1n", +"Action": "Gn\u00edomh", +"Shortcut": "Aicearra", +"Help": "Cabhair", +"Address": "Seoladh", +"Focus to menubar": "F\u00f3cas sa bharra roghchl\u00e1ir", +"Focus to toolbar": "F\u00f3cas sa bharra uirlis\u00ed", +"Focus to element path": "F\u00f3cas sa chonair eiliminte", +"Focus to contextual toolbar": "F\u00f3cas sa bharra uirlis\u00ed comhth\u00e9acs\u00fail", +"Insert link (if link plugin activated)": "Cuir nasc isteach (m\u00e1 t\u00e1 an breise\u00e1n naisc ar si\u00fal)", +"Save (if save plugin activated)": "S\u00e1bh\u00e1il (m\u00e1 t\u00e1 an breise\u00e1n s\u00e1bh\u00e1la ar si\u00fal)", +"Find (if searchreplace plugin activated)": "Aimsigh (m\u00e1 t\u00e1 an breise\u00e1n cuardaigh ar si\u00fal)", +"Plugins installed ({0}):": "Breise\u00e1in shuite\u00e1ilte ({0}):", +"Premium plugins:": "Scothbhreise\u00e1in:", +"Learn more...": "Tuilleadh eolais...", +"You are using {0}": "T\u00e1 t\u00fa ag \u00fas\u00e1id {0}", +"Plugins": "Breise\u00e1in", +"Handy Shortcuts": "Aicearra\u00ed \u00das\u00e1ideacha", +"Horizontal line": "L\u00edne chothrom\u00e1nach", +"Insert\/edit image": "Cuir \u00edomh\u00e1 isteach\/in eagar", +"Image description": "Cur s\u00edos ar an \u00edomh\u00e1", +"Source": "Foinse", +"Dimensions": "Tois\u00ed", +"Constrain proportions": "Comhr\u00e9ir faoi ghlas", +"General": "Ginear\u00e1lta", +"Advanced": "Casta", +"Style": "St\u00edl", +"Vertical space": "Sp\u00e1s ingearach", +"Horizontal space": "Sp\u00e1s cothrom\u00e1nach", +"Border": "Iml\u00edne", +"Insert image": "Cuir \u00edomh\u00e1 isteach", +"Image": "\u00cdomh\u00e1", +"Image list": "Liosta \u00edomh\u00e1nna", +"Rotate counterclockwise": "Rothlaigh ar tuathal", +"Rotate clockwise": "Rothlaigh ar deiseal", +"Flip vertically": "Cas go hingearach", +"Flip horizontally": "Cas go cothrom\u00e1nach", +"Edit image": "Cuir an \u00edomh\u00e1 in eagar", +"Image options": "Roghanna \u00edomh\u00e1", +"Zoom in": "Z\u00fam\u00e1il isteach", +"Zoom out": "Z\u00fam\u00e1il amach", +"Crop": "Bear", +"Resize": "Athraigh m\u00e9id", +"Orientation": "Treoshu\u00edomh", +"Brightness": "Gile", +"Sharpen": "G\u00e9araigh", +"Contrast": "Codarsnacht", +"Color levels": "Leibh\u00e9il datha", +"Gamma": "G\u00e1ma", +"Invert": "Inbh\u00e9artaigh", +"Apply": "Cuir i bhfeidhm", +"Back": "Siar", +"Insert date\/time": "Cuir d\u00e1ta\/am isteach", +"Date\/time": "D\u00e1ta\/am", +"Insert link": "Cuir nasc isteach", +"Insert\/edit link": "Cuir nasc isteach\/in eagar", +"Text to display": "T\u00e9acs le taispe\u00e1int", +"Url": "URL", +"Target": "Sprioc", +"None": "Dada", +"New window": "Fuinneog nua", +"Remove link": "Bain an nasc", +"Anchors": "Ancair\u00ed", +"Link": "Nasc", +"Paste or type a link": "Greamaigh n\u00f3 cl\u00f3scr\u00edobh nasc", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Is seoladh r\u00edomhphoist \u00e9 an URL a chuir t\u00fa isteach. An bhfuil fonn ort an r\u00e9im\u00edr riachtanach mailto: a chur leis?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Is nasc seachtrach \u00e9 an URL a chuir t\u00fa isteach. An bhfuil fonn ort an r\u00e9im\u00edr riachtanach http:\/\/ a chur leis?", +"Link list": "Liosta nascanna", +"Insert video": "Cuir f\u00edse\u00e1n isteach", +"Insert\/edit video": "Cuir f\u00edse\u00e1n isteach\/in eagar", +"Insert\/edit media": "Cuir me\u00e1n isteach\/in eagar", +"Alternative source": "Foinse mhalartach", +"Poster": "P\u00f3staer", +"Paste your embed code below:": "Greamaigh do ch\u00f3d leabaithe th\u00edos:", +"Embed": "Leabaigh", +"Media": "Me\u00e1in", +"Nonbreaking space": "Sp\u00e1s neamhbhristeach", +"Page break": "Briseadh leathanaigh", +"Paste as text": "Greamaigh mar th\u00e9acs", +"Preview": "R\u00e9amhamharc", +"Print": "Priont\u00e1il", +"Save": "S\u00e1bh\u00e1il", +"Find": "Aimsigh", +"Replace with": "Ionadaigh le", +"Replace": "Ionadaigh", +"Replace all": "Ionadaigh uile", +"Prev": "Siar", +"Next": "Ar aghaidh", +"Find and replace": "Aimsigh agus ionadaigh", +"Could not find the specified string.": "N\u00edor aims\u00edodh an teaghr\u00e1n.", +"Match case": "C\u00e1s-\u00edogair", +"Whole words": "Focail ioml\u00e1na", +"Spellcheck": "Seice\u00e1il an litri\u00fa", +"Ignore": "D\u00e9an neamhaird air", +"Ignore all": "D\u00e9an neamhaird orthu go l\u00e9ir", +"Finish": "Cr\u00edochnaigh", +"Add to Dictionary": "Cuir leis an bhFocl\u00f3ir \u00e9", +"Insert table": "Ions\u00e1igh t\u00e1bla", +"Table properties": "Air\u00edonna an t\u00e1bla", +"Delete table": "Scrios an t\u00e1bla", +"Cell": "Cill", +"Row": "R\u00f3", +"Column": "Col\u00fan", +"Cell properties": "Air\u00edonna na cille", +"Merge cells": "Cumaisc cealla", +"Split cell": "Roinn cill", +"Insert row before": "Ions\u00e1igh r\u00f3 os a chionn", +"Insert row after": "Ions\u00e1igh r\u00f3 faoi", +"Delete row": "Scrios an r\u00f3", +"Row properties": "Air\u00edonna an r\u00f3", +"Cut row": "Gearr an r\u00f3", +"Copy row": "C\u00f3ipe\u00e1il an r\u00f3", +"Paste row before": "Greamaigh r\u00f3 os a chionn", +"Paste row after": "Greamaigh r\u00f3 faoi", +"Insert column before": "Ions\u00e1igh col\u00fan ar chl\u00e9", +"Insert column after": "Ions\u00e1igh col\u00fan ar dheis", +"Delete column": "Scrios an col\u00fan", +"Cols": "Col\u00fain", +"Rows": "R\u00f3nna", +"Width": "Leithead", +"Height": "Airde", +"Cell spacing": "Sp\u00e1s\u00e1il ceall", +"Cell padding": "Stu\u00e1il ceall", +"Caption": "Fotheideal", +"Left": "Ar Chl\u00e9", +"Center": "Sa L\u00e1r", +"Right": "Ar Dheis", +"Cell type": "Cine\u00e1l na cille", +"Scope": "Sc\u00f3ip", +"Alignment": "Ail\u00edni\u00fa", +"H Align": "Ail\u00edni\u00fa C.", +"V Align": "Ail\u00edni\u00fa I.", +"Top": "Barr", +"Middle": "L\u00e1r", +"Bottom": "Bun", +"Header cell": "Cill cheannt\u00e1isc", +"Row group": "Gr\u00fapa r\u00f3nna", +"Column group": "Gr\u00fapa col\u00fan", +"Row type": "Cine\u00e1l an r\u00f3", +"Header": "Ceannt\u00e1sc", +"Body": "Corp", +"Footer": "Bunt\u00e1sc", +"Border color": "Dath na himl\u00edne", +"Insert template": "Ions\u00e1igh teimpl\u00e9ad", +"Templates": "Teimpl\u00e9id", +"Template": "Teimpl\u00e9ad", +"Text color": "Dath an t\u00e9acs", +"Background color": "Dath an ch\u00falra", +"Custom...": "Saincheap...", +"Custom color": "Dath saincheaptha", +"No color": "Gan dath", +"Table of Contents": "Cl\u00e1r na n\u00c1bhar", +"Show blocks": "Taispe\u00e1in blocanna", +"Show invisible characters": "Taispe\u00e1in carachtair dhofheicthe", +"Words: {0}": "Focail: {0}", +"{0} words": "{0} focal", +"File": "Comhad", +"Edit": "Eagar", +"Insert": "Ions\u00e1ig", +"View": "Amharc", +"Format": "Form\u00e1id", +"Table": "T\u00e1bla", +"Tools": "Uirlis\u00ed", +"Powered by {0}": "\u00c1 chumhacht\u00fa ag {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Limist\u00e9ar M\u00e9ith-Th\u00e9acs. Br\u00faigh ALT-F9 le haghaidh roghchl\u00e1ir, ALT-F10 le haghaidh barra uirlis\u00ed, agus ALT-0 le c\u00fanamh a fh\u00e1il" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/gl.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/gl.js new file mode 100644 index 0000000000..43c1900da7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/gl.js @@ -0,0 +1,253 @@ +tinymce.addI18n('gl',{ +"Redo": "Refacer", +"Undo": "Desfacer", +"Cut": "Cortar", +"Copy": "Copiar", +"Paste": "Pegar", +"Select all": "Seleccionar todo", +"New document": "Novo documento", +"Ok": "Aceptar", +"Cancel": "Cancelar", +"Visual aids": "Axudas visuais", +"Bold": "Negra", +"Italic": "Cursiva", +"Underline": "Subli\u00f1ado", +"Strikethrough": "Riscado", +"Superscript": "Super\u00edndice", +"Subscript": "Sub\u00edndice", +"Clear formatting": "Limpar o formato", +"Align left": "Ali\u00f1ar \u00e1 esquerda", +"Align center": "Ali\u00f1ar ao centro", +"Align right": "Ali\u00f1ar \u00e1 dereita", +"Justify": "Xustificar", +"Bullet list": "Lista de vi\u00f1etas", +"Numbered list": "Lista numerada", +"Decrease indent": "Reducir a sangr\u00eda", +"Increase indent": "Aumentar a sangr\u00eda", +"Close": "Pechar", +"Formats": "Formatos", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "O seu navegador non admite o acceso directo ao portapapeis. Empregue os atallos de teclado Ctrl+X\/C\/V no seu canto.", +"Headers": "Cabeceiras", +"Header 1": "Cabeceira 1", +"Header 2": "Cabeceira 2", +"Header 3": "Cabeceira 3", +"Header 4": "Cabeceira 4", +"Header 5": "Cabeceira 5", +"Header 6": "Cabeceira 6", +"Headings": "T\u00edtulo", +"Heading 1": "T\u00edtulo 1", +"Heading 2": "T\u00edtulo 2", +"Heading 3": "T\u00edtulo 3", +"Heading 4": "T\u00edtulo 4", +"Heading 5": "T\u00edtulo 5", +"Heading 6": "T\u00edtulo 6", +"Div": "Div", +"Pre": "Pre", +"Code": "C\u00f3digo", +"Paragraph": "Par\u00e1grafo", +"Blockquote": "Bloque entre comi\u00f1as", +"Inline": "En li\u00f1a", +"Blocks": "Bloques", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Neste momento o pegado est\u00e1 definido en modo de texto simple. Os contidos p\u00e9garanse como texto sen formato ata que se active esta opci\u00f3n.", +"Font Family": "Tipo de letra", +"Font Sizes": "Tama\u00f1o da letra", +"Class": "Clase", +"Browse for an image": "Buscar unha imaxe", +"OR": "OU", +"Drop an image here": "Soltar unha imaxe", +"Upload": "Cargar", +"Default": "Predeterminada", +"Circle": "Circulo", +"Disc": "Disco", +"Square": "Cadrado", +"Lower Alpha": "Alfa min\u00fascula", +"Lower Greek": "Grega min\u00fascula", +"Lower Roman": "Romana min\u00fascula", +"Upper Alpha": "Alfa mai\u00fascula", +"Upper Roman": "Romana mai\u00fascula", +"Anchor": "Ancoraxe", +"Name": "Nome", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "O ID debe comezar cunha letra, seguida s\u00f3 por letras, n\u00fameros, gui\u00f3ns, puntos, dos puntos ou gui\u00f3ns baixos.", +"You have unsaved changes are you sure you want to navigate away?": "Ten cambios sen gardar. Confirma que quere sa\u00edr?", +"Restore last draft": "Restaurar o \u00faltimo borrador", +"Special character": "Car\u00e1cter especial", +"Source code": "C\u00f3digo fonte", +"Insert\/Edit code sample": "Inserir\/editar mostra de c\u00f3digo", +"Language": "Idioma", +"Color": "Cor", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "De esquerda a dereita", +"Right to left": "De dereita a esquerda", +"Emoticons": "Emoticonas", +"Document properties": "Propiedades do documento", +"Title": "T\u00edtulo", +"Keywords": "Palabras clave", +"Description": "Descrici\u00f3n", +"Robots": "Robots", +"Author": "Autor", +"Encoding": "Codificaci\u00f3n", +"Fullscreen": "Pantalla completa", +"Action": "Action", +"Shortcut": "Shortcut", +"Help": "Help", +"Address": "Address", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "Insert link (if link plugin activated)", +"Save (if save plugin activated)": "Save (if save plugin activated)", +"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)", +"Plugins installed ({0}):": "Plugins installed ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "Learn more...", +"You are using {0}": "You are using {0}", +"Horizontal line": "Li\u00f1a horizontal", +"Insert\/edit image": "Inserir\/editar imaxe", +"Image description": "Descrici\u00f3n da imaxe", +"Source": "Orixe", +"Dimensions": "Dimensi\u00f3ns", +"Constrain proportions": "Restrinxir as proporci\u00f3ns", +"General": "Xeral", +"Advanced": "Avanzado", +"Style": "Estilo", +"Vertical space": "Espazo vertical", +"Horizontal space": "Espazo horizontal", +"Border": "Bordo", +"Insert image": "Inserir imaxe", +"Image": "Imaxe", +"Image list": "Lista de imaxes", +"Rotate counterclockwise": "Rotate counterclockwise", +"Rotate clockwise": "Rotate clockwise", +"Flip vertically": "Flip vertically", +"Flip horizontally": "Flip horizontally", +"Edit image": "Edit image", +"Image options": "Image options", +"Zoom in": "Zoom in", +"Zoom out": "Zoom out", +"Crop": "Crop", +"Resize": "Resize", +"Orientation": "Orientation", +"Brightness": "Brightness", +"Sharpen": "Sharpen", +"Contrast": "Contrast", +"Color levels": "Color levels", +"Gamma": "Gamma", +"Invert": "Invert", +"Apply": "Apply", +"Back": "Back", +"Insert date\/time": "Inserir data\/hora", +"Date\/time": "Data\/hora", +"Insert link": "Inserir ligaz\u00f3n", +"Insert\/edit link": "Inserir\/editar ligaz\u00f3n", +"Text to display": "Texto que amosar", +"Url": "URL", +"Target": "Destino", +"None": "Ning\u00fan", +"New window": "Nova xanela", +"Remove link": "Retirar a ligaz\u00f3n", +"Anchors": "Ancoraxes", +"Link": "Ligaz\u00f3n", +"Paste or type a link": "Pegue ou escriba unha ligaz\u00f3n", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "O URL que introduciu semella seren un enderezo de correo. Quere engadirlle o prefixo mailto: requirido?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "O URL que introduciu semella seren unha ligaz\u00f3n externa. Quere engadirlle o prefixo http:\/\/ requirido?", +"Link list": "Lista de ligaz\u00f3ns", +"Insert video": "Inserir v\u00eddeo", +"Insert\/edit video": "Inserir\/editar v\u00eddeo", +"Insert\/edit media": "Inserir\/editar medios", +"Alternative source": "Orixe alternativa", +"Poster": "Cartel", +"Paste your embed code below:": "Pegue embaixo o c\u00f3digo integrado:", +"Embed": "Integrado", +"Media": "Medios", +"Nonbreaking space": "Espazo irromp\u00edbel", +"Page break": "Quebra de p\u00e1xina", +"Paste as text": "Pegar como texto", +"Preview": "Vista previa", +"Print": "Imprimir", +"Save": "Gardar", +"Find": "Buscar", +"Replace with": "Substitu\u00edr con", +"Replace": "Substitu\u00edr", +"Replace all": "Substitu\u00edr todo", +"Prev": "Anterior", +"Next": "Seguinte", +"Find and replace": "Buscar e substitu\u00edr", +"Could not find the specified string.": "Non foi pos\u00edbel atopar a cadea de texto especificada.", +"Match case": "Distinguir mai\u00fasculas", +"Whole words": "Palabras completas", +"Spellcheck": "Corrector ortogr\u00e1fico", +"Ignore": "Ignorar", +"Ignore all": "Ignorar todo", +"Finish": "Rematar", +"Add to Dictionary": "Engadir ao dicionario", +"Insert table": "Inserir t\u00e1boa", +"Table properties": "Propiedades da t\u00e1boa", +"Delete table": "Eliminar t\u00e1boa", +"Cell": "Cela", +"Row": "Fila", +"Column": "Columna", +"Cell properties": "Propiedades da cela", +"Merge cells": "Combinar celas", +"Split cell": "Dividir celas", +"Insert row before": "Inserir unha fila enriba", +"Insert row after": "Inserir unha fila embaixo", +"Delete row": "Eliminar fila", +"Row properties": "Propiedades das filas", +"Cut row": "Cortar fila", +"Copy row": "Copiar fila", +"Paste row before": "Pegar fila embaixo", +"Paste row after": "Pegar fila enriba", +"Insert column before": "Inserir columna \u00e1 esquerda", +"Insert column after": "Inserir columna \u00e1 dereita", +"Delete column": "Eliminar columna", +"Cols": "Cols.", +"Rows": "Filas", +"Width": "Largo", +"Height": "Alto", +"Cell spacing": "Marxe entre celas", +"Cell padding": "Marxe interior da cela", +"Caption": "Subt\u00edtulo", +"Left": "Esquerda", +"Center": "Centro", +"Right": "Dereita", +"Cell type": "Tipo de cela", +"Scope": "\u00c1mbito", +"Alignment": "Ali\u00f1amento", +"H Align": "Ali\u00f1amento H", +"V Align": "Ali\u00f1amento V", +"Top": "Arriba", +"Middle": "Medio", +"Bottom": "Abaixo", +"Header cell": "Cela de cabeceira", +"Row group": "Grupo de filas", +"Column group": "Grupo de columnas", +"Row type": "Tipo de fila", +"Header": "Cabeceira", +"Body": "Corpo", +"Footer": "Rodap\u00e9", +"Border color": "Cor do bordo", +"Insert template": "Inserir modelo", +"Templates": "Modelos", +"Template": "Modelo", +"Text color": "Cor do texto", +"Background color": "Cor do fondo", +"Custom...": "Personalizado...", +"Custom color": "Cor personalizado", +"No color": "Sen cor", +"Table of Contents": "\u00cdndice de contidos", +"Show blocks": "Amosar os bloques", +"Show invisible characters": "Amosar caracteres invis\u00edbeis", +"Words: {0}": "Palabras: {0}", +"File": "Ficheiro", +"Edit": "Editar", +"Insert": "Inserir", +"View": "Ver", +"Format": "Formato", +"Table": "T\u00e1boa", +"Tools": "Ferramentas", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u00c1rea de texto mellorado. Prema ALT-F9 para o men\u00fa. Prema ALT-F10 para a barra de ferramentas. Prema ALT-0 para a axuda" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he.js deleted file mode 100644 index 0f4e12d7b5..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({he:{common:{"more_colors":"\u05e2\u05d5\u05d3 \u05e6\u05d1\u05e2\u05d9\u05dd","invalid_data":"\u05e9\u05d2\u05d9\u05d0\u05d4: \u05d4\u05d5\u05e7\u05dc\u05d3 \u05de\u05d9\u05d3\u05e2 \u05dc\u05d0 \u05ea\u05e7\u05e0\u05d9. \u05d4\u05de\u05d9\u05d3\u05e2 \u05e1\u05d5\u05de\u05df \u05d1\u05d0\u05d3\u05d5\u05dd.","popup_blocked":"\u05d7\u05d5\u05e1\u05dd \u05e4\u05e8\u05d9\u05d8\u05d9\u05dd \u05de\u05d5\u05e7\u05e4\u05e6\u05d9\u05dd \u05de\u05e0\u05e2 \u05de\u05d7\u05dc\u05d5\u05df \u05d7\u05e9\u05d5\u05d1 \u05de\u05dc\u05d4\u05e4\u05ea\u05d7,\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05db\u05dc\u05d9 \u05d6\u05d4 \u05e2\u05dc\u05d9\u05da \u05dc\u05d1\u05d8\u05dc \u05d0\u05ea \u05d7\u05d5\u05e1\u05dd \u05d4\u05e4\u05e8\u05d9\u05d8\u05d9\u05dd","clipboard_no_support":"\u05db\u05e8\u05d2\u05e2 \u05dc\u05d0 \u05e0\u05ea\u05de\u05da \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05d3\u05e4\u05d3\u05e4\u05df \u05e9\u05dc\u05da. \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e6\u05d5\u05e8\u05d9 \u05d4\u05de\u05e7\u05dc\u05d3\u05ea.","clipboard_msg":"\n \u05d4\u05e2\u05ea\u05e7\u05d4/\u05d2\u05d6\u05d9\u05e8\u05d4 \u05d5\u05d4\u05d3\u05d1\u05e7\u05d4 \u05d0\u05d9\u05e0\u05dd \u05d6\u05de\u05d9\u05e0\u05d9\u05dd \u05d1 Mozilla \u05d5\u05d1-Firefox.\n \u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05e2\u05dc \u05d4\u05e0\u05d5\u05e9\u05d0?\n ","not_set":"-- \u05dc\u05d0 \u05d4\u05d5\u05d2\u05d3\u05e8 --","class_name":"\u05de\u05d7\u05dc\u05e7\u05d4",browse:"\u05e2\u05d9\u05d5\u05df",close:"\u05e1\u05d2\u05d9\u05e8\u05d4",cancel:"\u05d1\u05d9\u05d8\u05d5\u05dc",update:"\u05e2\u05d3\u05db\u05d5\u05df",insert:"\u05d4\u05d5\u05e1\u05e4\u05d4",apply:"\u05d0\u05d9\u05e9\u05d5\u05e8","edit_confirm":"\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e2\u05d5\u05e8\u05da \u05d4\u05de\u05ea\u05e7\u05d3\u05dd?","invalid_data_number":"{#field} \u05d7\u05d9\u05d9\u05d1 \u05dc\u05d4\u05d9\u05d5\u05ea \u05de\u05e1\u05e4\u05e8","invalid_data_min":"{#field} \u05d4\u05de\u05e1\u05e4\u05e8 \u05d7\u05d9\u05d9\u05d1 \u05dc\u05d4\u05d9\u05d5\u05ea \u05d2\u05d3\u05d5\u05dc \u05de-{#min}","invalid_data_size":"{#field} \u05d4\u05e2\u05e8\u05da \u05d7\u05d9\u05d9\u05d1 \u05dc\u05d4\u05d9\u05d5\u05ea \u05de\u05e1\u05e4\u05e8 \u05d0\u05d5 \u05d0\u05d7\u05d5\u05d6",value:"(\u05e2\u05e8\u05da)"},contextmenu:{full:"\u05e9\u05e0\u05d9 \u05d4\u05e6\u05d3\u05d3\u05d9\u05dd",right:"\u05d9\u05de\u05d9\u05df",center:"\u05d0\u05de\u05e6\u05e2",left:"\u05e9\u05de\u05d0\u05dc",align:"\u05d9\u05d9\u05e9\u05d5\u05e8"},insertdatetime:{"day_short":"\u05d9\u05d5\u05dd \u05d0\',\u05d9\u05d5\u05dd \u05d1\',\u05d9\u05d5\u05dd \u05d2\',\u05d9\u05d5\u05dd \u05d3\',\u05d9\u05d5\u05dd \u05d4\',\u05d9\u05d5\u05dd \u05d5\',\u05e9\u05d1\u05ea,\u05d9\u05d5\u05dd \u05d0\'","day_long":"\u05d9\u05d5\u05dd \u05e8\u05d0\u05e9\u05d5\u05df,\u05d9\u05d5\u05dd \u05e9\u05e0\u05d9,\u05d9\u05d5\u05dd \u05e9\u05dc\u05d9\u05e9\u05d9,\u05d9\u05d5\u05dd \u05e8\u05d1\u05d9\u05e2\u05d9,\u05d9\u05d5\u05dd \u05d7\u05de\u05d9\u05e9\u05d9,\u05d9\u05d5\u05dd \u05e9\u05d9\u05e9,\u05d9\u05d5\u05dd \u05e9\u05d1\u05ea,\u05d9\u05d5\u05dd \u05e8\u05d0\u05e9\u05d5\u05df","months_short":"\u05d9\u05e0\u05d5\u05d0\u05e8,\u05e4\u05d1\u05e8\u05d5\u05d0\u05e8,\u05de\u05e8\u05e5,\u05d0\u05e4\u05e8\u05d9\u05dc,\u05de\u05d0\u05d9,\u05d9\u05d5\u05e0\u05e2,\u05d9\u05d5\u05dc\u05d9,\u05d0\u05d5\u05d2\u05d5\u05e1\u05d8,\u05e1\u05e4\u05d8\u05de\u05d1\u05e8,\u05d0\u05d5\u05e7\u05d8\u05d5\u05d1\u05e8,\u05e0\u05d5\u05d1\u05de\u05d1\u05e8,\u05d3\u05e6\u05de\u05d1\u05e8","months_long":"\u05d9\u05e0\u05d5\u05d0\u05e8,\u05e4\u05d1\u05e8\u05d5\u05d0\u05e8,\u05de\u05e8\u05e5,\u05d0\u05e4\u05e8\u05d9\u05dc,\u05de\u05d0\u05d9,\u05d9\u05d5\u05e0\u05e2,\u05d9\u05d5\u05dc\u05d9,\u05d0\u05d5\u05d2\u05d5\u05e1\u05d8,\u05e1\u05e4\u05d8\u05de\u05d1\u05e8,\u05d0\u05d5\u05e7\u05d8\u05d5\u05d1\u05e8,\u05e0\u05d5\u05d1\u05de\u05d1\u05e8,\u05d3\u05e6\u05de\u05d1\u05e8","inserttime_desc":"\u05d4\u05d5\u05e1\u05e4\u05ea \u05d6\u05de\u05df","insertdate_desc":"\u05d4\u05d5\u05e1\u05e4\u05ea \u05ea\u05d0\u05e8\u05d9\u05da","time_fmt":"%H:%M:%S","date_fmt":"%d-%m-%Y"},print:{"print_desc":"\u05d4\u05d3\u05e4\u05e1\u05d4"},preview:{"preview_desc":"\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4"},directionality:{"rtl_desc":"\u05db\u05d9\u05d5\u05d5\u05df \u05d8\u05e7\u05e1\u05d8 \u05de\u05d9\u05de\u05d9\u05df \u05dc\u05e9\u05de\u05d0\u05dc","ltr_desc":"\u05db\u05d9\u05d5\u05d5\u05df \u05d8\u05e7\u05e1\u05d8 \u05de\u05e9\u05de\u05d0\u05dc \u05dc\u05d9\u05de\u05d9\u05df"},layer:{content:"\u05e9\u05db\u05d1\u05d4 \u05d7\u05d3\u05e9\u05d4...","absolute_desc":"\u05d1\u05d7\u05d9\u05e8\u05ea \u05de\u05d9\u05e7\u05d5\u05dd \u05de\u05d5\u05d7\u05dc\u05d8","backward_desc":"\u05d4\u05e2\u05d1\u05e8\u05d4 \u05d0\u05d7\u05d5\u05e8\u05d4","forward_desc":"\u05d4\u05e2\u05d1\u05e8\u05d4 \u05e7\u05d3\u05d9\u05de\u05d4","insertlayer_desc":"\u05d4\u05d5\u05e1\u05e4\u05ea \u05e9\u05db\u05d1\u05d4 \u05d7\u05d3\u05e9\u05d4"},save:{"save_desc":"\u05e9\u05de\u05d9\u05e8\u05d4","cancel_desc":"\u05d1\u05d9\u05d8\u05d5\u05dc \u05db\u05dc \u05d4\u05e9\u05d9\u05e0\u05d5\u05d9\u05dd"},nonbreaking:{"nonbreaking_desc":"\u05d4\u05d5\u05e1\u05e4\u05ea \u05e8\u05d5\u05d5\u05d7"},iespell:{download:" \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 ieSpell. \u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05e7\u05d9\u05df?","iespell_desc":"\u05d1\u05d3\u05d9\u05e7\u05ea \u05d0\u05d9\u05d5\u05ea \u05d1\u05d0\u05e0\u05d2\u05dc\u05d9\u05ea"},advhr:{"advhr_desc":"\u05e7\u05d5 \u05d0\u05d5\u05e4\u05e7\u05d9","delta_height":"","delta_width":""},emotions:{"emotions_desc":"\u05e1\u05de\u05d9\u05d9\u05dc\u05d9\u05dd","delta_height":"","delta_width":""},searchreplace:{"replace_desc":"\u05d4\u05d7\u05dc\u05e4\u05d4","search_desc":"\u05d7\u05d9\u05e4\u05d5\u05e9","delta_width":"","delta_height":""},advimage:{"image_desc":"\u05d4\u05d5\u05e1\u05e4\u05d4/\u05e2\u05e8\u05d9\u05db\u05ea \u05ea\u05de\u05d5\u05e0\u05d4","delta_width":"","delta_height":""},advlink:{"link_desc":"\u05d4\u05d5\u05e1\u05e4\u05ea/\u05e2\u05e8\u05d9\u05db\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8","delta_height":"","delta_width":""},xhtmlxtras:{"attribs_desc":"\u05d4\u05db\u05e0\u05e1/\u05e2\u05e8\u05d5\u05da \u05ea\u05db\u05d5\u05e0\u05d5\u05ea","ins_desc":"\u05d4\u05db\u05e0\u05e1\u05d4","del_desc":"\u05de\u05d7\u05d9\u05e7\u05d4","acronym_desc":"\u05e8\u05d0\u05e9\u05d9 \u05ea\u05d9\u05d1\u05d5\u05ea","abbr_desc":"\u05e7\u05d9\u05e6\u05d5\u05e8","cite_desc":"\u05e6\u05d9\u05d8\u05d5\u05d8","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"\u05e2\u05d3\u05db\u05d5\u05df \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea CSS","delta_height":"","delta_width":""},paste:{"plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode_sticky":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"\u05d1\u05d7\u05e8 \u05d4\u05db\u05dc","paste_word_desc":"\u05d4\u05d3\u05d1\u05e7\u05d4 \u05de-WORD","paste_text_desc":"\u05d4\u05d3\u05d1\u05e7\u05d4 \u05db\u05d8\u05e7\u05e1\u05d8 \u05d1\u05dc\u05d1\u05d3"},"paste_dlg":{"word_title":"\u05d4\u05d3\u05d1\u05d9\u05e7\u05d5 \u05d1\u05d7\u05dc\u05d5\u05df \u05d6\u05d4 \u05d0\u05ea \u05d4\u05d8\u05e7\u05e1\u05d8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d4\u05de\u05e7\u05e9\u05d9\u05dd CTRL+V.","text_linebreaks":"\u05d4\u05e9\u05d0\u05e8 \u05d0\u05ea \u05e9\u05d5\u05e8\u05d5\u05ea \u05d4\u05e8\u05d5\u05d5\u05d7","text_title":"\u05d4\u05d3\u05d1\u05d9\u05e7\u05d5 \u05d1\u05d7\u05dc\u05d5\u05df \u05d6\u05d4 \u05d0\u05ea \u05d4\u05d8\u05e7\u05e1\u05d8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d4\u05de\u05e7\u05e9\u05d9\u05dd CTRL+V."},table:{cell:"\u05ea\u05d0",col:"\u05e2\u05de\u05d5\u05d3\u05d4",row:"\u05e9\u05d5\u05e8\u05d4",del:"\u05de\u05d7\u05d9\u05e7\u05ea \u05d8\u05d1\u05dc\u05d4","copy_row_desc":"\u05d4\u05e2\u05ea\u05e7\u05ea \u05e9\u05d5\u05e8\u05d4 \u05d1\u05d8\u05d1\u05dc\u05d4","cut_row_desc":"\u05d2\u05d6\u05d9\u05e8\u05ea \u05e9\u05d5\u05e8\u05d4 \u05d1\u05d8\u05d1\u05dc\u05d4","paste_row_after_desc":"\u05d4\u05d3\u05d1\u05e7\u05ea \u05e9\u05d5\u05e8\u05d4 \u05d1\u05d8\u05d1\u05dc\u05d4 \u05d0\u05d7\u05e8\u05d9","paste_row_before_desc":"\u05d4\u05d3\u05d1\u05e7\u05ea \u05e9\u05d5\u05e8\u05d4 \u05d1\u05d8\u05d1\u05dc\u05d4 \u05dc\u05e4\u05e0\u05d9","props_desc":"\u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05d4\u05d8\u05d1\u05dc\u05d4","cell_desc":"\u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05ea\u05d0 \u05d1\u05d8\u05d1\u05dc\u05d4","row_desc":"\u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05e9\u05d5\u05e8\u05d4 \u05d1\u05d8\u05d1\u05dc\u05d4","merge_cells_desc":"\u05d0\u05d9\u05d7\u05d5\u05d3 \u05ea\u05d0\u05d9\u05dd \u05d1\u05d8\u05d1\u05dc\u05d4","split_cells_desc":"\u05e4\u05d9\u05e6\u05d5\u05dc \u05ea\u05d0\u05d9\u05dd \u05d1\u05d8\u05d1\u05dc\u05d4","delete_col_desc":"\u05d4\u05e1\u05e8\u05ea \u05e2\u05de\u05d5\u05d3\u05d4","col_after_desc":"\u05d4\u05db\u05e0\u05e1\u05ea \u05e2\u05de\u05d5\u05d3\u05d4 \u05de\u05e9\u05de\u05d0\u05dc","col_before_desc":"\u05d4\u05db\u05e0\u05e1\u05ea \u05e2\u05de\u05d5\u05d3\u05d4 \u05de\u05d9\u05de\u05d9\u05df","delete_row_desc":"\u05de\u05d7\u05d9\u05e7\u05ea \u05e9\u05d5\u05e8\u05d4","row_after_desc":"\u05d4\u05db\u05e0\u05e1\u05ea \u05e9\u05d5\u05e8\u05d4 \u05de\u05ea\u05d7\u05ea","row_before_desc":"\u05d4\u05db\u05e0\u05e1\u05ea \u05e9\u05d5\u05e8\u05d4 \u05de\u05e2\u05dc",desc:"\u05d4\u05db\u05e0\u05e1\u05ea \u05d0\u05d5 \u05e2\u05e8\u05d9\u05db\u05ea \u05d8\u05d1\u05dc\u05d4","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"\u05d0\u05dd \u05ea\u05e9\u05d7\u05d6\u05e8 \u05d0\u05ea \u05d4\u05ea\u05d5\u05db\u05df \u05dc\u05d2\u05e8\u05d9\u05e1\u05d0 \u05d4\u05e9\u05de\u05d5\u05e8\u05d4, \u05ea\u05d0\u05d1\u05d3 \u05d0\u05ea \u05db\u05dc \u05d4\u05ea\u05d5\u05db\u05df \u05e9\u05e0\u05de\u05e6\u05d0 \u05db\u05e2\u05ea \u05d1\u05e2\u05d5\u05e8\u05da. \u05d4\u05d0\u05dd \u05d0\u05ea\u05d4 \u05d1\u05d8\u05d5\u05d7 \u05e9\u05d0\u05ea\u05d4 \u05e8\u05d5\u05e6\u05d4 \u05dc\u05e9\u05d7\u05d6\u05e8 \u05d0\u05ea \u05d4\u05ea\u05d5\u05db\u05df \u05dc\u05d2\u05d9\u05e8\u05e1\u05d0 \u05d4\u05e9\u05de\u05d5\u05e8\u05d4?.","restore_content":"\u05e9\u05d7\u05d6\u05d5\u05e8 \u05dc\u05d2\u05d9\u05e8\u05e1\u05d0 \u05e9\u05de\u05d5\u05e8\u05d4 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9\u05ea","unload_msg":"\u05d4\u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05e9\u05d1\u05d9\u05e6\u05e2\u05ea \u05dc\u05d0 \u05d9\u05e9\u05de\u05e8\u05d5 \u05d0\u05dd \u05ea\u05e2\u05d1\u05d5\u05e8 \u05de\u05d3\u05e3 \u05d6\u05d4"},fullscreen:{desc:"\u05de\u05e2\u05d1\u05e8 \u05dc\u05de\u05e1\u05da \u05de\u05dc\u05d0/\u05d7\u05dc\u05e7\u05d9"},media:{edit:"\u05e2\u05e8\u05d9\u05db\u05ea \u05e1\u05e8\u05d8\u05d5\u05df",desc:"\u05d4\u05d5\u05e1\u05e4\u05ea/\u05e2\u05e8\u05d9\u05db\u05ea \u05e1\u05e8\u05d8\u05d5\u05df","delta_height":"","delta_width":""},fullpage:{desc:"\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 \u05e2\u05de\u05d5\u05d3","delta_width":"","delta_height":""},template:{desc:"Insert predefined template content"},visualchars:{desc:"\u05d4\u05e6\u05d2/\u05d4\u05e1\u05ea\u05e8 \u05ea\u05d5\u05d5\u05d9 \u05d1\u05e7\u05e8\u05d4"},spellchecker:{desc:"\u05d4\u05e4\u05e2\u05dc\u05ea \u05d1\u05d5\u05d3\u05e7 \u05d0\u05d9\u05d5\u05ea",menu:"\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d1\u05d5\u05d3\u05e7 \u05d0\u05d9\u05d5\u05ea","ignore_word":"\u05dc\u05d4\u05ea\u05e2\u05dc\u05dd \u05de\u05d4\u05de\u05d9\u05dc\u05d4","ignore_words":"\u05dc\u05d4\u05ea\u05e2\u05dc\u05dd \u05de\u05d4\u05db\u05dc",langs:"\u05e9\u05e4\u05d5\u05ea",wait:"\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df..",sug:"\u05d4\u05e6\u05e2\u05d5\u05ea","no_sug":"\u05d0\u05d9\u05df \u05d4\u05e6\u05e2\u05d5\u05ea","no_mpell":"\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05e9\u05d2\u05d9\u05d0\u05d5\u05ea \u05d0\u05d9\u05d5\u05ea","learn_word":"\u05dc\u05de\u05d3 \u05de\u05d9\u05dc\u05d9\u05dd"},pagebreak:{desc:"\u05d4\u05d5\u05e1\u05e4\u05ea \u05de\u05e2\u05d1\u05e8 \u05d3\u05e3"},advlist:{types:"\u05e1\u05d5\u05d2\u05d9\u05dd",def:"\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc","lower_alpha":"Lower alpha","lower_greek":"Lower greek","lower_roman":"Lower roman","upper_alpha":"Upper alpha","upper_roman":"Upper roman",circle:"\u05e2\u05d2\u05d5\u05dc",disc:"\u05d3\u05d9\u05e1\u05e7",square:"\u05de\u05e8\u05d5\u05d1\u05e2"},colors:{"333300":"\u05d6\u05d9\u05ea \u05db\u05d4\u05d4","993300":"\u05db\u05ea\u05d5\u05dd \u05db\u05d4\u05d4","000000":"\u05e9\u05d7\u05d5\u05e8","003300":"\u05d9\u05e8\u05d5\u05e7 \u05db\u05d4\u05d4","003366":"\u05d8\u05d5\u05e8\u05e7\u05d9\u05d6 \u05db\u05d4\u05d4","000080":"\u05db\u05d7\u05d5\u05dc \u05e6\u05d9","333399":"\u05d0\u05d9\u05e0\u05d3\u05d9\u05d2\u05d5","333333":"\u05d0\u05e4\u05d5\u05e8 \u05db\u05d4\u05d4 \u05de\u05d0\u05d5\u05d3","800000":"\u05e2\u05e8\u05de\u05d5\u05e0\u05d9",FF6600:"\u05db\u05ea\u05d5\u05dd","808000":"\u05d6\u05d9\u05ea","008000":"\u05d9\u05e8\u05d5\u05e7","008080":"\u05d9\u05e8\u05d5\u05e7-\u05db\u05d7\u05d5\u05dc \u05e2\u05de\u05d5\u05e7","0000FF":"\u05db\u05d7\u05d5\u05dc","666699":"\u05db\u05d7\u05d5\u05dc \u05d0\u05e4\u05e8\u05e4\u05e8","808080":"\u05d0\u05e4\u05d5\u05e8",FF0000:"\u05d0\u05d3\u05d5\u05dd",FF9900:"\u05e2\u05e0\u05d1\u05e8","99CC00":"\u05d9\u05e8\u05d5\u05e7 \u05e6\u05d4\u05d1\u05d4\u05d1","339966":"\u05d9\u05e8\u05d5\u05e7 \u05d9\u05dd","33CCCC":"\u05d8\u05d5\u05e8\u05e7\u05d9\u05d6","3366FF":"\u05db\u05d7\u05d5\u05dc \u05e8\u05d5\u05d9\u05d0\u05dc","800080":"\u05e1\u05d2\u05d5\u05dc","999999":"\u05d0\u05e4\u05d5\u05e8 \u05d1\u05d9\u05e0\u05d9\u05d9\u05dd",FF00FF:"\u05e1\u05d2\u05d5\u05dc-\u05d5\u05e8\u05d5\u05d3 (\u05de\u05d2\u05f3\u05e0\u05d8\u05d4)",FFCC00:"\u05d6\u05d4\u05d1",FFFF00:"\u05e6\u05d4\u05d5\u05d1","00FF00":"\u05dc\u05d9\u05d9\u05dd","00FFFF":"\u05d8\u05d5\u05e8\u05e7\u05d9\u05d6 \u05de\u05d9\u05dd","00CCFF":"\u05ea\u05db\u05dc\u05ea","993366":"\u05d7\u05d5\u05dd",C0C0C0:"\u05db\u05e1\u05e3",FF99CC:"\u05d5\u05e8\u05d5\u05d3",FFCC99:"\u05d0\u05e4\u05e8\u05e1\u05e7",FFFF99:"\u05e6\u05d4\u05d5\u05d1 \u05d1\u05d4\u05d9\u05e8",CCFFCC:"\u05d9\u05e8\u05d5\u05e7 \u05d7\u05d9\u05d5\u05d5\u05e8",CCFFFF:"\u05d8\u05d5\u05e8\u05e7\u05d9\u05d6 \u05d1\u05d4\u05d9\u05e8","99CCFF":"\u05ea\u05db\u05dc\u05ea \u05d1\u05d4\u05d9\u05e8",CC99FF:"\u05d5\u05e8\u05d5\u05d3 \u05e2\u05de\u05d5\u05e7",FFFFFF:"\u05dc\u05d1\u05df"},aria:{"rich_text_area":"\u05d0\u05d6\u05d5\u05e8 \u05e2\u05d5\u05e8\u05da \u05d8\u05e7\u05e1\u05d8 \u05e2\u05e9\u05d9\u05e8"},wordcount:{words:"\u05de\u05d9\u05dc\u05d9\u05dd:"}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he_IL.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he_IL.js new file mode 100644 index 0000000000..e1af3e3866 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/he_IL.js @@ -0,0 +1,262 @@ +tinymce.addI18n('he_IL',{ +"Redo": "\u05d1\u05e6\u05e2 \u05e9\u05d5\u05d1", +"Undo": "\u05d1\u05d8\u05dc \u05e4\u05e2\u05d5\u05dc\u05d4", +"Cut": "\u05d2\u05d6\u05d5\u05e8", +"Copy": "\u05d4\u05e2\u05ea\u05e7", +"Paste": "\u05d4\u05d3\u05d1\u05e7", +"Select all": "\u05d1\u05d7\u05e8 \u05d4\u05db\u05dc", +"New document": "\u05de\u05e1\u05de\u05da \u05d7\u05d3\u05e9", +"Ok": "\u05d0\u05d9\u05e9\u05d5\u05e8", +"Cancel": "\u05d1\u05d8\u05dc", +"Visual aids": "\u05e2\u05d6\u05e8\u05d9\u05dd \u05d7\u05d6\u05d5\u05ea\u05d9\u05d9\u05dd", +"Bold": "\u05de\u05d5\u05d3\u05d2\u05e9", +"Italic": "\u05e0\u05d8\u05d5\u05d9", +"Underline": "\u05e7\u05d5 \u05ea\u05d7\u05ea\u05d9", +"Strikethrough": "\u05e7\u05d5 \u05d7\u05d5\u05e6\u05d4", +"Superscript": "\u05db\u05ea\u05d1 \u05e2\u05d9\u05dc\u05d9", +"Subscript": "\u05db\u05ea\u05d1 \u05ea\u05d7\u05ea\u05d9", +"Clear formatting": "\u05e0\u05e7\u05d4 \u05e2\u05d9\u05e6\u05d5\u05d1", +"Align left": "\u05d9\u05d9\u05e9\u05e8 \u05dc\u05e9\u05de\u05d0\u05dc", +"Align center": "\u05de\u05e8\u05db\u05d6", +"Align right": "\u05d9\u05d9\u05e9\u05e8 \u05dc\u05d9\u05de\u05d9\u05df", +"Justify": "\u05de\u05ea\u05d7 \u05dc\u05e6\u05d3\u05d3\u05d9\u05dd", +"Bullet list": "\u05e8\u05e9\u05d9\u05de\u05ea \u05ea\u05d1\u05dc\u05d9\u05d8\u05d9\u05dd", +"Numbered list": "\u05e8\u05e9\u05d9\u05de\u05d4 \u05de\u05de\u05d5\u05e1\u05e4\u05e8\u05ea", +"Decrease indent": "\u05d4\u05e7\u05d8\u05df \u05d4\u05d6\u05d7\u05d4", +"Increase indent": "\u05d4\u05d2\u05d3\u05dc \u05d4\u05d6\u05d7\u05d4", +"Close": "\u05e1\u05d2\u05d5\u05e8", +"Formats": "\u05e2\u05d9\u05e6\u05d5\u05d1\u05d9\u05dd", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u05d4\u05d3\u05e4\u05d3\u05e4\u05df \u05e9\u05dc\u05da \u05d0\u05d9\u05e0\u05d5 \u05de\u05d0\u05e4\u05e9\u05e8 \u05d2\u05d9\u05e9\u05d4 \u05d9\u05e9\u05d9\u05e8\u05d4 \u05dc\u05dc\u05d5\u05d7. \u05d0\u05e0\u05d0 \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e6\u05d5\u05e8\u05d9 \u05d4\u05de\u05e7\u05dc\u05d3\u05ea Ctrl+X\/C\/V \u05d1\u05de\u05e7\u05d5\u05dd.", +"Headers": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea", +"Header 1": "\u05db\u05d5\u05ea\u05e8\u05ea 1", +"Header 2": "\u05db\u05d5\u05ea\u05e8\u05ea 2", +"Header 3": "\u05db\u05d5\u05ea\u05e8\u05ea 3", +"Header 4": "\u05db\u05d5\u05ea\u05e8\u05ea 4", +"Header 5": "\u05db\u05d5\u05ea\u05e8\u05ea 5", +"Header 6": "\u05db\u05d5\u05ea\u05e8\u05ea 6", +"Headings": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea", +"Heading 1": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 1", +"Heading 2": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 2", +"Heading 3": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 3", +"Heading 4": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 4", +"Heading 5": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 5", +"Heading 6": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea 6", +"Preformatted": "\u05e2\u05e6\u05d1 \u05de\u05d7\u05d3\u05e9", +"Div": "\u05de\u05e7\u05d8\u05e2 \u05e7\u05d5\u05d3 Div", +"Pre": "\u05e7\u05d8\u05e2 \u05de\u05e7\u05d3\u05d9\u05dd Pre", +"Code": "\u05e7\u05d5\u05d3", +"Paragraph": "\u05e4\u05d9\u05e1\u05e7\u05d4", +"Blockquote": "\u05de\u05e7\u05d8\u05e2 \u05e6\u05d9\u05d8\u05d5\u05d8", +"Inline": "\u05d1\u05d2\u05d5\u05e3 \u05d4\u05d8\u05e7\u05e1\u05d8", +"Blocks": "\u05de\u05d1\u05e0\u05d9\u05dd", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u05d4\u05d3\u05d1\u05e7\u05d4 \u05d1\u05de\u05e6\u05d1 \u05d8\u05e7\u05e1\u05d8 \u05e8\u05d2\u05d9\u05dc. \u05ea\u05db\u05e0\u05d9\u05dd \u05d9\u05d5\u05d3\u05d1\u05e7\u05d5 \u05de\u05e2\u05ea\u05d4 \u05db\u05d8\u05e7\u05e1\u05d8 \u05e8\u05d2\u05d9\u05dc \u05e2\u05d3 \u05e9\u05ea\u05db\u05d1\u05d4 \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05d6\u05d5.", +"Font Family": "\u05e1\u05d5\u05d2 \u05d2\u05d5\u05e4\u05df", +"Font Sizes": "\u05d2\u05d5\u05d3\u05dc \u05d2\u05d5\u05e4\u05df", +"Class": "\u05de\u05d7\u05dc\u05e7\u05d4", +"Browse for an image": "\u05d1\u05d7\u05e8 \u05ea\u05de\u05d5\u05e0\u05d4 \u05dc\u05d4\u05e2\u05dc\u05d5\u05ea", +"OR": "\u05d0\u05d5", +"Drop an image here": "\u05e9\u05d7\u05e8\u05e8 \u05ea\u05de\u05d5\u05e0\u05d4 \u05db\u05d0\u05df", +"Upload": "\u05d4\u05e2\u05dc\u05d4", +"Block": "\u05d1\u05dc\u05d5\u05e7", +"Align": "\u05d9\u05d9\u05e9\u05e8", +"Default": "\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc", +"Circle": "\u05e2\u05d9\u05d2\u05d5\u05dc", +"Disc": "\u05d7\u05d9\u05e9\u05d5\u05e7", +"Square": "\u05e8\u05d9\u05d1\u05d5\u05e2", +"Lower Alpha": "\u05d0\u05d5\u05ea\u05d9\u05d5\u05ea \u05d0\u05e0\u05d2\u05dc\u05d9\u05d5\u05ea \u05e7\u05d8\u05e0\u05d5\u05ea", +"Lower Greek": "\u05d0\u05d5\u05ea\u05d9\u05d5\u05ea \u05d9\u05d5\u05d5\u05e0\u05d9\u05d5\u05ea \u05e7\u05d8\u05e0\u05d5\u05ea", +"Lower Roman": "\u05e1\u05e4\u05e8\u05d5\u05ea \u05e8\u05d5\u05de\u05d9\u05d5\u05ea \u05e7\u05d8\u05e0\u05d5\u05ea", +"Upper Alpha": "\u05d0\u05d5\u05ea\u05d9\u05d5\u05ea \u05d0\u05e0\u05d2\u05dc\u05d9\u05d5\u05ea \u05d2\u05d3\u05d5\u05dc\u05d5\u05ea", +"Upper Roman": "\u05e1\u05e4\u05e8\u05d5\u05ea \u05e8\u05d5\u05de\u05d9\u05d5\u05ea \u05d2\u05d3\u05d5\u05dc\u05d5\u05ea", +"Anchor": "\u05de\u05e7\u05d5\u05dd \u05e2\u05d9\u05d2\u05d5\u05df", +"Name": "\u05e9\u05dd", +"Id": "\u05de\u05d6\u05d4\u05d4", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u05d4\u05de\u05d6\u05d4\u05d4 \u05d7\u05d9\u05d9\u05d1 \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d0\u05d5\u05ea \u05d5\u05dc\u05d0\u05d7\u05e8\u05d9\u05d4 \u05e8\u05e7 \u05d0\u05d5\u05ea\u05d9\u05d5\u05ea, \u05de\u05e1\u05e4\u05e8\u05d9\u05dd, \u05de\u05e7\u05e4\u05d9\u05dd, \u05e0\u05e7\u05d5\u05d3\u05d5\u05ea, \u05e0\u05e7\u05d5\u05d3\u05ea\u05d9\u05d9\u05dd \u05d0\u05d5 \u05e7\u05d5\u05d5\u05d9\u05dd \u05ea\u05d7\u05ea\u05d9\u05d9\u05dd.", +"You have unsaved changes are you sure you want to navigate away?": "\u05d4\u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05dc\u05d0 \u05e0\u05e9\u05de\u05e8\u05d5. \u05d1\u05d8\u05d5\u05d7 \u05e9\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05e6\u05d0\u05ea \u05de\u05d4\u05d3\u05e3?", +"Restore last draft": "\u05e9\u05d7\u05d6\u05e8 \u05d8\u05d9\u05d5\u05d8\u05d4 \u05d0\u05d7\u05e8\u05d5\u05e0\u05d4", +"Special character": "\u05ea\u05d5\u05d5\u05d9\u05dd \u05de\u05d9\u05d5\u05d7\u05d3\u05d9\u05dd", +"Source code": "\u05e7\u05d5\u05d3 \u05de\u05e7\u05d5\u05e8", +"Insert\/Edit code sample": "\u05d4\u05db\u05e0\u05e1\/\u05e2\u05e8\u05d5\u05da \u05d3\u05d5\u05d2\u05de\u05ea \u05e7\u05d5\u05d3", +"Language": "\u05e9\u05e4\u05d4", +"Code sample": "\u05d3\u05d5\u05d2\u05de\u05ea \u05e7\u05d5\u05d3", +"Color": "\u05e6\u05d1\u05e2", +"R": "\u05d0'", +"G": "\u05d9'", +"B": "\u05db'", +"Left to right": "\u05de\u05e9\u05de\u05d0\u05dc \u05dc\u05d9\u05de\u05d9\u05df", +"Right to left": "\u05de\u05d9\u05de\u05d9\u05df \u05dc\u05e9\u05de\u05d0\u05dc", +"Emoticons": "\u05de\u05d7\u05d5\u05d5\u05ea", +"Document properties": "\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 \u05de\u05e1\u05de\u05da", +"Title": "\u05db\u05d5\u05ea\u05e8\u05ea", +"Keywords": "\u05de\u05d9\u05dc\u05d5\u05ea \u05de\u05e4\u05ea\u05d7", +"Description": "\u05ea\u05d9\u05d0\u05d5\u05e8", +"Robots": "\u05e8\u05d5\u05d1\u05d5\u05d8\u05d9\u05dd", +"Author": "\u05de\u05d7\u05d1\u05e8", +"Encoding": "\u05e7\u05d9\u05d3\u05d5\u05d3", +"Fullscreen": "\u05de\u05e1\u05da \u05de\u05dc\u05d0", +"Action": "\u05e4\u05e2\u05d5\u05dc\u05d4", +"Shortcut": "\u05e7\u05d9\u05e6\u05d5\u05e8", +"Help": "\u05e2\u05d6\u05e8\u05d4", +"Address": "\u05db\u05ea\u05d5\u05d1\u05ea", +"Focus to menubar": "\u05d4\u05e2\u05d1\u05e8 \u05de\u05d9\u05e7\u05d5\u05d3 \u05dc\u05e1\u05e8\u05d2\u05dc \u05d4\u05ea\u05e4\u05e8\u05d8\u05d9\u05dd", +"Focus to toolbar": "\u05d4\u05e2\u05d1\u05e8 \u05de\u05d9\u05e7\u05d5\u05d3 \u05dc\u05e1\u05e8\u05d2\u05dc \u05d4\u05db\u05dc\u05d9\u05dd", +"Focus to element path": "\u05e2\u05d1\u05e8 \u05de\u05d9\u05e7\u05d5\u05d3 \u05dc\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05e4\u05e8\u05d9\u05d8", +"Focus to contextual toolbar": "\u05d4\u05e2\u05d1\u05e8 \u05de\u05d9\u05e7\u05d5\u05d3 \u05dc\u05e1\u05e8\u05d2\u05dc \u05ea\u05d5\u05db\u05df", +"Insert link (if link plugin activated)": "\u05d4\u05db\u05e0\u05e1 \u05e7\u05d9\u05e9\u05d5\u05e8 (\u05d0\u05dd \u05ea\u05d5\u05e1\u05e3 \"\u05e7\u05d9\u05e9\u05d5\u05e8\u05d9\u05dd\" \u05e4\u05e2\u05d9\u05dc)", +"Save (if save plugin activated)": "\u05e9\u05de\u05d5\u05e8 (\u05d0\u05dd \u05ea\u05d5\u05e1\u05e3 \"\u05e9\u05de\u05d9\u05e8\u05d4\" \u05e4\u05e2\u05d9\u05dc)", +"Find (if searchreplace plugin activated)": "\u05d7\u05e4\u05e9 (\u05d0\u05dd \u05ea\u05d5\u05e1\u05e3 \"\u05d7\u05e4\u05e9 \u05d5\u05d4\u05d7\u05dc\u05e3\" \u05e4\u05e2\u05d9\u05dc)", +"Plugins installed ({0}):": "\u05ea\u05d5\u05e1\u05e4\u05d9\u05dd \u05de\u05d5\u05ea\u05e7\u05e0\u05d9\u05dd ({0}):", +"Premium plugins:": "\u05ea\u05d5\u05e1\u05e4\u05d9\u05dd \u05d1\u05ea\u05e9\u05dc\u05d5\u05dd:", +"Learn more...": "\u05dc\u05de\u05d3 \u05e2\u05d5\u05d3...", +"You are using {0}": "\u05d0\u05ea\\\u05d4 \u05de\u05e9\u05ea\u05de\u05e9\\\u05ea {0}", +"Plugins": "\u05ea\u05d5\u05e1\u05e4\u05d9\u05dd", +"Handy Shortcuts": "\u05e7\u05d9\u05e6\u05d5\u05e8\u05d9\u05dd \u05e9\u05d9\u05de\u05d5\u05e9\u05d9\u05d9\u05dd", +"Horizontal line": "\u05e7\u05d5 \u05d0\u05d5\u05e4\u05e7\u05d9", +"Insert\/edit image": "\u05d4\u05db\u05e0\u05e1\/\u05e2\u05e8\u05d5\u05da \u05ea\u05de\u05d5\u05e0\u05d4", +"Image description": "\u05ea\u05d9\u05d0\u05d5\u05e8 \u05d4\u05ea\u05de\u05d5\u05e0\u05d4", +"Source": "\u05de\u05e7\u05d5\u05e8", +"Dimensions": "\u05de\u05d9\u05de\u05d3\u05d9\u05dd", +"Constrain proportions": "\u05d4\u05d2\u05d1\u05dc\u05ea \u05e4\u05e8\u05d5\u05e4\u05d5\u05e8\u05e6\u05d9\u05d5\u05ea", +"General": "\u05db\u05dc\u05dc\u05d9", +"Advanced": "\u05de\u05ea\u05e7\u05d3\u05dd", +"Style": "\u05e1\u05d2\u05e0\u05d5\u05df", +"Vertical space": "\u05de\u05e8\u05d5\u05d5\u05d7 \u05d0\u05e0\u05db\u05d9", +"Horizontal space": "\u05de\u05e8\u05d5\u05d5\u05d7 \u05d0\u05d5\u05e4\u05e7\u05d9", +"Border": "\u05de\u05e1\u05d2\u05e8\u05ea", +"Insert image": "\u05d4\u05db\u05e0\u05e1 \u05ea\u05de\u05d5\u05e0\u05d4", +"Image": "\u05ea\u05de\u05d5\u05e0\u05d4", +"Image list": "\u05e8\u05e9\u05d9\u05de\u05ea \u05ea\u05de\u05d5\u05e0\u05d5\u05ea", +"Rotate counterclockwise": "\u05e1\u05d5\u05d1\u05d1 \u05d1\u05db\u05d9\u05d5\u05d5\u05df \u05d4\u05e4\u05d5\u05da \u05dc\u05e9\u05e2\u05d5\u05df", +"Rotate clockwise": "\u05e1\u05d5\u05d1\u05d1 \u05d1\u05db\u05d9\u05d5\u05d5\u05df \u05d4\u05e9\u05e2\u05d5\u05df", +"Flip vertically": "\u05d4\u05e4\u05d5\u05da \u05d0\u05e0\u05db\u05d9\u05ea", +"Flip horizontally": "\u05d4\u05e4\u05d5\u05da \u05d0\u05d5\u05e4\u05e7\u05d9\u05ea", +"Edit image": "\u05e2\u05e8\u05d5\u05da \u05ea\u05de\u05d5\u05e0\u05d4", +"Image options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05ea\u05de\u05d5\u05e0\u05d4", +"Zoom in": "\u05d4\u05d2\u05d3\u05dc \u05ea\u05e6\u05d5\u05d2\u05d4", +"Zoom out": "\u05d4\u05e7\u05d8\u05df \u05ea\u05e6\u05d5\u05d2\u05d4", +"Crop": "\u05e7\u05e6\u05e5", +"Resize": "\u05e9\u05e0\u05d4 \u05d2\u05d5\u05d3\u05dc", +"Orientation": "\u05db\u05d9\u05d5\u05d5\u05df \u05dc\u05d0\u05d5\u05e8\u05da \/ \u05dc\u05e8\u05d5\u05d7\u05d1", +"Brightness": "\u05d1\u05d4\u05d9\u05e8\u05d5\u05ea", +"Sharpen": "\u05d7\u05d3\u05d3", +"Contrast": "\u05e0\u05d9\u05d2\u05d5\u05d3\u05d9\u05d5\u05ea", +"Color levels": "\u05e8\u05de\u05d5\u05ea \u05e6\u05d1\u05e2\u05d9\u05dd", +"Gamma": "\u05d2\u05d0\u05de\u05d4", +"Invert": "\u05d4\u05d9\u05e4\u05d5\u05da \u05e6\u05d1\u05e2\u05d9\u05dd", +"Apply": "\u05d9\u05d9\u05e9\u05dd", +"Back": "\u05d7\u05d6\u05d5\u05e8", +"Insert date\/time": "\u05d4\u05db\u05e0\u05e1 \u05ea\u05d0\u05e8\u05d9\u05da\/\u05e9\u05e2\u05d4", +"Date\/time": "\u05ea\u05d0\u05e8\u05d9\u05da\/\u05e9\u05e2\u05d4", +"Insert link": "\u05d4\u05db\u05e0\u05e1 \u05e7\u05d9\u05e9\u05d5\u05e8", +"Insert\/edit link": "\u05d4\u05db\u05e0\u05e1\/\u05e2\u05e8\u05d5\u05da \u05e7\u05d9\u05e9\u05d5\u05e8", +"Text to display": "\u05d8\u05e7\u05e1\u05d8 \u05dc\u05d4\u05e6\u05d2\u05d4", +"Url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8", +"Target": "\u05de\u05d8\u05e8\u05d4", +"None": "\u05dc\u05dc\u05d0", +"New window": "\u05d7\u05dc\u05d5\u05df \u05d7\u05d3\u05e9", +"Remove link": "\u05de\u05d7\u05e7 \u05e7\u05d9\u05e9\u05d5\u05e8", +"Anchors": "\u05e2\u05d5\u05d2\u05e0\u05d9\u05dd", +"Link": "\u05e7\u05d9\u05e9\u05d5\u05e8", +"Paste or type a link": "\u05d4\u05d3\u05d1\u05e7 \u05d0\u05d5 \u05d4\u05e7\u05dc\u05d3 \u05e7\u05d9\u05e9\u05d5\u05e8", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u05e0\u05e8\u05d0\u05d4 \u05e9\u05d4\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05d4\u05db\u05e0\u05e1\u05ea \u05d4\u05d9\u05d0 \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05d9\u05de\u05d9\u05d9\u05dc. \u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea \u05d4\u05e7\u05d9\u05d3\u05d5\u05de\u05ea :mailto?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u05e0\u05e8\u05d0\u05d4 \u05e9\u05d4\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05d4\u05db\u05e0\u05e1\u05ea \u05d4\u05d9\u05d0 \u05e7\u05d9\u05e9\u05d5\u05e8 \u05d7\u05d9\u05e6\u05d5\u05e0\u05d9 \u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea http:\/\/?", +"Link list": "\u05e8\u05e9\u05d9\u05de\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8\u05d9\u05dd", +"Insert video": "\u05d4\u05db\u05e0\u05e1 \u05e1\u05e8\u05d8\u05d5\u05df", +"Insert\/edit video": "\u05d4\u05db\u05e0\u05e1\/\u05e2\u05e8\u05d5\u05da \u05e1\u05e8\u05d8\u05d5\u05df", +"Insert\/edit media": "\u05d4\u05db\u05e0\u05e1\/\u05e2\u05e8\u05d5\u05da \u05de\u05d3\u05d9\u05d4", +"Alternative source": "\u05de\u05e7\u05d5\u05e8 \u05de\u05e9\u05e0\u05d9", +"Poster": "\u05e4\u05d5\u05e1\u05d8\u05e8", +"Paste your embed code below:": "\u05d4\u05d3\u05d1\u05e7 \u05e7\u05d5\u05d3 \u05d4\u05d8\u05de\u05e2\u05d4 \u05de\u05ea\u05d7\u05ea:", +"Embed": "\u05d4\u05d8\u05de\u05e2", +"Media": "\u05de\u05d3\u05d9\u05d4", +"Nonbreaking space": "\u05e8\u05d5\u05d5\u05d7 (\u05dc\u05dc\u05d0 \u05e9\u05d1\u05d9\u05e8\u05ea \u05e9\u05d5\u05e8\u05d4)", +"Page break": "\u05d3\u05e3 \u05d7\u05d3\u05e9", +"Paste as text": "\u05d4\u05d3\u05d1\u05e7 \u05db\u05d8\u05e7\u05e1\u05d8", +"Preview": "\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4", +"Print": "\u05d4\u05d3\u05e4\u05e1", +"Save": "\u05e9\u05de\u05d9\u05e8\u05d4", +"Find": "\u05d7\u05e4\u05e9", +"Replace with": "\u05d4\u05d7\u05dc\u05e3 \u05d1", +"Replace": "\u05d4\u05d7\u05dc\u05e3", +"Replace all": "\u05d4\u05d7\u05dc\u05e3 \u05d4\u05db\u05dc", +"Prev": "\u05e7\u05d5\u05d3\u05dd", +"Next": "\u05d4\u05d1\u05d0", +"Find and replace": "\u05d7\u05e4\u05e9 \u05d5\u05d4\u05d7\u05dc\u05e3", +"Could not find the specified string.": "\u05de\u05d7\u05e8\u05d5\u05d6\u05ea \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d4", +"Match case": "\u05d4\u05d1\u05d7\u05df \u05d1\u05d9\u05df \u05d0\u05d5\u05ea\u05d9\u05d5\u05ea \u05e7\u05d8\u05e0\u05d5\u05ea \u05dc\u05d2\u05d3\u05d5\u05dc\u05d5\u05ea", +"Whole words": "\u05de\u05d9\u05dc\u05d4 \u05e9\u05dc\u05de\u05d4", +"Spellcheck": "\u05d1\u05d5\u05d3\u05e7 \u05d0\u05d9\u05d5\u05ea", +"Ignore": "\u05d4\u05ea\u05e2\u05dc\u05dd", +"Ignore all": "\u05d4\u05ea\u05e2\u05dc\u05dd \u05de\u05d4\u05db\u05dc", +"Finish": "\u05e1\u05d9\u05d9\u05dd", +"Add to Dictionary": "\u05d4\u05d5\u05e1\u05e3 \u05dc\u05de\u05d9\u05dc\u05d5\u05df", +"Insert table": "\u05d4\u05db\u05e0\u05e1 \u05d8\u05d1\u05dc\u05d4", +"Table properties": "\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 \u05d8\u05d1\u05dc\u05d4", +"Delete table": "\u05de\u05d7\u05e7 \u05d8\u05d1\u05dc\u05d4", +"Cell": "\u05ea\u05d0", +"Row": "\u05e9\u05d5\u05e8\u05d4", +"Column": "\u05e2\u05de\u05d5\u05d3\u05d4", +"Cell properties": "\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 \u05ea\u05d0", +"Merge cells": "\u05de\u05d6\u05d2 \u05ea\u05d0\u05d9\u05dd", +"Split cell": "\u05e4\u05e6\u05dc \u05ea\u05d0", +"Insert row before": "\u05d4\u05d5\u05e1\u05e3 \u05e9\u05d5\u05e8\u05d4 \u05dc\u05e4\u05e0\u05d9", +"Insert row after": "\u05d4\u05d5\u05e1\u05e3 \u05e9\u05d5\u05e8\u05d4 \u05d0\u05d7\u05e8\u05d9", +"Delete row": "\u05de\u05d7\u05e7 \u05e9\u05d5\u05e8\u05d4", +"Row properties": "\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 \u05e9\u05d5\u05e8\u05d4", +"Cut row": "\u05d2\u05d6\u05d5\u05e8 \u05e9\u05d5\u05e8\u05d4", +"Copy row": "\u05d4\u05e2\u05ea\u05e7 \u05e9\u05d5\u05e8\u05d4", +"Paste row before": "\u05d4\u05d3\u05d1\u05e7 \u05e9\u05d5\u05e8\u05d4 \u05dc\u05e4\u05e0\u05d9", +"Paste row after": "\u05d4\u05e2\u05ea\u05e7 \u05e9\u05d5\u05e8\u05d4 \u05d0\u05d7\u05e8\u05d9", +"Insert column before": "\u05d4\u05e2\u05ea\u05e7 \u05e2\u05de\u05d5\u05d3\u05d4 \u05dc\u05e4\u05e0\u05d9", +"Insert column after": "\u05d4\u05e2\u05ea\u05e7 \u05e2\u05de\u05d5\u05d3\u05d4 \u05d0\u05d7\u05e8\u05d9", +"Delete column": "\u05de\u05d7\u05e7 \u05e2\u05de\u05d5\u05d3\u05d4", +"Cols": "\u05e2\u05de\u05d5\u05d3\u05d5\u05ea", +"Rows": "\u05e9\u05d5\u05e8\u05d5\u05ea", +"Width": "\u05e8\u05d5\u05d7\u05d1", +"Height": "\u05d2\u05d5\u05d1\u05d4", +"Cell spacing": "\u05e9\u05d5\u05dc\u05d9\u05d9\u05dd \u05d7\u05d9\u05e6\u05d5\u05e0\u05d9\u05dd \u05dc\u05ea\u05d0", +"Cell padding": "\u05e9\u05d5\u05dc\u05d9\u05d9\u05dd \u05e4\u05e0\u05d9\u05de\u05d9\u05d9\u05dd \u05dc\u05ea\u05d0", +"Caption": "\u05db\u05d9\u05ea\u05d5\u05d1", +"Left": "\u05e9\u05de\u05d0\u05dc", +"Center": "\u05de\u05e8\u05db\u05d6", +"Right": "\u05d9\u05de\u05d9\u05df", +"Cell type": "\u05e1\u05d5\u05d2 \u05ea\u05d0", +"Scope": "\u05d4\u05d9\u05e7\u05e3", +"Alignment": "\u05d9\u05d9\u05e9\u05d5\u05e8", +"H Align": "\u05d9\u05d9\u05e9\u05d5\u05e8 \u05d0\u05d5\u05e4\u05e7\u05d9", +"V Align": "\u05d9\u05d9\u05e9\u05d5\u05e8 \u05d0\u05e0\u05db\u05d9", +"Top": "\u05e2\u05dc\u05d9\u05d5\u05df", +"Middle": "\u05d0\u05de\u05e6\u05e2", +"Bottom": "\u05ea\u05d7\u05ea\u05d9\u05ea", +"Header cell": "\u05db\u05d5\u05ea\u05e8\u05ea \u05dc\u05ea\u05d0", +"Row group": "\u05e7\u05d9\u05d1\u05d5\u05e5 \u05e9\u05d5\u05e8\u05d5\u05ea", +"Column group": "\u05e7\u05d9\u05d1\u05d5\u05e5 \u05e2\u05de\u05d5\u05d3\u05d5\u05ea", +"Row type": "\u05e1\u05d5\u05d2 \u05e9\u05d5\u05e8\u05d4", +"Header": "\u05db\u05d5\u05ea\u05e8\u05ea", +"Body": "\u05d2\u05d5\u05e3 \u05d4\u05d8\u05d1\u05dc\u05d0", +"Footer": "\u05db\u05d5\u05ea\u05e8\u05ea \u05ea\u05d7\u05ea\u05d5\u05e0\u05d4", +"Border color": "\u05e6\u05d1\u05e2 \u05d2\u05d1\u05d5\u05dc", +"Insert template": "\u05d4\u05db\u05e0\u05e1 \u05ea\u05d1\u05e0\u05d9\u05ea", +"Templates": "\u05ea\u05d1\u05e0\u05d9\u05d5\u05ea", +"Template": "\u05ea\u05d1\u05e0\u05d9\u05ea", +"Text color": "\u05e6\u05d1\u05e2 \u05d4\u05db\u05ea\u05d1", +"Background color": "\u05e6\u05d1\u05e2 \u05e8\u05e7\u05e2", +"Custom...": "\u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea...", +"Custom color": "\u05e6\u05d1\u05e2 \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea", +"No color": "\u05dc\u05dc\u05d0 \u05e6\u05d1\u05e2", +"Table of Contents": "\u05ea\u05d5\u05db\u05df \u05e2\u05e0\u05d9\u05d9\u05e0\u05d9\u05dd", +"Show blocks": "\u05d4\u05e6\u05d2 \u05ea\u05d9\u05d1\u05d5\u05ea", +"Show invisible characters": "\u05d4\u05e6\u05d2 \u05ea\u05d5\u05d5\u05d9\u05dd \u05dc\u05d0 \u05e0\u05e8\u05d0\u05d9\u05dd", +"Words: {0}": "\u05de\u05d9\u05dc\u05d9\u05dd: {0}", +"{0} words": "{0} \u05de\u05d9\u05dc\u05d9\u05dd", +"File": "\u05e7\u05d5\u05d1\u05e5", +"Edit": "\u05e2\u05e8\u05d9\u05db\u05d4", +"Insert": "\u05d4\u05d5\u05e1\u05e4\u05d4", +"View": "\u05ea\u05e6\u05d5\u05d2\u05d4", +"Format": "\u05e4\u05d5\u05e8\u05de\u05d8", +"Table": "\u05d8\u05d1\u05dc\u05d4", +"Tools": "\u05db\u05dc\u05d9\u05dd", +"Powered by {0}": "\u05de\u05d5\u05e4\u05e2\u05dc \u05e2\"\u05d9 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u05ea\u05d9\u05d1\u05ea \u05e2\u05e8\u05d9\u05db\u05d4 \u05d7\u05db\u05de\u05d4. \u05dc\u05d7\u05e5 Alt-F9 \u05dc\u05ea\u05e4\u05e8\u05d9\u05d8. Alt-F10 \u05dc\u05ea\u05e6\u05d5\u05d2\u05ea \u05db\u05e4\u05ea\u05d5\u05e8\u05d9\u05dd, Alt-0 \u05dc\u05e2\u05d6\u05e8\u05d4", +"_dir": "rtl" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js new file mode 100644 index 0000000000..d52f861ce9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js @@ -0,0 +1,253 @@ +tinymce.addI18n('hr',{ +"Redo": "Vrati", +"Undo": "Poni\u0161ti", +"Cut": "Izre\u017ei", +"Copy": "Kopiraj", +"Paste": "Zalijepi", +"Select all": "Ozna\u010di sve", +"New document": "Novi dokument", +"Ok": "U redu", +"Cancel": "Odustani", +"Visual aids": "Vizualna pomo\u0107", +"Bold": "Podebljano", +"Italic": "Kurziv", +"Underline": "Crta ispod", +"Strikethrough": "Crta kroz sredinu", +"Superscript": "Eksponent", +"Subscript": "Indeks", +"Clear formatting": "Ukloni oblikovanje", +"Align left": "Poravnaj lijevo", +"Align center": "Poravnaj po sredini", +"Align right": "Poravnaj desno", +"Justify": "Obostrano poravnanje", +"Bullet list": "Lista", +"Numbered list": "Numerirana lista", +"Decrease indent": "Smanji uvla\u010denje", +"Increase indent": "Pove\u0107aj uvla\u010denje", +"Close": "Zatvori", +"Formats": "Formati", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Va\u0161 preglednik ne podr\u017eava direktan pristup me\u0111uspremniku. Molimo Vas da umjesto toga koristite tipkovni\u010dke kratice Ctrl+X\/C\/V.", +"Headers": "Zaglavlja", +"Header 1": "Zaglavlje 1", +"Header 2": "Zaglavlje 2", +"Header 3": "Zaglavlje 3", +"Header 4": "Zaglavlje 4", +"Header 5": "Zaglavlje 5", +"Header 6": "Zaglavlje 6", +"Headings": "Naslovi", +"Heading 1": "Naslov 1", +"Heading 2": "Naslov 2", +"Heading 3": "Naslov 3", +"Heading 4": "Naslov 4", +"Heading 5": "Naslov 5", +"Heading 6": "Naslov 6", +"Div": "DIV", +"Pre": "PRE", +"Code": "CODE oznaka", +"Paragraph": "Paragraf", +"Blockquote": "BLOCKQUOTE", +"Inline": "Unutarnje", +"Blocks": "Blokovi", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Akcija zalijepi od sada lijepi \u010disti tekst. Sadr\u017eaj \u0107e biti zaljepljen kao \u010disti tekst sve dok ne isklju\u010dite ovu opciju.", +"Font Family": "Obitelj fonta", +"Font Sizes": "Veli\u010dine fonta", +"Class": "Class", +"Browse for an image": "Browse for an image", +"OR": "OR", +"Drop an image here": "Drop an image here", +"Upload": "Upload", +"Default": "Zadano", +"Circle": "Krug", +"Disc": "To\u010dka", +"Square": "Kvadrat", +"Lower Alpha": "Mala slova", +"Lower Greek": "Mala gr\u010dka slova", +"Lower Roman": "Mala rimska slova", +"Upper Alpha": "Velika slova", +"Upper Roman": "Velika rimska slova", +"Anchor": "Sidro", +"Name": "Ime", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id treba po\u010dinjati slovom, a nakon toga slijede samo slova, brojevi, crtice, to\u010dke, dvoto\u010dke i podvlake.", +"You have unsaved changes are you sure you want to navigate away?": "Postoje ne pohranjene izmjene, jeste li sigurni da \u017eelite oti\u0107i?", +"Restore last draft": "Vrati posljednju skicu", +"Special character": "Poseban znak", +"Source code": "Izvorni kod", +"Insert\/Edit code sample": "Umetni\/Uredi primjer k\u00f4da", +"Language": "Jezik", +"Color": "Boja", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "S lijeva na desno", +"Right to left": "S desna na lijevo", +"Emoticons": "Emotikoni", +"Document properties": "Svojstva dokumenta", +"Title": "Naslov", +"Keywords": "Klju\u010dne rije\u010di", +"Description": "Opis", +"Robots": "Roboti pretra\u017eiva\u010da", +"Author": "Autor", +"Encoding": "Kodna stranica", +"Fullscreen": "Cijeli ekran", +"Action": "Action", +"Shortcut": "Shortcut", +"Help": "Help", +"Address": "Address", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "Insert link (if link plugin activated)", +"Save (if save plugin activated)": "Save (if save plugin activated)", +"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)", +"Plugins installed ({0}):": "Plugins installed ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "Learn more...", +"You are using {0}": "You are using {0}", +"Horizontal line": "Horizontalna linija", +"Insert\/edit image": "Umetni\/izmijeni sliku", +"Image description": "Opis slike", +"Source": "Izvor", +"Dimensions": "Dimenzije", +"Constrain proportions": "Zadr\u017ei proporcije", +"General": "Op\u0107enito", +"Advanced": "Napredno", +"Style": "Stil", +"Vertical space": "Okomit razmak", +"Horizontal space": "Horizontalan razmak", +"Border": "Rub", +"Insert image": "Umetni sliku", +"Image": "Slika", +"Image list": "Image list", +"Rotate counterclockwise": "Rotiraj lijevo", +"Rotate clockwise": "Rotiraj desno", +"Flip vertically": "Obrni vertikalno", +"Flip horizontally": "Obrni horizontalno", +"Edit image": "Uredi sliku", +"Image options": "Opcije slike", +"Zoom in": "Pove\u0107aj", +"Zoom out": "Smanji", +"Crop": "Obre\u017ei", +"Resize": "Promjeni veli\u010dinu", +"Orientation": "Orijentacija", +"Brightness": "Svjetlina", +"Sharpen": "Izo\u0161travanje", +"Contrast": "Kontrast", +"Color levels": "Razine boje", +"Gamma": "Gamma", +"Invert": "Invertiraj", +"Apply": "Primijeni", +"Back": "Natrag", +"Insert date\/time": "Umetni datum\/vrijeme", +"Date\/time": "Datum\/vrijeme", +"Insert link": "Umetni poveznicu", +"Insert\/edit link": "Umetni\/izmijeni poveznicu", +"Text to display": "Tekst za prikaz", +"Url": "Url", +"Target": "Meta", +"None": "Ni\u0161ta", +"New window": "Novi prozor", +"Remove link": "Ukloni poveznicu", +"Anchors": "Kra\u0107e poveznice", +"Link": "Link", +"Paste or type a link": "Zalijepi ili upi\u0161i link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Izgleda da je URL koji ste upisali e-mail adresa. \u017delite li dodati obavezan mailto: prefiks?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Izgleda da je URL koji ste upisali vanjski link. \u017delite li dodati obavezan http:\/\/ prefiks?", +"Link list": "Link list", +"Insert video": "Umetni video", +"Insert\/edit video": "Umetni\/izmijeni video", +"Insert\/edit media": "Umetni\/uredi mediju", +"Alternative source": "Alternativni izvor", +"Poster": "Poster", +"Paste your embed code below:": "Umetnite va\u0161 kod za ugradnju ispod:", +"Embed": "Ugradi", +"Media": "Media", +"Nonbreaking space": "Neprekidaju\u0107i razmak", +"Page break": "Prijelom stranice", +"Paste as text": "Zalijepi kao tekst", +"Preview": "Pregled", +"Print": "Ispis", +"Save": "Spremi", +"Find": "Tra\u017ei", +"Replace with": "Zamijeni s", +"Replace": "Zamijeni", +"Replace all": "Zamijeni sve", +"Prev": "Prethodni", +"Next": "Slijede\u0107i", +"Find and replace": "Prona\u0111i i zamijeni", +"Could not find the specified string.": "Tra\u017eeni tekst nije prona\u0111en", +"Match case": "Pazi na mala i velika slova", +"Whole words": "Cijele rije\u010di", +"Spellcheck": "Provjeri pravopis", +"Ignore": "Zanemari", +"Ignore all": "Zanemari sve", +"Finish": "Zavr\u0161i", +"Add to Dictionary": "Dodaj u rje\u010dnik", +"Insert table": "Umetni tablicu", +"Table properties": "Svojstva tablice", +"Delete table": "Izbri\u0161i tablicu", +"Cell": "Polje", +"Row": "Redak", +"Column": "Stupac", +"Cell properties": "Svojstva polja", +"Merge cells": "Spoji polja", +"Split cell": "Razdvoji polja", +"Insert row before": "Umetni redak prije", +"Insert row after": "Umetni redak nakon", +"Delete row": "Izbri\u0161i redak", +"Row properties": "Svojstva redka", +"Cut row": "Izre\u017ei redak", +"Copy row": "Kopiraj redak", +"Paste row before": "Zalijepi redak prije", +"Paste row after": "Zalijepi redak nakon", +"Insert column before": "Umetni stupac prije", +"Insert column after": "Umetni stupac nakon", +"Delete column": "Izbri\u0161i stupac", +"Cols": "Stupci", +"Rows": "Redci", +"Width": "\u0160irina", +"Height": "Visina", +"Cell spacing": "Razmak izme\u0111u polja", +"Cell padding": "Razmak unutar polja", +"Caption": "Natpis", +"Left": "Lijevo", +"Center": "Sredina", +"Right": "Desno", +"Cell type": "Vrsta polja", +"Scope": "Doseg", +"Alignment": "Poravnanje", +"H Align": "H Poravnavanje", +"V Align": "V Poravnavanje", +"Top": "Vrh", +"Middle": "Sredina", +"Bottom": "Dno", +"Header cell": "Polje zaglavlja", +"Row group": "Grupirani redci", +"Column group": "Grupirani stupci", +"Row type": "Vrsta redka", +"Header": "Zaglavlje", +"Body": "Sadr\u017eaj", +"Footer": "Podno\u017eje", +"Border color": "Boja ruba", +"Insert template": "Umetni predlo\u017eak", +"Templates": "Predlo\u0161ci", +"Template": "Predlo\u017eak", +"Text color": "Boja teksta", +"Background color": "Boja pozadine", +"Custom...": "Prilago\u0111eno...", +"Custom color": "Prilago\u0111ena boja", +"No color": "Bez boje", +"Table of Contents": "Sadr\u017eaj", +"Show blocks": "Prika\u017ei blokove", +"Show invisible characters": "Prika\u017ei nevidljive znakove", +"Words: {0}": "Rije\u010di: {0}", +"File": "Datoteka", +"Edit": "Izmijeni", +"Insert": "Umetni", +"View": "Pogled", +"Format": "Oblikuj", +"Table": "Tablica", +"Tools": "Alati", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Pritisni ALT-F9 za izbornik. Pritisni ALT-F10 za alatnu traku. Pritisni ALT-0 za pomo\u0107" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hu_HU.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hu_HU.js new file mode 100644 index 0000000000..3972dc2b3c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hu_HU.js @@ -0,0 +1,261 @@ +tinymce.addI18n('hu_HU',{ +"Redo": "Ism\u00e9t", +"Undo": "Visszavon\u00e1s", +"Cut": "Kiv\u00e1g\u00e1s", +"Copy": "M\u00e1sol\u00e1s", +"Paste": "Beilleszt\u00e9s", +"Select all": "Minden kijel\u00f6l\u00e9se", +"New document": "\u00daj dokumentum", +"Ok": "Rendben", +"Cancel": "M\u00e9gse", +"Visual aids": "Vizu\u00e1lis seg\u00e9deszk\u00f6z\u00f6k", +"Bold": "F\u00e9lk\u00f6v\u00e9r", +"Italic": "D\u0151lt", +"Underline": "Al\u00e1h\u00fazott", +"Strikethrough": "\u00c1th\u00fazott", +"Superscript": "Fels\u0151 index", +"Subscript": "Als\u00f3 index", +"Clear formatting": "Form\u00e1z\u00e1s t\u00f6rl\u00e9se", +"Align left": "Balra igaz\u00edt", +"Align center": "K\u00f6z\u00e9pre z\u00e1r", +"Align right": "Jobbra igaz\u00edt", +"Justify": "Sorkiz\u00e1r\u00e1s", +"Bullet list": "Felsorol\u00e1s", +"Numbered list": "Sz\u00e1moz\u00e1s", +"Decrease indent": "Beh\u00faz\u00e1s cs\u00f6kkent\u00e9se", +"Increase indent": "Beh\u00faz\u00e1s n\u00f6vel\u00e9se", +"Close": "Bez\u00e1r", +"Formats": "Form\u00e1tumok", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "A b\u00f6ng\u00e9sz\u0151d nem t\u00e1mogatja a k\u00f6zvetlen hozz\u00e1f\u00e9r\u00e9st a v\u00e1g\u00f3laphoz. K\u00e9rlek haszn\u00e1ld a Ctrl+X\/C\/V billenty\u0171ket.", +"Headers": "C\u00edmsorok", +"Header 1": "C\u00edmsor 1", +"Header 2": "C\u00edmsor 2", +"Header 3": "C\u00edmsor 3", +"Header 4": "C\u00edmsor 4", +"Header 5": "C\u00edmsor 5", +"Header 6": "C\u00edmsor 6", +"Headings": "Fejl\u00e9cek", +"Heading 1": "Fejl\u00e9c 1", +"Heading 2": "Fejl\u00e9c 2", +"Heading 3": "Fejl\u00e9c 3", +"Heading 4": "Fejl\u00e9c 4", +"Heading 5": "Fejl\u00e9c 5", +"Heading 6": "Fejl\u00e9c 6", +"Preformatted": "El\u0151form\u00e1zott", +"Div": "Div", +"Pre": "El\u0151form\u00e1zott", +"Code": "K\u00f3d", +"Paragraph": "Bekezd\u00e9s", +"Blockquote": "Id\u00e9zetblokk", +"Inline": "Sz\u00f6vegk\u00f6zi", +"Blocks": "Blokkok", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Beilleszt\u00e9s mostant\u00f3l egyszer\u0171 sz\u00f6veg m\u00f3dban. A tartalmak mostant\u00f3l egyszer\u0171 sz\u00f6vegk\u00e9nt lesznek beillesztve, am\u00edg nem kapcsolod ki ezt az opci\u00f3t.", +"Font Family": "Bet\u0171t\u00edpus", +"Font Sizes": "Bet\u0171m\u00e9retek", +"Class": "Oszt\u00e1ly", +"Browse for an image": "K\u00e9p tall\u00f3z\u00e1sa", +"OR": "vagy", +"Drop an image here": "Dobj ide egy k\u00e9pet", +"Upload": "Felt\u00f6lt\u00e9s", +"Block": "Blokk", +"Align": "Igaz\u00edt\u00e1s", +"Default": "Alap\u00e9rtelmezett", +"Circle": "K\u00f6r", +"Disc": "Pont", +"Square": "N\u00e9gyzet", +"Lower Alpha": "Kisbet\u0171", +"Lower Greek": "Kis g\u00f6r\u00f6g sz\u00e1m", +"Lower Roman": "Kis r\u00f3mai sz\u00e1m", +"Upper Alpha": "Nagybet\u0171", +"Upper Roman": "Nagy r\u00f3mai sz\u00e1m", +"Anchor": "Horgony", +"Name": "N\u00e9v", +"Id": "Azonos\u00edt\u00f3", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Az azonos\u00edt\u00f3nak bet\u0171vel kell kezd\u0151dnie, azut\u00e1n csak bet\u0171ket, sz\u00e1mokat, gondolatjeleket, pontokat, kett\u0151spontokat vagy al\u00e1h\u00faz\u00e1st tartalmazhat.", +"You have unsaved changes are you sure you want to navigate away?": "Nem mentett m\u00f3dos\u00edt\u00e1said vannak, biztos hogy el akarsz navig\u00e1lni?", +"Restore last draft": "Utols\u00f3 piszkozat vissza\u00e1ll\u00edt\u00e1sa", +"Special character": "Speci\u00e1lis karakter", +"Source code": "Forr\u00e1sk\u00f3d", +"Insert\/Edit code sample": "K\u00f3dminta besz\u00far\u00e1sa\/szerkeszt\u00e9se", +"Language": "Nyelv", +"Code sample": "K\u00f3d p\u00e9lda", +"Color": "Sz\u00edn", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Balr\u00f3l jobbra", +"Right to left": "Jobbr\u00f3l balra", +"Emoticons": "Vigyorok", +"Document properties": "Dokumentum tulajdons\u00e1gai", +"Title": "C\u00edm", +"Keywords": "Kulcsszavak", +"Description": "Le\u00edr\u00e1s", +"Robots": "Robotok", +"Author": "Szerz\u0151", +"Encoding": "K\u00f3dol\u00e1s", +"Fullscreen": "Teljes k\u00e9perny\u0151", +"Action": "M\u0171velet", +"Shortcut": "Parancsikon", +"Help": "S\u00fag\u00f3", +"Address": "C\u00edm", +"Focus to menubar": "F\u00f3kusz a men\u00fcre", +"Focus to toolbar": "F\u00f3kusz az eszk\u00f6zt\u00e1rra", +"Focus to element path": "F\u00f3kusz az elemek \u00fatvonal\u00e1ra", +"Focus to contextual toolbar": "F\u00f3kusz a k\u00f6rnyezetf\u00fcgg\u0151 eszk\u00f6zt\u00e1rra", +"Insert link (if link plugin activated)": "Hivatkoz\u00e1s besz\u00far\u00e1sa (ha a hivatkoz\u00e1s b\u0151v\u00edtm\u00e9ny enged\u00e9lyezett)", +"Save (if save plugin activated)": "Ment\u00e9s (ha a ment\u00e9s b\u0151v\u00edtm\u00e9ny enged\u00e9lyezett)", +"Find (if searchreplace plugin activated)": "Keres\u00e9s (ha a keres\u00e9s \u00e9s csere b\u0151v\u00edtm\u00e9ny enged\u00e9lyezett)", +"Plugins installed ({0}):": "Telep\u00edtett b\u0151v\u00edtm\u00e9nyek ({0}):", +"Premium plugins:": "Pr\u00e9mium b\u0151v\u00edtm\u00e9nyek:", +"Learn more...": "Tudj meg t\u00f6bbet...", +"You are using {0}": "Haszn\u00e1latban: {0}", +"Plugins": "Pluginek", +"Handy Shortcuts": "Hasznos linkek", +"Horizontal line": "V\u00edzszintes vonal", +"Insert\/edit image": "K\u00e9p beilleszt\u00e9se\/szerkeszt\u00e9se", +"Image description": "K\u00e9p le\u00edr\u00e1sa", +"Source": "Forr\u00e1s", +"Dimensions": "M\u00e9retek", +"Constrain proportions": "M\u00e9retar\u00e1ny", +"General": "\u00c1ltal\u00e1nos", +"Advanced": "Halad\u00f3", +"Style": "St\u00edlus", +"Vertical space": "Vertik\u00e1lis hely", +"Horizontal space": "Horizont\u00e1lis hely", +"Border": "Szeg\u00e9ly", +"Insert image": "K\u00e9p beilleszt\u00e9se", +"Image": "K\u00e9p", +"Image list": "K\u00e9p lista", +"Rotate counterclockwise": "Forgat\u00e1s az \u00f3ramutat\u00f3 j\u00e1r\u00e1s\u00e1val ellent\u00e9tesen", +"Rotate clockwise": "Forgat\u00e1s az \u00f3ramutat\u00f3 j\u00e1r\u00e1s\u00e1val megegyez\u0151en", +"Flip vertically": "F\u00fcgg\u0151leges t\u00fckr\u00f6z\u00e9s", +"Flip horizontally": "V\u00edzszintes t\u00fckr\u00f6z\u00e9s", +"Edit image": "K\u00e9p szerkeszt\u00e9se", +"Image options": "K\u00e9p be\u00e1ll\u00edt\u00e1sok", +"Zoom in": "Nagy\u00edt\u00e1s", +"Zoom out": "Kicsiny\u00edt\u00e9s", +"Crop": "K\u00e9p v\u00e1g\u00e1s", +"Resize": "\u00c1tm\u00e9retez\u00e9s", +"Orientation": "K\u00e9p t\u00e1jol\u00e1s", +"Brightness": "F\u00e9nyer\u0151", +"Sharpen": "\u00c9less\u00e9g", +"Contrast": "Kontraszt", +"Color levels": "Sz\u00ednszint", +"Gamma": "Gamma", +"Invert": "Inverz k\u00e9p", +"Apply": "Ment\u00e9s", +"Back": "Vissza", +"Insert date\/time": "D\u00e1tum\/id\u0151 beilleszt\u00e9se", +"Date\/time": "D\u00e1tum\/id\u0151", +"Insert link": "Hivatkoz\u00e1s beilleszt\u00e9se", +"Insert\/edit link": "Hivatkoz\u00e1s beilleszt\u00e9se\/szerkeszt\u00e9se", +"Text to display": "Megjelen\u0151 sz\u00f6veg", +"Url": "Url", +"Target": "C\u00e9l", +"None": "Nincs", +"New window": "\u00daj ablak", +"Remove link": "Hivatkoz\u00e1s t\u00f6rl\u00e9se", +"Anchors": "Horgonyok", +"Link": "Hivatkoz\u00e1s", +"Paste or type a link": "Hivatkoz\u00e1s be\u00edr\u00e1sa vagy beilleszt\u00e9se", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "A megadott URL email c\u00edmnek t\u0171nik. Szeretn\u00e9d hozz\u00e1adni a sz\u00fcks\u00e9ges mailto: el\u0151tagot?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "A megadott URL k\u00fcls\u0151 c\u00edmnek t\u0171nik. Szeretn\u00e9d hozz\u00e1adni a sz\u00fcks\u00e9ges http:\/\/ el\u0151tagot?", +"Link list": "Hivatkoz\u00e1slista", +"Insert video": "Vide\u00f3 beilleszt\u00e9se", +"Insert\/edit video": "Vide\u00f3 beilleszt\u00e9se\/szerkeszt\u00e9se", +"Insert\/edit media": "M\u00e9dia besz\u00far\u00e1sa\/beilleszt\u00e9se", +"Alternative source": "Alternat\u00edv forr\u00e1s", +"Poster": "El\u0151n\u00e9zeti k\u00e9p", +"Paste your embed code below:": "Illeszd be a be\u00e1gyaz\u00f3 k\u00f3dot alulra:", +"Embed": "Be\u00e1gyaz\u00e1s", +"Media": "M\u00e9dia", +"Nonbreaking space": "Nem t\u00f6rhet\u0151 sz\u00f3k\u00f6z", +"Page break": "Oldalt\u00f6r\u00e9s", +"Paste as text": "Beilleszt\u00e9s sz\u00f6vegk\u00e9nt", +"Preview": "El\u0151n\u00e9zet", +"Print": "Nyomtat\u00e1s", +"Save": "Ment\u00e9s", +"Find": "Keres\u00e9s", +"Replace with": "Csere erre", +"Replace": "Csere", +"Replace all": "Az \u00f6sszes cser\u00e9je", +"Prev": "El\u0151z\u0151", +"Next": "K\u00f6vetkez\u0151", +"Find and replace": "Keres\u00e9s \u00e9s csere", +"Could not find the specified string.": "A be\u00edrt kifejez\u00e9s nem tal\u00e1lhat\u00f3.", +"Match case": "Kis \u00e9s nagybet\u0171k megk\u00fcl\u00f6nb\u00f6ztet\u00e9se", +"Whole words": "Csak ha ez a teljes sz\u00f3", +"Spellcheck": "Helyes\u00edr\u00e1s ellen\u0151rz\u00e9s", +"Ignore": "Figyelmen k\u00edv\u00fcl hagy", +"Ignore all": "Mindent figyelmen k\u00edv\u00fcl hagy", +"Finish": "Befejez\u00e9s", +"Add to Dictionary": "Sz\u00f3t\u00e1rhoz ad", +"Insert table": "T\u00e1bl\u00e1zat beilleszt\u00e9se", +"Table properties": "T\u00e1bl\u00e1zat tulajdons\u00e1gok", +"Delete table": "T\u00e1bl\u00e1zat t\u00f6rl\u00e9se", +"Cell": "Cella", +"Row": "Sor", +"Column": "Oszlop", +"Cell properties": "Cella tulajdons\u00e1gok", +"Merge cells": "Cell\u00e1k egyes\u00edt\u00e9se", +"Split cell": "Cell\u00e1k sz\u00e9tv\u00e1laszt\u00e1sa", +"Insert row before": "Sor besz\u00far\u00e1sa el\u00e9", +"Insert row after": "Sor besz\u00far\u00e1sa m\u00f6g\u00e9", +"Delete row": "Sor t\u00f6rl\u00e9se", +"Row properties": "Sor tulajdons\u00e1gai", +"Cut row": "Sor kiv\u00e1g\u00e1sa", +"Copy row": "Sor m\u00e1sol\u00e1sa", +"Paste row before": "Sor beilleszt\u00e9se el\u00e9", +"Paste row after": "Sor beilleszt\u00e9se m\u00f6g\u00e9", +"Insert column before": "Oszlop besz\u00far\u00e1sa el\u00e9", +"Insert column after": "Oszlop besz\u00far\u00e1sa m\u00f6g\u00e9", +"Delete column": "Oszlop t\u00f6rl\u00e9se", +"Cols": "Oszlopok", +"Rows": "Sorok", +"Width": "Sz\u00e9less\u00e9g", +"Height": "Magass\u00e1g", +"Cell spacing": "Cell\u00e1k t\u00e1vols\u00e1ga", +"Cell padding": "Cella m\u00e9rete", +"Caption": "Felirat", +"Left": "Bal", +"Center": "K\u00f6z\u00e9p", +"Right": "Jobb", +"Cell type": "Cella t\u00edpusa", +"Scope": "Hat\u00f3k\u00f6r", +"Alignment": "Igaz\u00edt\u00e1s", +"H Align": "V\u00edzszintes igaz\u00edt\u00e1s", +"V Align": "F\u00fcgg\u0151leges igaz\u00edt\u00e1s", +"Top": "Fel\u00fcl", +"Middle": "K\u00f6z\u00e9pen", +"Bottom": "Alul", +"Header cell": "Fejl\u00e9c cella", +"Row group": "Sor csoport", +"Column group": "Oszlop csoport", +"Row type": "Sor t\u00edpus", +"Header": "Fejl\u00e9c", +"Body": "Sz\u00f6vegt\u00f6rzs", +"Footer": "L\u00e1bl\u00e9c", +"Border color": "Szeg\u00e9ly sz\u00edne", +"Insert template": "Sablon beilleszt\u00e9se", +"Templates": "Sablonok", +"Template": "Sablon", +"Text color": "Sz\u00f6veg sz\u00edne", +"Background color": "H\u00e1tt\u00e9r sz\u00edn", +"Custom...": "Egy\u00e9ni...", +"Custom color": "Egy\u00e9ni sz\u00edn", +"No color": "Nincs sz\u00edn", +"Table of Contents": "Tartalomjegyz\u00e9k", +"Show blocks": "Blokkok mutat\u00e1sa", +"Show invisible characters": "L\u00e1thatatlan karakterek mutat\u00e1sa", +"Words: {0}": "Szavak: {0}", +"{0} words": "{0} sz\u00f3", +"File": "F\u00e1jl", +"Edit": "Szerkeszt\u00e9s", +"Insert": "Beilleszt\u00e9s", +"View": "N\u00e9zet", +"Format": "Form\u00e1tum", +"Table": "T\u00e1bl\u00e1zat", +"Tools": "Eszk\u00f6z\u00f6k", +"Powered by {0}": "\u00dczemelteti: {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text ter\u00fclet. Nyomj ALT-F9-et a men\u00fch\u00f6z. Nyomj ALT-F10-et az eszk\u00f6zt\u00e1rhoz. Nyomj ALT-0-t a s\u00fag\u00f3hoz" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/id.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/id.js new file mode 100644 index 0000000000..af2d4078d3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/id.js @@ -0,0 +1,261 @@ +tinymce.addI18n('id',{ +"Redo": "Ulang", +"Undo": "Batal", +"Cut": "Penggal", +"Copy": "Salin", +"Paste": "Tempel", +"Select all": "Pilih semua", +"New document": "Dokumen baru", +"Ok": "Ok", +"Cancel": "Batal", +"Visual aids": "Alat bantu visual", +"Bold": "Tebal", +"Italic": "Miring", +"Underline": "Garis bawah", +"Strikethrough": "Coret", +"Superscript": "Superskrip", +"Subscript": "Subskrip", +"Clear formatting": "Hapus format", +"Align left": "Rata kiri", +"Align center": "Rata tengah", +"Align right": "Rata kanan", +"Justify": "Penuh", +"Bullet list": "Daftar bersimbol", +"Numbered list": "Daftar bernomor", +"Decrease indent": "Turunkan inden", +"Increase indent": "Tambah inden", +"Close": "Tutup", +"Formats": "Format", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Browser anda tidak mendukung akses langsung ke clipboard. Silahkan gunakan Ctrl+X\/C\/V dari keyboard.", +"Headers": "Judul", +"Header 1": "Judul 1", +"Header 2": "Judul 2", +"Header 3": "Judul 3", +"Header 4": "Judul 4", +"Header 5": "Judul 5", +"Header 6": "Judul 6", +"Headings": "Judul", +"Heading 1": "Judul 1", +"Heading 2": "Judul 2", +"Heading 3": "Judul 3", +"Heading 4": "Judul 4", +"Heading 5": "Judul 5", +"Heading 6": "Judul 6", +"Preformatted": "Monokode", +"Div": "Div", +"Pre": "Pre", +"Code": "Kode", +"Paragraph": "Paragraf", +"Blockquote": "Kutipan", +"Inline": "Baris", +"Blocks": "Blok", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Penempelan sekarang dalam modus teks biasa. Konten sekarang akan disisipkan sebagai teks biasa sampai Anda memadamkan pilihan ini.", +"Font Family": "Jenis Huruf", +"Font Sizes": "Ukuran Huruf", +"Class": "Klas", +"Browse for an image": "Cari gambar", +"OR": "Atau", +"Drop an image here": "Letakan gambar di sini", +"Upload": "Unggah", +"Block": "Blok", +"Align": "Menyelaraskan", +"Default": "Bawaan", +"Circle": "Lingkaran", +"Disc": "Cakram", +"Square": "Kotak", +"Lower Alpha": "Huruf Kecil", +"Lower Greek": "Huruf Kecil Yunani", +"Lower Roman": "Huruf Kecil Romawi", +"Upper Alpha": "Huruf Besar", +"Upper Roman": "Huruf Besar Romawi", +"Anchor": "Jangkar", +"Name": "Nama", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id harus dimulai dengan huruf, dan hanya diikuti oleh huruf, angka, koma, titik, titik koma atau garis bawah.", +"You have unsaved changes are you sure you want to navigate away?": "Anda memiliki perubahan yang belum disimpan, yakin ingin beralih ?", +"Restore last draft": "Muat kembali draft sebelumnya", +"Special character": "Spesial karakter", +"Source code": "Kode sumber", +"Insert\/Edit code sample": "Tambah\/Edit contoh kode", +"Language": "Bahasa", +"Code sample": "Contoh kode", +"Color": "Warna", +"R": "M", +"G": "H", +"B": "B", +"Left to right": "Kiri ke kanan", +"Right to left": "Kanan ke kiri", +"Emoticons": "Emotikon", +"Document properties": "Properti dokumwn", +"Title": "Judul", +"Keywords": "Kata kunci", +"Description": "Deskripsi", +"Robots": "Robot", +"Author": "Penulis", +"Encoding": "Enkoding", +"Fullscreen": "Layar penuh", +"Action": "Tindakan", +"Shortcut": "Pintasan", +"Help": "Bantuan", +"Address": "Alamat", +"Focus to menubar": "Fokus ke menubar", +"Focus to toolbar": "Fokus ke toolbar", +"Focus to element path": "Fokus ke jalur elemen", +"Focus to contextual toolbar": "Fokus ke toolbar kontekstual", +"Insert link (if link plugin activated)": "Masukan link (jika plugin diaktifkan)", +"Save (if save plugin activated)": "Simpan (jika plugin simpan diaktifkan)", +"Find (if searchreplace plugin activated)": "Cari (jika plugin searchplace diaktifkan)", +"Plugins installed ({0}):": "Plugin terpasang ({0})", +"Premium plugins:": "Plugin premium:", +"Learn more...": "Pelajari selengkapnya...", +"You are using {0}": "Anda menggunakan {0}", +"Plugins": "Plugin", +"Handy Shortcuts": "Pintasan Praktis", +"Horizontal line": "Garis horisontal", +"Insert\/edit image": "Sisip\/sunting gambar", +"Image description": "Deskripsi gambar", +"Source": "Sumber", +"Dimensions": "Dimensi", +"Constrain proportions": "Samakan proporsi", +"General": "Umum", +"Advanced": "Lanjutan", +"Style": "Gaya", +"Vertical space": "Spasi vertikal", +"Horizontal space": "Spasi horisontal", +"Border": "Batas", +"Insert image": "Sisipkan gambar", +"Image": "Gambar", +"Image list": "Daftar gambar", +"Rotate counterclockwise": "Putar berlawananjarumjam", +"Rotate clockwise": "Putar searahjarumjam", +"Flip vertically": "Balik vertikal", +"Flip horizontally": "Balik horisontal", +"Edit image": "Sunting gambar", +"Image options": "Opsi gambar", +"Zoom in": "Perbesar", +"Zoom out": "Perkecil", +"Crop": "Krop", +"Resize": "Ubah ukuran", +"Orientation": "Orientasi", +"Brightness": "Kecerahan", +"Sharpen": "Ketajaman", +"Contrast": "Kontras", +"Color levels": "Tingakt warna", +"Gamma": "Gamma", +"Invert": "Kebalikan", +"Apply": "Terapkan", +"Back": "Kembali", +"Insert date\/time": "Sisipkan tanggal\/waktu", +"Date\/time": "Tanggal\/waktu", +"Insert link": "Sisipkan tautan", +"Insert\/edit link": "Sisip\/sunting tautan", +"Text to display": "Teks yang ditampilkan", +"Url": "Tautan", +"Target": "Jendela tujuan", +"None": "Tidak ada", +"New window": "Jendela baru", +"Remove link": "Buang tautan", +"Anchors": "Jangkar", +"Link": "Tautan", +"Paste or type a link": "Tempel atau ketik sebuah tautan", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Tautan yang anda masukkan sepertinya adalah alamat email. Apakah Anda ingin menambahkan prefiks mailto: yang dibutuhkan?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Tautan yang anda masukkan sepertinya adalah tautan eksternal. Apakah Anda ingin menambahkan prefiks http:\/\/ yang dibutuhkan?", +"Link list": "Daftar tautan", +"Insert video": "Sisipkan video", +"Insert\/edit video": "Sisip\/sunting video", +"Insert\/edit media": "Sisip\/sunting media", +"Alternative source": "Sumber alternatif", +"Poster": "Penulis", +"Paste your embed code below:": "Tempel kode yang diembed dibawah ini:", +"Embed": "Embed", +"Media": "Media", +"Nonbreaking space": "Spasi", +"Page break": "Baris baru", +"Paste as text": "Tempel sebagai teks biasa", +"Preview": "Pratinjau", +"Print": "Cetak", +"Save": "Simpan", +"Find": "Cari", +"Replace with": "Ganti dengan", +"Replace": "Ganti", +"Replace all": "Ganti semua", +"Prev": "Sebelumnya", +"Next": "Berikutnya", +"Find and replace": "Cari dan ganti", +"Could not find the specified string.": "Tidak dapat menemukan string yang dimaksud.", +"Match case": "Samakan besar kecil huruf", +"Whole words": "Semua kata", +"Spellcheck": "Periksa ejaan", +"Ignore": "Abaikan", +"Ignore all": "Abaikan semua", +"Finish": "Selesai", +"Add to Dictionary": "Tambahkan ke kamus", +"Insert table": "Sisipkan tabel", +"Table properties": "Properti tabel", +"Delete table": "Hapus tabel", +"Cell": "Sel", +"Row": "Baris", +"Column": "Kolom", +"Cell properties": "Properti sel", +"Merge cells": "Gabung sel", +"Split cell": "Bagi sel", +"Insert row before": "Sisipkan baris sebelum", +"Insert row after": "Sisipkan baris setelah", +"Delete row": "Hapus baris", +"Row properties": "Properti baris", +"Cut row": "Penggal baris", +"Copy row": "Salin baris", +"Paste row before": "Tempel baris sebelum", +"Paste row after": "Tempel baris setelah", +"Insert column before": "Sisipkan kolom sebelum", +"Insert column after": "Sisipkan kolom setelah", +"Delete column": "Hapus kolom", +"Cols": "Kolom", +"Rows": "Baris", +"Width": "Lebar", +"Height": "Tinggi", +"Cell spacing": "Spasi sel ", +"Cell padding": "Lapisan sel", +"Caption": "Caption", +"Left": "Kiri", +"Center": "Tengah", +"Right": "Kanan", +"Cell type": "Tipe sel", +"Scope": "Skup", +"Alignment": "Penjajaran", +"H Align": "Rata Samping", +"V Align": "Rata Atas", +"Top": "Atas", +"Middle": "Tengah", +"Bottom": "Bawah", +"Header cell": "Judul sel", +"Row group": "Kelompok baris", +"Column group": "Kelompok kolom", +"Row type": "Tipe baris", +"Header": "Judul", +"Body": "Body", +"Footer": "Footer", +"Border color": "Warna batas", +"Insert template": "Sisipkan templat", +"Templates": "Templat", +"Template": "Templat", +"Text color": "Warna teks", +"Background color": "Warna latar", +"Custom...": "Atur sendiri...", +"Custom color": "Warna sendiri", +"No color": "Tidak berwarna", +"Table of Contents": "Daftar Isi", +"Show blocks": "Tampilkan blok", +"Show invisible characters": "Tampilkan karakter tak tampak", +"Words: {0}": "Kata: {0}", +"{0} words": "{0} kata", +"File": "Berkas", +"Edit": "Sunting", +"Insert": "Sisip", +"View": "Tampilan", +"Format": "Format", +"Table": "Tabel", +"Tools": "Alat", +"Powered by {0}": "Didukung oleh {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Area teks kaya. Tekan ALT-F9 untuk menu. Tekan ALT-F10 untuk toolbar. Tekan ALT-0 untuk bantuan" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js index d97803d2b3..5ffc0c0f80 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js @@ -1,219 +1,261 @@ tinymce.addI18n('it',{ -"Cut": "Taglia", -"Heading 5": "Intestazione 5", -"Header 2": "Header 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Il tuo browser non supporta l'accesso diretto negli Appunti. Per favore usa i tasti di scelta rapida Ctrl+X\/C\/V.", -"Heading 4": "Intestazione 4", -"Div": "Div", -"Heading 2": "Intestazione 2", -"Paste": "Incolla", -"Close": "Chiudi", -"Font Family": "Famiglia font", -"Pre": "Pre", -"Align right": "Allinea a Destra", -"New document": "Nuovo Documento", -"Blockquote": "Blockquote", -"Numbered list": "Elenchi Numerati", -"Heading 1": "Intestazione 1", -"Headings": "Intestazioni", -"Increase indent": "Aumenta Rientro", -"Formats": "Formattazioni", -"Headers": "Intestazioni", -"Select all": "Seleziona Tutto", -"Header 3": "Intestazione 3", -"Blocks": "Blocchi", -"Undo": "Indietro", -"Strikethrough": "Barrato", -"Bullet list": "Elenchi Puntati", -"Header 1": "Intestazione 1", -"Superscript": "Apice", -"Clear formatting": "Cancella Formattazione", -"Font Sizes": "Dimensioni font", -"Subscript": "Pedice", -"Header 6": "Intestazione 6", "Redo": "Ripeti", -"Paragraph": "Paragrafo", -"Ok": "Ok", -"Bold": "Grassetto", -"Code": "Codice", -"Italic": "Corsivo", -"Align center": "Allinea al Cento", -"Header 5": "Intestazione 5", -"Heading 6": "Intestazione 6", -"Heading 3": "Intestazione 3", -"Decrease indent": "Riduci Rientro", -"Header 4": "Intestazione 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Incolla \u00e8 in modalit\u00e0 testo normale. I contenuti sono incollati come testo normale se non disattivi l'opzione.", -"Underline": "Sottolineato", -"Cancel": "Annulla", -"Justify": "Giustifica", -"Inline": "Inlinea", +"Undo": "Indietro", +"Cut": "Taglia", "Copy": "Copia", -"Align left": "Allinea a Sinistra", +"Paste": "Incolla", +"Select all": "Seleziona Tutto", +"New document": "Nuovo Documento", +"Ok": "Ok", +"Cancel": "Annulla", "Visual aids": "Elementi Visivi", -"Lower Greek": "Greek Minore", -"Square": "Quadrato", +"Bold": "Grassetto", +"Italic": "Corsivo", +"Underline": "Sottolineato", +"Strikethrough": "Barrato", +"Superscript": "Apice", +"Subscript": "Pedice", +"Clear formatting": "Cancella Formattazione", +"Align left": "Allinea a Sinistra", +"Align center": "Allinea al Cento", +"Align right": "Allinea a Destra", +"Justify": "Giustifica", +"Bullet list": "Elenchi Puntati", +"Numbered list": "Elenchi Numerati", +"Decrease indent": "Riduci Rientro", +"Increase indent": "Aumenta Rientro", +"Close": "Chiudi", +"Formats": "Formattazioni", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Il tuo browser non supporta l'accesso diretto negli Appunti. Per favore usa i tasti di scelta rapida Ctrl+X\/C\/V.", +"Headers": "Intestazioni", +"Header 1": "Intestazione 1", +"Header 2": "Header 2", +"Header 3": "Intestazione 3", +"Header 4": "Intestazione 4", +"Header 5": "Intestazione 5", +"Header 6": "Intestazione 6", +"Headings": "Intestazioni", +"Heading 1": "Intestazione 1", +"Heading 2": "Intestazione 2", +"Heading 3": "Intestazione 3", +"Heading 4": "Intestazione 4", +"Heading 5": "Intestazione 5", +"Heading 6": "Intestazione 6", +"Preformatted": "Preformattato", +"Div": "Div", +"Pre": "Pre", +"Code": "Codice", +"Paragraph": "Paragrafo", +"Blockquote": "Blockquote", +"Inline": "Inlinea", +"Blocks": "Blocchi", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Incolla \u00e8 in modalit\u00e0 testo normale. I contenuti sono incollati come testo normale se non disattivi l'opzione.", +"Font Family": "Famiglia font", +"Font Sizes": "Dimensioni font", +"Class": "Classe", +"Browse for an image": "Scegli un'immagine", +"OR": "o", +"Drop an image here": "Incolla un'immagine qui", +"Upload": "Carica", +"Block": "Blocco", +"Align": "Allinea", "Default": "Default", -"Lower Alpha": "Alpha Minore", "Circle": "Cerchio", "Disc": "Disco", +"Square": "Quadrato", +"Lower Alpha": "Alpha Minore", +"Lower Greek": "Greek Minore", +"Lower Roman": "Roman Minore", "Upper Alpha": "Alpha Superiore", "Upper Roman": "Roman Superiore", -"Lower Roman": "Roman Minore", -"Name": "Nome", "Anchor": "Fissa", +"Name": "Nome", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "L'id dovrebbe cominciare con una lettera, seguito solo da lettere, numeri, linee, punti, virgole.", "You have unsaved changes are you sure you want to navigate away?": "Non hai salvato delle modifiche, sei sicuro di andartene?", "Restore last draft": "Ripristina l'ultima bozza.", "Special character": "Carattere Speciale", "Source code": "Codice Sorgente", -"B": "B", +"Insert\/Edit code sample": "Inserisci\/Modifica esempio di codice", +"Language": "Lingua", +"Code sample": "Esempio di codice", +"Color": "Colore", "R": "R", "G": "G", -"Color": "Colore", -"Right to left": "Da Destra a Sinistra", +"B": "B", "Left to right": "Da Sinistra a Destra", +"Right to left": "Da Destra a Sinistra", "Emoticons": "Emoction", -"Robots": "Robot", "Document properties": "Propriet\u00e0 Documento", "Title": "Titolo", "Keywords": "Parola Chiave", -"Encoding": "Codifica", "Description": "Descrizione", +"Robots": "Robot", "Author": "Autore", +"Encoding": "Codifica", "Fullscreen": "Schermo Intero", +"Action": "Azione", +"Shortcut": "Scorciatoia", +"Help": "Aiuto", +"Address": "Indirizzo", +"Focus to menubar": "Focus sulla barra del menu", +"Focus to toolbar": "Focus sulla barra degli strumenti", +"Focus to element path": "Focus sul percorso dell'elemento", +"Focus to contextual toolbar": "Focus sulla barra degli strumenti contestuale", +"Insert link (if link plugin activated)": "Inserisci link (se il plugin link \u00e8 attivato)", +"Save (if save plugin activated)": "Salva (se il plugin save \u00e8 attivato)", +"Find (if searchreplace plugin activated)": "Trova (se il plugin searchreplace \u00e8 attivato)", +"Plugins installed ({0}):": "Plugin installati ({0}):", +"Premium plugins:": "Plugin Premium:", +"Learn more...": "Per saperne di pi\u00f9...", +"You are using {0}": "Stai usando {0}", +"Plugins": "Plugin", +"Handy Shortcuts": "Scorciatoia pratica", "Horizontal line": "Linea Orizzontale", -"Horizontal space": "Spazio Orizzontale", "Insert\/edit image": "Aggiungi\/Modifica Immagine", +"Image description": "Descrizione Immagine", +"Source": "Fonte", +"Dimensions": "Dimenzioni", +"Constrain proportions": "Mantieni Proporzioni", "General": "Generale", "Advanced": "Avanzato", -"Source": "Fonte", -"Border": "Bordo", -"Constrain proportions": "Mantieni Proporzioni", -"Vertical space": "Spazio Verticale", -"Image description": "Descrizione Immagine", "Style": "Stile", -"Dimensions": "Dimenzioni", +"Vertical space": "Spazio Verticale", +"Horizontal space": "Spazio Orizzontale", +"Border": "Bordo", "Insert image": "Inserisci immagine", -"Zoom in": "Ingrandisci", -"Contrast": "Contrasto", -"Back": "Indietro", -"Gamma": "Gamma", -"Flip horizontally": "Rifletti orizzontalmente", -"Resize": "Ridimensiona", -"Sharpen": "Contrasta", -"Zoom out": "Rimpicciolisci", -"Image options": "Opzioni immagine", -"Apply": "Applica", -"Brightness": "Luminosit\u00e0", -"Rotate clockwise": "Ruota in senso orario", +"Image": "Immagine", +"Image list": "Elenco immagini", "Rotate counterclockwise": "Ruota in senso antiorario", -"Edit image": "Modifica immagine", -"Color levels": "Livelli colore", -"Crop": "Taglia", -"Orientation": "Orientamento", +"Rotate clockwise": "Ruota in senso orario", "Flip vertically": "Rifletti verticalmente", +"Flip horizontally": "Rifletti orizzontalmente", +"Edit image": "Modifica immagine", +"Image options": "Opzioni immagine", +"Zoom in": "Ingrandisci", +"Zoom out": "Rimpicciolisci", +"Crop": "Taglia", +"Resize": "Ridimensiona", +"Orientation": "Orientamento", +"Brightness": "Luminosit\u00e0", +"Sharpen": "Contrasta", +"Contrast": "Contrasto", +"Color levels": "Livelli colore", +"Gamma": "Gamma", "Invert": "Inverti", +"Apply": "Applica", +"Back": "Indietro", "Insert date\/time": "Inserisci Data\/Ora", -"Remove link": "Rimuovi link", -"Url": "Url", -"Text to display": "Testo da Visualizzare", -"Anchors": "Anchors", +"Date\/time": "Data\/Ora", "Insert link": "Inserisci il Link", -"New window": "Nuova Finestra", -"None": "No", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL inserito sembra essere un collegamento esterno. Vuoi aggiungere il prefisso necessario http:\/\/?", -"Target": "Target", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL inserito sembra essere un indirizzo email. Vuoi aggiungere il prefisso necessario mailto:?", "Insert\/edit link": "Inserisci\/Modifica Link", -"Insert\/edit video": "Inserisci\/Modifica Video", -"Poster": "Anteprima", -"Alternative source": "Alternativo", -"Paste your embed code below:": "Incolla il codice d'incorporamento qui:", +"Text to display": "Testo da Visualizzare", +"Url": "Url", +"Target": "Target", +"None": "No", +"New window": "Nuova Finestra", +"Remove link": "Rimuovi link", +"Anchors": "Anchors", +"Link": "Collegamento", +"Paste or type a link": "Incolla o digita un collegamento", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL inserito sembra essere un indirizzo email. Vuoi aggiungere il prefisso necessario mailto:?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL inserito sembra essere un collegamento esterno. Vuoi aggiungere il prefisso necessario http:\/\/?", +"Link list": "Elenco link", "Insert video": "Inserisci Video", +"Insert\/edit video": "Inserisci\/Modifica Video", +"Insert\/edit media": "Inserisci\/Modifica Media", +"Alternative source": "Alternativo", +"Poster": "Anteprima", +"Paste your embed code below:": "Incolla il codice d'incorporamento qui:", "Embed": "Incorporare", +"Media": "Media", "Nonbreaking space": "Spazio unificatore", "Page break": "Interruzione di pagina", "Paste as text": "incolla come testo", "Preview": "Anteprima", "Print": "Stampa", "Save": "Salva", -"Could not find the specified string.": "Impossibile trovare la parola specifica.", -"Replace": "Sostituisci", -"Next": "Successivo", -"Whole words": "Parole Sbagliate", -"Find and replace": "Trova e Sostituisci", -"Replace with": "Sostituisci Con", "Find": "Trova", +"Replace with": "Sostituisci Con", +"Replace": "Sostituisci", "Replace all": "Sostituisci Tutto", -"Match case": "Maiuscole\/Minuscole ", "Prev": "Precedente", +"Next": "Successivo", +"Find and replace": "Trova e Sostituisci", +"Could not find the specified string.": "Impossibile trovare la parola specifica.", +"Match case": "Maiuscole\/Minuscole ", +"Whole words": "Parole Sbagliate", "Spellcheck": "Controllo ortografico", -"Finish": "Termina", -"Ignore all": "Ignora Tutto", "Ignore": "Ignora", +"Ignore all": "Ignora Tutto", +"Finish": "Termina", "Add to Dictionary": "Aggiungi al Dizionario", -"Insert row before": "Inserisci una Riga Prima", -"Rows": "Righe", -"Height": "Altezza", -"Paste row after": "Incolla una Riga Dopo", -"Alignment": "Allineamento", -"Border color": "Colore bordo", -"Column group": "Gruppo di Colonne", -"Row": "Riga", -"Insert column before": "Inserisci una Colonna Prima", -"Split cell": "Dividi Cella", -"Cell padding": "Padding della Cella", -"Cell spacing": "Spaziatura della Cella", -"Row type": "Tipo di Riga", "Insert table": "Inserisci Tabella", -"Body": "Body", -"Caption": "Didascalia", -"Footer": "Footer", -"Delete row": "Cancella Riga", -"Paste row before": "Incolla una Riga Prima", -"Scope": "Campo", -"Delete table": "Cancella Tabella", -"H Align": "Allineamento H", -"Top": "In alto", -"Header cell": "cella d'intestazione", -"Column": "Colonna", -"Row group": "Gruppo di Righe", -"Cell": "Cella", -"Middle": "In mezzo", -"Cell type": "Tipo di Cella", -"Copy row": "Copia Riga", -"Row properties": "Propriet\u00e0 della Riga", "Table properties": "Propiet\u00e0 della Tabella", -"Bottom": "In fondo", -"V Align": "Allineamento V", -"Header": "Header", -"Right": "Destra", -"Insert column after": "Inserisci una Colonna Dopo", -"Cols": "Colonne", -"Insert row after": "Inserisci una Riga Dopo", -"Width": "Larghezza", +"Delete table": "Cancella Tabella", +"Cell": "Cella", +"Row": "Riga", +"Column": "Colonna", "Cell properties": "Propiet\u00e0 della Cella", -"Left": "Sinistra", -"Cut row": "Taglia Riga", -"Delete column": "Cancella Colonna", -"Center": "Centro", "Merge cells": "Unisci Cella", +"Split cell": "Dividi Cella", +"Insert row before": "Inserisci una Riga Prima", +"Insert row after": "Inserisci una Riga Dopo", +"Delete row": "Cancella Riga", +"Row properties": "Propriet\u00e0 della Riga", +"Cut row": "Taglia Riga", +"Copy row": "Copia Riga", +"Paste row before": "Incolla una Riga Prima", +"Paste row after": "Incolla una Riga Dopo", +"Insert column before": "Inserisci una Colonna Prima", +"Insert column after": "Inserisci una Colonna Dopo", +"Delete column": "Cancella Colonna", +"Cols": "Colonne", +"Rows": "Righe", +"Width": "Larghezza", +"Height": "Altezza", +"Cell spacing": "Spaziatura della Cella", +"Cell padding": "Padding della Cella", +"Caption": "Didascalia", +"Left": "Sinistra", +"Center": "Centro", +"Right": "Destra", +"Cell type": "Tipo di Cella", +"Scope": "Campo", +"Alignment": "Allineamento", +"H Align": "Allineamento H", +"V Align": "Allineamento V", +"Top": "In alto", +"Middle": "In mezzo", +"Bottom": "In fondo", +"Header cell": "cella d'intestazione", +"Row group": "Gruppo di Righe", +"Column group": "Gruppo di Colonne", +"Row type": "Tipo di Riga", +"Header": "Header", +"Body": "Body", +"Footer": "Footer", +"Border color": "Colore bordo", "Insert template": "Inserisci Template", "Templates": "Template", +"Template": "Modello", +"Text color": "Colore Testo", "Background color": "Colore Background", "Custom...": "Personalizzato...", "Custom color": "Colore personalizzato", "No color": "Nessun colore", -"Text color": "Colore Testo", +"Table of Contents": "Tabella dei contenuti", "Show blocks": "Mostra Blocchi", "Show invisible characters": "Mostra Caratteri Invisibili", "Words: {0}": "Parole: {0}", -"Insert": "Inserisci", +"{0} words": "{0} parole", "File": "File", "Edit": "Modifica", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Premi ALT-F9 per il men\u00f9. Premi ALT-F10 per la barra degli strumenti. Premi ALT-0 per l'aiuto.", -"Tools": "Strumenti", +"Insert": "Inserisci", "View": "Visualiza", +"Format": "Formato", "Table": "Tabella", -"Format": "Formato" +"Tools": "Strumenti", +"Powered by {0}": "Fornito da {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Premi ALT-F9 per il men\u00f9. Premi ALT-F10 per la barra degli strumenti. Premi ALT-0 per l'aiuto." }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ja.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ja.js index 848cbd36b9..61f0ba61a1 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ja.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ja.js @@ -1,219 +1,261 @@ tinymce.addI18n('ja',{ -"Cut": "\u5207\u308a\u53d6\u308a", -"Heading 5": "\u898b\u51fa\u3057 5", -"Header 2": "\u30d8\u30c3\u30c0\u30fc 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u304a\u4f7f\u3044\u306e\u30d6\u30e9\u30a6\u30b6\u3067\u306f\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u6a5f\u80fd\u3092\u5229\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3002\u30ad\u30fc\u30dc\u30fc\u30c9\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\uff08Ctrl+X, Ctrl+C, Ctrl+V\uff09\u3092\u304a\u4f7f\u3044\u4e0b\u3055\u3044\u3002", -"Heading 4": "\u898b\u51fa\u3057 4", -"Div": "Div", -"Heading 2": "\u898b\u51fa\u3057 2", -"Paste": "\u8cbc\u308a\u4ed8\u3051", -"Close": "\u9589\u3058\u308b", -"Font Family": "\u30d5\u30a9\u30f3\u30c8\u30d5\u30a1\u30df\u30ea\u30fc", -"Pre": "Pre", -"Align right": "\u53f3\u5bc4\u305b", -"New document": "\u65b0\u898f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8", -"Blockquote": "\u5f15\u7528", -"Numbered list": "\u756a\u53f7\u4ed8\u304d\u7b87\u6761\u66f8\u304d", -"Heading 1": "\u898b\u51fa\u3057 1", -"Headings": "\u898b\u51fa\u3057", -"Increase indent": "\u30a4\u30f3\u30c7\u30f3\u30c8\u3092\u5897\u3084\u3059", -"Formats": "\u66f8\u5f0f", -"Headers": "\u30d8\u30c3\u30c0\u30fc", -"Select all": "\u5168\u3066\u3092\u9078\u629e", -"Header 3": "\u30d8\u30c3\u30c0\u30fc 3", -"Blocks": "\u30d6\u30ed\u30c3\u30af", -"Undo": "\u5143\u306b\u623b\u3059", -"Strikethrough": "\u53d6\u308a\u6d88\u3057\u7dda", -"Bullet list": "\u7b87\u6761\u66f8\u304d", -"Header 1": "\u30d8\u30c3\u30c0\u30fc 1", -"Superscript": "\u4e0a\u4ed8\u304d\u6587\u5b57", -"Clear formatting": "\u66f8\u5f0f\u3092\u30af\u30ea\u30a2", -"Font Sizes": "\u30d5\u30a9\u30f3\u30c8\u30b5\u30a4\u30ba", -"Subscript": "\u4e0b\u4ed8\u304d\u6587\u5b57", -"Header 6": "\u30d8\u30c3\u30c0\u30fc 6", "Redo": "\u3084\u308a\u76f4\u3059", -"Paragraph": "\u6bb5\u843d", -"Ok": "OK", -"Bold": "\u592a\u5b57", -"Code": "\u30b3\u30fc\u30c9", -"Italic": "\u659c\u4f53", -"Align center": "\u4e2d\u592e\u63c3\u3048", -"Header 5": "\u30d8\u30c3\u30c0\u30fc 5", -"Heading 6": "\u898b\u51fa\u3057 6", -"Heading 3": "\u898b\u51fa\u3057 3", -"Decrease indent": "\u30a4\u30f3\u30c7\u30f3\u30c8\u3092\u6e1b\u3089\u3059", -"Header 4": "\u30d8\u30c3\u30c0\u30fc 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u8cbc\u308a\u4ed8\u3051\u306f\u73fe\u5728\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\u30e2\u30fc\u30c9\u3067\u3059\u3002\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u30aa\u30d5\u306b\u3057\u306a\u3044\u9650\u308a\u5185\u5bb9\u306f\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\u3068\u3057\u3066\u8cbc\u308a\u4ed8\u3051\u3089\u308c\u307e\u3059\u3002", -"Underline": "\u4e0b\u7dda", -"Cancel": "\u30ad\u30e3\u30f3\u30bb\u30eb", -"Justify": "\u4e21\u7aef\u63c3\u3048", -"Inline": "\u30a4\u30f3\u30e9\u30a4\u30f3", +"Undo": "\u5143\u306b\u623b\u3059", +"Cut": "\u5207\u308a\u53d6\u308a", "Copy": "\u30b3\u30d4\u30fc", -"Align left": "\u5de6\u5bc4\u305b", +"Paste": "\u8cbc\u308a\u4ed8\u3051", +"Select all": "\u5168\u3066\u3092\u9078\u629e", +"New document": "\u65b0\u898f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8", +"Ok": "OK", +"Cancel": "\u30ad\u30e3\u30f3\u30bb\u30eb", "Visual aids": "\u8868\u306e\u67a0\u7dda\u3092\u70b9\u7dda\u3067\u8868\u793a", -"Lower Greek": "\u5c0f\u6587\u5b57\u306e\u30ae\u30ea\u30b7\u30e3\u6587\u5b57", -"Square": "\u56db\u89d2", +"Bold": "\u592a\u5b57", +"Italic": "\u659c\u4f53", +"Underline": "\u4e0b\u7dda", +"Strikethrough": "\u53d6\u308a\u6d88\u3057\u7dda", +"Superscript": "\u4e0a\u4ed8\u304d\u6587\u5b57", +"Subscript": "\u4e0b\u4ed8\u304d\u6587\u5b57", +"Clear formatting": "\u66f8\u5f0f\u3092\u30af\u30ea\u30a2", +"Align left": "\u5de6\u5bc4\u305b", +"Align center": "\u4e2d\u592e\u63c3\u3048", +"Align right": "\u53f3\u5bc4\u305b", +"Justify": "\u4e21\u7aef\u63c3\u3048", +"Bullet list": "\u7b87\u6761\u66f8\u304d", +"Numbered list": "\u756a\u53f7\u4ed8\u304d\u7b87\u6761\u66f8\u304d", +"Decrease indent": "\u30a4\u30f3\u30c7\u30f3\u30c8\u3092\u6e1b\u3089\u3059", +"Increase indent": "\u30a4\u30f3\u30c7\u30f3\u30c8\u3092\u5897\u3084\u3059", +"Close": "\u9589\u3058\u308b", +"Formats": "\u66f8\u5f0f", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u304a\u4f7f\u3044\u306e\u30d6\u30e9\u30a6\u30b6\u3067\u306f\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u6a5f\u80fd\u3092\u5229\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3002\u30ad\u30fc\u30dc\u30fc\u30c9\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\uff08Ctrl+X, Ctrl+C, Ctrl+V\uff09\u3092\u304a\u4f7f\u3044\u4e0b\u3055\u3044\u3002", +"Headers": "\u30d8\u30c3\u30c0\u30fc", +"Header 1": "\u30d8\u30c3\u30c0\u30fc 1", +"Header 2": "\u30d8\u30c3\u30c0\u30fc 2", +"Header 3": "\u30d8\u30c3\u30c0\u30fc 3", +"Header 4": "\u30d8\u30c3\u30c0\u30fc 4", +"Header 5": "\u30d8\u30c3\u30c0\u30fc 5", +"Header 6": "\u30d8\u30c3\u30c0\u30fc 6", +"Headings": "\u898b\u51fa\u3057", +"Heading 1": "\u898b\u51fa\u3057 1", +"Heading 2": "\u898b\u51fa\u3057 2", +"Heading 3": "\u898b\u51fa\u3057 3", +"Heading 4": "\u898b\u51fa\u3057 4", +"Heading 5": "\u898b\u51fa\u3057 5", +"Heading 6": "\u898b\u51fa\u3057 6", +"Preformatted": "Preformatted", +"Div": "Div", +"Pre": "Pre", +"Code": "\u30b3\u30fc\u30c9", +"Paragraph": "\u6bb5\u843d", +"Blockquote": "\u5f15\u7528", +"Inline": "\u30a4\u30f3\u30e9\u30a4\u30f3", +"Blocks": "\u30d6\u30ed\u30c3\u30af", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u8cbc\u308a\u4ed8\u3051\u306f\u73fe\u5728\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\u30e2\u30fc\u30c9\u3067\u3059\u3002\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u30aa\u30d5\u306b\u3057\u306a\u3044\u9650\u308a\u5185\u5bb9\u306f\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\u3068\u3057\u3066\u8cbc\u308a\u4ed8\u3051\u3089\u308c\u307e\u3059\u3002", +"Font Family": "\u30d5\u30a9\u30f3\u30c8\u30d5\u30a1\u30df\u30ea\u30fc", +"Font Sizes": "\u30d5\u30a9\u30f3\u30c8\u30b5\u30a4\u30ba", +"Class": "\u30af\u30e9\u30b9", +"Browse for an image": "\u30a4\u30e1\u30fc\u30b8\u3092\u53c2\u7167", +"OR": "\u307e\u305f\u306f", +"Drop an image here": "\u3053\u3053\u306b\u753b\u50cf\u3092\u30c9\u30ed\u30c3\u30d7", +"Upload": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9", +"Block": "\u30d6\u30ed\u30c3\u30af", +"Align": "\u914d\u7f6e", "Default": "\u30c7\u30d5\u30a9\u30eb\u30c8", -"Lower Alpha": "\u5c0f\u6587\u5b57\u306e\u30a2\u30eb\u30d5\u30a1\u30d9\u30c3\u30c8", "Circle": "\u5186", "Disc": "\u70b9", +"Square": "\u56db\u89d2", +"Lower Alpha": "\u5c0f\u6587\u5b57\u306e\u30a2\u30eb\u30d5\u30a1\u30d9\u30c3\u30c8", +"Lower Greek": "\u5c0f\u6587\u5b57\u306e\u30ae\u30ea\u30b7\u30e3\u6587\u5b57", +"Lower Roman": "\u5c0f\u6587\u5b57\u306e\u30ed\u30fc\u30de\u6570\u5b57", "Upper Alpha": "\u5927\u6587\u5b57\u306e\u30a2\u30eb\u30d5\u30a1\u30d9\u30c3\u30c8", "Upper Roman": "\u5927\u6587\u5b57\u306e\u30ed\u30fc\u30de\u6570\u5b57", -"Lower Roman": "\u5c0f\u6587\u5b57\u306e\u30ed\u30fc\u30de\u6570\u5b57", -"Name": "\u30a2\u30f3\u30ab\u30fc\u540d", "Anchor": "\u30a2\u30f3\u30ab\u30fc\uff08\u30ea\u30f3\u30af\u306e\u5230\u9054\u70b9\uff09", +"Name": "\u30a2\u30f3\u30ab\u30fc\u540d", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID\u306f\u6587\u5b57\u3067\u59cb\u307e\u308a\u3001\u6587\u5b57\u3001\u6570\u5b57\u3001\u30c0\u30c3\u30b7\u30e5\u3001\u30c9\u30c3\u30c8\u3001\u30b3\u30ed\u30f3\u307e\u305f\u306f\u30a2\u30f3\u30c0\u30fc\u30b9\u30b3\u30a2\u3067\u59cb\u307e\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", "You have unsaved changes are you sure you want to navigate away?": "\u307e\u3060\u4fdd\u5b58\u3057\u3066\u3044\u306a\u3044\u5909\u66f4\u304c\u3042\u308a\u307e\u3059\u304c\u3001\u672c\u5f53\u306b\u3053\u306e\u30da\u30fc\u30b8\u3092\u96e2\u308c\u307e\u3059\u304b\uff1f", "Restore last draft": "\u524d\u56de\u306e\u4e0b\u66f8\u304d\u3092\u5fa9\u6d3b\u3055\u305b\u308b", "Special character": "\u7279\u6b8a\u6587\u5b57", "Source code": "\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9", -"B": "B", +"Insert\/Edit code sample": "\u30b3\u30fc\u30c9\u30b5\u30f3\u30d7\u30eb\u306e\u633f\u5165\u30fb\u7de8\u96c6", +"Language": "\u8a00\u8a9e", +"Code sample": "\u30b3\u30fc\u30c9\u30b5\u30f3\u30d7\u30eb", +"Color": "\u30ab\u30e9\u30fc", "R": "R", "G": "G", -"Color": "\u30ab\u30e9\u30fc", -"Right to left": "\u53f3\u304b\u3089\u5de6", +"B": "B", "Left to right": "\u5de6\u304b\u3089\u53f3", +"Right to left": "\u53f3\u304b\u3089\u5de6", "Emoticons": "\u7d75\u6587\u5b57", -"Robots": "\u30ed\u30dc\u30c3\u30c4", "Document properties": "\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u30d7\u30ed\u30d1\u30c6\u30a3", "Title": "\u30bf\u30a4\u30c8\u30eb", "Keywords": "\u30ad\u30fc\u30ef\u30fc\u30c9", -"Encoding": "\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0", "Description": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u5185\u5bb9", +"Robots": "\u30ed\u30dc\u30c3\u30c4", "Author": "\u8457\u8005", +"Encoding": "\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0", "Fullscreen": "\u5168\u753b\u9762\u8868\u793a", +"Action": "\u30a2\u30af\u30b7\u30e7\u30f3", +"Shortcut": "\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8", +"Help": "\u30d8\u30eb\u30d7", +"Address": "\u30a2\u30c9\u30ec\u30b9", +"Focus to menubar": "\u30e1\u30cb\u30e5\u30fc\u30d0\u30fc\u306b\u30d5\u30a9\u30fc\u30ab\u30b9", +"Focus to toolbar": "\u30c4\u30fc\u30eb\u30d0\u30fc\u306b\u30d5\u30a9\u30fc\u30ab\u30b9", +"Focus to element path": "\u8981\u7d20\u30d1\u30b9\u306b\u30d5\u30a9\u30fc\u30ab\u30b9", +"Focus to contextual toolbar": "\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30c4\u30fc\u30eb\u30d0\u30fc\u306b\u30d5\u30a9\u30fc\u30ab\u30b9", +"Insert link (if link plugin activated)": "\u30ea\u30f3\u30af\u3092\u633f\u5165 (\u30ea\u30f3\u30af\u30d7\u30e9\u30b0\u30a4\u30f3\u6709\u52b9\u6642)", +"Save (if save plugin activated)": "\u4fdd\u5b58 (\u4fdd\u5b58\u30d7\u30e9\u30b0\u30a4\u30f3\u6709\u52b9\u6642)", +"Find (if searchreplace plugin activated)": "\u691c\u7d22(\u7f6e\u63db\u30d7\u30e9\u30b0\u30a4\u30f3\u6709\u52b9\u6642)", +"Plugins installed ({0}):": "\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08\u30d7\u30e9\u30b0\u30a4\u30f3 ({0}):", +"Premium plugins:": "\u30d7\u30ec\u30df\u30a2\u30e0\u30d7\u30e9\u30b0\u30a4\u30f3:", +"Learn more...": "\u8a73\u7d30...", +"You are using {0}": "\u3042\u306a\u305f\u306f {0} \u4f7f\u7528\u4e2d", +"Plugins": "\u30d7\u30e9\u30b0\u30a4\u30f3", +"Handy Shortcuts": "\u4fbf\u5229\u306a\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8", "Horizontal line": "\u6c34\u5e73\u7f6b\u7dda", -"Horizontal space": "\u6a2a\u65b9\u5411\u306e\u4f59\u767d", "Insert\/edit image": "\u753b\u50cf\u306e\u633f\u5165\u30fb\u7de8\u96c6", +"Image description": "\u753b\u50cf\u306e\u8aac\u660e\u6587", +"Source": "\u753b\u50cf\u306e\u30bd\u30fc\u30b9", +"Dimensions": "\u753b\u50cf\u30b5\u30a4\u30ba\uff08\u6a2a\u30fb\u7e26\uff09", +"Constrain proportions": "\u7e26\u6a2a\u6bd4\u3092\u4fdd\u6301\u3059\u308b", "General": "\u4e00\u822c", "Advanced": "\u8a73\u7d30\u8a2d\u5b9a", -"Source": "\u753b\u50cf\u306e\u30bd\u30fc\u30b9", -"Border": "\u67a0\u7dda", -"Constrain proportions": "\u7e26\u6a2a\u6bd4\u3092\u4fdd\u6301\u3059\u308b", -"Vertical space": "\u7e26\u65b9\u5411\u306e\u4f59\u767d", -"Image description": "\u753b\u50cf\u306e\u8aac\u660e\u6587", "Style": "\u30b9\u30bf\u30a4\u30eb", -"Dimensions": "\u753b\u50cf\u30b5\u30a4\u30ba\uff08\u6a2a\u30fb\u7e26\uff09", +"Vertical space": "\u7e26\u65b9\u5411\u306e\u4f59\u767d", +"Horizontal space": "\u6a2a\u65b9\u5411\u306e\u4f59\u767d", +"Border": "\u67a0\u7dda", "Insert image": "\u753b\u50cf\u306e\u633f\u5165", -"Zoom in": "\u30ba\u30fc\u30e0\u30a4\u30f3", -"Contrast": "\u30b3\u30f3\u30c8\u30e9\u30b9\u30c8", -"Back": "\u623b\u308b", -"Gamma": "\u30ac\u30f3\u30de", -"Flip horizontally": "\u6c34\u5e73\u306b\u53cd\u8ee2", -"Resize": "\u30ea\u30b5\u30a4\u30ba", -"Sharpen": "\u30b7\u30e3\u30fc\u30d7\u5316", -"Zoom out": "\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8", -"Image options": "\u753b\u50cf\u30aa\u30d7\u30b7\u30e7\u30f3", -"Apply": "\u9069\u7528", -"Brightness": "\u660e\u308b\u3055", -"Rotate clockwise": "\u6642\u8a08\u56de\u308a\u306b\u56de\u8ee2", +"Image": "\u753b\u50cf", +"Image list": "\u753b\u50cf\u4e00\u89a7", "Rotate counterclockwise": "\u53cd\u6642\u8a08\u56de\u308a\u306b\u56de\u8ee2", -"Edit image": "\u753b\u50cf\u306e\u7de8\u96c6", -"Color levels": "\u30ab\u30e9\u30fc\u30ec\u30d9\u30eb", -"Crop": "\u30af\u30ed\u30c3\u30d7", -"Orientation": "\u5411\u304d", +"Rotate clockwise": "\u6642\u8a08\u56de\u308a\u306b\u56de\u8ee2", "Flip vertically": "\u4e0a\u4e0b\u306b\u53cd\u8ee2", +"Flip horizontally": "\u6c34\u5e73\u306b\u53cd\u8ee2", +"Edit image": "\u753b\u50cf\u306e\u7de8\u96c6", +"Image options": "\u753b\u50cf\u30aa\u30d7\u30b7\u30e7\u30f3", +"Zoom in": "\u30ba\u30fc\u30e0\u30a4\u30f3", +"Zoom out": "\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8", +"Crop": "\u30af\u30ed\u30c3\u30d7", +"Resize": "\u30ea\u30b5\u30a4\u30ba", +"Orientation": "\u5411\u304d", +"Brightness": "\u660e\u308b\u3055", +"Sharpen": "\u30b7\u30e3\u30fc\u30d7\u5316", +"Contrast": "\u30b3\u30f3\u30c8\u30e9\u30b9\u30c8", +"Color levels": "\u30ab\u30e9\u30fc\u30ec\u30d9\u30eb", +"Gamma": "\u30ac\u30f3\u30de", "Invert": "\u53cd\u8ee2", +"Apply": "\u9069\u7528", +"Back": "\u623b\u308b", "Insert date\/time": "\u65e5\u4ed8\u30fb\u6642\u523b", -"Remove link": "\u30ea\u30f3\u30af\u306e\u524a\u9664", -"Url": "\u30ea\u30f3\u30af\u5148URL", -"Text to display": "\u30ea\u30f3\u30af\u5143\u30c6\u30ad\u30b9\u30c8", -"Anchors": "\u30a2\u30f3\u30ab\u30fc\uff08\u30ea\u30f3\u30af\u306e\u5230\u9054\u70b9\uff09", +"Date\/time": "\u65e5\u4ed8\u30fb\u6642\u523b", "Insert link": "\u30ea\u30f3\u30af", -"New window": "\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6", -"None": "\u306a\u3057", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u5165\u529b\u3055\u308c\u305fURL\u306f\u5916\u90e8\u30ea\u30f3\u30af\u306e\u3088\u3046\u3067\u3059\u3002\u300chttp:\/\/\u300d\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f", -"Target": "\u30bf\u30fc\u30b2\u30c3\u30c8\u5c5e\u6027", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u5165\u529b\u3055\u308c\u305fURL\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u3088\u3046\u3067\u3059\u3002\u300cmailto:\u300d\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f", "Insert\/edit link": "\u30ea\u30f3\u30af\u306e\u633f\u5165\u30fb\u7de8\u96c6", -"Insert\/edit video": "\u52d5\u753b\u306e\u633f\u5165\u30fb\u7de8\u96c6", -"Poster": "\u4ee3\u66ff\u753b\u50cf\u306e\u5834\u6240", -"Alternative source": "\u4ee3\u66ff\u52d5\u753b\u306e\u5834\u6240", -"Paste your embed code below:": "\u57cb\u3081\u8fbc\u307f\u7528\u30b3\u30fc\u30c9\u3092\u4e0b\u8a18\u306b\u8cbc\u308a\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002", +"Text to display": "\u30ea\u30f3\u30af\u5143\u30c6\u30ad\u30b9\u30c8", +"Url": "\u30ea\u30f3\u30af\u5148URL", +"Target": "\u30bf\u30fc\u30b2\u30c3\u30c8\u5c5e\u6027", +"None": "\u306a\u3057", +"New window": "\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6", +"Remove link": "\u30ea\u30f3\u30af\u306e\u524a\u9664", +"Anchors": "\u30a2\u30f3\u30ab\u30fc\uff08\u30ea\u30f3\u30af\u306e\u5230\u9054\u70b9\uff09", +"Link": "\u30ea\u30f3\u30af", +"Paste or type a link": "\u30ea\u30f3\u30af\u3092\u30da\u30fc\u30b9\u30c8\u307e\u305f\u306f\u5165\u529b", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u5165\u529b\u3055\u308c\u305fURL\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u3088\u3046\u3067\u3059\u3002\u300cmailto:\u300d\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u5165\u529b\u3055\u308c\u305fURL\u306f\u5916\u90e8\u30ea\u30f3\u30af\u306e\u3088\u3046\u3067\u3059\u3002\u300chttp:\/\/\u300d\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f", +"Link list": "\u30ea\u30f3\u30af\u4e00\u89a7", "Insert video": "\u52d5\u753b", +"Insert\/edit video": "\u52d5\u753b\u306e\u633f\u5165\u30fb\u7de8\u96c6", +"Insert\/edit media": "\u30e1\u30c7\u30a3\u30a2\u306e\u633f\u5165\u30fb\u7de8\u96c6", +"Alternative source": "\u4ee3\u66ff\u52d5\u753b\u306e\u5834\u6240", +"Poster": "\u4ee3\u66ff\u753b\u50cf\u306e\u5834\u6240", +"Paste your embed code below:": "\u57cb\u3081\u8fbc\u307f\u7528\u30b3\u30fc\u30c9\u3092\u4e0b\u8a18\u306b\u8cbc\u308a\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002", "Embed": "\u57cb\u3081\u8fbc\u307f", +"Media": "\u30e1\u30c7\u30a3\u30a2", "Nonbreaking space": "\u56fa\u5b9a\u30b9\u30da\u30fc\u30b9\uff08 \uff09", "Page break": "\u30da\u30fc\u30b8\u533a\u5207\u308a", "Paste as text": "\u30c6\u30ad\u30b9\u30c8\u3068\u3057\u3066\u8cbc\u308a\u4ed8\u3051", "Preview": "\u30d7\u30ec\u30d3\u30e5\u30fc", "Print": "\u5370\u5237", "Save": "\u4fdd\u5b58", -"Could not find the specified string.": "\u304a\u63a2\u3057\u306e\u6587\u5b57\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002", -"Replace": "\u7f6e\u304d\u63db\u3048", -"Next": "\u6b21", -"Whole words": "\u5358\u8a9e\u5358\u4f4d\u3067\u691c\u7d22\u3059\u308b", -"Find and replace": "\u691c\u7d22\u3068\u7f6e\u304d\u63db\u3048", -"Replace with": "\u7f6e\u304d\u63db\u3048\u308b\u6587\u5b57", "Find": "\u691c\u7d22", +"Replace with": "\u7f6e\u304d\u63db\u3048\u308b\u6587\u5b57", +"Replace": "\u7f6e\u304d\u63db\u3048", "Replace all": "\u5168\u3066\u3092\u7f6e\u304d\u63db\u3048\u308b", -"Match case": "\u5927\u6587\u5b57\u30fb\u5c0f\u6587\u5b57\u3092\u533a\u5225\u3059\u308b", "Prev": "\u524d", +"Next": "\u6b21", +"Find and replace": "\u691c\u7d22\u3068\u7f6e\u304d\u63db\u3048", +"Could not find the specified string.": "\u304a\u63a2\u3057\u306e\u6587\u5b57\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002", +"Match case": "\u5927\u6587\u5b57\u30fb\u5c0f\u6587\u5b57\u3092\u533a\u5225\u3059\u308b", +"Whole words": "\u5358\u8a9e\u5358\u4f4d\u3067\u691c\u7d22\u3059\u308b", "Spellcheck": "\u30b9\u30da\u30eb\u30c1\u30a7\u30c3\u30af", -"Finish": "\u7d42\u4e86", -"Ignore all": "\u5168\u3066\u3092\u7121\u8996", "Ignore": "\u7121\u8996", +"Ignore all": "\u5168\u3066\u3092\u7121\u8996", +"Finish": "\u7d42\u4e86", "Add to Dictionary": "\u8f9e\u66f8\u306b\u8ffd\u52a0", -"Insert row before": "\u4e0a\u5074\u306b\u884c\u3092\u633f\u5165", -"Rows": "\u884c\u6570", -"Height": "\u9ad8\u3055", -"Paste row after": "\u4e0b\u5074\u306b\u884c\u3092\u8cbc\u308a\u4ed8\u3051", -"Alignment": "\u914d\u7f6e", -"Border color": "\u67a0\u7dda\u306e\u8272", -"Column group": "\u5217\u30b0\u30eb\u30fc\u30d7", -"Row": "\u884c", -"Insert column before": "\u5de6\u5074\u306b\u5217\u3092\u633f\u5165", -"Split cell": "\u30bb\u30eb\u306e\u5206\u5272", -"Cell padding": "\u30bb\u30eb\u5185\u4f59\u767d\uff08\u30d1\u30c7\u30a3\u30f3\u30b0\uff09", -"Cell spacing": "\u30bb\u30eb\u306e\u9593\u9694", -"Row type": "\u884c\u30bf\u30a4\u30d7", "Insert table": "\u8868\u306e\u633f\u5165", -"Body": "\u30dc\u30c7\u30a3\u30fc", -"Caption": "\u8868\u984c", -"Footer": "\u30d5\u30c3\u30bf\u30fc", -"Delete row": "\u884c\u306e\u524a\u9664", -"Paste row before": "\u4e0a\u5074\u306b\u884c\u3092\u8cbc\u308a\u4ed8\u3051", -"Scope": "\u30b9\u30b3\u30fc\u30d7", -"Delete table": "\u8868\u306e\u524a\u9664", -"H Align": "\u6c34\u5e73\u65b9\u5411\u306e\u914d\u7f6e", -"Top": "\u4e0a", -"Header cell": "\u30d8\u30c3\u30c0\u30fc\u30bb\u30eb", -"Column": "\u5217", -"Row group": "\u884c\u30b0\u30eb\u30fc\u30d7", -"Cell": "\u30bb\u30eb", -"Middle": "\u4e2d\u592e", -"Cell type": "\u30bb\u30eb\u30bf\u30a4\u30d7", -"Copy row": "\u884c\u306e\u30b3\u30d4\u30fc", -"Row properties": "\u884c\u306e\u8a73\u7d30\u8a2d\u5b9a", "Table properties": "\u8868\u306e\u8a73\u7d30\u8a2d\u5b9a", -"Bottom": "\u4e0b", -"V Align": "\u5782\u76f4\u65b9\u5411\u306e\u914d\u7f6e", -"Header": "\u30d8\u30c3\u30c0\u30fc", -"Right": "\u53f3\u5bc4\u305b", -"Insert column after": "\u53f3\u5074\u306b\u5217\u3092\u633f\u5165", -"Cols": "\u5217\u6570", -"Insert row after": "\u4e0b\u5074\u306b\u884c\u3092\u633f\u5165", -"Width": "\u5e45", +"Delete table": "\u8868\u306e\u524a\u9664", +"Cell": "\u30bb\u30eb", +"Row": "\u884c", +"Column": "\u5217", "Cell properties": "\u30bb\u30eb\u306e\u8a73\u7d30\u8a2d\u5b9a", -"Left": "\u5de6\u5bc4\u305b", -"Cut row": "\u884c\u306e\u5207\u308a\u53d6\u308a", -"Delete column": "\u5217\u306e\u524a\u9664", -"Center": "\u4e2d\u592e\u63c3\u3048", "Merge cells": "\u30bb\u30eb\u306e\u7d50\u5408", +"Split cell": "\u30bb\u30eb\u306e\u5206\u5272", +"Insert row before": "\u4e0a\u5074\u306b\u884c\u3092\u633f\u5165", +"Insert row after": "\u4e0b\u5074\u306b\u884c\u3092\u633f\u5165", +"Delete row": "\u884c\u306e\u524a\u9664", +"Row properties": "\u884c\u306e\u8a73\u7d30\u8a2d\u5b9a", +"Cut row": "\u884c\u306e\u5207\u308a\u53d6\u308a", +"Copy row": "\u884c\u306e\u30b3\u30d4\u30fc", +"Paste row before": "\u4e0a\u5074\u306b\u884c\u3092\u8cbc\u308a\u4ed8\u3051", +"Paste row after": "\u4e0b\u5074\u306b\u884c\u3092\u8cbc\u308a\u4ed8\u3051", +"Insert column before": "\u5de6\u5074\u306b\u5217\u3092\u633f\u5165", +"Insert column after": "\u53f3\u5074\u306b\u5217\u3092\u633f\u5165", +"Delete column": "\u5217\u306e\u524a\u9664", +"Cols": "\u5217\u6570", +"Rows": "\u884c\u6570", +"Width": "\u5e45", +"Height": "\u9ad8\u3055", +"Cell spacing": "\u30bb\u30eb\u306e\u9593\u9694", +"Cell padding": "\u30bb\u30eb\u5185\u4f59\u767d\uff08\u30d1\u30c7\u30a3\u30f3\u30b0\uff09", +"Caption": "\u8868\u984c", +"Left": "\u5de6\u5bc4\u305b", +"Center": "\u4e2d\u592e\u63c3\u3048", +"Right": "\u53f3\u5bc4\u305b", +"Cell type": "\u30bb\u30eb\u30bf\u30a4\u30d7", +"Scope": "\u30b9\u30b3\u30fc\u30d7", +"Alignment": "\u914d\u7f6e", +"H Align": "\u6c34\u5e73\u65b9\u5411\u306e\u914d\u7f6e", +"V Align": "\u5782\u76f4\u65b9\u5411\u306e\u914d\u7f6e", +"Top": "\u4e0a", +"Middle": "\u4e2d\u592e", +"Bottom": "\u4e0b", +"Header cell": "\u30d8\u30c3\u30c0\u30fc\u30bb\u30eb", +"Row group": "\u884c\u30b0\u30eb\u30fc\u30d7", +"Column group": "\u5217\u30b0\u30eb\u30fc\u30d7", +"Row type": "\u884c\u30bf\u30a4\u30d7", +"Header": "\u30d8\u30c3\u30c0\u30fc", +"Body": "\u30dc\u30c7\u30a3\u30fc", +"Footer": "\u30d5\u30c3\u30bf\u30fc", +"Border color": "\u67a0\u7dda\u306e\u8272", "Insert template": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u633f\u5165", "Templates": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u540d", +"Template": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8", +"Text color": "\u30c6\u30ad\u30b9\u30c8\u306e\u8272", "Background color": "\u80cc\u666f\u8272", "Custom...": "\u30ab\u30b9\u30bf\u30e0...", "Custom color": "\u30ab\u30b9\u30bf\u30e0\u30ab\u30e9\u30fc", "No color": "\u30ab\u30e9\u30fc\u306a\u3057", -"Text color": "\u30c6\u30ad\u30b9\u30c8\u306e\u8272", +"Table of Contents": "\u76ee\u6b21", "Show blocks": "\u6587\u7ae0\u306e\u533a\u5207\u308a\u3092\u70b9\u7dda\u3067\u8868\u793a", "Show invisible characters": "\u4e0d\u53ef\u8996\u6587\u5b57\u3092\u8868\u793a", "Words: {0}": "\u5358\u8a9e\u6570: {0}", -"Insert": "\u633f\u5165", +"{0} words": "{0} \u30ef\u30fc\u30c9", "File": "\u30d5\u30a1\u30a4\u30eb", "Edit": "\u7de8\u96c6", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u66f8\u5f0f\u4ed8\u304d\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6\u753b\u9762\u3002ALT-F9\u3067\u30e1\u30cb\u30e5\u30fc\u3001ALT-F10\u3067\u30c4\u30fc\u30eb\u30d0\u30fc\u3001ALT-0\u3067\u30d8\u30eb\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002", -"Tools": "\u30c4\u30fc\u30eb", +"Insert": "\u633f\u5165", "View": "\u8868\u793a", +"Format": "\u66f8\u5f0f", "Table": "\u8868", -"Format": "\u66f8\u5f0f" +"Tools": "\u30c4\u30fc\u30eb", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u66f8\u5f0f\u4ed8\u304d\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6\u753b\u9762\u3002ALT-F9\u3067\u30e1\u30cb\u30e5\u30fc\u3001ALT-F10\u3067\u30c4\u30fc\u30eb\u30d0\u30fc\u3001ALT-0\u3067\u30d8\u30eb\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002" }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js new file mode 100644 index 0000000000..9bffb8040d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js @@ -0,0 +1,230 @@ +tinymce.addI18n('ka_GE',{ +"Cut": "\u10d0\u10db\u10dd\u10ed\u10e0\u10d0", +"Heading 5": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 5", +"Header 2": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u10d7\u10e5\u10d5\u10d4\u10dc \u10d1\u10e0\u10d0\u10e3\u10d6\u10d4\u10e0\u10e1 \u10d0\u10e0 \u10d0\u10e5\u10d5\u10e1 \u10d1\u10e3\u10e4\u10e0\u10e2\u10e8\u10d8 \u10e8\u10d4\u10ee\u10ec\u10d4\u10d5\u10d8\u10e1 \u10db\u10ee\u10d0\u10e0\u10d3\u10d0\u10ed\u10d4\u10e0\u10d0. \u10d2\u10d7\u10ee\u10dd\u10d5\u10d7 \u10e1\u10d0\u10dc\u10d0\u10ea\u10d5\u10da\u10dd\u10d3 \u10d8\u10e1\u10d0\u10e0\u10d2\u10d4\u10d1\u10da\u10dd\u10d7 Ctrl+X\/C\/V \u10db\u10d0\u10da\u10e1\u10d0\u10ee\u10db\u10dd\u10d1\u10d8 \u10d9\u10dd\u10db\u10d1\u10d8\u10dc\u10d0\u10ea\u10d8\u10d4\u10d1\u10d8\u10d7.", +"Heading 4": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 4", +"Div": "\u10d2\u10d0\u10dc\u10d0\u10ec\u10d8\u10da\u10d4\u10d1\u10d0", +"Heading 2": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 2", +"Paste": "\u10e9\u10d0\u10e1\u10db\u10d0", +"Close": "\u10d3\u10d0\u10ee\u10e3\u10e0\u10d5\u10d0", +"Font Family": "\u10e4\u10dd\u10dc\u10e2\u10d8", +"Pre": "\u10de\u10e0\u10d4\u10e4\u10dd\u10e0\u10db\u10d0\u10e2\u10d8", +"Align right": "\u10d2\u10d0\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4 \u10db\u10d0\u10e0\u10ef\u10d5\u10dc\u10d8\u10d5", +"New document": "\u10d0\u10ee\u10d0\u10da\u10d8 \u10d3\u10dd\u10d9\u10e3\u10db\u10d4\u10dc\u10e2\u10d8", +"Blockquote": "\u10d1\u10da\u10dd\u10d9\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8 \u10ea\u10d8\u10e2\u10d0\u10e2\u10d0", +"Numbered list": "\u10d3\u10d0\u10dc\u10dd\u10db\u10e0\u10d8\u10da\u10d8 \u10e1\u10d8\u10d0", +"Heading 1": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 1", +"Headings": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8", +"Increase indent": "\u10d0\u10d1\u10d6\u10d0\u10ea\u10d8\u10e1 \u10d2\u10d0\u10d6\u10e0\u10d3\u10d0", +"Formats": "\u10e4\u10dd\u10e0\u10db\u10d0\u10e2\u10d4\u10d1\u10d8", +"Headers": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d4\u10d1\u10d8", +"Select all": "\u10e7\u10d5\u10d4\u10da\u10d0\u10e1 \u10db\u10dd\u10e6\u10dc\u10d8\u10e8\u10d5\u10dc\u10d0", +"Header 3": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 3", +"Blocks": "\u10d1\u10da\u10dd\u10d9\u10d4\u10d1\u10d8", +"Undo": "\u10d3\u10d0\u10d1\u10e0\u10e3\u10dc\u10d4\u10d1\u10d0", +"Strikethrough": "\u10e8\u10e3\u10d0 \u10ee\u10d0\u10d6\u10d8", +"Bullet list": "\u10d1\u10e3\u10da\u10d4\u10e2 \u10e1\u10d8\u10d0", +"Header 1": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 1", +"Superscript": "\u10d6\u10d4\u10d3\u10d0 \u10d8\u10dc\u10d3\u10d4\u10e5\u10e1\u10d8", +"Clear formatting": "\u10e4\u10dd\u10e0\u10db\u10d0\u10e2\u10d8\u10e0\u10d4\u10d1\u10d8\u10e1 \u10d2\u10d0\u10e1\u10e3\u10e4\u10d7\u10d0\u10d5\u10d4\u10d1\u10d0", +"Font Sizes": "\u10e4\u10dd\u10dc\u10e2\u10d8\u10e1 \u10d6\u10dd\u10db\u10d0", +"Subscript": "\u10e5\u10d5\u10d4\u10d3\u10d0 \u10d8\u10dc\u10d3\u10d4\u10e5\u10e1\u10d8", +"Header 6": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 6", +"Redo": "\u10d2\u10d0\u10db\u10d4\u10dd\u10e0\u10d4\u10d1\u10d0", +"Paragraph": "\u10de\u10d0\u10e0\u10d0\u10d2\u10e0\u10d0\u10e4\u10d8", +"Ok": "\u10d9\u10d0\u10e0\u10d2\u10d8", +"Bold": "\u10db\u10d9\u10d5\u10d4\u10d7\u10e0\u10d8", +"Code": "\u10d9\u10dd\u10d3\u10d8", +"Italic": "\u10d3\u10d0\u10ee\u10e0\u10d8\u10da\u10d8", +"Align center": "\u10d2\u10d0\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4 \u10ea\u10d4\u10dc\u10e2\u10e0\u10e8\u10d8", +"Header 5": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 5", +"Heading 6": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 6", +"Heading 3": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 3", +"Decrease indent": "\u10d0\u10d1\u10d6\u10d0\u10ea\u10d8\u10e1 \u10e8\u10d4\u10db\u10ea\u10d8\u10e0\u10d4\u10d1\u10d0", +"Header 4": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u10e2\u10d4\u10e5\u10e1\u10e2\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0 \u10e9\u10d5\u10d4\u10e3\u10da\u10d4\u10d1\u10e0\u10d8\u10d5 \u10e0\u10d4\u10df\u10d8\u10db\u10e8\u10d8\u10d0. \u10e2\u10d4\u10e5\u10e1\u10e2\u10d8 \u10e9\u10d0\u10d8\u10e1\u10db\u10d4\u10d5\u10d0 \u10e3\u10e4\u10dd\u10e0\u10db\u10d0\u10e2\u10dd\u10d7 \u10e1\u10d0\u10dc\u10d0\u10db \u10d0\u10db \u10d7\u10d5\u10d8\u10e1\u10d4\u10d1\u10d0\u10e1 \u10d0\u10e0 \u10d2\u10d0\u10d7\u10d8\u10e8\u10d0\u10d5\u10d7.", +"Underline": "\u10e5\u10d5\u10d4\u10d3\u10d0 \u10ee\u10d0\u10d6\u10d8", +"Cancel": "\u10d2\u10d0\u10e3\u10e5\u10db\u10d4\u10d1\u10d0", +"Justify": "\u10d2\u10d0\u10db\u10d0\u10e0\u10d7\u10e3\u10da\u10d8", +"Inline": "\u10ee\u10d0\u10d6\u10e8\u10d8\u10d3\u10d0", +"Copy": "\u10d9\u10dd\u10de\u10d8\u10e0\u10d4\u10d1\u10d0", +"Align left": "\u10d2\u10d0\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4 \u10db\u10d0\u10e0\u10ea\u10ee\u10dc\u10d8\u10d5", +"Visual aids": "\u10d5\u10d8\u10d6\u10e3\u10d0\u10da\u10d8\u10d6\u10d0\u10ea\u10d8\u10d0", +"Lower Greek": "\u10d3\u10d0\u10d1\u10d0\u10da\u10d8 \u10d1\u10d4\u10e0\u10eb\u10dc\u10e3\u10da\u10d8", +"Square": "\u10d9\u10d5\u10d0\u10d3\u10e0\u10d0\u10e2\u10d8", +"Default": "\u10e1\u10e2\u10d0\u10dc\u10d3\u10d0\u10e0\u10e2\u10e3\u10da\u10d8", +"Lower Alpha": "\u10d3\u10d0\u10d1\u10d0\u10da\u10d8 \u10d0\u10da\u10e4\u10d0", +"Circle": "\u10ec\u10e0\u10d4", +"Disc": "\u10d3\u10d8\u10e1\u10d9\u10d8", +"Upper Alpha": "\u10db\u10d0\u10e6\u10d0\u10da\u10d8 \u10d0\u10da\u10e4\u10d0", +"Upper Roman": "\u10db\u10d0\u10e6\u10d0\u10da\u10d8 \u10e0\u10dd\u10db\u10d0\u10e3\u10da\u10d8", +"Lower Roman": "\u10d3\u10d0\u10d1\u10d0\u10da\u10d8 \u10e0\u10dd\u10db\u10d0\u10e3\u10da\u10d8", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "id \u10e3\u10dc\u10d3\u10d0 \u10d8\u10ec\u10e7\u10d4\u10d1\u10dd\u10d3\u10d4\u10e1 \u10d0\u10e1\u10dd\u10d7\u10d8, \u10e0\u10dd\u10db\u10d4\u10da\u10e1\u10d0\u10ea \u10db\u10dd\u10e7\u10d5\u10d4\u10d1\u10d0 \u10db\u10ee\u10dd\u10da\u10dd\u10d3 \u10d0\u10e1\u10dd\u10d4\u10d1\u10d8, \u10ea\u10d8\u10e4\u10e0\u10d4\u10d1\u10d8, \u10e2\u10d8\u10e0\u10d4, \u10ec\u10d4\u10e0\u10e2\u10d8\u10da\u10d4\u10d1\u10d8, \u10dd\u10e0\u10d8 \u10ec\u10d4\u10e0\u10e2\u10d8\u10da\u10d8 \u10d0\u10dc \u10e5\u10d5\u10d4\u10d3\u10d0 \u10e2\u10d8\u10e0\u10d4. ", +"Name": "\u10e1\u10d0\u10ee\u10d4\u10da\u10d8", +"Anchor": "\u10e6\u10e3\u10d6\u10d0", +"Id": "id", +"You have unsaved changes are you sure you want to navigate away?": "\u10d7\u10e5\u10d5\u10d4\u10dc \u10d2\u10d0\u10e5\u10d5\u10d7 \u10e8\u10d4\u10e3\u10dc\u10d0\u10ee\u10d0\u10d5\u10d8 \u10e8\u10d4\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d4\u10d1\u10d8, \u10d3\u10d0\u10e0\u10ec\u10db\u10e3\u10dc\u10d4\u10d1\u10e3\u10da\u10d8 \u10ee\u10d0\u10d7 \u10e0\u10dd\u10db \u10e1\u10ee\u10d5\u10d0\u10d2\u10d0\u10dc \u10d2\u10d0\u10d3\u10d0\u10e1\u10d5\u10da\u10d0 \u10d2\u10e1\u10e3\u10e0\u10d7?", +"Restore last draft": "\u10d1\u10dd\u10da\u10dd\u10e1 \u10e8\u10d4\u10dc\u10d0\u10ee\u10e3\u10da\u10d8\u10e1 \u10d0\u10e6\u10d3\u10d2\u10d4\u10dc\u10d0", +"Special character": "\u10e1\u10de\u10d4\u10ea\u10d8\u10d0\u10da\u10e3\u10e0\u10d8 \u10e1\u10d8\u10db\u10d1\u10dd\u10da\u10dd", +"Source code": "\u10ec\u10e7\u10d0\u10e0\u10dd\u10e1 \u10d9\u10dd\u10d3\u10d8", +"Language": "\u10d4\u10dc\u10d0", +"Insert\/Edit code sample": "\u10e9\u10d0\u10e1\u10d5\u10d8\/\u10e8\u10d4\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4 \u10d9\u10dd\u10d3\u10d8\u10e1 \u10db\u10d0\u10d2\u10d0\u10da\u10d8\u10d7\u10d8", +"B": "\u10da", +"R": "\u10ec", +"G": "\u10db", +"Color": "\u10e4\u10d4\u10e0\u10d8", +"Right to left": "\u10db\u10d0\u10e0\u10ef\u10d5\u10dc\u10d8\u10d3\u10d0\u10dc \u10db\u10d0\u10e0\u10ea\u10ee\u10dc\u10d8\u10d5", +"Left to right": "\u10db\u10d0\u10e0\u10ea\u10ee\u10dc\u10d8\u10d3\u10d0\u10dc \u10db\u10d0\u10e0\u10ef\u10d5\u10dc\u10d8\u10d5", +"Emoticons": "\u10e1\u10db\u10d0\u10d8\u10da\u10d8\u10d9\u10d4\u10d1\u10d8", +"Robots": "\u10e0\u10dd\u10d1\u10dd\u10d4\u10d1\u10d8", +"Document properties": "\u10d3\u10dd\u10d9\u10e3\u10db\u10d4\u10dc\u10e2\u10d8\u10e1 \u10d7\u10d5\u10d8\u10e1\u10d4\u10d1\u10d4\u10d1\u10d8", +"Title": "\u10e1\u10d0\u10d7\u10d0\u10e3\u10e0\u10d8", +"Keywords": "\u10e1\u10d0\u10d9\u10d5\u10d0\u10dc\u10eb\u10dd \u10e1\u10d8\u10e2\u10e7\u10d5\u10d4\u10d1\u10d8", +"Encoding": "\u10d9\u10dd\u10d3\u10d8\u10e0\u10d4\u10d1\u10d0", +"Description": "\u10d0\u10ee\u10ec\u10d4\u10e0\u10d0", +"Author": "\u10d0\u10d5\u10e2\u10dd\u10e0\u10d8", +"Fullscreen": "\u10e1\u10d0\u10d5\u10e1\u10d4 \u10d4\u10d9\u10e0\u10d0\u10dc\u10d8", +"Horizontal line": "\u10f0\u10dd\u10e0\u10d8\u10d6\u10dd\u10dc\u10e2\u10d0\u10da\u10e3\u10e0\u10d8 \u10ee\u10d0\u10d6\u10d8", +"Horizontal space": "\u10f0\u10dd\u10e0\u10d8\u10d6\u10dd\u10dc\u10e2\u10d0\u10da\u10e3\u10e0\u10d8 \u10e1\u10d8\u10d5\u10e0\u10ea\u10d4", +"Insert\/edit image": "\u10e9\u10d0\u10e1\u10d5\u10d8\/\u10e8\u10d4\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4 \u10e1\u10e3\u10e0\u10d0\u10d7\u10d8", +"General": "\u10db\u10d7\u10d0\u10d5\u10d0\u10e0\u10d8", +"Advanced": "\u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d8\u10d7\u10d8", +"Source": "\u10d1\u10db\u10e3\u10da\u10d8", +"Border": "\u10e1\u10d0\u10d6\u10e6\u10d5\u10d0\u10e0\u10d8", +"Constrain proportions": "\u10de\u10e0\u10dd\u10de\u10dd\u10e0\u10ea\u10d8\u10d8\u10e1 \u10d3\u10d0\u10ea\u10d5\u10d0", +"Vertical space": "\u10d5\u10d4\u10e0\u10e2\u10d8\u10d9\u10d0\u10da\u10e3\u10e0\u10d8 \u10e1\u10d8\u10d5\u10e0\u10ea\u10d4", +"Image description": "\u10e1\u10e3\u10e0\u10d0\u10d7\u10d8\u10e1 \u10d3\u10d0\u10ee\u10d0\u10e1\u10d8\u10d0\u10d7\u10d4\u10d1\u10d0", +"Style": "\u10e1\u10e2\u10d8\u10da\u10d8", +"Dimensions": "\u10d2\u10d0\u10dc\u10d6\u10dd\u10db\u10d8\u10da\u10d4\u10d1\u10d0", +"Insert image": "\u10e1\u10e3\u10e0\u10d0\u10d7\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Image": "\u10d2\u10d0\u10db\u10dd\u10e1\u10d0\u10ee\u10e3\u10da\u10d4\u10d1\u10d0", +"Zoom in": "\u10d2\u10d0\u10d3\u10d8\u10d3\u10d8\u10d4\u10d1\u10d0", +"Contrast": "\u10d9\u10dd\u10dc\u10e2\u10e0\u10d0\u10e1\u10e2\u10d8", +"Back": "\u10e3\u10d9\u10d0\u10dc", +"Gamma": "\u10d2\u10d0\u10db\u10d0", +"Flip horizontally": "\u10f0\u10dd\u10e0\u10d8\u10d6\u10dd\u10dc\u10e2\u10d0\u10da\u10e3\u10e0\u10d0\u10d3 \u10e8\u10d4\u10e2\u10e0\u10d8\u10d0\u10da\u10d4\u10d1\u10d0", +"Resize": "\u10d6\u10dd\u10db\u10d8\u10e1 \u10e8\u10d4\u10ea\u10d5\u10da\u10d0", +"Sharpen": "\u10d2\u10d0\u10da\u10d4\u10e1\u10d5\u10d0", +"Zoom out": "\u10d3\u10d0\u10de\u10d0\u10e2\u10d0\u10e0\u10d0\u10d5\u10d4\u10d1\u10d0", +"Image options": "\u10e1\u10e3\u10e0\u10d0\u10d7\u10d8\u10e1 \u10de\u10d0\u10e0\u10d0\u10db\u10d4\u10e2\u10e0\u10d4\u10d1\u10d8", +"Apply": "\u10db\u10d8\u10e6\u10d4\u10d1\u10d0", +"Brightness": "\u10e1\u10d8\u10d9\u10d0\u10e8\u10d9\u10d0\u10e8\u10d4", +"Rotate clockwise": "\u10e1\u10d0\u10d0\u10d7\u10d8\u10e1 \u10d8\u10e1\u10e0\u10d8\u10e1 \u10db\u10d8\u10db\u10d0\u10e0\u10d7\u10e3\u10da\u10d4\u10d1\u10d8\u10d7 \u10db\u10dd\u10d1\u10e0\u10e3\u10dc\u10d4\u10d1\u10d0", +"Rotate counterclockwise": "\u10e1\u10d0\u10d0\u10d7\u10d8\u10e1 \u10d8\u10e1\u10e0\u10d8\u10e1 \u10db\u10d8\u10db\u10d0\u10e0\u10d7\u10e3\u10da\u10d4\u10d1\u10d8\u10e1 \u10e1\u10d0\u10ec\u10d8\u10dc\u10d0\u10d0\u10e6\u10db\u10d3\u10d4\u10d2\u10dd\u10d2 \u10db\u10dd\u10d1\u10e0\u10e3\u10dc\u10d4\u10d1\u10d0", +"Edit image": "\u10e1\u10e3\u10e0\u10d0\u10d7\u10d8\u10e1 \u10e0\u10d4\u10d3\u10d0\u10e5\u10e2\u10d8\u10e0\u10d4\u10d1\u10d0", +"Color levels": "\u10e4\u10d4\u10e0\u10d8\u10e1 \u10d3\u10dd\u10dc\u10d4", +"Crop": "\u10db\u10dd\u10ed\u10e0\u10d0", +"Orientation": "\u10dd\u10e0\u10d8\u10d4\u10dc\u10e2\u10d0\u10ea\u10d8\u10d0", +"Flip vertically": "\u10d5\u10d4\u10e0\u10e2\u10d8\u10d9\u10d0\u10da\u10e3\u10e0\u10d0\u10d3 \u10d0\u10e2\u10e0\u10d8\u10d0\u10da\u10d4\u10d1\u10d0", +"Invert": "\u10e8\u10d4\u10d1\u10e0\u10e3\u10dc\u10d4\u10d1\u10d0", +"Date\/time": "\u10d7\u10d0\u10e0\u10d8\u10e6\u10d8\/\u10d3\u10e0\u10dd", +"Insert date\/time": "\u10d7\u10d0\u10e0\u10d8\u10e6\u10d8\/\u10d3\u10e0\u10dd\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Remove link": "\u10d1\u10db\u10e3\u10da\u10d8\u10e1 \u10ec\u10d0\u10e8\u10da\u10d0", +"Url": "Url", +"Text to display": "\u10e2\u10d4\u10e5\u10e1\u10e2\u10d8", +"Anchors": "\u10e6\u10e3\u10d6\u10d0", +"Insert link": "\u10d1\u10db\u10e3\u10da\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Link": "\u10d1\u10db\u10e3\u10da\u10d8", +"New window": "\u10d0\u10ee\u10d0\u10da \u10e4\u10d0\u10dc\u10ef\u10d0\u10e0\u10d0\u10e8\u10d8", +"None": "\u10d0\u10e0\u10ea\u10d4\u10e0\u10d7\u10d8", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u10d7\u10e5\u10d5\u10d4\u10dc\u10e1 \u10db\u10d8\u10d4\u10e0 \u10db\u10d8\u10d7\u10d8\u10d7\u10d4\u10d1\u10e3\u10da\u10d8 \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8 \u10ec\u10d0\u10e0\u10db\u10dd\u10d0\u10d3\u10d2\u10d4\u10dc\u10e1 \u10d2\u10d0\u10e0\u10d4 \u10d1\u10db\u10e3\u10da\u10e1. \u10d2\u10e1\u10e3\u10e0\u10d7, \u10e0\u10dd\u10db \u10db\u10d8\u10d5\u10d0\u10dc\u10d8\u10ed\u10dd http:\/\/ \u10e4\u10e0\u10d4\u10e4\u10d8\u10e5\u10e1\u10d8?", +"Paste or type a link": "\u10e9\u10d0\u10e1\u10d5\u10d8\u10d7 \u10d0\u10dc \u10e8\u10d4\u10d8\u10e7\u10d5\u10d0\u10dc\u10d4\u10d7 \u10d1\u10db\u10e3\u10da\u10d8", +"Target": "\u10d2\u10d0\u10ee\u10e1\u10dc\u10d0", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u10d7\u10e5\u10d5\u10d4\u10dc \u10db\u10d8\u10e3\u10d7\u10d8\u10d7\u10d4\u10d7 \u10d4\u10da-\u10e4\u10dd\u10e1\u10e2\u10d8\u10e1 \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8 \u10dc\u10d0\u10ea\u10d5\u10da\u10d0\u10d3 \u10d5\u10d4\u10d1-\u10d2\u10d5\u10d4\u10e0\u10d3\u10d8\u10e1\u10d0. \u10d2\u10e1\u10e3\u10e0\u10d7, \u10e0\u10dd\u10db \u10db\u10d8\u10d5\u10d0\u10dc\u10d8\u10ed\u10dd mailto: \u10e4\u10e0\u10d4\u10e4\u10d8\u10e5\u10e1\u10d8?", +"Insert\/edit link": "\u10d1\u10db\u10e3\u10da\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0\/\u10e0\u10d4\u10d3\u10d0\u10e5\u10e2\u10d8\u10e0\u10d4\u10d0", +"Insert\/edit video": "\u10d5\u10d8\u10d3\u10d4\u10dd\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0\/\u10e0\u10d4\u10d3\u10d0\u10e5\u10e2\u10d8\u10e0\u10d4\u10d1\u10d0", +"Media": "\u10db\u10d4\u10d3\u10d8\u10d0", +"Alternative source": "\u10d0\u10da\u10e2\u10d4\u10e0\u10dc\u10d0\u10e2\u10d8\u10e3\u10da\u10d8 \u10ec\u10e7\u10d0\u10e0\u10dd", +"Paste your embed code below:": "\u10d0\u10e5 \u10e9\u10d0\u10e1\u10d5\u10d8\u10d7 \u10d7\u10e5\u10d5\u10d4\u10dc\u10d8 \u10d9\u10dd\u10d3\u10d8:", +"Insert video": "\u10d5\u10d8\u10d3\u10d4\u10dd\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Poster": "\u10de\u10da\u10d0\u10d9\u10d0\u10e2\u10d8", +"Insert\/edit media": "\u10db\u10d4\u10d3\u10d8\u10d0\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0\/\u10e0\u10d4\u10d3\u10d0\u10e5\u10e2\u10d8\u10e0\u10d4\u10d1\u10d0", +"Embed": "\u10e9\u10d0\u10e8\u10d4\u10dc\u10d4\u10d1\u10d0", +"Nonbreaking space": "\u10e3\u10ec\u10e7\u10d5\u10d4\u10e2\u10d8 \u10e1\u10d8\u10d5\u10e0\u10ea\u10d4", +"Page break": "\u10d2\u10d5\u10d4\u10e0\u10d3\u10d8\u10e1 \u10d2\u10d0\u10ec\u10e7\u10d5\u10d4\u10e2\u10d0", +"Paste as text": "\u10e9\u10d0\u10e1\u10d5\u10d8\u10d7 \u10e0\u10dd\u10d2\u10dd\u10e0\u10ea \u10e2\u10d4\u10e5\u10e1\u10e2\u10d8", +"Preview": "\u10ec\u10d8\u10dc\u10d0\u10e1\u10ec\u10d0\u10e0 \u10dc\u10d0\u10ee\u10d5\u10d0", +"Print": "\u10d0\u10db\u10dd\u10d1\u10d4\u10ed\u10d5\u10d3\u10d0", +"Save": "\u10e8\u10d4\u10dc\u10d0\u10ee\u10d5\u10d0", +"Could not find the specified string.": "\u10db\u10dd\u10ea\u10d4\u10db\u10e3\u10da\u10d8 \u10e9\u10d0\u10dc\u10d0\u10ec\u10d4\u10e0\u10d8 \u10d5\u10d4\u10e0 \u10db\u10dd\u10d8\u10eb\u10d4\u10d1\u10dc\u10d0.", +"Replace": "\u10e8\u10d4\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d0", +"Next": "\u10e8\u10d4\u10db\u10d3\u10d4\u10d2\u10d8", +"Whole words": "\u10e1\u10e0\u10e3\u10da\u10d8 \u10e1\u10d8\u10e2\u10e7\u10d5\u10d4\u10d1\u10d8", +"Find and replace": "\u10db\u10dd\u10eb\u10d4\u10d1\u10dc\u10d4 \u10d3\u10d0 \u10e8\u10d4\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4", +"Replace with": "\u10e8\u10d4\u10e1\u10d0\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d4\u10da\u10d8 \u10e1\u10d8\u10e2\u10e7\u10d5\u10d0", +"Find": "\u10eb\u10d4\u10d1\u10dc\u10d0", +"Replace all": "\u10e7\u10d5\u10d4\u10da\u10d0\u10e1 \u10e8\u10d4\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d0", +"Match case": "\u10d3\u10d0\u10d0\u10db\u10d7\u10ee\u10d5\u10d8\u10d4 \u10d0\u10e1\u10dd\u10d4\u10d1\u10d8\u10e1 \u10d6\u10dd\u10db\u10d0", +"Prev": "\u10ec\u10d8\u10dc\u10d0", +"Spellcheck": "\u10db\u10d0\u10e0\u10d7\u10da\u10ec\u10d4\u10e0\u10d8\u10e1 \u10e8\u10d4\u10db\u10dd\u10ec\u10db\u10d4\u10d1\u10d0", +"Finish": "\u10d3\u10d0\u10e1\u10d0\u10e1\u10e0\u10e3\u10da\u10d8", +"Ignore all": "\u10e7\u10d5\u10d4\u10da\u10d0\u10e1 \u10d8\u10d2\u10dc\u10dd\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0", +"Ignore": "\u10d8\u10d2\u10dc\u10dd\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0", +"Add to Dictionary": "\u10da\u10d4\u10e5\u10e1\u10d8\u10d9\u10dd\u10dc\u10e8\u10d8 \u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d0", +"Insert row before": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d7\u10d0\u10d5\u10e8\u10d8 \u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d0", +"Rows": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d4\u10d1\u10d8", +"Height": "\u10e1\u10d8\u10db\u10d0\u10e6\u10da\u10d4", +"Paste row after": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d1\u10dd\u10da\u10dd\u10e8\u10d8 \u10e9\u10d0\u10e1\u10db\u10d0", +"Alignment": "\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d0", +"Border color": "\u10e1\u10d0\u10d6\u10d0\u10e0\u10d8\u10e1 \u10e4\u10d4\u10e0\u10d8", +"Column group": "\u10e1\u10d5\u10d4\u10e2\u10d8\u10e1 \u10ef\u10d2\u10e3\u10e4\u10d8", +"Row": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8", +"Insert column before": "\u10e1\u10d5\u10d4\u10e2\u10d8\u10e1 \u10d7\u10d0\u10d5\u10e8\u10d8 \u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d0", +"Split cell": "\u10e3\u10ef\u10e0\u10d8\u10e1 \u10d2\u10d0\u10e7\u10dd\u10e4\u10d0", +"Cell padding": "\u10e3\u10ef\u10e0\u10d8\u10e1 \u10e4\u10d0\u10e0\u10d7\u10dd\u10d1\u10d8", +"Cell spacing": "\u10e3\u10ef\u10e0\u10d8\u10e1 \u10d3\u10d0\u10e8\u10dd\u10e0\u10d4\u10d1\u10d0", +"Row type": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10e2\u10d8\u10de\u10d8", +"Insert table": "\u10ea\u10ee\u10e0\u10d8\u10da\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Body": "\u10e2\u10d0\u10dc\u10d8", +"Caption": "\u10ec\u10d0\u10e0\u10ec\u10d4\u10e0\u10d0", +"Footer": "\u10eb\u10d8\u10e0\u10d8", +"Delete row": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10ec\u10d0\u10e8\u10da\u10d0", +"Paste row before": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d7\u10d0\u10d5\u10e8\u10d8 \u10e9\u10d0\u10e1\u10db\u10d0", +"Scope": "\u10e9\u10d0\u10e0\u10e9\u10dd", +"Delete table": "\u10ea\u10ee\u10e0\u10d8\u10da\u10d8\u10e1 \u10ec\u10d0\u10e8\u10da\u10d0", +"H Align": "H \u10e9\u10d0\u10db\u10ec\u10d9\u10e0\u10d8\u10d5\u10d4\u10d1\u10d0", +"Top": "\u10db\u10d0\u10e6\u10da\u10d0", +"Header cell": "\u10d7\u10d0\u10d5\u10d8\u10e1 \u10e3\u10ef\u10e0\u10d0", +"Column": "\u10e1\u10d5\u10d4\u10e2\u10d8", +"Row group": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10ef\u10d2\u10e3\u10e4\u10d8", +"Cell": "\u10e3\u10ef\u10e0\u10d0", +"Middle": "\u10e8\u10e3\u10d0", +"Cell type": "\u10e3\u10ef\u10e0\u10d8\u10e1 \u10e2\u10d8\u10de\u10d8", +"Copy row": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d9\u10dd\u10de\u10d8\u10e0\u10d4\u10d1\u10d0", +"Row properties": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d7\u10d5\u10d8\u10e1\u10d4\u10d1\u10d4\u10d1\u10d8", +"Table properties": "\u10ea\u10ee\u10e0\u10d8\u10da\u10d8\u10e1 \u10d7\u10d5\u10d8\u10e1\u10d4\u10d1\u10d4\u10d1\u10d8", +"Bottom": "\u10e5\u10d5\u10d4\u10d3\u10d0", +"V Align": "V \u10e9\u10d0\u10db\u10ec\u10d9\u10e0\u10d8\u10d5\u10d4\u10d1\u10d0", +"Header": "\u10d7\u10d0\u10d5\u10d8", +"Right": "\u10db\u10d0\u10e0\u10ef\u10d5\u10dc\u10d8\u10d5", +"Insert column after": "\u10e1\u10d5\u10d4\u10e2\u10d8\u10e1 \u10d1\u10dd\u10da\u10dd\u10e8\u10d8 \u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d0", +"Cols": "\u10e1\u10d5\u10d4\u10e2\u10d4\u10d1\u10d8", +"Insert row after": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d1\u10dd\u10da\u10dd\u10e8\u10d8 \u10d3\u10d0\u10db\u10d0\u10e2\u10d4\u10d1\u10d0", +"Width": "\u10e1\u10d8\u10d2\u10d0\u10dc\u10d4", +"Cell properties": "\u10e3\u10ef\u10e0\u10d8\u10e1 \u10d7\u10d5\u10d8\u10e1\u10d4\u10d1\u10d4\u10d1\u10d8", +"Left": "\u10db\u10d0\u10e0\u10ea\u10ee\u10dc\u10d8\u10d5", +"Cut row": "\u10e1\u10e2\u10e0\u10d8\u10e5\u10dd\u10dc\u10d8\u10e1 \u10d0\u10db\u10dd\u10ed\u10e0\u10d0", +"Delete column": "\u10e1\u10d5\u10d4\u10e2\u10d8\u10e1 \u10ec\u10d0\u10e8\u10da\u10d0", +"Center": "\u10ea\u10d4\u10dc\u10e2\u10e0\u10e8\u10d8", +"Merge cells": "\u10e3\u10ef\u10e0\u10d4\u10d1\u10d8\u10e1 \u10d2\u10d0\u10d4\u10e0\u10d7\u10d8\u10d0\u10dc\u10d4\u10d1\u10d0", +"Insert template": "\u10e8\u10d0\u10d1\u10da\u10dd\u10dc\u10d8\u10e1 \u10e9\u10d0\u10e1\u10db\u10d0", +"Templates": "\u10e8\u10d0\u10d1\u10da\u10dd\u10dc\u10d4\u10d1\u10d8", +"Background color": "\u10e3\u10d9\u10d0\u10dc\u10d0 \u10e4\u10d4\u10e0\u10d8", +"Custom...": "\u10db\u10dd\u10e0\u10d2\u10d4\u10d1\u10e3\u10da\u10d8", +"Custom color": "\u10db\u10dd\u10e0\u10d2\u10d4\u10d1\u10e3\u10da\u10d8 \u10e4\u10d4\u10e0\u10d8", +"No color": "\u10e4\u10d4\u10e0\u10d8\u10e1 \u10d2\u10d0\u10e0\u10d4\u10e8\u10d4", +"Text color": "\u10e2\u10d4\u10e5\u10e1\u10e2\u10d8\u10e1 \u10e4\u10d4\u10e0\u10d8", +"Table of Contents": "\u10e1\u10d0\u10e0\u10e9\u10d4\u10d5\u10d8", +"Show blocks": "\u10d1\u10da\u10dd\u10d9\u10d4\u10d1\u10d8\u10e1 \u10e9\u10d5\u10d4\u10dc\u10d4\u10d1\u10d0", +"Show invisible characters": "\u10e3\u10ee\u10d8\u10da\u10d0\u10d5\u10d8 \u10e1\u10d8\u10db\u10d1\u10dd\u10da\u10dd\u10d4\u10d1\u10d8\u10e1 \u10e9\u10d5\u10d4\u10dc\u10d4\u10d1\u10d0", +"Words: {0}": "\u10e1\u10d8\u10e2\u10e7\u10d5\u10d4\u10d1\u10d8: {0}", +"Insert": "\u10e9\u10d0\u10e1\u10db\u10d0", +"File": "\u10e4\u10d0\u10d8\u10da\u10d8", +"Edit": "\u10e8\u10d4\u10e1\u10ec\u10dd\u10e0\u10d4\u10d1\u10d0", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u10e2\u10d4\u10e5\u10e1\u10e2\u10d8\u10e1 \u10e4\u10d0\u10e0\u10d7\u10d8. \u10d3\u10d0\u10d0\u10ed\u10d8\u10e0\u10d4\u10d7 ALT-F9\u10e1 \u10db\u10d4\u10dc\u10d8\u10e3\u10e1 \u10d2\u10d0\u10db\u10dd\u10e1\u10d0\u10eb\u10d0\u10ee\u10d4\u10d1\u10da\u10d0\u10d3. \u10d3\u10d0\u10d0\u10ed\u10d8\u10e0\u10d4\u10d7 ALT-F10\u10e1 \u10de\u10d0\u10dc\u10d4\u10da\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1. \u10d3\u10d0\u10d0\u10ed\u10d8\u10e0\u10d4\u10d7 ALT-0\u10e1 \u10d3\u10d0\u10ee\u10db\u10d0\u10e0\u10d4\u10d1\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1", +"Tools": "\u10d8\u10d0\u10e0\u10d0\u10e6\u10d4\u10d1\u10d8", +"View": "\u10dc\u10d0\u10ee\u10d5\u10d0", +"Table": "\u10ea\u10ee\u10e0\u10d8\u10da\u10d8", +"Format": "\u10e4\u10dd\u10e0\u10db\u10d0\u10e2\u10d8" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kab.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kab.js new file mode 100644 index 0000000000..b9f9bccf40 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kab.js @@ -0,0 +1,261 @@ +tinymce.addI18n('kab',{ +"Redo": "Err-d", +"Undo": "Semmet", +"Cut": "Gzem", +"Copy": "N\u0263el", +"Paste": "Sente\u1e0d", +"Select all": "Fren kulec", +"New document": "Attaftar amaynut", +"Ok": "Ih", +"Cancel": "Semmet", +"Visual aids": "Visual aids", +"Bold": "Tira tazurant", +"Italic": "Tira yeknan", +"Underline": "Aderrer", +"Strikethrough": "Strikethrough", +"Superscript": "Superscript", +"Subscript": "Subscript", +"Clear formatting": "Clear formatting", +"Align left": "Tarigla \u0263er zelma\u1e0d", +"Align center": "Di tlemast", +"Align right": "tarigla \u0263er zelma\u1e0d", +"Justify": "Justify", +"Bullet list": "Tabdart s tlillac", +"Numbered list": "Tabdart s wu\u1e6d\u1e6dunen", +"Decrease indent": "Simc\u1e6du\u1e25 asi\u1e93i", +"Increase indent": "Sim\u0263ur asi\u1e93i", +"Close": "Mdel", +"Formats": "Imasalen", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.", +"Headers": "Izwal", +"Header 1": "Azwel 1", +"Header 2": "Azwel 2", +"Header 3": "Azwel 3", +"Header 4": "Azwel 4", +"Header 5": "Header 5", +"Header 6": "Azwel 6", +"Headings": "Izewlen", +"Heading 1": "Inixf 1", +"Heading 2": "Inixf 2", +"Heading 3": "Inixf 3", +"Heading 4": "Inixf 4", +"Heading 5": "Inixf 5", +"Heading 6": "Inixf 6", +"Preformatted": "Yettwamsel si tazwara", +"Div": "Div", +"Pre": "Pre", +"Code": "Tangalt", +"Paragraph": "taseddart", +"Blockquote": "Tanebdurt", +"Inline": "Inline", +"Blocks": "I\u1e25edran", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.", +"Font Family": "Tasefsit", +"Font Sizes": "Tiddi n tsefsit", +"Class": "Asmil", +"Browse for an image": "Snirem iwakken ad tferne\u1e0d tugna", +"OR": "Ih", +"Drop an image here": "Ssers tugna dagi", +"Upload": "Sili", +"Block": "Sew\u1e25el", +"Align": "Settef", +"Default": "Lex\u1e63as", +"Circle": "Tawinest", +"Disc": "A\u1e0debsi", +"Square": "Amku\u1e93", +"Lower Alpha": "Alpha ame\u1e93yan", +"Lower Greek": "Grik ame\u1e93yan", +"Lower Roman": "Ruman amectu\u1e25", +"Upper Alpha": "Alfa ameqran", +"Upper Roman": "Ruman ameqran", +"Anchor": "Tamdeyt", +"Name": "Isem", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "id ilaq ad ibdu s usekkil, ad yettwa\u1e0dfer kan s isekkilen, im\u1e0danen, ijerri\u1e0den, tinqi\u1e0din, snat n tenqi\u1e0din ne\u0263 ijerri\u1e0den n wadda.", +"You have unsaved changes are you sure you want to navigate away?": "Ibeddilen ur twaskelsen ara teb\u0263i\u1e0d ad teff\u0263e\u1e0d ?", +"Restore last draft": "Restore last draft", +"Special character": "Askil uslig", +"Source code": "Tangalt ta\u0263balut", +"Insert\/Edit code sample": "Ger\/\u1e92reg tangalt n umedya", +"Language": "Tutlayt", +"Code sample": "Tikkest n tengalt", +"Color": "Ini", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Seg zelma\u1e0d \u0263er yefus", +"Right to left": "Seg yefus \u0263er zelma\u1e0d", +"Emoticons": "Emoticons", +"Document properties": "Iraten n warat", +"Title": "Azwel", +"Keywords": "Awalen yufraren", +"Description": "Aglam", +"Robots": "Robots", +"Author": "Ameskar", +"Encoding": "Asettengel", +"Fullscreen": "Agdil a\u010duran", +"Action": "Tigawt", +"Shortcut": "Anegzum", +"Help": "Tallalt", +"Address": "Tansa", +"Focus to menubar": "Asa\u1e0des \u0263ef tfeggagt n wumu\u0263", +"Focus to toolbar": "Asa\u1e0des \u0263ef tfeggagt n ifecka", +"Focus to element path": "Asa\u1e0des \u0263ef ubrid n uferdis", +"Focus to contextual toolbar": "Asa\u1e0des \u0263ef tfeggagt n ifecka tanattalt", +"Insert link (if link plugin activated)": "Ger ase\u0263wen (ma yermed uzegrir n use\u0263wen)", +"Save (if save plugin activated)": "Sekles (ma yermed uzegrir save)", +"Find (if searchreplace plugin activated)": "Nadi (ma yermed uzegrir searchreplace)", +"Plugins installed ({0}):": "Izegriren yettwasbedden ({0}):", +"Premium plugins:": "Izegriren premium :", +"Learn more...": "\u1e92er ugar...", +"You are using {0}": "Tsseqdace\u1e0d {0}", +"Plugins": "Isi\u0263zifen", +"Handy Shortcuts": "Inegzumen", +"Horizontal line": "Ajerri\u1e0d aglawan", +"Insert\/edit image": "Ger\/\u1e92reg tugna", +"Image description": "Aglam n tugna", +"Source": "A\u0263balu", +"Dimensions": "Tisekta", +"Constrain proportions": "Constrain proportions", +"General": "Amatu", +"Advanced": "Ana\u1e93i", +"Style": "A\u0263anib", +"Vertical space": "Talunt taratakt", +"Horizontal space": "Talunt taglawant", +"Border": "Iri", +"Insert image": "Ger tugna", +"Image": "Tugna", +"Image list": "Tabdart n tugniwin", +"Rotate counterclockwise": "Tuzya mgal tamrilt", +"Rotate clockwise": "Tuzya yugdan tamrilt", +"Flip vertically": "Tuzya taratakt", +"Flip horizontally": "Tuzttya tagrawant", +"Edit image": "\u1e92reg tugna", +"Image options": "Tixti\u1e5biyin n tugna", +"Zoom in": "Zoom in", +"Zoom out": "Zoom out", +"Crop": "Rogner", +"Resize": "Beddel tiddi", +"Orientation": "Ta\u0263da", +"Brightness": "Tafat", +"Sharpen": "Affiner", +"Contrast": "Contrast", +"Color levels": "Iswiren n yini", +"Gamma": "Gamma", +"Invert": "Tti", +"Apply": "Snes", +"Back": "Tu\u0263alin", +"Insert date\/time": "Ger azemz\/asrag", +"Date\/time": "Azemz\/Asrag", +"Insert link": "Ger azday", +"Insert\/edit link": "Ger\/\u1e93reg azday", +"Text to display": "A\u1e0dris ara yettwabeqq\u1e0den", +"Url": "Url", +"Target": "Target", +"None": "Ulac", +"New window": "Asfaylu amaynut", +"Remove link": "Kkes azday", +"Anchors": "Timdyin", +"Link": "Ase\u0263wen", +"Paste or type a link": "Sente\u1e0d ne\u0263 sekcem ase\u0263wen", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL i teskecme\u1e0d tettban-d d tansa email. teb\u0263i\u1e0d ad s-ternu\u1e0d azwir mailto : ?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL i teskecme\u1e0d tettban-d d azday uffi\u0263. Teb\u0263i\u1e0d ad s-ternu\u1e0d azwir http:\/\/ ?", +"Link list": "Tabdart n is\u0263ewnen", +"Insert video": "Ger avidyu", +"Insert\/edit video": "Ger\/\u1e93reg avidyu", +"Insert\/edit media": "Ger\/\u1e92reg amiya", +"Alternative source": "A\u0263balu amlellay", +"Poster": "Poster", +"Paste your embed code below:": "Paste your embed code below:", +"Embed": "Embed", +"Media": "Amidya", +"Nonbreaking space": "Talunt ur nettwagzam ara", +"Page break": "Angaz n usebter", +"Paste as text": "Sente\u1e0d d a\u1e0dris", +"Preview": "Sken", +"Print": "Siggez", +"Save": "Sekles", +"Find": "Nadi", +"Replace with": "Semselsi s", +"Replace": "Semselsi", +"Replace all": "Semselsi kulec", +"Prev": "Win yezrin", +"Next": "Win \u0263ers", +"Find and replace": "Nadi semselsi", +"Could not find the specified string.": "Ur d-nufi ara azrar i d-yettunefken.", +"Match case": "Match case", +"Whole words": "Awal ummid", +"Spellcheck": "Ase\u0263ti n tira", +"Ignore": "Zgel", +"Ignore all": "Zgel kulec", +"Finish": "Fak", +"Add to Dictionary": "Rnu-t s amawal", +"Insert table": "Ger tafelwit", +"Table properties": "Iraten n tfelwit", +"Delete table": "Kkes tafelwit", +"Cell": "Taxxamt", +"Row": "Adur", +"Column": "Tagejdit", +"Cell properties": "Iraten n texxamt", +"Merge cells": "Seddukel tixxamin", +"Split cell": "B\u1e0du tixxamin", +"Insert row before": "Ger adur deffir", +"Insert row after": "Ger adur sdat", +"Delete row": "Kkes tagejdit", +"Row properties": "Iraten n udur", +"Cut row": "Gzem adur", +"Copy row": "N\u0263el adur", +"Paste row before": "Sente\u1e0d adur sdat", +"Paste row after": "Sente\u1e0d adur deffir", +"Insert column before": "Sente\u1e0d tagejdit sdat", +"Insert column after": "Sente\u1e0d tagejdit deffir", +"Delete column": "Kkes tagejdit", +"Cols": "Tigejda", +"Rows": "Aduren", +"Width": "Tehri", +"Height": "Te\u0263zi", +"Cell spacing": "Tlunt ger texxamin", +"Cell padding": "Tama n texxamt", +"Caption": "Caption", +"Left": "\u0194er zelma\u1e0d", +"Center": "Di tlemmast", +"Right": "\u0194er yefus", +"Cell type": "Anaw n texxamt", +"Scope": "Scope", +"Alignment": "Tarigla", +"H Align": "Tarigla taglawant", +"V Align": "Tarigla taratakt", +"Top": "Uksawen", +"Middle": "Di tlemmast", +"Bottom": "Uksar", +"Header cell": "Tasen\u1e6di\u1e0dt n texxamt", +"Row group": "Agraw n waduren", +"Column group": "Agraw n tgejda", +"Row type": "Anaw n wadur", +"Header": "Tasenti\u1e0dt", +"Body": "Tafka", +"Footer": "A\u1e0dar", +"Border color": "Ini n yiri", +"Insert template": "Ger tamuddimt", +"Templates": "Timudimin", +"Template": "Tine\u0263rufin", +"Text color": "Ini n u\u1e0dris", +"Background color": "Ini n ugilal", +"Custom...": "Custom...", +"Custom color": "Custom color", +"No color": "Ulac ini", +"Table of Contents": "Tafelwit n ugbur", +"Show blocks": "Beqqe\u1e0d i\u1e25edran", +"Show invisible characters": "Beqqe\u1e0d isekkilen uffiren", +"Words: {0}": "Words: {0}", +"{0} words": "{0} n wawalen", +"File": "Afaylu", +"Edit": "\u1e92reg", +"Insert": "Ger", +"View": "Tamu\u0263li", +"Format": "Amasal", +"Table": "Tafelwit", +"Tools": "Ifecka", +"Powered by {0}": "Iteddu s {0} ", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kk.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kk.js new file mode 100644 index 0000000000..7cec8ab1d3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kk.js @@ -0,0 +1,230 @@ +tinymce.addI18n('kk',{ +"Cut": "\u049a\u0438\u044b\u043f \u0430\u043b\u0443", +"Heading 5": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 5", +"Header 2": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0411\u0440\u0430\u0443\u0437\u0435\u0440\u0456\u04a3\u0456\u0437 \u0430\u043b\u043c\u0430\u0441\u0443 \u0431\u0443\u0444\u0435\u0440\u0456\u043d\u0435 \u0442\u0456\u043a\u0435\u043b\u0435\u0439 \u049b\u0430\u0442\u044b\u043d\u0430\u0439 \u0430\u043b\u043c\u0430\u0439\u0434\u044b. Ctrl+X\/C\/V \u043f\u0435\u0440\u043d\u0435\u043b\u0435\u0440 \u0442\u0456\u0440\u043a\u0435\u0441\u0456\u043c\u0456\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u044b\u04a3\u044b\u0437.", +"Heading 4": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 4", +"Div": "Div", +"Heading 2": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 2", +"Paste": "\u049a\u043e\u044e", +"Close": "\u0416\u0430\u0431\u0443", +"Font Family": "\u049a\u0430\u0440\u0456\u043f\u0442\u0435\u0440 \u0442\u043e\u0431\u044b", +"Pre": "Pre", +"Align right": "\u041e\u04a3\u0493\u0430 \u043e\u0440\u043d\u0430\u043b\u0430\u0441\u0442\u044b\u0440\u0443", +"New document": "\u0416\u0430\u04a3\u0430 \u049b\u04b1\u0436\u0430\u0442", +"Blockquote": "\u0414\u04d9\u0439\u0435\u043a\u0441\u04e9\u0437", +"Numbered list": "\u041d\u04e9\u043c\u0456\u0440\u043b\u0435\u043d\u0433\u0435\u043d \u0442\u0456\u0437\u0456\u043c", +"Heading 1": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 1", +"Headings": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f", +"Increase indent": "\u0428\u0435\u0433\u0456\u043d\u0456\u0441\u0442\u0456 \u0430\u0440\u0442\u0442\u044b\u0440\u0443", +"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0430\u0440", +"Headers": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430", +"Select all": "\u0411\u0430\u0440\u043b\u044b\u0493\u044b\u043d \u0442\u0430\u04a3\u0434\u0430\u0443", +"Header 3": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 3", +"Blocks": "\u0411\u043b\u043e\u043a\u0442\u0435\u043a\u0442\u0435\u0441 (Block)", +"Undo": "\u0411\u043e\u043b\u0434\u044b\u0440\u043c\u0430\u0443", +"Strikethrough": "\u0411\u0435\u043b\u0456\u043d\u0435\u043d \u0441\u044b\u0437\u044b\u043b\u0493\u0430\u043d", +"Bullet list": "\u0422\u0430\u04a3\u0431\u0430\u043b\u0430\u043d\u0493\u0430\u043d \u0442\u0456\u0437\u0456\u043c", +"Header 1": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 1", +"Superscript": "\u04ae\u0441\u0442\u0456\u04a3\u0433\u0456 \u0438\u043d\u0434\u0435\u043a\u0441", +"Clear formatting": "\u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0430\u0443\u0434\u0430\u043d \u0442\u0430\u0437\u0430\u0440\u0442\u0443", +"Font Sizes": "\u049a\u0430\u0440\u0456\u043f\u0442\u0435\u0440 \u04e9\u043b\u0448\u0435\u043c\u0456", +"Subscript": "\u0410\u0441\u0442\u044b\u04a3\u0493\u044b \u0438\u043d\u0434\u0435\u043a\u0441", +"Header 6": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 6", +"Redo": "\u049a\u0430\u0439\u0442\u0430\u0440\u0443", +"Paragraph": "\u0410\u0431\u0437\u0430\u0446", +"Ok": "\u041e\u041a", +"Bold": "\u0416\u0443\u0430\u043d", +"Code": "\u041a\u043e\u0434", +"Italic": "\u041a\u04e9\u043b\u0431\u0435\u0443", +"Align center": "\u041e\u0440\u0442\u0430\u0441\u044b\u043d\u0430 \u043e\u0440\u043d\u0430\u043b\u0430\u0441\u0442\u044b\u0440\u0443", +"Header 5": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 5", +"Heading 6": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 6", +"Heading 3": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f 3", +"Decrease indent": "\u0428\u0435\u0433\u0456\u043d\u0456\u0441\u0442\u0456 \u043a\u0435\u043c\u0456\u0442\u0443", +"Header 4": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u041e\u0441\u044b \u043e\u043f\u0446\u0438\u044f \u04e9\u0448\u0456\u0440\u0456\u043b\u043c\u0435\u0433\u0435\u043d\u0448\u0435, \u0431\u0443\u0444\u0435\u0440\u0434\u0435\u0433\u0456 \u043c\u04d9\u0442\u0456\u043d \u043a\u04d9\u0434\u0456\u043c\u0433\u0456 \u043c\u04d9\u0442\u0456\u043d \u0440\u0435\u0442\u0456\u043d\u0434\u0435 \u049b\u043e\u0439\u044b\u043b\u0430\u0434\u044b.", +"Underline": "\u0410\u0441\u0442\u044b \u0441\u044b\u0437\u044b\u043b\u0493\u0430\u043d", +"Cancel": "\u0411\u0430\u0441 \u0442\u0430\u0440\u0442\u0443", +"Justify": "\u0422\u043e\u043b\u0442\u044b\u0440\u0443", +"Inline": "\u041a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0456\u043b\u0433\u0435\u043d (Inline)", +"Copy": "\u041a\u04e9\u0448\u0456\u0440\u0443", +"Align left": "\u0421\u043e\u043b\u0493\u0430 \u043e\u0440\u043d\u0430\u043b\u0430\u0441\u0442\u044b\u0440\u0443", +"Visual aids": "\u041a\u04e9\u043c\u0435\u043a\u0448\u0456 \u0431\u0435\u043b\u0433\u0456\u043b\u0435\u0440", +"Lower Greek": "\u041a\u0456\u0448\u0456 \u0433\u0440\u0435\u043a \u04d9\u0440\u0456\u043f\u0442\u0435\u0440\u0456", +"Square": "\u0428\u0430\u0440\u0448\u044b", +"Default": "\u04d8\u0434\u0435\u043f\u043a\u0456", +"Lower Alpha": "\u041a\u0456\u0448\u0456 \u04d9\u0440\u0456\u043f\u0442\u0435\u0440", +"Circle": "\u0428\u0435\u04a3\u0431\u0435\u0440", +"Disc": "\u0414\u0438\u0441\u043a", +"Upper Alpha": "\u0411\u0430\u0441 \u04d9\u0440\u0456\u043f\u0442\u0435\u0440", +"Upper Roman": "\u0411\u0430\u0441 \u0440\u0438\u043c \u0446\u0438\u0444\u0440\u043b\u0430\u0440\u044b", +"Lower Roman": "\u041a\u0456\u0448\u0456 \u0440\u0438\u043c \u0446\u0438\u0444\u0440\u043b\u0430\u0440\u044b", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u0442\u0435\u043a \u049b\u0430\u043d\u0430 \u04d9\u0440\u0456\u043f\u0442\u0435\u043d \u0431\u0430\u0441\u0442\u0430\u043b\u044b\u043f, \u04d9\u0440\u0456\u043f\u0442\u0435\u0440, \u0441\u0430\u043d\u0434\u0430\u0440, \u0441\u044b\u0437\u044b\u049b\u0448\u0430\u043b\u0430\u0440, \u043d\u04af\u043a\u0442\u0435\u043b\u0435\u0440 \u0436\u04d9\u043d\u0435 \u0442.\u0431 \u0436\u0430\u043b\u0493\u0430\u0441\u0443\u044b \u0442\u0438\u0456\u0441.", +"Name": "\u0410\u0442\u044b", +"Anchor": "\u0411\u0435\u0442\u0431\u0435\u043b\u0433\u0456", +"Id": "Id", +"You have unsaved changes are you sure you want to navigate away?": "\u0421\u0430\u049b\u0442\u0430\u043b\u043c\u0430\u0493\u0430\u043d \u04e9\u0437\u0433\u0435\u0440\u0456\u0441\u0442\u0435\u0440 \u0431\u0430\u0440. \u0421\u0456\u0437 \u0448\u044b\u043d\u044b\u043c\u0435\u043d \u0431\u0430\u0441\u049b\u0430 \u0436\u0435\u0440\u0433\u0435 \u043a\u0435\u0442\u0443\u0434\u0456 \u049b\u0430\u043b\u0430\u0439\u0441\u044b\u0437 \u0431\u0430?", +"Restore last draft": "\u0421\u043e\u04a3\u0493\u044b \u0441\u0430\u049b\u0442\u0430\u043b\u0493\u0430\u043d\u0434\u044b \u049b\u0430\u043b\u043f\u044b\u043d\u0430 \u043a\u0435\u043b\u0442\u0456\u0440\u0443", +"Special character": "\u0410\u0440\u043d\u0430\u0439\u044b \u0442\u0430\u04a3\u0431\u0430", +"Source code": "\u0411\u0430\u0441\u0442\u0430\u043f\u049b\u044b \u043a\u043e\u0434", +"Language": "\u0422\u0456\u043b", +"Insert\/Edit code sample": "\u041a\u043e\u0434 \u04af\u043b\u0433\u0456\u0441\u0456\u043d \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443\/\u0442\u04af\u0437\u0435\u0442\u0443", +"B": "B", +"R": "R", +"G": "G", +"Color": "\u0422\u04af\u0441", +"Right to left": "\u041e\u04a3\u043d\u0430\u043d \u0441\u043e\u043b\u0493\u0430", +"Left to right": "\u0421\u043e\u043b\u0434\u0430\u043d \u043e\u04a3\u0493\u0430", +"Emoticons": "\u0421\u043c\u0430\u0439\u043b\u0438\u043a\u0442\u0430\u0440", +"Robots": "Meta-robots", +"Document properties": "\u049a\u04b1\u0436\u0430\u0442 \u0441\u0438\u043f\u0430\u0442\u0442\u0430\u0440\u044b", +"Title": "\u0410\u0442\u0430\u0443\u044b", +"Keywords": "Meta-keywords", +"Encoding": "Meta-charset", +"Description": "\u0421\u0438\u043f\u0430\u0442\u0442\u0430\u043c\u0430\u0441\u044b", +"Author": "Meta-author", +"Fullscreen": "\u0422\u043e\u043b\u044b\u049b \u044d\u043a\u0440\u0430\u043d", +"Horizontal line": "\u041a\u04e9\u043b\u0434\u0435\u043d\u0435\u04a3 \u0441\u044b\u0437\u044b\u049b", +"Horizontal space": "\u041a\u04e9\u043b\u0434\u0435\u043d\u0435\u04a3\u0456\u043d\u0435\u043d \u049b\u0430\u043b\u0430\u0442\u044b\u043d \u043e\u0440\u044b\u043d", +"Insert\/edit image": "\u0421\u0443\u0440\u0435\u0442 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443\/\u0442\u04af\u0437\u0435\u0442\u0443", +"General": "\u0416\u0430\u043b\u043f\u044b", +"Advanced": "\u049a\u043e\u0441\u044b\u043c\u0448\u0430", +"Source": "\u0410\u0434\u0440\u0435\u0441\u0456", +"Border": "\u0416\u0438\u0435\u0433\u0456", +"Constrain proportions": "\u041f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u044f\u043b\u0430\u0440\u0434\u044b \u0441\u0430\u049b\u0442\u0430\u0443", +"Vertical space": "\u0422\u0456\u043a \u043a\u0435\u04a3\u0434\u0456\u0433\u0456", +"Image description": "\u0421\u0443\u0440\u0435\u0442 \u0441\u0438\u043f\u0430\u0442\u0442\u0430\u043c\u0430\u0441\u044b", +"Style": "\u0421\u0442\u0438\u043b\u0456", +"Dimensions": "\u04e8\u043b\u0448\u0435\u043c\u0434\u0435\u0440\u0456", +"Insert image": "\u0421\u0443\u0440\u0435\u0442 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Image": "\u0421\u0443\u0440\u0435\u0442", +"Zoom in": "\u0416\u0430\u049b\u044b\u043d\u0434\u0430\u0442\u0443", +"Contrast": "\u049a\u043e\u044e\u043b\u0430\u0442\u0443", +"Back": "\u0410\u0440\u0442\u049b\u0430", +"Gamma": "\u0413\u0430\u043c\u043c\u0430", +"Flip horizontally": "\u041a\u04e9\u043b\u0434\u0435\u043d\u0435\u04a3\u043d\u0435\u043d \u0430\u0443\u0434\u0430\u0440\u0443", +"Resize": "\u04e8\u043b\u0448\u0435\u043c\u0456\u043d \u04e9\u0437\u0433\u0435\u0440\u0442\u0443", +"Sharpen": "\u041d\u0430\u049b\u0442\u044b\u043b\u0430\u0443", +"Zoom out": "\u0410\u043b\u044b\u0441\u0442\u0430\u0442\u0443", +"Image options": "\u0421\u0443\u0440\u0435\u0442 \u0431\u0430\u043f\u0442\u0430\u0443\u043b\u0430\u0440\u044b", +"Apply": "\u0421\u0430\u049b\u0442\u0430\u0443", +"Brightness": "\u0410\u0448\u044b\u049b\u0442\u0430\u0443", +"Rotate clockwise": "\u0421\u0430\u0493\u0430\u0442 \u0442\u0456\u043b\u0456\u043d\u0456\u04a3 \u0431\u0430\u0493\u044b\u0442\u044b\u043c\u0435\u043d \u0431\u04b1\u0440\u0443", +"Rotate counterclockwise": "\u0421\u0430\u0493\u0430\u0442 \u0442\u0456\u043b\u0456\u043d\u0456\u04a3 \u0431\u0430\u0493\u044b\u0442\u044b\u043d\u0430 \u049b\u0430\u0440\u0441\u044b \u0431\u04b1\u0440\u0443", +"Edit image": "\u0421\u0443\u0440\u0435\u0442\u0442\u0456 \u04e9\u0437\u0433\u0435\u0440\u0442\u0443", +"Color levels": "\u0422\u04af\u0441 \u0434\u0435\u04a3\u0433\u0435\u0439\u043b\u0435\u0440\u0456", +"Crop": "\u041a\u0435\u0441\u0443", +"Orientation": "\u0411\u0430\u0493\u0434\u0430\u0440", +"Flip vertically": "\u0422\u0456\u0433\u0456\u043d\u0435\u043d \u0430\u0443\u0434\u0430\u0440\u0443", +"Invert": "\u041a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Date\/time": "\u041a\u04af\u043d\/\u0443\u0430\u049b\u044b\u0442", +"Insert date\/time": "\u041a\u04af\u043d\/\u0443\u0430\u049b\u044b\u0442 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Remove link": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435\u043d\u0456 \u0430\u043b\u044b\u043f \u0442\u0430\u0441\u0442\u0430\u0443", +"Url": "URL-\u0430\u0434\u0440\u0435\u0441\u0456", +"Text to display": "\u041a\u04e9\u0440\u0441\u0435\u0442\u0456\u043b\u0435\u0442\u0456\u043d \u043c\u04d9\u0442\u0456\u043d", +"Anchors": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435\u043b\u0435\u0440", +"Insert link": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Link": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435", +"New window": "\u0416\u0430\u04a3\u0430 \u0442\u0435\u0440\u0435\u0437\u0435", +"None": "\u0416\u043e\u049b", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0421\u0456\u0437 \u0435\u04a3\u0433\u0456\u0437\u0456\u043f \u0442\u04b1\u0440\u0493\u0430\u043d URL \u0441\u044b\u0440\u0442\u049b\u044b \u0441\u0456\u043b\u0442\u0435\u043c\u0435 \u0431\u043e\u043b\u044b\u043f \u0442\u0430\u0431\u044b\u043b\u0430\u0434\u044b. \u0410\u043b\u0434\u044b\u043d\u0430 http:\/\/ \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0456\u043d \u049b\u043e\u0441\u0443\u0434\u044b \u049b\u0430\u043b\u0430\u0439\u0441\u044b\u0437 \u0431\u0430?", +"Paste or type a link": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435\u043d\u0456 \u049b\u043e\u0439\u044b\u04a3\u044b\u0437 \u043d\u0435\u043c\u0435\u0441\u0435 \u0442\u0435\u0440\u0456\u04a3\u0456\u0437", +"Target": "\u0410\u0448\u044b\u043b\u0430\u0442\u044b\u043d \u0436\u0435\u0440\u0456", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0421\u0456\u0437 \u0435\u04a3\u0433\u0456\u0437\u0456\u043f \u0442\u04b1\u0440\u0493\u0430\u043d URL e-mail \u0430\u0434\u0440\u0435\u0441\u0456 \u0431\u043e\u043b\u044b\u043f \u0442\u0430\u0431\u044b\u043b\u0430\u0434\u044b. \u0410\u043b\u0434\u044b\u043d\u0430 mailto: \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0456\u043d \u049b\u043e\u0441\u0443\u0434\u044b \u049b\u0430\u043b\u0430\u0439\u0441\u044b\u0437 \u0431\u0430?", +"Insert\/edit link": "\u0421\u0456\u043b\u0442\u0435\u043c\u0435 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443\/\u0442\u04af\u0437\u0435\u0442\u0443", +"Insert\/edit video": "\u0412\u0438\u0434\u0435\u043e \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443\/\u0442\u04af\u0437\u0435\u0442\u0443", +"Media": "\u041c\u0435\u0434\u0438\u0430", +"Alternative source": "\u049a\u043e\u0441\u044b\u043c\u0448\u0430 \u0430\u0434\u0440\u0435\u0441\u0456", +"Paste your embed code below:": "\u0422\u04e9\u043c\u0435\u043d\u0434\u0435\u0433\u0456 \u043a\u043e\u0434\u0442\u044b \u043a\u04e9\u0448\u0456\u0440\u0456\u043f \u0430\u043b\u044b\u043f, \u049b\u043e\u0439\u044b\u04a3\u044b\u0437:", +"Insert video": "\u0412\u0438\u0434\u0435\u043e \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Poster": "\u041f\u043e\u0441\u0442\u0435\u0440\u0456", +"Insert\/edit media": "\u041c\u0435\u0434\u0438\u0430 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443\/\u0442\u04af\u0437\u0435\u0442\u0443", +"Embed": "\u0415\u043d\u0434\u0456\u0440\u0443", +"Nonbreaking space": "\u04ae\u0437\u0434\u0456\u043a\u0441\u0456\u0437 \u0431\u043e\u0441 \u043e\u0440\u044b\u043d", +"Page break": "\u0411\u0435\u0442 \u04af\u0437\u0456\u043b\u0456\u043c\u0456", +"Paste as text": "\u041c\u04d9\u0442\u0456\u043d \u0440\u0435\u0442\u0456\u043d\u0434\u0435 \u049b\u043e\u044e", +"Preview": "\u0410\u043b\u0434\u044b\u043d-\u0430\u043b\u0430 \u049b\u0430\u0440\u0430\u0443", +"Print": "\u0411\u0430\u0441\u044b\u043f \u0448\u044b\u0493\u0430\u0440\u0443", +"Save": "\u0421\u0430\u049b\u0442\u0430\u0443", +"Could not find the specified string.": "\u041a\u04e9\u0440\u0441\u0435\u0442\u0456\u043b\u0433\u0435\u043d \u0436\u043e\u043b \u0442\u0430\u0431\u044b\u043b\u043c\u0430\u0434\u044b.", +"Replace": "\u0410\u0443\u044b\u0441\u0442\u044b\u0440\u0443", +"Next": "\u041a\u0435\u043b\u0435\u0441\u0456", +"Whole words": "\u0422\u04b1\u0442\u0430\u0441 \u0441\u04e9\u0437\u0434\u0435\u0440", +"Find and replace": "\u0422\u0430\u0431\u0443 \u0436\u04d9\u043d\u0435 \u0430\u0443\u044b\u0441\u0442\u044b\u0440\u0443", +"Replace with": "\u0410\u0443\u044b\u0441\u0442\u044b\u0440\u0430\u0442\u044b\u043d \u043c\u04d9\u0442\u0456\u043d", +"Find": "\u0422\u0430\u0431\u044b\u043b\u0430\u0442\u044b\u043d \u043c\u04d9\u0442\u0456\u043d", +"Replace all": "\u0411\u0430\u0440\u043b\u044b\u0493\u044b\u043d \u0430\u0443\u044b\u0441\u0442\u044b\u0440\u0443", +"Match case": "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0434\u0456 \u0435\u0441\u043a\u0435\u0440\u0443", +"Prev": "\u0410\u043b\u0434\u044b\u04a3\u0493\u044b", +"Spellcheck": "\u0415\u043c\u043b\u0435 \u0442\u0435\u043a\u0441\u0435\u0440\u0443", +"Finish": "\u0410\u044f\u049b\u0442\u0430\u0443", +"Ignore all": "\u0415\u0448\u049b\u0430\u0439\u0441\u044b\u0441\u044b\u043d \u0435\u043b\u0435\u043c\u0435\u0443", +"Ignore": "\u0415\u043b\u0435\u043c\u0435\u0443", +"Add to Dictionary": "\u0421\u04e9\u0437\u0434\u0456\u043a\u043a\u0435 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Insert row before": "\u04ae\u0441\u0442\u0456\u043d\u0435 \u0436\u043e\u043b \u049b\u043e\u0441\u0443", +"Rows": "\u0416\u043e\u043b\u044b", +"Height": "\u0411\u0438\u0456\u043a\u0442\u0456\u0433\u0456", +"Paste row after": "\u0416\u043e\u043b\u0434\u044b\u04a3 \u0430\u0441\u0442\u044b\u043d\u0430 \u049b\u043e\u044e", +"Alignment": "\u041e\u0440\u043d\u0430\u043b\u0430\u0441\u0443\u044b", +"Border color": "\u0416\u0438\u0435\u043a \u0442\u04af\u0441\u0456", +"Column group": "\u0411\u0430\u0493\u0430\u043d \u0442\u043e\u0431\u044b", +"Row": "\u0416\u043e\u043b", +"Insert column before": "\u0410\u043b\u0434\u044b\u043d\u0430 \u0431\u0430\u0493\u0430\u043d \u049b\u043e\u0441\u0443", +"Split cell": "\u04b0\u044f\u0448\u044b\u049b\u0442\u044b \u0431\u04e9\u043b\u0443", +"Cell padding": "\u04b0\u044f\u0448\u044b\u049b \u043a\u0435\u04a3\u0434\u0456\u0433\u0456", +"Cell spacing": "\u04b0\u044f\u0448\u044b\u049b \u0430\u0440\u0430\u043b\u044b\u0493\u044b", +"Row type": "\u0416\u043e\u043b \u0442\u0438\u043f\u0456", +"Insert table": "\u041a\u0435\u0441\u0442\u0435 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Body": "\u041d\u0435\u0433\u0456\u0437\u0433\u0456 \u0431\u04e9\u043b\u0456\u0433\u0456", +"Caption": "\u0410\u0442\u0430\u0443\u044b", +"Footer": "\u0410\u044f\u049b \u0436\u0430\u0493\u044b", +"Delete row": "\u0416\u043e\u043b\u0434\u044b \u0436\u043e\u044e", +"Paste row before": "\u0416\u043e\u043b\u0434\u044b\u04a3 \u04af\u0441\u0442\u0456\u043d\u0435 \u049b\u043e\u044e", +"Scope": "\u0410\u0443\u043c\u0430\u0493\u044b", +"Delete table": "\u041a\u0435\u0441\u0442\u0435\u043d\u0456 \u0436\u043e\u044e", +"H Align": "\u041a\u04e9\u043b\u0434\u0435\u043d\u0435\u04a3\u043d\u0435\u043d \u0442\u0443\u0440\u0430\u043b\u0430\u0443", +"Top": "\u04ae\u0441\u0442\u0456", +"Header cell": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0448\u0430 \u04b1\u044f\u0448\u044b\u049b", +"Column": "\u0411\u0430\u0493\u0430\u043d", +"Row group": "\u0416\u043e\u043b \u0442\u043e\u0431\u044b", +"Cell": "\u04b0\u044f\u0448\u044b\u049b", +"Middle": "\u041e\u0440\u0442\u0430\u0441\u044b", +"Cell type": "\u04b0\u044f\u0448\u044b\u049b \u0442\u0438\u043f\u0456", +"Copy row": "\u0416\u043e\u043b\u0434\u044b \u043a\u04e9\u0448\u0456\u0440\u0443", +"Row properties": "\u0416\u043e\u043b \u0441\u0438\u043f\u0430\u0442\u0442\u0430\u0440\u044b", +"Table properties": "\u041a\u0435\u0441\u0442\u0435 \u0441\u0438\u043f\u0430\u0442\u0442\u0430\u0440\u044b", +"Bottom": "\u0410\u0441\u0442\u044b", +"V Align": "\u0422\u0456\u0433\u0456\u043d\u0435\u043d \u0442\u0443\u0440\u0430\u043b\u0430\u0443", +"Header": "\u0411\u0430\u0441 \u0436\u0430\u0493\u044b", +"Right": "\u041e\u04a3\u0493\u0430", +"Insert column after": "\u0410\u0440\u0442\u044b\u043d\u0430 \u0431\u0430\u0493\u0430\u043d \u049b\u043e\u0441\u0443", +"Cols": "\u0411\u0430\u0493\u0430\u043d\u044b", +"Insert row after": "\u0410\u0441\u0442\u044b\u043d\u0430 \u0436\u043e\u043b \u049b\u043e\u0441\u0443", +"Width": "\u04b0\u0437\u044b\u043d\u0434\u044b\u0493\u044b", +"Cell properties": "\u04b0\u044f\u0448\u044b\u049b \u0441\u0438\u043f\u0430\u0442\u0442\u0430\u0440\u044b", +"Left": "\u0421\u043e\u043b\u0493\u0430", +"Cut row": "\u0416\u043e\u043b\u0434\u044b \u049b\u0438\u044b\u043f \u0430\u043b\u0443", +"Delete column": "\u0411\u0430\u0493\u0430\u043d\u0434\u044b \u0436\u043e\u044e", +"Center": "\u041e\u0440\u0442\u0430\u0441\u044b\u043d\u0430", +"Merge cells": "\u04b0\u044f\u0448\u044b\u049b\u0442\u0430\u0440\u0434\u044b \u0431\u0456\u0440\u0456\u043a\u0442\u0456\u0440\u0443", +"Insert template": "\u04ae\u043b\u0433\u0456 \u043a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"Templates": "\u04ae\u043b\u0433\u0456\u043b\u0435\u0440", +"Background color": "\u04e8\u04a3\u0456\u043d\u0456\u04a3 \u0442\u04af\u0441\u0456", +"Custom...": "\u04e8\u0437\u0433\u0435\u0440\u0442\u0443", +"Custom color": "\u0422\u04af\u0441 \u04e9\u0437\u0433\u0435\u0440\u0442\u0443", +"No color": "\u0422\u04af\u0441\u0441\u0456\u0437", +"Text color": "\u041c\u04d9\u0442\u0456\u043d \u0442\u04af\u0441\u0456", +"Table of Contents": "\u041c\u0430\u0437\u043c\u04b1\u043d\u0434\u0430\u0440 \u043a\u0435\u0441\u0442\u0435\u0441\u0456", +"Show blocks": "\u0411\u043b\u043e\u043a\u0442\u0430\u0440\u0434\u044b \u043a\u04e9\u0440\u0441\u0435\u0442\u0443", +"Show invisible characters": "\u041a\u04e9\u0440\u0456\u043d\u0431\u0435\u0439\u0442\u0456\u043d \u0442\u0430\u04a3\u0431\u0430\u043b\u0430\u0440\u0434\u044b \u043a\u04e9\u0440\u0441\u0435\u0442\u0443", +"Words: {0}": "\u0421\u04e9\u0437 \u0441\u0430\u043d\u044b: {0}", +"Insert": "\u041a\u0456\u0440\u0456\u0441\u0442\u0456\u0440\u0443", +"File": "\u0424\u0430\u0439\u043b", +"Edit": "\u0422\u04af\u0437\u0435\u0442\u0443", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0430\u043b\u0493\u0430\u043d \u043c\u04d9\u0442\u0456\u043d \u0430\u0443\u043c\u0430\u0493\u044b. \u041c\u0435\u043d\u044e \u043a\u04e9\u0440\u0441\u0435\u0442\u0443 \u04af\u0448\u0456\u043d ALT-F9 \u0431\u0430\u0441\u044b\u04a3\u044b\u0437. \u049a\u04b1\u0440\u0430\u043b\u0434\u0430\u0440 \u043f\u0430\u043d\u0435\u043b\u0456\u043d \u043a\u04e9\u0440\u0441\u0435\u0442\u0443 \u04af\u0448\u0456\u043d ALT-F10 \u0431\u0430\u0441\u044b\u04a3\u044b\u0437. \u041a\u04e9\u043c\u0435\u043a \u0430\u043b\u0443 \u04af\u0448\u0456\u043d ALT-0 \u0431\u0430\u0441\u044b\u04a3\u044b\u0437.", +"Tools": "\u049a\u04b1\u0440\u0430\u043b\u0434\u0430\u0440", +"View": "\u041a\u04e9\u0440\u0456\u043d\u0456\u0441", +"Table": "\u041a\u0435\u0441\u0442\u0435", +"Format": "\u0424\u043e\u0440\u043c\u0430\u0442" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js new file mode 100644 index 0000000000..5c4b055126 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js @@ -0,0 +1,253 @@ +tinymce.addI18n('km_KH',{ +"Redo": "\u1792\u17d2\u179c\u17be\u200b\u179c\u17b7\u1789", +"Undo": "\u1798\u17b7\u1793\u200b\u1792\u17d2\u179c\u17be\u200b\u179c\u17b7\u1789", +"Cut": "\u1780\u17b6\u178f\u17cb", +"Copy": "\u1785\u1798\u17d2\u179b\u1784", +"Paste": "\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb", +"Select all": "\u1787\u17d2\u179a\u17be\u179f\u200b\u1791\u17b6\u17c6\u1784\u200b\u17a2\u179f\u17cb", +"New document": "\u17af\u1780\u179f\u17b6\u179a\u200b\u17a2\u178f\u17d2\u1790\u1794\u1791\u200b\u1790\u17d2\u1798\u17b8", +"Ok": "\u1796\u17d2\u179a\u1798", +"Cancel": "\u1794\u17c4\u17c7\u200b\u1794\u1784\u17cb", +"Visual aids": "\u1791\u17b7\u178a\u17d2\u178b\u1797\u17b6\u1796\u200b\u1787\u17c6\u1793\u17bd\u1799", +"Bold": "\u178a\u17b7\u178f", +"Italic": "\u1791\u17d2\u179a\u17c1\u178f", +"Underline": "\u1782\u17bc\u179f\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u1796\u17b8\u200b\u1780\u17d2\u179a\u17c4\u1798", +"Strikethrough": "\u1782\u17bc\u179f\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u1786\u17bc\u178f", +"Superscript": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u178f\u17bc\u1785\u200b\u179b\u17be", +"Subscript": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u178f\u17bc\u1785\u200b\u1780\u17d2\u179a\u17c4\u1798", +"Clear formatting": "\u179f\u1798\u17d2\u17a2\u17b6\u178f\u200b\u1791\u1798\u17d2\u179a\u1784\u17cb", +"Align left": "\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1791\u17c5\u200b\u1786\u17d2\u179c\u17c1\u1784", +"Align center": "\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1791\u17c5\u200b\u1780\u178e\u17d2\u178a\u17b6\u179b", +"Align right": "\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1791\u17c5\u200b\u179f\u17d2\u178a\u17b6\u17c6", +"Justify": "\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1796\u17c1\u1789", +"Bullet list": "\u1794\u1789\u17d2\u1787\u17b8\u200b\u1787\u17b6\u200b\u1785\u17c6\u178e\u17bb\u1785", +"Numbered list": "\u1794\u1789\u17d2\u1787\u17b8\u200b\u1787\u17b6\u200b\u179b\u17c1\u1781", +"Decrease indent": "\u1781\u17b7\u178f\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u1785\u17c1\u1789", +"Increase indent": "\u1781\u17b7\u178f\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u1785\u17bc\u179b", +"Close": "\u1794\u17b7\u1791", +"Formats": "\u1791\u17d2\u179a\u1784\u17cb\u1791\u17d2\u179a\u17b6\u1799", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8\u200b\u17a2\u17ca\u17b8\u1793\u1792\u17ba\u178e\u17b7\u178f\u200b\u179a\u1794\u179f\u17cb\u200b\u17a2\u17d2\u1793\u1780\u200b\u1798\u17b7\u1793\u200b\u17a2\u17b6\u1785\u200b\u1785\u17bc\u179b\u200b\u1795\u17d2\u1791\u17b6\u179b\u17cb\u200b\u1791\u17c5\u200b\u1780\u17b6\u1793\u17cb\u200b\u1783\u17d2\u179b\u17b8\u1794\u1794\u178f\u200b\u1791\u17c1\u17d4 \u179f\u17bc\u1798\u200b\u1794\u17d2\u179a\u17be Ctrl+X\/C\/V \u179b\u17be\u200b\u1780\u17d2\u178a\u17b6\u179a\u200b\u1785\u17bb\u1785\u200b\u1787\u17c6\u1793\u17bd\u179f\u200b\u179c\u17b7\u1789\u17d4", +"Headers": "\u1780\u17d2\u1794\u17b6\u179b", +"Header 1": "\u1780\u17d2\u1794\u17b6\u179b 1", +"Header 2": "\u1780\u17d2\u1794\u17b6\u179b 2", +"Header 3": "\u1780\u17d2\u1794\u17b6\u179b 3", +"Header 4": "\u1780\u17d2\u1794\u17b6\u179b 4", +"Header 5": "\u1780\u17d2\u1794\u17b6\u179b 5", +"Header 6": "\u1780\u17d2\u1794\u17b6\u179b 6", +"Headings": "\u1780\u17d2\u1794\u17b6\u179b", +"Heading 1": "\u1780\u17d2\u1794\u17b6\u179b 1", +"Heading 2": "\u1780\u17d2\u1794\u17b6\u179b 2", +"Heading 3": "\u1780\u17d2\u1794\u17b6\u179b 3", +"Heading 4": "\u1780\u17d2\u1794\u17b6\u179b 4", +"Heading 5": "\u1780\u17d2\u1794\u17b6\u179b 5", +"Heading 6": "\u1780\u17d2\u1794\u17b6\u179b 6", +"Div": "Div", +"Pre": "Pre", +"Code": "\u1780\u17bc\u178a", +"Paragraph": "\u1780\u1790\u17b6\u1781\u178e\u17d2\u178c", +"Blockquote": "\u1794\u17d2\u179b\u17bb\u1780\u200b\u1796\u17b6\u1780\u17d2\u1799\u200b\u179f\u1798\u17d2\u179a\u1784\u17cb", +"Inline": "\u1780\u17d2\u1793\u17bb\u1784\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb", +"Blocks": "\u1794\u17d2\u179b\u17bb\u1780", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u1780\u17b6\u179a\u200b\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1796\u17c1\u179b\u200b\u1793\u17c1\u17c7 \u179f\u17d2\u1790\u17b7\u178f\u200b\u1780\u17d2\u1793\u17bb\u1784\u200b\u1794\u17c2\u1794\u200b\u1795\u17c2\u1793\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u1792\u1798\u17d2\u1798\u178f\u17b6\u17d4 \u1794\u1785\u17d2\u1785\u17bb\u1794\u17d2\u1794\u1793\u17d2\u1793\u200b\u1793\u17c1\u17c7 \u1798\u17b6\u178f\u17b7\u1780\u17b6\u200b\u1791\u17b6\u17c6\u1784\u200b\u17a1\u17b6\u1799\u200b\u1793\u17b9\u1784\u200b\u178f\u17d2\u179a\u17bc\u179c\u200b\u1794\u17b6\u1793\u200b\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1787\u17b6\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u1792\u1798\u17d2\u1798\u178f\u17b6 \u179b\u17bb\u17c7\u178f\u17d2\u179a\u17b6\u200b\u178f\u17c2\u200b\u17a2\u17d2\u1793\u1780\u200b\u1794\u17b7\u1791\u200b\u1787\u1798\u17d2\u179a\u17be\u179f\u200b\u1793\u17c1\u17c7\u17d4", +"Font Family": "\u1782\u17d2\u179a\u17bd\u179f\u17b6\u179a\u200b\u1796\u17bb\u1798\u17d2\u1796\u200b\u17a2\u1780\u17d2\u179f\u179a", +"Font Sizes": "\u1791\u17c6\u17a0\u17c6\u200b\u17a2\u1780\u17d2\u179f\u179a", +"Class": "Class", +"Browse for an image": "\u179a\u1780\u1798\u17be\u179b\u200b\u179a\u17bc\u1794\u1797\u17b6\u1796", +"OR": "\u17ac", +"Drop an image here": "\u1791\u1798\u17d2\u179b\u17b6\u1794\u17cb\u200b\u179a\u17bc\u1794\u1797\u17b6\u1796\u200b\u1793\u17c5\u200b\u178f\u17d2\u179a\u1784\u17cb\u200b\u1793\u17c1\u17c7", +"Upload": "\u1795\u17d2\u1791\u17bb\u1780\u17a1\u17be\u1784", +"Default": "\u179b\u17c6\u1793\u17b6\u17c6\u200b\u178a\u17be\u1798", +"Circle": "\u1798\u17bc\u179b", +"Disc": "\u1790\u17b6\u179f", +"Square": "\u1787\u17d2\u179a\u17bb\u1784", +"Lower Alpha": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u178f\u17bc\u1785", +"Lower Greek": "\u179b\u17c1\u1781\u200b\u1780\u17d2\u179a\u17b7\u1780\u200b\u178f\u17bc\u1785", +"Lower Roman": "\u179b\u17c1\u1781\u200b\u179a\u17c9\u17bc\u1798\u17c9\u17b6\u17c6\u1784\u200b\u178f\u17bc\u1785", +"Upper Alpha": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u1792\u17c6", +"Upper Roman": "\u179b\u17c1\u1781\u200b\u179a\u17c9\u17bc\u1798\u17c9\u17b6\u17c6\u1784\u200b\u1792\u17c6", +"Anchor": "\u1799\u17bb\u1790\u17d2\u1780\u17b6", +"Name": "\u1788\u17d2\u1798\u17c4\u17c7", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u1782\u17bd\u179a\u178f\u17c2\u200b\u1795\u17d2\u178a\u17be\u1798\u200b\u1787\u17b6\u1798\u17bd\u1799\u200b\u178f\u17bd\u17a2\u1780\u17d2\u179f\u179a \u17a0\u17be\u1799\u200b\u1794\u1793\u17d2\u178f\u200b\u1787\u17b6\u1798\u17bd\u1799\u200b\u178f\u17c2\u200b\u178f\u17bd\u17a2\u1780\u17d2\u179f\u179a \u179b\u17c1\u1781 \u179f\u1789\u17d2\u1789\u17b6\u200b\u178a\u1780 \u179f\u1789\u17d2\u1789\u17b6\u200b\u1785\u17bb\u1785 \u179f\u1789\u17d2\u1789\u17b6\u200b\u1785\u17bb\u1785\u1796\u17b8\u179a \u17ac\u200b\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u1780\u17d2\u179a\u17c4\u1798\u17d4", +"You have unsaved changes are you sure you want to navigate away?": "\u1798\u17b6\u1793\u200b\u1794\u1793\u17d2\u179b\u17b6\u179f\u17cb\u200b\u1794\u17d2\u178a\u17bc\u179a\u200b\u1798\u17b7\u1793\u200b\u1791\u17b6\u1793\u17cb\u200b\u1794\u17b6\u1793\u200b\u179a\u1780\u17d2\u179f\u17b6\u200b\u1791\u17bb\u1780\u17d4 \u178f\u17be\u200b\u17a2\u17d2\u1793\u1780\u200b\u1796\u17b7\u178f\u200b\u1787\u17b6\u200b\u1785\u1784\u17cb\u200b\u1785\u17b6\u1780\u200b\u1785\u17c1\u1789\u200b\u1796\u17b8\u1791\u17b8\u1793\u17c1\u17c7\u200b\u1798\u17c2\u1793\u1791\u17c1?", +"Restore last draft": "\u179f\u17d2\u178a\u17b6\u179a\u200b\u179f\u17c1\u1785\u1780\u17d2\u178a\u17b8\u200b\u1796\u17d2\u179a\u17b6\u1784\u200b\u1796\u17b8\u200b\u1798\u17bb\u1793", +"Special character": "\u178f\u17bd\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u1796\u17b7\u179f\u17c1\u179f", +"Source code": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u1780\u17bc\u178a", +"Insert\/Edit code sample": "\u1794\u1789\u17d2\u1785\u17bc\u179b\/\u1780\u17c2\u179f\u1798\u17d2\u179a\u17bd\u179b \u1780\u17bc\u178a\u200b\u1782\u17c6\u179a\u17bc", +"Language": "\u1797\u17b6\u179f\u17b6", +"Color": "\u1796\u178e\u17cc", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u1786\u17d2\u179c\u17c1\u1784\u200b\u1791\u17c5\u200b\u179f\u17d2\u178a\u17b6\u17c6", +"Right to left": "\u179f\u17d2\u178a\u17b6\u17c6\u200b\u1791\u17c5\u200b\u1786\u17d2\u179c\u17c1\u1784", +"Emoticons": "\u179a\u17bc\u1794\u200b\u179f\u1789\u17d2\u1789\u17b6\u178e\u200b\u17a2\u17b6\u179a\u1798\u17d2\u1798\u178e\u17cd", +"Document properties": "\u179b\u1780\u17d2\u1781\u178e\u17c8\u200b\u179f\u1798\u17d2\u1794\u178f\u17d2\u178f\u17b7\u200b\u17af\u1780\u179f\u17b6\u179a", +"Title": "\u1785\u17c6\u178e\u1784\u200b\u1787\u17be\u1784", +"Keywords": "\u1796\u17b6\u1780\u17d2\u1799\u200b\u1782\u1793\u17d2\u179b\u17b9\u17c7", +"Description": "\u179f\u17c1\u1785\u1780\u17d2\u178a\u17b8\u200b\u17a2\u1792\u17b7\u1794\u17d2\u1794\u17b6\u1799", +"Robots": "\u179a\u17bc\u1794\u1799\u1793\u17d2\u178f", +"Author": "\u17a2\u17d2\u1793\u1780\u200b\u1793\u17b7\u1796\u1793\u17d2\u1792", +"Encoding": "\u1780\u17b6\u179a\u200b\u17a2\u17ca\u17b8\u1793\u1780\u17bc\u178a", +"Fullscreen": "\u1796\u17c1\u1789\u200b\u17a2\u17c1\u1780\u17d2\u179a\u1784\u17cb", +"Action": "\u179f\u1780\u1798\u17d2\u1798\u1797\u17b6\u1796", +"Shortcut": "\u1795\u17d2\u179b\u17bc\u179c\u1780\u17b6\u178f\u17cb", +"Help": "\u1787\u17c6\u1793\u17bd\u1799", +"Address": "\u17a2\u17b6\u179f\u1799\u178a\u17d2\u178b\u17b6\u1793", +"Focus to menubar": "\u1795\u17d2\u178a\u17c4\u178f\u200b\u1791\u17c5\u179b\u17be\u200b\u179a\u1794\u17b6\u179a\u200b\u1798\u17c9\u17ba\u1793\u17bb\u1799", +"Focus to toolbar": "\u1795\u17d2\u178a\u17c4\u178f\u200b\u1791\u17c5\u179b\u17be\u200b\u179a\u1794\u17b6\u179a\u200b\u17a7\u1794\u1780\u179a\u178e\u17cd", +"Focus to element path": "\u1795\u17d2\u178a\u17c4\u178f\u200b\u1791\u17c5\u179b\u17be\u200b\u1791\u17b8\u178f\u17b6\u17c6\u1784\u200b\u179a\u1794\u179f\u17cb\u200b\u1792\u17b6\u178f\u17bb", +"Focus to contextual toolbar": "\u1795\u17d2\u178a\u17c4\u178f\u200b\u1791\u17c5\u200b\u179b\u17be\u200b\u179a\u1794\u17b6\u179a\u17a7\u1794\u1780\u179a\u178e\u17cd\u200b\u178f\u17b6\u1798\u200b\u1794\u179a\u17b7\u1794\u1791", +"Insert link (if link plugin activated)": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u178f\u17c6\u178e (\u1794\u17d2\u179a\u179f\u17b7\u1793\u1794\u17be\u200b\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8 plugin \u1794\u17b6\u1793\u1794\u17be\u1780)", +"Save (if save plugin activated)": "\u179a\u1780\u17d2\u179f\u17b6\u1791\u17bb\u1780 (\u1794\u17d2\u179a\u179f\u17b7\u1793\u1794\u17be\u200b\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8 save \u1794\u17b6\u1793\u1794\u17be\u1780)", +"Find (if searchreplace plugin activated)": "\u179f\u17d2\u179c\u17c2\u1784\u179a\u1780 (\u1794\u17d2\u179a\u179f\u17b7\u1793\u200b\u1794\u17be\u200b\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8 searchreplace \u1794\u17b6\u1793\u200b\u1794\u17be\u1780)", +"Plugins installed ({0}):": "\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8\u200b\u1794\u1793\u17d2\u1790\u17c2\u1798\u200b\u178a\u17c2\u179b\u1794\u17b6\u1793\u200b\u178a\u17c6\u17a1\u17be\u1784 ({0})\u17d6", +"Premium plugins:": "\u1780\u1798\u17d2\u1798\u179c\u17b7\u1792\u17b8\u200b\u1782\u17b7\u178f\u200b\u1794\u17d2\u179a\u17b6\u1780\u17cb\u17d6", +"Learn more...": "\u179f\u17b7\u1780\u17d2\u179f\u17b6\u200b\u1794\u1793\u17d2\u1790\u17c2\u1798...", +"You are using {0}": "\u17a2\u17d2\u1793\u1780\u200b\u1780\u17c6\u1796\u17bb\u1784\u200b\u1794\u17d2\u179a\u17be {0}", +"Horizontal line": "\u1794\u1793\u17d2\u1791\u17b6\u178f\u17cb\u200b\u178a\u17c1\u1780", +"Insert\/edit image": "\u1794\u1789\u17d2\u1785\u17bc\u179b\/\u1780\u17c2 \u179a\u17bc\u1794\u200b\u1797\u17b6\u1796", +"Image description": "\u179f\u17c1\u1785\u1780\u17d2\u178a\u17b8\u200b\u17a2\u1792\u17b7\u1794\u17d2\u1794\u17b6\u1799\u200b\u1796\u17b8\u200b\u179a\u17bc\u1794", +"Source": "\u1794\u17d2\u179a\u1797\u1796", +"Dimensions": "\u179c\u17b7\u1798\u17b6\u178f\u17d2\u179a", +"Constrain proportions": " \u1794\u1784\u17d2\u1781\u17c6\u200b\u17b2\u17d2\u1799\u200b\u1798\u17b6\u1793\u200b\u179f\u1798\u17b6\u1798\u17b6\u178f\u17d2\u179a", +"General": "\u1791\u17bc\u1791\u17c5", +"Advanced": "\u1780\u1798\u17d2\u179a\u17b7\u178f\u200b\u1781\u17d2\u1796\u179f\u17cb", +"Style": "\u179a\u1785\u1793\u17b6\u1794\u1790", +"Vertical space": "\u179b\u17c6\u17a0\u200b\u1794\u1789\u17d2\u1788\u179a", +"Horizontal space": "\u179b\u17c6\u17a0\u200b\u1795\u17d2\u178a\u17c1\u1780", +"Border": "\u179f\u17ca\u17bb\u1798", +"Insert image": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u179a\u17bc\u1794\u200b\u1797\u17b6\u1796", +"Image": "\u179a\u17bc\u1794\u1797\u17b6\u1796", +"Image list": "\u1794\u1789\u17d2\u1787\u17b8\u179a\u17bc\u1794\u1797\u17b6\u1796", +"Rotate counterclockwise": "\u1794\u1784\u17d2\u179c\u17b7\u179b\u200b\u1785\u17d2\u179a\u17b6\u179f\u200b\u1791\u17d2\u179a\u1793\u17b7\u1785\u200b\u1793\u17b6\u17a1\u17b7\u1780\u17b6", +"Rotate clockwise": "\u1794\u1784\u17d2\u179c\u17b7\u179b\u200b\u179f\u17d2\u179a\u1794\u200b\u1791\u17d2\u179a\u1793\u17b7\u1785\u200b\u1793\u17b6\u17a1\u17b7\u1780\u17b6", +"Flip vertically": "\u178f\u17d2\u179a\u17a1\u1794\u17cb\u200b\u1794\u1789\u17d2\u1788\u179a", +"Flip horizontally": "\u178f\u17d2\u179a\u17a1\u1794\u17cb\u200b\u1795\u17d2\u178a\u17c1\u1780", +"Edit image": "\u1780\u17c2\u179f\u1798\u17d2\u179a\u17bd\u179b\u200b\u179a\u17bc\u1794\u1797\u17b6\u1796", +"Image options": "\u1787\u1798\u17d2\u179a\u17be\u179f\u200b\u179a\u17bc\u1794\u1797\u17b6\u1796", +"Zoom in": "\u1796\u1784\u17d2\u179a\u17b8\u1780", +"Zoom out": "\u1794\u1784\u17d2\u179a\u17bd\u1798", +"Crop": "\u1785\u17d2\u179a\u17b9\u1794", +"Resize": "\u1794\u17d2\u178a\u17bc\u179a\u200b\u1791\u17c6\u17a0\u17c6", +"Orientation": "\u1791\u17b7\u179f", +"Brightness": "\u1796\u1793\u17d2\u179b\u17ba", +"Sharpen": "\u1785\u17d2\u1794\u17b6\u179f\u17cb", +"Contrast": "\u1780\u1798\u17d2\u179a\u17b7\u178f\u200b\u1796\u178e\u17cc", +"Color levels": "\u1780\u1798\u17d2\u179a\u17b7\u178f\u200b\u1796\u178e\u17cc", +"Gamma": "\u17a0\u17d2\u1782\u17b6\u1798\u17c9\u17b6", +"Invert": "\u178a\u17b6\u1780\u17cb\u200b\u1794\u1789\u17d2\u1785\u17d2\u179a\u17b6\u179f", +"Apply": "\u17a2\u1793\u17bb\u179c\u178f\u17d2\u178f", +"Back": "\u1790\u1799\u1780\u17d2\u179a\u17c4\u1799", +"Insert date\/time": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1780\u17b6\u179b\u200b\u1794\u179a\u17b7\u1785\u17d2\u1786\u17c1\u1791\/\u1798\u17c9\u17c4\u1784", +"Date\/time": "\u1780\u17b6\u179b\u1794\u179a\u17b7\u1785\u17d2\u1786\u17c1\u1791\/\u1798\u17c9\u17c4\u1784", +"Insert link": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u178f\u17c6\u178e", +"Insert\/edit link": "\u1794\u1789\u17d2\u1785\u17bc\u179b\/\u1780\u17c2 \u178f\u17c6\u178e", +"Text to display": "\u17a2\u1780\u17d2\u179f\u179a\u200b\u178f\u17d2\u179a\u17bc\u179c\u200b\u1794\u1784\u17d2\u17a0\u17b6\u1789", +"Url": "Url", +"Target": "\u1791\u17b7\u179f\u178a\u17c5", +"None": "\u1798\u17b7\u1793\u200b\u1798\u17b6\u1793", +"New window": "\u1795\u17d2\u1791\u17b6\u17c6\u1784\u200b\u179c\u17b8\u1793\u178a\u17bc\u200b\u1790\u17d2\u1798\u17b8", +"Remove link": "\u178a\u1780\u200b\u178f\u17c6\u178e\u200b\u1785\u17c1\u1789", +"Anchors": "\u1799\u17bb\u1790\u17d2\u1780\u17b6", +"Link": "\u178f\u17c6\u178e", +"Paste or type a link": "\u1794\u17b7\u1791\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u17ac\u200b\u179c\u17b6\u1799\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u178f\u17c6\u178e", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u17a2\u17d2\u1793\u1780\u200b\u1794\u17b6\u1793\u200b\u1794\u1789\u17d2\u1785\u17bc\u179b URL \u178a\u17c2\u179b\u200b\u1798\u17b6\u1793\u200b\u179f\u178e\u17d2\u178b\u17b6\u1793\u200b\u178a\u17bc\u1785\u200b\u17a2\u17b6\u179f\u1799\u178a\u17d2\u178b\u17b6\u1793\u200b\u17a2\u17ca\u17b8\u1798\u17c2\u179b\u17d4 \u178f\u17be\u200b\u17a2\u17d2\u1793\u1780\u200b\u1785\u1784\u17cb\u200b\u1794\u1793\u17d2\u1790\u17c2\u1798\u200b\u1794\u17bb\u1796\u17d2\u179c\u1794\u200b\u1791 mailto: \u178a\u17c2\u179a\u200b\u17ac\u1791\u17c1?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u17a2\u17d2\u1793\u1780\u200b\u1794\u17b6\u1793\u200b\u1794\u1789\u17d2\u1785\u17bc\u179b URL \u178a\u17c2\u179b\u200b\u1787\u17b6\u200b\u178f\u17c6\u178e\u200b\u1791\u17c5\u200b\u1781\u17b6\u1784\u200b\u1780\u17d2\u179a\u17c5\u17d4 \u178f\u17be\u200b\u17a2\u17d2\u1793\u1780\u200b\u1785\u1784\u17cb\u200b\u1794\u1793\u17d2\u1790\u17c2\u1798\u200b\u1794\u17bb\u1796\u17d2\u179c\u1794\u200b\u1791 http:\/\/ \u178a\u17c2\u179a\u200b\u17ac\u1791\u17c1?", +"Link list": "\u1794\u1789\u17d2\u1787\u17b8\u178f\u17c6\u178e", +"Insert video": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u179c\u17b8\u178a\u17c1\u17a2\u17bc", +"Insert\/edit video": "\u1794\u1789\u17d2\u1785\u17bc\u179b\/\u1780\u17c2 \u179c\u17b8\u178a\u17c1\u17a2\u17bc", +"Insert\/edit media": "\u1794\u1789\u17d2\u1787\u17bc\u179b\u200b\/\u1780\u17c2\u179f\u1798\u17d2\u179a\u17bd\u179b \u1798\u17c1\u178c\u17b6", +"Alternative source": "\u1794\u17d2\u179a\u1797\u1796\u200b\u178a\u1791\u17c3\u200b\u1791\u17c0\u178f", +"Poster": "\u17a2\u17d2\u1793\u1780\u200b\u1795\u17d2\u179f\u17b6\u1799", +"Paste your embed code below:": "\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1780\u17bc\u178a\u200b\u1794\u1784\u17d2\u1780\u1794\u17cb\u200b\u1793\u17c5\u200b\u1781\u17b6\u1784\u200b\u1780\u17d2\u179a\u17c4\u1798:", +"Embed": "\u1794\u1784\u17d2\u1780\u1794\u17cb", +"Media": "\u1798\u17c1\u178c\u17b6", +"Nonbreaking space": "\u178a\u17c6\u178e\u1780\u200b\u1783\u17d2\u179b\u17b6\u200b\u1798\u17b7\u1793\u200b\u1794\u17c6\u1794\u17c2\u1780", +"Page break": "\u1794\u17c6\u1794\u17c2\u1780\u200b\u1791\u17c6\u1796\u17d0\u179a", +"Paste as text": "\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1787\u17b6\u200b\u17a2\u1780\u17d2\u179f\u179a", +"Preview": "\u1798\u17be\u179b\u200b\u1787\u17b6\u200b\u1798\u17bb\u1793", +"Print": "\u1794\u17c4\u17c7\u200b\u1796\u17bb\u1798\u17d2\u1796", +"Save": "\u179a\u1780\u17d2\u179f\u17b6\u200b\u1791\u17bb\u1780", +"Find": "\u179f\u17d2\u179c\u17c2\u1784\u200b\u179a\u1780", +"Replace with": "\u1787\u17c6\u1793\u17bd\u179f\u200b\u178a\u17c4\u1799", +"Replace": "\u1787\u17c6\u1793\u17bd\u179f", +"Replace all": "\u1787\u17c6\u1793\u17bd\u179f\u200b\u1791\u17b6\u17c6\u1784\u200b\u17a2\u179f\u17cb", +"Prev": "\u1780\u17d2\u179a\u17c4\u1799", +"Next": "\u1798\u17bb\u1781", +"Find and replace": "\u179f\u17d2\u179c\u17c2\u1784\u200b\u179a\u1780\u200b\u1793\u17b7\u1784\u200b\u1787\u17c6\u1793\u17bd\u179f", +"Could not find the specified string.": "\u1798\u17b7\u1793\u200b\u17a2\u17b6\u1785\u200b\u179a\u1780\u200b\u1783\u17be\u1789\u200b\u1781\u17d2\u179f\u17c2\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u178a\u17c2\u179b\u200b\u1794\u17b6\u1793\u200b\u1780\u17c6\u178e\u178f\u17cb\u17d4", +"Match case": "\u1780\u179a\u178e\u17b8\u200b\u178a\u17c6\u178e\u17bc\u1785", +"Whole words": "\u1796\u17b6\u1780\u17d2\u1799\u200b\u1791\u17b6\u17c6\u1784\u200b\u1798\u17bc\u179b", +"Spellcheck": "\u1796\u17b7\u1793\u17b7\u178f\u17d2\u1799\u200b\u17a2\u1780\u17d2\u1781\u179a\u17b6\u179c\u17b7\u179a\u17bb\u1791\u17d2\u1792", +"Ignore": "\u1798\u17b7\u1793\u200b\u17a2\u17be\u200b\u1796\u17be", +"Ignore all": "\u1798\u17b7\u1793\u200b\u17a2\u17be\u1796\u17be\u200b\u1791\u17b6\u17c6\u1784\u200b\u17a2\u179f\u17cb", +"Finish": "\u1794\u1789\u17d2\u1785\u1794\u17cb", +"Add to Dictionary": "\u1794\u1793\u17d2\u1790\u17c2\u1798\u200b\u1791\u17c5\u200b\u179c\u1785\u1793\u17b6\u1793\u17bb\u1780\u17d2\u179a\u1798", +"Insert table": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u178f\u17b6\u179a\u17b6\u1784", +"Table properties": "\u179b\u1780\u17d2\u1781\u178e\u17c8\u200b\u178f\u17b6\u179a\u17b6\u1784", +"Delete table": "\u179b\u17bb\u1794\u200b\u178f\u17b6\u179a\u17b6\u1784", +"Cell": "\u1780\u17d2\u179a\u17a1\u17b6", +"Row": "\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Column": "\u1787\u17bd\u179a\u200b\u1788\u179a", +"Cell properties": "\u179b\u1780\u17d2\u1781\u178e\u17c8\u200b\u1780\u17d2\u179a\u17a1\u17b6", +"Merge cells": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1780\u17d2\u179a\u17a1\u17b6\u200b\u1785\u17bc\u179b\u200b\u1782\u17d2\u1793\u17b6", +"Split cell": "\u1789\u17c2\u1780\u200b\u1780\u17d2\u179a\u17a1\u17b6", +"Insert row before": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1788\u17bd\u179a\u200b\u178a\u17c1\u1780\u200b\u1796\u17b8\u200b\u1798\u17bb\u1781", +"Insert row after": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780\u200b\u1796\u17b8\u200b\u1780\u17d2\u179a\u17c4\u1799", +"Delete row": "\u179b\u17bb\u1794\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Row properties": "\u179b\u1780\u17d2\u1781\u178e\u17c8\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Cut row": "\u1780\u17b6\u178f\u17cb\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Copy row": "\u1785\u1798\u17d2\u179b\u1784\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Paste row before": "\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780\u200b\u1796\u17b8\u200b\u1798\u17bb\u1781", +"Paste row after": "\u1794\u17b7\u1791\u200b\u1797\u17d2\u1787\u17b6\u1794\u17cb\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780\u200b\u1796\u17b8\u200b\u1780\u17d2\u179a\u17c4\u1799", +"Insert column before": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1787\u17bd\u179a\u200b\u1788\u179a\u200b\u1796\u17b8\u200b\u1798\u17bb\u1781", +"Insert column after": "\u1794\u1789\u17d2\u1787\u17bc\u179b\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780\u200b\u1796\u17b8\u200b\u1780\u17d2\u179a\u17c4\u1799", +"Delete column": "\u179b\u17bb\u1794\u200b\u1787\u17bd\u179a\u200b\u1788\u179a", +"Cols": "\u1787\u17bd\u179a\u200b\u1788\u179a", +"Rows": "\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Width": "\u1791\u1791\u17b9\u1784", +"Height": "\u1780\u1798\u17d2\u1796\u179f\u17cb", +"Cell spacing": "\u1782\u1798\u17d2\u179b\u17b6\u178f\u200b\u1780\u17d2\u179a\u17a1\u17b6", +"Cell padding": "\u1785\u1793\u17d2\u179b\u17c4\u17c7\u200b\u1780\u17d2\u179a\u17a1\u17b6", +"Caption": "\u1785\u17c6\u178e\u1784\u200b\u1787\u17be\u1784", +"Left": "\u1786\u17d2\u179c\u17c1\u1784", +"Center": "\u1780\u178e\u17d2\u178a\u17b6\u179b", +"Right": "\u179f\u17d2\u178a\u17b6\u17c6", +"Cell type": "\u1794\u17d2\u179a\u1797\u17c1\u1791\u200b\u1780\u17d2\u179a\u17a1\u17b6", +"Scope": "\u179c\u17b7\u179f\u17b6\u179b\u200b\u1797\u17b6\u1796", +"Alignment": "\u1780\u17b6\u179a\u200b\u178f\u1798\u17d2\u179a\u17b9\u1798", +"H Align": "\u1780\u17b6\u179a\u200b\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1795\u17d2\u178a\u17c1\u1780", +"V Align": "\u1780\u17b6\u179a\u200b\u178f\u1798\u17d2\u179a\u17b9\u1798\u200b\u1794\u1789\u17d2\u1788\u179a", +"Top": "\u179b\u17be", +"Middle": "\u1780\u178e\u17d2\u178a\u17b6\u179b", +"Bottom": "\u1780\u17d2\u179a\u17c4\u1798", +"Header cell": "\u1780\u17d2\u179a\u17a1\u17b6\u200b\u1785\u17c6\u178e\u1784\u200b\u1787\u17be\u1784", +"Row group": "\u1780\u17d2\u179a\u17bb\u1798\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Column group": "\u1780\u17d2\u179a\u17bb\u1798\u200b\u1787\u17bd\u179a\u200b\u1788\u179a", +"Row type": "\u1794\u17d2\u179a\u1797\u17c1\u1791\u200b\u1787\u17bd\u179a\u200b\u178a\u17c1\u1780", +"Header": "\u1785\u17c6\u178e\u1784\u200b\u1787\u17be\u1784", +"Body": "\u178f\u17bd\u200b\u179f\u17c1\u1785\u1780\u17d2\u178a\u17b8", +"Footer": "\u1794\u178b\u1798\u200b\u1780\u1790\u17b6", +"Border color": "\u1796\u178e\u17cc\u200b\u179f\u17ca\u17bb\u1798", +"Insert template": "\u1794\u1789\u17d2\u1785\u17bc\u179b\u200b\u1796\u17bb\u1798\u17d2\u1796\u200b\u1782\u1798\u17d2\u179a\u17bc", +"Templates": "\u1796\u17bb\u1798\u17d2\u1796\u200b\u1782\u17c6\u179a\u17bc", +"Template": "\u1796\u17bb\u1798\u17d2\u1796\u1782\u17c6\u179a\u17bc", +"Text color": "\u1796\u178e\u17cc\u200b\u17a2\u1780\u17d2\u179f\u179a", +"Background color": "\u1796\u178e\u17cc\u200b\u1795\u17d2\u1791\u17c3\u200b\u1780\u17d2\u179a\u17c4\u1799", +"Custom...": "\u1795\u17d2\u1791\u17b6\u179b\u17cb\u200b\u1781\u17d2\u179b\u17bd\u1793...", +"Custom color": "\u1796\u178e\u17cc\u200b\u1795\u17d2\u1791\u17b6\u179b\u17cb\u200b\u1781\u17d2\u179b\u17bd\u1793", +"No color": "\u1782\u17d2\u1798\u17b6\u1793\u200b\u1796\u178e\u17cc", +"Table of Contents": "\u178f\u17b6\u179a\u17b6\u1784\u200b\u1793\u17c3\u200b\u1798\u17b6\u178f\u17b7\u1780\u17b6", +"Show blocks": "\u1794\u1784\u17d2\u17a0\u17b6\u1789\u200b\u1794\u17d2\u179b\u17bb\u1780", +"Show invisible characters": "\u1794\u1784\u17d2\u17a0\u17b6\u1789\u200b\u178f\u17bd\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u1780\u17c6\u1794\u17b6\u17c6\u1784", +"Words: {0}": "\u1796\u17b6\u1780\u17d2\u1799: {0}", +"File": "\u17af\u1780\u179f\u17b6\u179a", +"Edit": "\u1780\u17c2\u1794\u17d2\u179a\u17c2", +"Insert": "\u1794\u1789\u17d2\u1785\u17bc\u179b", +"View": "\u1791\u17b7\u178a\u17d2\u178b\u1797\u17b6\u1796", +"Format": "\u1791\u17d2\u179a\u1784\u17cb\u1791\u17d2\u179a\u17b6\u1799", +"Table": "\u178f\u17b6\u179a\u17b6\u1784", +"Tools": "\u17a7\u1794\u1780\u179a\u178e\u17cd", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u1791\u17b8\u178f\u17b6\u17c6\u1784\u200b\u17a2\u1780\u17d2\u179f\u179a\u200b\u179f\u17c6\u1794\u17bc\u179a\u1794\u17c2\u1794\u17d4 \u1785\u17bb\u1785 ALT-F9 \u179f\u1798\u17d2\u179a\u17b6\u1794\u17cb\u200b\u1798\u17c9\u17ba\u1793\u17bb\u1799\u17d4 \u1785\u17bb\u1785 ALT-F10 \u179f\u1798\u17d2\u179a\u17b6\u1794\u17cb\u200b\u179a\u1794\u17b6\u179a\u200b\u17a7\u1794\u1780\u179a\u178e\u17cd\u17d4 \u1785\u17bb\u1785 ALT-0 \u179f\u1798\u17d2\u179a\u17b6\u1794\u17cb\u200b\u1787\u17c6\u1793\u17bd\u1799\u17d4" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ko_KR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ko_KR.js new file mode 100644 index 0000000000..ce0e42c7cf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ko_KR.js @@ -0,0 +1,261 @@ +tinymce.addI18n('ko_KR',{ +"Redo": "\ub2e4\uc2dc\uc2e4\ud589", +"Undo": "\uc2e4\ud589\ucde8\uc18c", +"Cut": "\uc798\ub77c\ub0b4\uae30", +"Copy": "\ubcf5\uc0ac\ud558\uae30", +"Paste": "\ubd99\uc5ec\ub123\uae30", +"Select all": "\uc804\uccb4\uc120\ud0dd", +"New document": "\uc0c8 \ubb38\uc11c", +"Ok": "\ud655\uc778", +"Cancel": "\ucde8\uc18c", +"Visual aids": "\uc2dc\uac01\uad50\uc7ac", +"Bold": "\uad75\uac8c", +"Italic": "\uae30\uc6b8\uc784\uaf34", +"Underline": "\ubc11\uc904", +"Strikethrough": "\ucde8\uc18c\uc120", +"Superscript": "\uc717\ucca8\uc790", +"Subscript": "\uc544\ub798\ucca8\uc790", +"Clear formatting": "\ud3ec\ub9f7\ucd08\uae30\ud654", +"Align left": "\uc67c\ucabd\uc815\ub82c", +"Align center": "\uac00\uc6b4\ub370\uc815\ub82c", +"Align right": "\uc624\ub978\ucabd\uc815\ub82c", +"Justify": "\uc591\ucabd\uc815\ub82c", +"Bullet list": "\uc810\ub9ac\uc2a4\ud2b8", +"Numbered list": "\uc22b\uc790\ub9ac\uc2a4\ud2b8", +"Decrease indent": "\ub0b4\uc5b4\uc4f0\uae30", +"Increase indent": "\ub4e4\uc5ec\uc4f0\uae30", +"Close": "\ub2eb\uae30", +"Formats": "\ud3ec\ub9f7", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\ube0c\ub77c\uc6b0\uc838\uac00 \ud074\ub9bd\ubcf4\ub4dc \uc811\uadfc\uc744 \ud5c8\uc6a9\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Ctrl+X\/C\/V \ud0a4\ub97c \uc774\uc6a9\ud574 \uc8fc\uc138\uc694.", +"Headers": "\uc2a4\ud0c0\uc77c", +"Header 1": "\uc81c\ubaa9 1", +"Header 2": "\uc81c\ubaa9 2", +"Header 3": "\uc81c\ubaa9 3", +"Header 4": "\uc81c\ubaa9 4", +"Header 5": "\uc81c\ubaa9 5", +"Header 6": "\uc81c\ubaa9 6", +"Headings": "\uc81c\ubaa9", +"Heading 1": "\uc81c\ubaa9 1", +"Heading 2": "\uc81c\ubaa9 2", +"Heading 3": "\uc81c\ubaa9 3", +"Heading 4": "\uc81c\ubaa9 4", +"Heading 5": "\uc81c\ubaa9 5", +"Heading 6": "\uc81c\ubaa9 6", +"Preformatted": "Preformatted", +"Div": "\uad6c\ubd84", +"Pre": "Pre", +"Code": "\ucf54\ub4dc", +"Paragraph": "\ub2e8\ub77d", +"Blockquote": "\uad6c\ud68d", +"Inline": "\ub77c\uc778 \uc124\uc815", +"Blocks": "\ube14\ub85d \uc124\uc815", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\uc2a4\ud0c0\uc77c\ubcf5\uc0ac \ub044\uae30. \uc774 \uc635\uc158\uc744 \ub044\uae30 \uc804\uc5d0\ub294 \ubcf5\uc0ac \uc2dc, \uc2a4\ud0c0\uc77c\uc774 \ubcf5\uc0ac\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", +"Font Family": "\uae00\uaf34", +"Font Sizes": "\ud3f0\ud2b8 \uc0ac\uc774\uc988", +"Class": "\ud074\ub798\uc2a4", +"Browse for an image": "\uc774\ubbf8\uc9c0 \ucc3e\uae30", +"OR": "\ud639\uc740", +"Drop an image here": "\uc774\ubbf8\uc9c0 \ub4dc\ub86d", +"Upload": "\uc5c5\ub85c\ub4dc", +"Block": "\ube14\ub85d", +"Align": "\uc815\ub82c", +"Default": "\uae30\ubcf8", +"Circle": "\uc6d0", +"Disc": "\uc6d0\ubc18", +"Square": "\uc0ac\uac01", +"Lower Alpha": "\uc54c\ud30c\ubcb3 \uc18c\ubb38\uc790", +"Lower Greek": "\uadf8\ub9ac\uc2a4\uc5b4 \uc18c\ubb38\uc790", +"Lower Roman": "\ub85c\ub9c8\uc790 \uc18c\ubb38\uc790", +"Upper Alpha": "\uc54c\ud30c\ubcb3 \uc18c\ubb38\uc790", +"Upper Roman": "\ub85c\ub9c8\uc790 \ub300\ubb38\uc790", +"Anchor": "\uc575\ucee4", +"Name": "\uc774\ub984", +"Id": "\uc544\uc774\ub514", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\uc544\uc774\ub514\ub294 \ubb38\uc790, \uc22b\uc790, \ub300\uc2dc, \uc810, \ucf5c\ub860 \ub610\ub294 \ubc11\uc904\ub85c \uc2dc\uc791\ud574\uc57c\ud569\ub2c8\ub2e4.", +"You have unsaved changes are you sure you want to navigate away?": "\uc800\uc7a5\ud558\uc9c0 \uc54a\uc740 \uc815\ubcf4\uac00 \uc788\uc2b5\ub2c8\ub2e4. \uc774 \ud398\uc774\uc9c0\ub97c \ubc97\uc5b4\ub098\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", +"Restore last draft": "\ub9c8\uc9c0\ub9c9 \ucd08\uc548 \ubcf5\uc6d0", +"Special character": "\ud2b9\uc218\ubb38\uc790", +"Source code": "\uc18c\uc2a4\ucf54\ub4dc", +"Insert\/Edit code sample": "\ucf54\ub4dc\uc0d8\ud50c \uc0bd\uc785\/\ud3b8\uc9d1", +"Language": "\uc5b8\uc5b4", +"Code sample": "\ucf54\ub4dc\uc0d8\ud50c", +"Color": "\uc0c9\uc0c1", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\uc67c\ucabd\uc5d0\uc11c \uc624\ub978\ucabd", +"Right to left": "\uc624\ub978\ucabd\uc5d0\uc11c \uc67c\ucabd", +"Emoticons": "\uc774\ubaa8\ud2f0\ucf58", +"Document properties": "\ubb38\uc11c \uc18d\uc131", +"Title": "\uc81c\ubaa9", +"Keywords": "\ud0a4\uc6cc\ub4dc", +"Description": "\uc124\uba85", +"Robots": "\ub85c\ubd07", +"Author": "\uc800\uc790", +"Encoding": "\uc778\ucf54\ub529", +"Fullscreen": "\uc804\uccb4\ud654\uba74", +"Action": "\ub3d9\uc791", +"Shortcut": "\ub2e8\ucd95\ud0a4", +"Help": "\ub3c4\uc6c0\ub9d0", +"Address": "\uc8fc\uc18c", +"Focus to menubar": "\uba54\ub274\uc5d0 \ud3ec\ucee4\uc2a4", +"Focus to toolbar": "\ud234\ubc14\uc5d0 \ud3ec\ucee4\uc2a4", +"Focus to element path": "element path\uc5d0 \ud3ec\ucee4\uc2a4", +"Focus to contextual toolbar": "\ucf04\ud14d\uc2a4\ud2b8 \ud234\ubc14\uc5d0 \ud3ec\ucee4\uc2a4", +"Insert link (if link plugin activated)": "\ub9c1\ud06c \uc0bd\uc785 (link \ud50c\ub7ec\uadf8\uc778\uc774 \ud65c\uc131\ud654\ub41c \uc0c1\ud0dc\uc5d0\uc11c)", +"Save (if save plugin activated)": "\uc800\uc7a5 (save \ud50c\ub7ec\uadf8\uc778\uc774 \ud65c\uc131\ud654\ub41c \uc0c1\ud0dc\uc5d0\uc11c)", +"Find (if searchreplace plugin activated)": "\ucc3e\uae30(searchreplace \ud50c\ub7ec\uadf8\uc778\uc774 \ud65c\uc131\ud654\ub41c \uc0c1\ud0dc\uc5d0\uc11c)", +"Plugins installed ({0}):": "\uc124\uce58\ub41c \ud50c\ub7ec\uadf8\uc778 ({0}):", +"Premium plugins:": "\uace0\uae09 \ud50c\ub7ec\uadf8\uc778", +"Learn more...": "\uc880 \ub354 \uc0b4\ud3b4\ubcf4\uae30", +"You are using {0}": "{0}\ub97c \uc0ac\uc6a9\uc911", +"Plugins": "\ud50c\ub7ec\uadf8\uc778", +"Handy Shortcuts": "\ub2e8\ucd95\ud0a4", +"Horizontal line": "\uac00\ub85c", +"Insert\/edit image": "\uc774\ubbf8\uc9c0 \uc0bd\uc785\/\uc218\uc815", +"Image description": "\uc774\ubbf8\uc9c0 \uc124\uba85", +"Source": "\uc18c\uc2a4", +"Dimensions": "\ud06c\uae30", +"Constrain proportions": "\uc791\uc5c5 \uc81c\ud55c", +"General": "\uc77c\ubc18", +"Advanced": "\uace0\uae09", +"Style": "\uc2a4\ud0c0\uc77c", +"Vertical space": "\uc218\uc9c1 \uacf5\ubc31", +"Horizontal space": "\uc218\ud3c9 \uacf5\ubc31", +"Border": "\ud14c\ub450\ub9ac", +"Insert image": "\uc774\ubbf8\uc9c0 \uc0bd\uc785", +"Image": "\uc774\ubbf8\uc9c0", +"Image list": "\uc774\ubbf8\uc9c0 \ubaa9\ub85d", +"Rotate counterclockwise": "\uc2dc\uacc4\ubc18\ub300\ubc29\ud5a5\uc73c\ub85c \ud68c\uc804", +"Rotate clockwise": "\uc2dc\uacc4\ubc29\ud5a5\uc73c\ub85c \ud68c\uc804", +"Flip vertically": "\uc218\uc9c1 \ub4a4\uc9d1\uae30", +"Flip horizontally": "\uc218\ud3c9 \ub4a4\uc9d1\uae30", +"Edit image": "\uc774\ubbf8\uc9c0 \ud3b8\uc9d1", +"Image options": "\uc774\ubbf8\uc9c0 \uc635\uc158", +"Zoom in": "\ud655\ub300", +"Zoom out": "\ucd95\uc18c", +"Crop": "\uc790\ub974\uae30", +"Resize": "\ud06c\uae30 \uc870\uc808", +"Orientation": "\ubc29\ud5a5", +"Brightness": "\ubc1d\uae30", +"Sharpen": "\uc120\uba85\ud558\uac8c", +"Contrast": "\ub300\ube44", +"Color levels": "\uc0c9\uc0c1\ub808\ubca8", +"Gamma": "\uac10\ub9c8", +"Invert": "\ubc18\uc804", +"Apply": "\uc801\uc6a9", +"Back": "\ub4a4\ub85c", +"Insert date\/time": "\ub0a0\uc9dc\/\uc2dc\uac04\uc0bd\uc785", +"Date\/time": "\ub0a0\uc9dc\/\uc2dc\uac04", +"Insert link": "\ub9c1\ud06c \uc0bd\uc785 ", +"Insert\/edit link": "\ub9c1\ud06c \uc0bd\uc785\/\uc218\uc815", +"Text to display": "\ubcf8\ubb38", +"Url": "\uc8fc\uc18c", +"Target": "\ub300\uc0c1", +"None": "\uc5c6\uc74c", +"New window": "\uc0c8\ucc3d", +"Remove link": "\ub9c1\ud06c\uc0ad\uc81c", +"Anchors": "\ucc45\uac08\ud53c", +"Link": "\ub9c1\ud06c", +"Paste or type a link": "\ub9c1\ud06c\ub97c \ubd99\uc5ec\ub123\uac70\ub098 \uc785\ub825\ud558\uc138\uc694", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\ud604\uc7ac E-mail\uc8fc\uc18c\ub97c \uc785\ub825\ud558\uc168\uc2b5\ub2c8\ub2e4. E-mail \uc8fc\uc18c\uc5d0 \ub9c1\ud06c\ub97c \uac78\uae4c\uc694?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\ud604\uc7ac \uc6f9\uc0ac\uc774\ud2b8 \uc8fc\uc18c\ub97c \uc785\ub825\ud558\uc168\uc2b5\ub2c8\ub2e4. \ud574\ub2f9 \uc8fc\uc18c\uc5d0 \ub9c1\ud06c\ub97c \uac78\uae4c\uc694?", +"Link list": "\ub9c1\ud06c \ub9ac\uc2a4\ud2b8", +"Insert video": "\ube44\ub514\uc624 \uc0bd\uc785", +"Insert\/edit video": "\ube44\ub514\uc624 \uc0bd\uc785\/\uc218\uc815", +"Insert\/edit media": "\ubbf8\ub514\uc5b4 \uc0bd\uc785\/\uc218\uc815", +"Alternative source": "\ub300\uccb4 \uc18c\uc2a4", +"Poster": "\ud3ec\uc2a4\ud130", +"Paste your embed code below:": "\uc544\ub798\uc5d0 \ucf54\ub4dc\ub97c \ubd99\uc5ec\ub123\uc73c\uc138\uc694:", +"Embed": "\uc0bd\uc785", +"Media": "\ubbf8\ub514\uc5b4", +"Nonbreaking space": "\ub744\uc5b4\uc4f0\uae30", +"Page break": "\ud398\uc774\uc9c0 \uad6c\ubd84\uc790", +"Paste as text": "\ud14d\uc2a4\ud2b8\ub85c \ubd99\uc5ec\ub123\uae30", +"Preview": "\ubbf8\ub9ac\ubcf4\uae30", +"Print": "\ucd9c\ub825", +"Save": "\uc800\uc7a5", +"Find": "\ucc3e\uae30", +"Replace with": "\uad50\uccb4", +"Replace": "\uad50\uccb4", +"Replace all": "\uc804\uccb4 \uad50\uccb4", +"Prev": "\uc774\uc804", +"Next": "\ub2e4\uc74c", +"Find and replace": "\ucc3e\uc544\uc11c \uad50\uccb4", +"Could not find the specified string.": "\ubb38\uc790\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", +"Match case": "\ub300\uc18c\ubb38\uc790 \uc77c\uce58", +"Whole words": "\uc804\uccb4 \ub2e8\uc5b4", +"Spellcheck": "\ubb38\ubc95\uccb4\ud06c", +"Ignore": "\ubb34\uc2dc", +"Ignore all": "\uc804\uccb4\ubb34\uc2dc", +"Finish": "\uc644\ub8cc", +"Add to Dictionary": "\uc0ac\uc804\uc5d0 \ucd94\uac00", +"Insert table": "\ud14c\uc774\ube14 \uc0bd\uc785", +"Table properties": "\ud14c\uc774\ube14 \uc18d\uc131", +"Delete table": "\ud14c\uc774\ube14 \uc0ad\uc81c", +"Cell": "\uc140", +"Row": "\uc5f4", +"Column": "\ud589", +"Cell properties": "\uc140 \uc18d", +"Merge cells": "\uc140 \ud569\uce58\uae30", +"Split cell": "\uc140 \ub098\ub204\uae30", +"Insert row before": "\uc774\uc804\uc5d0 \ud589 \uc0bd\uc785", +"Insert row after": "\ub2e4\uc74c\uc5d0 \ud589 \uc0bd\uc785", +"Delete row": "\ud589 \uc9c0\uc6b0\uae30", +"Row properties": "\ud589 \uc18d\uc131", +"Cut row": "\ud589 \uc798\ub77c\ub0b4\uae30", +"Copy row": "\ud589 \ubcf5\uc0ac", +"Paste row before": "\uc774\uc804\uc5d0 \ud589 \ubd99\uc5ec\ub123\uae30", +"Paste row after": "\ub2e4\uc74c\uc5d0 \ud589 \ubd99\uc5ec\ub123\uae30", +"Insert column before": "\uc774\uc804\uc5d0 \ud589 \uc0bd\uc785", +"Insert column after": "\ub2e4\uc74c\uc5d0 \uc5f4 \uc0bd\uc785", +"Delete column": "\uc5f4 \uc9c0\uc6b0\uae30", +"Cols": "\uc5f4", +"Rows": "\ud589", +"Width": "\ub113\uc774", +"Height": "\ub192\uc774", +"Cell spacing": "\uc140 \uac04\uaca9", +"Cell padding": "\uc140 \uc548\ucabd \uc5ec\ubc31", +"Caption": "\ucea1\uc158", +"Left": "\uc67c\ucabd", +"Center": "\uac00\uc6b4\ub370", +"Right": "\uc624\ub978\ucabd", +"Cell type": "\uc140 \ud0c0\uc785", +"Scope": "\ubc94\uc704", +"Alignment": "\uc815\ub82c", +"H Align": "\uac00\ub85c \uc815\ub82c", +"V Align": "\uc138\ub85c \uc815\ub82c", +"Top": "\uc0c1\ub2e8", +"Middle": "\uc911\uac04", +"Bottom": "\ud558\ub2e8", +"Header cell": "\ud5e4\ub354 \uc140", +"Row group": "\ud589 \uadf8\ub8f9", +"Column group": "\uc5f4 \uadf8\ub8f9", +"Row type": "\ud589 \ud0c0\uc785", +"Header": "\ud5e4\ub354", +"Body": "\ubc14\ub514", +"Footer": "\ud478\ud130", +"Border color": "\ud14c\ub450\ub9ac \uc0c9", +"Insert template": "\ud15c\ud50c\ub9bf \uc0bd\uc785", +"Templates": "\ud15c\ud50c\ub9bf", +"Template": "\ud15c\ud50c\ub9bf", +"Text color": "\ubb38\uc790 \uc0c9\uae54", +"Background color": "\ubc30\uacbd\uc0c9", +"Custom...": "\uc9c1\uc811 \uc0c9\uae54 \uc9c0\uc815\ud558\uae30", +"Custom color": "\uc9c1\uc811 \uc9c0\uc815\ud55c \uc0c9\uae54", +"No color": "\uc0c9\uc0c1 \uc5c6\uc74c", +"Table of Contents": "\ubaa9\ucc28", +"Show blocks": "\ube14\ub7ed \ubcf4\uc5ec\uc8fc\uae30", +"Show invisible characters": "\uc548\ubcf4\uc774\ub294 \ubb38\uc790 \ubcf4\uc774\uae30", +"Words: {0}": "\ub2e8\uc5b4: {0}", +"{0} words": "{0} \ub2e8\uc5b4", +"File": "\ud30c\uc77c", +"Edit": "\uc218\uc815", +"Insert": "\uc0bd\uc785", +"View": "\ubcf4\uae30", +"Format": "\ud3ec\ub9f7", +"Table": "\ud14c\uc774\ube14", +"Tools": "\ub3c4\uad6c", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\uc11c\uc2dd \uc788\ub294 \ud14d\uc2a4\ud2b8 \ud3b8\uc9d1\uae30 \uc785\ub2c8\ub2e4. ALT-F9\ub97c \ub204\ub974\uba74 \uba54\ub274, ALT-F10\ub97c \ub204\ub974\uba74 \ud234\ubc14, ALT-0\uc744 \ub204\ub974\uba74 \ub3c4\uc6c0\ub9d0\uc744 \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lt.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lt.js new file mode 100644 index 0000000000..2a279686be --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lt.js @@ -0,0 +1,261 @@ +tinymce.addI18n('lt',{ +"Redo": "Gr\u0105\u017einti", +"Undo": "Atstatyti", +"Cut": "I\u0161kirpti", +"Copy": "Kopijuoti", +"Paste": "\u012ed\u0117ti", +"Select all": "Pa\u017eym\u0117ti visk\u0105", +"New document": "Naujas dokumentas", +"Ok": "Gerai", +"Cancel": "Atsisakyti", +"Visual aids": "Vaizdin\u0117s priemon\u0117s", +"Bold": "Pary\u0161kintas", +"Italic": "Kursyvinis", +"Underline": "Pabrauktas", +"Strikethrough": "Perbrauktas", +"Superscript": "Vir\u0161utinis indeksas", +"Subscript": "Apatinis indeksas", +"Clear formatting": "Naikinti formatavim\u0105", +"Align left": "Lygiuoti kair\u0117je", +"Align center": "Centruoti", +"Align right": "Lygiuoti de\u0161in\u0117je", +"Justify": "I\u0161d\u0117styti per vis\u0105 plot\u012f", +"Bullet list": "\u017denklinimo s\u0105ra\u0161as", +"Numbered list": "Skaitmeninis s\u0105ra\u0161as", +"Decrease indent": "Ma\u017einti \u012ftrauk\u0105", +"Increase indent": "Didinti \u012ftrauk\u0105", +"Close": "U\u017edaryti", +"Formats": "Formatai", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Nar\u0161ykl\u0117s nustatymai neleid\u017eia redaktoriui tiesiogiai pasiekti laikinosios atminties. Pra\u0161ome naudoti klaviat\u016bros klavi\u0161us Ctrl+X\/C\/V.", +"Headers": "Antra\u0161t\u0117s", +"Header 1": "Antra\u0161t\u0117 1", +"Header 2": "Antra\u0161t\u0117 2", +"Header 3": "Antra\u0161t\u0117 3", +"Header 4": "Antra\u0161t\u0117 4", +"Header 5": "Antra\u0161t\u0117 5", +"Header 6": "Antra\u0161t\u0117 6", +"Headings": "Antra\u0161t\u0117s", +"Heading 1": "Antra\u0161t\u0117 1", +"Heading 2": "Antra\u0161t\u0117 2", +"Heading 3": "Antra\u0161t\u0117 3", +"Heading 4": "Antra\u0161t\u0117 4", +"Heading 5": "Antra\u0161t\u0117 5", +"Heading 6": "Antra\u0161t\u0117 6", +"Preformatted": "Suformuotas i\u0161 anksto", +"Div": "Div", +"Pre": "Pre", +"Code": "Kodas", +"Paragraph": "Paragrafas", +"Blockquote": "Citata", +"Inline": "Inline", +"Blocks": "Blokai", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Dabar \u012fterpiama paprastojo teksto re\u017eimu. Kol \u0161i parinktis \u012fjungta, turinys bus \u012fterptas kaip paprastas tekstas.", +"Font Family": "\u0160riftas", +"Font Sizes": "\u0160rifto dyd\u017eiai", +"Class": "Klas\u0117", +"Browse for an image": "Ie\u0161koti paveiksl\u0117lio", +"OR": "ARBA", +"Drop an image here": "Tempkite paveiksl\u0117l\u012f \u010dia", +"Upload": "\u012ekelti", +"Block": "Blokas", +"Align": "Lygiavimas", +"Default": "Pagrindinis", +"Circle": "Apskritimas", +"Disc": "Diskas", +"Square": "Kvadratas", +"Lower Alpha": "Ma\u017eosios raid\u0117s", +"Lower Greek": "Ma\u017eosios graik\u0173", +"Lower Roman": "Ma\u017eosios rom\u0117n\u0173", +"Upper Alpha": "Did\u017eiosios raid\u0117s", +"Upper Roman": "Did\u017eiosios rom\u0117n\u0173", +"Anchor": "\u017dym\u0117", +"Name": "Pavadinimas", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID turi prasid\u0117ti raide, po kurios gali b\u016bti raid\u0117s, skai\u010diai, br\u016bk\u0161niai, ta\u0161kai, kabliata\u0161kiai ar apatiniai pabraukimai.", +"You have unsaved changes are you sure you want to navigate away?": "Turite nei\u0161saugot\u0173 pakeitim\u0173! Ar tikrai norite i\u0161eiti?", +"Restore last draft": "Atstatyti paskutin\u012f projekt\u0105", +"Special character": "Specialus simbolis", +"Source code": "Pirminis \u0161altinis", +"Insert\/Edit code sample": "Prid\u0117ti \/ keisti kodo pavyzd\u012f", +"Language": "Kalba", +"Code sample": "Kodo pavyzdys", +"Color": "Spalva", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "I\u0161 kair\u0117s \u012f de\u0161in\u0119", +"Right to left": "I\u0161 de\u0161in\u0117s \u012f kair\u0119", +"Emoticons": "Jaustukai", +"Document properties": "Dokumento savyb\u0117s", +"Title": "Pavadinimas", +"Keywords": "\u017dymos", +"Description": "Apra\u0161as", +"Robots": "Robotai", +"Author": "Autorius", +"Encoding": "Kodavimas", +"Fullscreen": "Visas ekranas", +"Action": "Veiksmas", +"Shortcut": "Nuoroda", +"Help": "Pagalba", +"Address": "Adresas", +"Focus to menubar": "Fokusuoti \u012f meniu", +"Focus to toolbar": "Fokusuoti \u012f \u012franki\u0173 juost\u0105", +"Focus to element path": "Fokusuoti \u012f elemento keli\u0105", +"Focus to contextual toolbar": "Fokusuoti \u012f kontekstin\u012f \u012franki\u0173 juost\u0105", +"Insert link (if link plugin activated)": "Prid\u0117ti nuorod\u0105 (jei link priedas aktyvuotas)", +"Save (if save plugin activated)": "I\u0161saugoti (jei save priedas aktyvuotas)", +"Find (if searchreplace plugin activated)": "Ie\u0161koti (jei searchreplace priedas aktyvuotas)", +"Plugins installed ({0}):": "\u012ediegti priedai ({0}):", +"Premium plugins:": "Mokami priedai:", +"Learn more...": "Su\u017einoti daugiau...", +"You are using {0}": "Naudojate {0}", +"Plugins": "Priedai", +"Handy Shortcuts": "Patogios nuorodos", +"Horizontal line": "Horizontali linija", +"Insert\/edit image": "\u012eterpti|Tvarkyti paveiksl\u0117l\u012f", +"Image description": "Paveiksl\u0117lio apra\u0161as", +"Source": "Pirmin\u0117 nuoroda", +"Dimensions": "Matmenys", +"Constrain proportions": "Laikytis proporcij\u0173", +"General": "Bendra", +"Advanced": "I\u0161pl\u0117stas", +"Style": "Stilius", +"Vertical space": "Vertikalus tarpas", +"Horizontal space": "Horizontalus tarpas", +"Border": "R\u0117melis", +"Insert image": "\u012eterpti paveiksl\u0117l\u012f", +"Image": "Paveiksl\u0117lis", +"Image list": "Paveiksl\u0117li\u0173 s\u0105ra\u0161as", +"Rotate counterclockwise": "Pasukti prie\u0161 laikrod\u017eio rodykl\u0119", +"Rotate clockwise": "Pasukti pagal laikrod\u017eio rodykl\u0119", +"Flip vertically": "Apversti vertikaliai", +"Flip horizontally": "Apversti horizontaliai", +"Edit image": "Redaguoti paveiksl\u0117l\u012f", +"Image options": "Paveiksl\u0117lio nustatymai", +"Zoom in": "Priartinti", +"Zoom out": "Atitolinti", +"Crop": "Atkarpyti", +"Resize": "Keisti dyd\u012f", +"Orientation": "Pasukimas", +"Brightness": "\u0160viesumas", +"Sharpen": "Ry\u0161kumas", +"Contrast": "Kontrastas", +"Color levels": "Spalv\u0173 lygiai", +"Gamma": "Gama", +"Invert": "Prie\u0161ingos spalvos", +"Apply": "Taikyti", +"Back": "Atgal", +"Insert date\/time": "\u012eterpti dat\u0105\/laik\u0105", +"Date\/time": "Data \/ laikas", +"Insert link": "\u012eterpti nuorod\u0105", +"Insert\/edit link": "\u012eterpti\/taisyti nuorod\u0105", +"Text to display": "Rodomas tekstas", +"Url": "Nuoroda", +"Target": "Tikslin\u0117 nuoroda", +"None": "Nieko", +"New window": "Naujas langas", +"Remove link": "\u0160alinti nuorod\u0105", +"Anchors": "\u017dym\u0117", +"Link": "Nuoroda", +"Paste or type a link": "\u012eklijuokite arba \u012fra\u0161ykite nuorod\u0105", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Atrodo, kad \u012fvesta nuoroda yra elektroninio pa\u0161to adresas. Ar norite prie\u0161 j\u012f \u012fvesti reikalaujam\u0105 \u201emailto:\u201c?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Atrodo, kad \u012fved\u0117te nuotolin\u0119 nuorod\u0105. Ar norite prie\u0161 j\u0105 \u012fvesti reikalaujam\u0105 \u201ehttp:\/\/\u201c?", +"Link list": "Nuorod\u0173 s\u0105ra\u0161as", +"Insert video": "\u012eterpti video", +"Insert\/edit video": "\u012eterpti\/tvarkyti video", +"Insert\/edit media": "Prid\u0117ti \/ keisti medij\u0105", +"Alternative source": "Alternatyvus \u0161altinis", +"Poster": "Plakatas", +"Paste your embed code below:": "\u012eterpkite kod\u0105 \u017eemiau:", +"Embed": "\u012eterpti", +"Media": "Medija", +"Nonbreaking space": "Nepertraukiamos vietos", +"Page break": "Puslapio skirtukas", +"Paste as text": "\u012eklijuoti kaip tekst\u0105", +"Preview": "Per\u017ei\u016bra", +"Print": "Spausdinti", +"Save": "I\u0161saugoti", +"Find": "Ie\u0161koti", +"Replace with": "Kuo pakeisti", +"Replace": "Pakeisti", +"Replace all": "Pakeisti visk\u0105", +"Prev": "Ankstesnis", +"Next": "Sekantis", +"Find and replace": "Surasti ir pakeisti", +"Could not find the specified string.": "Nepavyko rasti nurodytos eilut\u0117s.", +"Match case": "Atitinkamus", +"Whole words": "Visus \u017eod\u017eius", +"Spellcheck": "Ra\u0161ybos tikrinimas", +"Ignore": "Ignoruoti", +"Ignore all": "Ignoruoti visk\u0105", +"Finish": "Baigti", +"Add to Dictionary": "Prid\u0117ti \u012f \u017dodyn\u0105", +"Insert table": "\u012eterpti lentel\u0119", +"Table properties": "Lentel\u0117s savyb\u0117s", +"Delete table": "\u0160alinti lentel\u0119", +"Cell": "Langeliai", +"Row": "Eilut\u0117s", +"Column": "Stulpelis", +"Cell properties": "Langelio savyb\u0117s", +"Merge cells": "Sujungti langelius", +"Split cell": "Skaidyti langelius", +"Insert row before": "\u012eterpti eilut\u0119 prie\u0161", +"Insert row after": "\u012eterpti eilut\u0119 po", +"Delete row": "Naikinti eilut\u0119", +"Row properties": "Eilut\u0117s savyb\u0117s", +"Cut row": "I\u0161kirpti eilut\u0119", +"Copy row": "Kopijuoti eilut\u0119", +"Paste row before": "\u012ed\u0117ti eilut\u0119 prie\u0161", +"Paste row after": "\u012ed\u0117ti eilut\u0119 po", +"Insert column before": "\u012eterpti stulpel\u012f prie\u0161", +"Insert column after": "\u012eterpti stulpel\u012f po", +"Delete column": "Naikinti stulpel\u012f", +"Cols": "Stulpeliai", +"Rows": "Eilut\u0117s", +"Width": "Plotis", +"Height": "Auk\u0161tis", +"Cell spacing": "Tarpas tarp langeli\u0173", +"Cell padding": "Tarpas nuo langelio iki teksto", +"Caption": "Antra\u0161t\u0117", +"Left": "Kair\u0117", +"Center": "Centras", +"Right": "De\u0161in\u0117", +"Cell type": "Langelio tipas", +"Scope": "Strukt\u016bra", +"Alignment": "Lygiavimas", +"H Align": "H Lygiavimas", +"V Align": "V Lygiavimas", +"Top": "Vir\u0161uje", +"Middle": "Viduryje", +"Bottom": "Apa\u010dioje", +"Header cell": "Antra\u0161t\u0117s langelis", +"Row group": "Eilu\u010di\u0173 grup\u0117", +"Column group": "Stulpeli\u0173 grup\u0117", +"Row type": "Eilu\u010di\u0173 tipas", +"Header": "Antra\u0161t\u0117", +"Body": "Turinys", +"Footer": "Apa\u010dia", +"Border color": "R\u0117melio spalva", +"Insert template": "\u012eterpti \u0161ablon\u0105", +"Templates": "\u0160ablonai", +"Template": "\u0160ablonas", +"Text color": "Teksto spalva", +"Background color": "Fono spalva", +"Custom...": "Pasirinktinas...", +"Custom color": "Pasirinktina spalva", +"No color": "Jokios spalvos", +"Table of Contents": "Turinys", +"Show blocks": "Rodyti blokus", +"Show invisible characters": "Rodyti nematomus simbolius", +"Words: {0}": "\u017dod\u017eiai: {0}", +"{0} words": "{0} \u017eod\u017eiai", +"File": "Failas", +"Edit": "Redaguoti", +"Insert": "\u012eterpti", +"View": "Per\u017ei\u016bra", +"Format": "Formatas", +"Table": "Lentel\u0117", +"Tools": "\u012erankiai", +"Powered by {0}": "Sukurta {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Suformatuoto teksto laukas. D\u0117l meniu spauskite ALT-F9. U\u017eduo\u010di\u0173 juostos \u012fjungimui spauskite ALT-F10. Pagalbai - spauskite ALT-0." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lv.js new file mode 100644 index 0000000000..9f88e6f5c3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/lv.js @@ -0,0 +1,260 @@ +tinymce.addI18n('lv',{ +"Redo": "Solis uz priek\u0161u", +"Undo": "Solis atpaka\u013c", +"Cut": "Izgriezt", +"Copy": "Kop\u0113t", +"Paste": "Iel\u012bm\u0113t", +"Select all": "Iez\u012bm\u0113t visu", +"New document": "Jauns dokuments", +"Ok": "Ok", +"Cancel": "Atcelt", +"Visual aids": "Vizu\u0101l\u0101 pal\u012bdz\u012bba", +"Bold": "Treknraksts", +"Italic": "Sl\u012bpraksts", +"Underline": "Pasv\u012btrot", +"Strikethrough": "Nosv\u012btrot", +"Superscript": "Aug\u0161raksts", +"Subscript": "Apak\u0161raksts", +"Clear formatting": "No\u0146emt format\u0113jumu", +"Align left": "Pa kreisi", +"Align center": "Centr\u0113t", +"Align right": "Pa labi", +"Justify": "Gar ab\u0101m mal\u0101m", +"Bullet list": "Nenumur\u0113ts saraksts", +"Numbered list": "Numur\u0113ts saraksts", +"Decrease indent": "Samazin\u0101t atk\u0101pi", +"Increase indent": "Palielin\u0101t atk\u0101pi", +"Close": "Aizv\u0113rt", +"Formats": "Format\u0113jumi", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "J\u016bsu p\u0101rl\u016bkprogramma neatbalsta piek\u013cuvi starpliktuvei. L\u016bdzu, lietojiet Ctrl+X\/C\/V klaviat\u016bras sa\u012bsnes.", +"Headers": "Virsraksti", +"Header 1": "1. l\u012bme\u0146a virsraksts", +"Header 2": "2. l\u012bme\u0146a virsraksts", +"Header 3": "3. l\u012bme\u0146a virsraksts", +"Header 4": "4. l\u012bme\u0146a virsraksts", +"Header 5": "5. l\u012bme\u0146a virsraksts", +"Header 6": "6. l\u012bme\u0146a virsraksts", +"Headings": "Virsraksti", +"Heading 1": "1. l\u012bme\u0146a virsraksts", +"Heading 2": "2. l\u012bme\u0146a virsraksts", +"Heading 3": "3. l\u012bme\u0146a virsraksts", +"Heading 4": "4. l\u012bme\u0146a virsraksts", +"Heading 5": "5. l\u012bme\u0146a virsraksts", +"Heading 6": "6. l\u012bme\u0146a virsraksts", +"Div": "Div", +"Pre": "Pre", +"Code": "Kods", +"Paragraph": "Rindkopa", +"Blockquote": "Cit\u0101ts", +"Inline": "Inline elementi", +"Blocks": "Bloka elementi", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Iel\u012bm\u0113\u0161ana vienk\u0101r\u0161\u0101 teksta re\u017e\u012bm\u0101. Saturs tiks iel\u012bm\u0113ts bez format\u0113juma l\u012bdz \u0161\u012b opcija tiks atsl\u0113gta.", +"Font Family": "Fontu saime", +"Font Sizes": "Fontu izm\u0113ri", +"Class": "Klase", +"Browse for an image": "Izv\u0113l\u0113ties att\u0113lu", +"OR": "VAI", +"Drop an image here": "Ievelciet att\u0113lu \u0161eit", +"Upload": "Aug\u0161upiel\u0101d\u0113t", +"Block": "Bloks", +"Align": "L\u012bdzin\u0101t", +"Default": "Parastais", +"Circle": "Aplis", +"Disc": "Disks", +"Square": "Kvadr\u0101ts", +"Lower Alpha": "Lat\u012b\u0146u mazie burti", +"Lower Greek": "Grie\u0137u mazie burti", +"Lower Roman": "Romie\u0161u mazie burti", +"Upper Alpha": "Lat\u012b\u0146u lielie burti", +"Upper Roman": "Romie\u0161u lielie burti", +"Anchor": "Iek\u0161\u0113j\u0101 saite", +"Name": "Nosaukums", +"Id": "Identifikators", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Identifikatoram j\u0101s\u0101kas ar burtu, p\u0113c tam var satur\u0113t: burtus, ciparus, domuz\u012bmes, punktus, kolus vai pasv\u012btrojumz\u012bmes. ", +"You have unsaved changes are you sure you want to navigate away?": "Saturs ir labots un nav saglab\u0101ts. Vai tie\u0161\u0101m v\u0113laties atst\u0101t \u0161o lapu?", +"Restore last draft": "Atjaunot p\u0113d\u0113jo melnrakstu", +"Special character": "Speci\u0101l\u0101 rakstz\u012bme", +"Source code": "Pirmkods", +"Insert\/Edit code sample": "Ievad\u012bt\/Labot koda paraugu", +"Language": "Valoda", +"Code sample": "Koda paraugs", +"Color": "Kr\u0101sa", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "No kreis\u0101s uz labo", +"Right to left": "No lab\u0101s uz kreiso", +"Emoticons": "Emocijas", +"Document properties": "Dokumenta parametri", +"Title": "Nosaukums", +"Keywords": "Atsl\u0113gv\u0101rdi", +"Description": "Apraksts", +"Robots": "Programmas", +"Author": "Autors", +"Encoding": "Kod\u0113\u0161ana", +"Fullscreen": "Pilnekr\u0101na re\u017e\u012bms", +"Action": "Darb\u012bba", +"Shortcut": "Sa\u012bsne", +"Help": "Pal\u012bdz\u012bba", +"Address": "Adrese", +"Focus to menubar": "Fokuss uz izv\u0113lni", +"Focus to toolbar": "Fokuss uz r\u012bkjoslu", +"Focus to element path": "Fokuss uz elementa ce\u013cu", +"Focus to contextual toolbar": "Fokuss uz papildizv\u0113lni", +"Insert link (if link plugin activated)": "Ievietot saiti (Ja sai\u0161u spraudnis ir akt\u012bvs)", +"Save (if save plugin activated)": "Saglab\u0101t (Ja saglab\u0101\u0161anas spraudnis ir akt\u012bvs)", +"Find (if searchreplace plugin activated)": "Atrast (Ja \"searchreplace\" spraudnis ir akt\u012bvs)", +"Plugins installed ({0}):": "Spraud\u0146i instal\u0113ti ({0}):", +"Premium plugins:": "\u012apa\u0161ie spraud\u0146i:", +"Learn more...": "Uzzin\u0101t vair\u0101k...", +"You are using {0}": "J\u016bs lietojiet {0}", +"Plugins": "Spraud\u0146i", +"Handy Shortcuts": "Paroc\u012bgi \u012bsce\u013ci", +"Horizontal line": "Horizont\u0101l\u0101 l\u012bnija", +"Insert\/edit image": "Ievietot\/labot att\u0113lu", +"Image description": "Apraksts", +"Source": "Avots", +"Dimensions": "Izm\u0113ri", +"Constrain proportions": "Saglab\u0101t malu attiec\u012bbu", +"General": "Pamata info", +"Advanced": "Papildus", +"Style": "Stils", +"Vertical space": "Vertik\u0101l\u0101 atstarpe", +"Horizontal space": "Horizont\u0101l\u0101 atstarpe", +"Border": "Apmale", +"Insert image": "Ievietot att\u0113lu", +"Image": "Att\u0113ls", +"Image list": "Att\u0113lu saraksts", +"Rotate counterclockwise": "Pagriezt pret\u0113ji pulkste\u0146a r\u0101d\u012bt\u0101ja virzienam", +"Rotate clockwise": "Pagriezt pulkste\u0146a r\u0101d\u012bt\u0101ja virzien\u0101", +"Flip vertically": "Apmest vertik\u0101li", +"Flip horizontally": "Apmest horizont\u0101li", +"Edit image": "Redi\u0123\u0113t att\u0113lu", +"Image options": "Att\u0113la parametri", +"Zoom in": "Pietuvin\u0101t", +"Zoom out": "Att\u0101lin\u0101t", +"Crop": "Apgriezt", +"Resize": "Main\u012bt izm\u0113ru", +"Orientation": "Orient\u0101cija", +"Brightness": "Gai\u0161ums", +"Sharpen": "Asums", +"Contrast": "Kontrasts", +"Color levels": "Kr\u0101su l\u012bme\u0146i", +"Gamma": "Gamma", +"Invert": "Pret\u0113j\u0101s kr\u0101sas", +"Apply": "Pielietot", +"Back": "Atgriezties", +"Insert date\/time": "Ievietot datumu\/laiku", +"Date\/time": "Datums\/laiks", +"Insert link": "Ievietot saiti", +"Insert\/edit link": "Ievietot\/labot saiti", +"Text to display": "Nosaukums", +"Url": "Adrese", +"Target": "Kur atv\u0113rt", +"None": "\u2014", +"New window": "Jaun\u0101 \u0161\u0137irkl\u012b", +"Remove link": "No\u0146emt saiti", +"Anchors": "Saites", +"Link": "Saite", +"Paste or type a link": "Iekop\u0113jiet vai ierakstiet saiti", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "J\u016bs ievad\u012bj\u0101t e-pasta adresi. Lai t\u0101 korekti darbotos, ir nepiecie\u0161ams to papildin\u0101t ar \"mailto:\" priek\u0161\u0101. Vai v\u0113laties to izdar\u012bt?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "J\u016bs ievad\u012bj\u0101t \u0101r\u0113jo saiti. Lai t\u0101 korekti darbotos, ir nepiecie\u0161ams to papildin\u0101t ar \"http:\/\/\" priek\u0161\u0101. Vai v\u0113laties to izdar\u012bt?", +"Link list": "Sai\u0161u saraksts", +"Insert video": "Ievietot video", +"Insert\/edit video": "Ievietot\/redi\u0123\u0113t video", +"Insert\/edit media": "Ievietot\/labot att\u0113lu", +"Alternative source": "Alternat\u012bvs avots", +"Poster": "Att\u0113ls", +"Paste your embed code below:": "Iekop\u0113jiet Embed kodu \u0161eit:", +"Embed": "Embed kods", +"Media": "Att\u0113ls vai video", +"Nonbreaking space": "Nedal\u0101m\u0101 atstarpe", +"Page break": "P\u0101reja uz jauno lapu", +"Paste as text": "Iel\u012bm\u0113t bez format\u0113juma", +"Preview": "Priek\u0161skat\u012bt", +"Print": "Druk\u0101t", +"Save": "Saglab\u0101t", +"Find": "Mekl\u0113t", +"Replace with": "Aizvietot ar", +"Replace": "Aizvietot", +"Replace all": "Aizvietot visu", +"Prev": "Iepriek\u0161\u0113jais", +"Next": "N\u0101kamais", +"Find and replace": "Mekl\u0113t un aizvietot", +"Could not find the specified string.": "Mekl\u0113tais teksts netika atrasts", +"Match case": "At\u0161\u0137irt lielos un mazos burtus", +"Whole words": "Tikai pilnos v\u0101rdus", +"Spellcheck": "Pareizrakst\u012bbas p\u0101rbaude", +"Ignore": "Ignor\u0113t", +"Ignore all": "Ignor\u0113t visu", +"Finish": "Pabeigt", +"Add to Dictionary": "Pievienot v\u0101rdn\u012bcai", +"Insert table": "Ievietot tabulu", +"Table properties": "Tabulas parametri", +"Delete table": "Dz\u0113st tabulu", +"Cell": "\u0160\u016bna", +"Row": "Rinda", +"Column": "Kolonna", +"Cell properties": "\u0160\u016bnas parametri", +"Merge cells": "Apvienot \u0161\u016bnas", +"Split cell": "Sadal\u012bt \u0161\u016bnas", +"Insert row before": "Jauna rinda augst\u0101k", +"Insert row after": "Jauna rinda zem\u0101k", +"Delete row": "Dz\u0113st rindu", +"Row properties": "Rindas parametri", +"Cut row": "Izgriezt rindu", +"Copy row": "Kop\u0113t rindu", +"Paste row before": "Iel\u012bm\u0113t rindu augst\u0101k", +"Paste row after": "Iel\u012bm\u0113t rindu zem\u0101k", +"Insert column before": "Jauna kolonna pa kreisi", +"Insert column after": "Jauna kolonna pa labi", +"Delete column": "Dz\u0113st kolonu", +"Cols": "Kolonnas", +"Rows": "Rindas", +"Width": "Platums", +"Height": "Augstums", +"Cell spacing": "\u0160\u016bnu atstarpe", +"Cell padding": "Iek\u0161\u0113j\u0101 atstarpe", +"Caption": "Ar virsrakstu", +"Left": "Pa kreisi", +"Center": "Centr\u0113t", +"Right": "Pa labi", +"Cell type": "\u0160\u016bnas veids", +"Scope": "Attiecin\u0101t uz", +"Alignment": "Izl\u012bdzin\u0101\u0161ana", +"H Align": "Horizont\u0101lais novietojums", +"V Align": "Vertik\u0101lais novietojums", +"Top": "Aug\u0161\u0101", +"Middle": "Pa vidu", +"Bottom": "Apak\u0161\u0101", +"Header cell": "Galvenes \u0161\u016bna", +"Row group": "Rindu grupa", +"Column group": "Kolonnu grupa", +"Row type": "Rindas veids", +"Header": "Galvene", +"Body": "Saturs", +"Footer": "K\u0101jene", +"Border color": "Apmales kr\u0101sa", +"Insert template": "Ievietot veidni", +"Templates": "Veidnes", +"Template": "Veidne", +"Text color": "Teksta kr\u0101sa", +"Background color": "Fona kr\u0101sa", +"Custom...": "Izv\u0113l\u0113ties citu...", +"Custom color": "Specifisk\u0101 kr\u0101sa", +"No color": "Nenor\u0101d\u012bt kr\u0101su", +"Table of Contents": "Saturs", +"Show blocks": "R\u0101d\u012bt blokus", +"Show invisible characters": "R\u0101d\u012bt neredzam\u0101s rakstz\u012bmes", +"Words: {0}": "V\u0101rdi: {0}", +"{0} words": "{0} v\u0101rdi", +"File": "Datne", +"Edit": "Labot", +"Insert": "Ievietot", +"View": "Skat\u012bt", +"Format": "Format\u0113t", +"Table": "Tabula", +"Tools": "R\u012bki", +"Powered by {0}": "Darb\u012bbu nodro\u0161ina {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Satura redaktors. Nospiediet ALT-F9 lai par\u0101d\u012btu izv\u0113lni, ALT-F10 - r\u012bkjoslu vai ALT-0 - pal\u012bdz\u012bbu." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js new file mode 100644 index 0000000000..59233450b0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js @@ -0,0 +1,261 @@ +tinymce.addI18n('nb_NO',{ +"Redo": "Utf\u00f8r likevel", +"Undo": "Angre", +"Cut": "Klipp ut", +"Copy": "Kopier", +"Paste": "Lim inn", +"Select all": "Marker alt", +"New document": "Nytt dokument", +"Ok": "OK", +"Cancel": "Avbryt", +"Visual aids": "Visuelle hjelpemidler", +"Bold": "Halvfet", +"Italic": "Kursiv", +"Underline": "Understreket", +"Strikethrough": "Gjennomstreket", +"Superscript": "Hevet skrift", +"Subscript": "Senket skrift", +"Clear formatting": "Fjern formateringer", +"Align left": "Venstrejustert", +"Align center": "Midtstilt", +"Align right": "H\u00f8yrejustert", +"Justify": "Juster alle linjer", +"Bullet list": "Punktliste", +"Numbered list": "Nummerliste", +"Decrease indent": "Reduser innrykk", +"Increase indent": "\u00d8k innrykk", +"Close": "Lukk", +"Formats": "Stiler", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Nettleseren din st\u00f8tter ikke direkte tilgang til utklippsboken. Bruk istedet tastatur-snarveiene Ctrl+X\/C\/V, eller Cmd+X\/C\/V p\u00e5 Mac.", +"Headers": "Overskrifter", +"Header 1": "Overskrift 1", +"Header 2": "Overskrift 2", +"Header 3": "Overskrift 3", +"Header 4": "Overskrift 4", +"Header 5": "Overskrift 5", +"Header 6": "Overskrift 6", +"Headings": "Overskrifter", +"Heading 1": "Overskrift 1", +"Heading 2": "Overskrift 2", +"Heading 3": "Overskrift 3", +"Heading 4": "Overskrift 4", +"Heading 5": "Overskrift 5", +"Heading 6": "Overskrift 6", +"Preformatted": "Forh\u00e5ndsformatert", +"Div": "Delblokk
", +"Pre": "Definert
",
+"Code": "Kode ",
+"Paragraph": "Avsnitt 

", +"Blockquote": "Sitatblokk

", +"Inline": "Innkapslet ", +"Blocks": "Blokker", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Lim inn er n\u00e5 i ren-tekst modus. Kopiert innhold vil bli limt inn som ren tekst inntil du sl\u00e5r av dette valget.", +"Font Family": "Skriftsnitt", +"Font Sizes": "St\u00f8rrelse", +"Class": "Klasse", +"Browse for an image": "S\u00f8k etter bilde", +"OR": "ELLER", +"Drop an image here": "Slipp et bilde her", +"Upload": "Last opp", +"Block": "Blokk", +"Align": "Juster", +"Default": "Normal", +"Circle": "\u00c5pen sirkel", +"Disc": "Fylt sirkel", +"Square": "Fylt firkant", +"Lower Alpha": "Minuskler", +"Lower Greek": "Greske minuskler", +"Lower Roman": "Romerske minuskler", +"Upper Alpha": "Versaler", +"Upper Roman": "Romerske versaler", +"Anchor": "Anker", +"Name": "Navn", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id burde starte med en bokstav, bare fulgt av bokstaver, nummer, streker, punktum, koloner eller understreker.", +"You have unsaved changes are you sure you want to navigate away?": "Du har ikke arkivert endringene. Vil du fortsette uten \u00e5 arkivere?", +"Restore last draft": "Gjenopprett siste utkast", +"Special character": "Spesialtegn", +"Source code": "Kildekode", +"Insert\/Edit code sample": "Sett inn\/endre kodeeksempel", +"Language": "Spr\u00e5k", +"Code sample": "Kodeeksempel", +"Color": "Farge", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Venstre til h\u00f8yre", +"Right to left": "H\u00f8yre til venstre", +"Emoticons": "Hum\u00f8rfjes", +"Document properties": "Dokumentegenskaper", +"Title": "Tittel", +"Keywords": "N\u00f8kkelord", +"Description": "Beskrivelse", +"Robots": "Roboter", +"Author": "Forfatter", +"Encoding": "Tegnkoding", +"Fullscreen": "Fullskjerm", +"Action": "Handling", +"Shortcut": "Snarvei", +"Help": "Hjelp", +"Address": "Adresse", +"Focus to menubar": "Fokus p\u00e5 menylinje", +"Focus to toolbar": "Fokus p\u00e5 verkt\u00f8ylinje", +"Focus to element path": "Fokus p\u00e5 elementsti", +"Focus to contextual toolbar": "Fokus p\u00e5 kontekstuell verkt\u00f8ylinje", +"Insert link (if link plugin activated)": "Sett inn lenke (dersom lenketillegg er aktivert)", +"Save (if save plugin activated)": "Lagre (dersom lagretillegg er aktivert)", +"Find (if searchreplace plugin activated)": "Finn (dersom tillegg for s\u00f8k og erstatt er aktivert)", +"Plugins installed ({0}):": "Installerte tillegg ({0}):", +"Premium plugins:": "Premiumtillegg:", +"Learn more...": "Les mer ...", +"You are using {0}": "Du bruker {0}", +"Plugins": "Tillegg", +"Handy Shortcuts": "Nyttige snarveier", +"Horizontal line": "Horisontal linje", +"Insert\/edit image": "Sett inn\/endre bilde", +"Image description": "Bildebeskrivelse", +"Source": "Bildelenke", +"Dimensions": "Dimensjoner", +"Constrain proportions": "Behold proporsjoner", +"General": "Generelt", +"Advanced": "Avansert", +"Style": "Stil", +"Vertical space": "Vertikal marg", +"Horizontal space": "Horisontal marg", +"Border": "Ramme", +"Insert image": "Sett inn bilde", +"Image": "Bilde", +"Image list": "Bildeliste", +"Rotate counterclockwise": "Roter mot venstre", +"Rotate clockwise": "Roter mot h\u00f8yre", +"Flip vertically": "Speilvend vertikalt", +"Flip horizontally": "Speilvend horisontalt", +"Edit image": "Rediger bilde", +"Image options": "Bilde innstillinger", +"Zoom in": "Zoom inn", +"Zoom out": "Zoom ut", +"Crop": "Beskj\u00e6r", +"Resize": "Skaler", +"Orientation": "Orientering", +"Brightness": "Lysstyrke", +"Sharpen": "Skarphet", +"Contrast": "Kontrast", +"Color levels": "Fargeniv\u00e5", +"Gamma": "Gamma", +"Invert": "Inverter", +"Apply": "Utf\u00f8r", +"Back": "Tilbake", +"Insert date\/time": "Sett inn dato\/tid", +"Date\/time": "Dato\/tid", +"Insert link": "Sett inn lenke", +"Insert\/edit link": "Sett inn\/endre lenke", +"Text to display": "Tekst som skal vises", +"Url": "Url", +"Target": "M\u00e5l", +"None": "Ingen", +"New window": "Nytt vindu", +"Remove link": "Fjern lenke", +"Anchors": "Anker", +"Link": "Lenke", +"Paste or type a link": "Lim inn eller skriv en lenke", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Oppgitte URL ser ut til \u00e5 v\u00e6re en epost-adresse. \u00d8nsker du \u00e5 sette inn p\u00e5krevet mailto: prefiks forran epost-adressen?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Oppgitt URL ser ut til \u00e5 v\u00e6re en e-postadresse. \u00d8nsker du \u00e5 sette inn p\u00e5krevd mailto:-prefiks foran e-postadressen?", +"Link list": "Lenkeliste", +"Insert video": "Sett inn video", +"Insert\/edit video": "Sett inn\/rediger video", +"Insert\/edit media": "Sett inn\/endre media", +"Alternative source": "Alternativ kilde", +"Poster": "Plakatbilde", +"Paste your embed code below:": "Lim inn inkluderings-koden nedenfor", +"Embed": "Inkluder", +"Media": "Media", +"Nonbreaking space": "Hardt mellomrom", +"Page break": "Sideskifte", +"Paste as text": "Lim inn som tekst", +"Preview": "Forh\u00e5ndsvisning", +"Print": "Skriv ut", +"Save": "Arkiver", +"Find": "Finn", +"Replace with": "Erstatt med", +"Replace": "Erstatt", +"Replace all": "Erstatt alle", +"Prev": "Forrige", +"Next": "Neste", +"Find and replace": "Finn og erstatt", +"Could not find the specified string.": "Kunne ikke finne den spesifiserte teksten", +"Match case": "Match store og sm\u00e5 bokstaver", +"Whole words": "Hele ord", +"Spellcheck": "Stavekontroll", +"Ignore": "Ignorer", +"Ignore all": "Ignorer alle", +"Finish": "Avslutt", +"Add to Dictionary": "Legg til i ordliste", +"Insert table": "Sett inn tabell", +"Table properties": "Tabell egenskaper", +"Delete table": "Slett tabell", +"Cell": "Celle", +"Row": "Rad", +"Column": "Kolonne", +"Cell properties": "Celle egenskaper", +"Merge cells": "Sl\u00e5 sammen celler", +"Split cell": "Splitt celle", +"Insert row before": "Sett inn rad f\u00f8r", +"Insert row after": "Sett in rad etter", +"Delete row": "Slett rad", +"Row properties": "Rad egenskaper", +"Cut row": "Klipp ut rad", +"Copy row": "Kopier rad", +"Paste row before": "Lim inn rad f\u00f8r", +"Paste row after": "Lim inn rad etter", +"Insert column before": "Sett inn kolonne f\u00f8r", +"Insert column after": "Sett inn kolonne etter", +"Delete column": "Slett kolonne", +"Cols": "Kolonner", +"Rows": "Rader", +"Width": "Bredde", +"Height": "H\u00f8yde", +"Cell spacing": "Celleavstand", +"Cell padding": "Cellemarg", +"Caption": "Tittel", +"Left": "Venstre", +"Center": "Midtstilt", +"Right": "H\u00f8yre", +"Cell type": "Celletype", +"Scope": "Omfang", +"Alignment": "Justering", +"H Align": "H Justering", +"V Align": "V Justering", +"Top": "Topp", +"Middle": "Midten", +"Bottom": "Bunn", +"Header cell": "Topptekst-celle", +"Row group": "Radgruppe", +"Column group": "Kolonnegruppe", +"Row type": "Rad-type", +"Header": "Topptekst", +"Body": "Br\u00f8dtekst", +"Footer": "Bunntekst", +"Border color": "Rammefarge", +"Insert template": "Sett inn mal", +"Templates": "Maler", +"Template": "Mal", +"Text color": "Tekstfarge", +"Background color": "Bakgrunnsfarge", +"Custom...": "Tilpass...", +"Custom color": "Tilpasset farge", +"No color": "Ingen farge", +"Table of Contents": "Innholdsfortegnelse", +"Show blocks": "Vis blokker", +"Show invisible characters": "Vis skjulte tegn", +"Words: {0}": "Antall ord: {0}", +"{0} words": "{0} ord", +"File": "Arkiv", +"Edit": "Rediger", +"Insert": "Sett inn", +"View": "Vis", +"Format": "Format", +"Table": "Tabell", +"Tools": "Verkt\u00f8y", +"Powered by {0}": "Redigert med {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Tekstredigering. Tast ALT-F9 for meny. Tast ALT-F10 for verkt\u00f8ys-rader. Tast ALT-0 for hjelp." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js index ee1f335624..c80590b297 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js @@ -1,219 +1,261 @@ tinymce.addI18n('nl',{ -"Cut": "Knippen", -"Heading 5": "Kop 5", -"Header 2": "Kop 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Uw browser ondersteunt geen toegang tot het clipboard. Gelieve ctrl+X\/C\/V sneltoetsen te gebruiken.", -"Heading 4": "Kop 4", -"Div": "Div", -"Heading 2": "Kop 2", -"Paste": "Plakken", -"Close": "Sluiten", -"Font Family": "Lettertype", -"Pre": "Pre", -"Align right": "Rechts uitlijnen", -"New document": "Nieuw document", -"Blockquote": "Quote", -"Numbered list": "Nummering", -"Heading 1": "Kop 1", -"Headings": "Koppen", -"Increase indent": "Inspringen vergroten", -"Formats": "Opmaak", -"Headers": "Kopteksten", -"Select all": "Alles selecteren", -"Header 3": "Kop 3", -"Blocks": "Blok", -"Undo": "Ongedaan maken", -"Strikethrough": "Doorhalen", -"Bullet list": "Opsommingsteken", -"Header 1": "Kop 1", -"Superscript": "Superscript", -"Clear formatting": "Opmaak verwijderen", -"Font Sizes": "Tekengrootte", -"Subscript": "Subscript", -"Header 6": "Kop 6", "Redo": "Opnieuw", -"Paragraph": "Paragraaf", -"Ok": "Ok\u00e9", -"Bold": "Vet", -"Code": "Code", -"Italic": "Cursief", -"Align center": "Centreren", -"Header 5": "Kop 5", -"Heading 6": "Kop 6", -"Heading 3": "Kop 3", -"Decrease indent": "Inspringen verkleinen", -"Header 4": "Kop 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Plakken gebeurt nu als platte tekst. Tekst wordt nu ingevoegd zonder opmaak tot deze optie uitgeschakeld wordt.", -"Underline": "Onderstreept", -"Cancel": "Annuleren", -"Justify": "Uitlijnen", -"Inline": "Inlijn", +"Undo": "Ongedaan maken", +"Cut": "Knippen", "Copy": "Kopi\u00ebren", -"Align left": "Links uitlijnen", +"Paste": "Plakken", +"Select all": "Alles selecteren", +"New document": "Nieuw document", +"Ok": "Ok\u00e9", +"Cancel": "Annuleren", "Visual aids": "Hulpmiddelen", -"Lower Greek": "Griekse letters", -"Square": "Vierkant", +"Bold": "Vet", +"Italic": "Cursief", +"Underline": "Onderstreept", +"Strikethrough": "Doorhalen", +"Superscript": "Superscript", +"Subscript": "Subscript", +"Clear formatting": "Opmaak verwijderen", +"Align left": "Links uitlijnen", +"Align center": "Centreren", +"Align right": "Rechts uitlijnen", +"Justify": "Uitlijnen", +"Bullet list": "Opsommingsteken", +"Numbered list": "Nummering", +"Decrease indent": "Inspringen verkleinen", +"Increase indent": "Inspringen vergroten", +"Close": "Sluiten", +"Formats": "Opmaak", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Uw browser ondersteunt geen toegang tot het clipboard. Gelieve ctrl+X\/C\/V sneltoetsen te gebruiken.", +"Headers": "Kopteksten", +"Header 1": "Kop 1", +"Header 2": "Kop 2", +"Header 3": "Kop 3", +"Header 4": "Kop 4", +"Header 5": "Kop 5", +"Header 6": "Kop 6", +"Headings": "Koppen", +"Heading 1": "Kop 1", +"Heading 2": "Kop 2", +"Heading 3": "Kop 3", +"Heading 4": "Kop 4", +"Heading 5": "Kop 5", +"Heading 6": "Kop 6", +"Preformatted": "Voor-opgemaakt", +"Div": "Div", +"Pre": "Pre", +"Code": "Code", +"Paragraph": "Paragraaf", +"Blockquote": "Quote", +"Inline": "Inlijn", +"Blocks": "Blok", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Plakken gebeurt nu als platte tekst. Tekst wordt nu ingevoegd zonder opmaak tot deze optie uitgeschakeld wordt.", +"Font Family": "Lettertype", +"Font Sizes": "Tekengrootte", +"Class": "Class", +"Browse for an image": "Zoek naar een afbeelding", +"OR": "OF", +"Drop an image here": "Plaats hier een afbeelding", +"Upload": "Uploaden", +"Block": "Blok", +"Align": "Uitlijnen", "Default": "Standaard", -"Lower Alpha": "Kleine letters", "Circle": "Cirkel", "Disc": "Bolletje", +"Square": "Vierkant", +"Lower Alpha": "Kleine letters", +"Lower Greek": "Griekse letters", +"Lower Roman": "Romeinse cijfers klein", "Upper Alpha": "Hoofdletters", "Upper Roman": "Romeinse cijfers groot", -"Lower Roman": "Romeinse cijfers klein", -"Name": "Naam", "Anchor": "Anker", +"Name": "Naam", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID moet beginnen met een letter, gevolgd door letters, nummers, streepjes, punten, dubbele punten of underscores.", "You have unsaved changes are you sure you want to navigate away?": "U hebt niet alles opgeslagen bent u zeker dat u de pagina wenst te verlaten?", "Restore last draft": "Herstel het laatste concept", "Special character": "Speciale karakters", "Source code": "Broncode", -"B": "Blauw", +"Insert\/Edit code sample": "Broncode invoegen\/bewerken", +"Language": "Programmeertaal", +"Code sample": "Broncode voorbeeld", +"Color": "Kleur", "R": "Rood", "G": "Groen", -"Color": "Kleur", -"Right to left": "Rechts naar links", +"B": "Blauw", "Left to right": "Links naar rechts", +"Right to left": "Rechts naar links", "Emoticons": "Emoticons", -"Robots": "Robots", "Document properties": "Document eigenschappen", "Title": "Titel", "Keywords": "Sleutelwoorden", -"Encoding": "Codering", "Description": "Omschrijving", +"Robots": "Robots", "Author": "Auteur", +"Encoding": "Codering", "Fullscreen": "Volledig scherm", +"Action": "Actie", +"Shortcut": "Snelkoppeling", +"Help": "Help", +"Address": "Adres", +"Focus to menubar": "Menubalk selecteren", +"Focus to toolbar": "Werkbalk selecteren", +"Focus to element path": "Element pad selecteren", +"Focus to contextual toolbar": "Contextuele werkbalk selecteren", +"Insert link (if link plugin activated)": "Link invoegen (als link plug-in geactiveerd is)", +"Save (if save plugin activated)": "Opslaan (als opslaan plug-in ingeschakeld is)", +"Find (if searchreplace plugin activated)": "Zoeken (als zoeken\/vervangen plug-in ingeschakeld is)", +"Plugins installed ({0}):": "Plug-ins ge\u00efnstalleerd ({0}):", +"Premium plugins:": "Premium plug-ins:", +"Learn more...": "Leer meer...", +"You are using {0}": "Je gebruikt {0}", +"Plugins": "Plug-ins", +"Handy Shortcuts": "Handige snelkoppelingen", "Horizontal line": "Horizontale lijn", -"Horizontal space": "Horizontale ruimte", "Insert\/edit image": "Afbeelding invoegen\/bewerken", +"Image description": "Afbeelding omschrijving", +"Source": "Bron", +"Dimensions": "Afmetingen", +"Constrain proportions": "Verhoudingen behouden", "General": "Algemeen", "Advanced": "Geavanceerd", -"Source": "Bron", -"Border": "Rand", -"Constrain proportions": "Verhoudingen behouden", -"Vertical space": "Verticale ruimte", -"Image description": "Afbeelding omschrijving", "Style": "Stijl", -"Dimensions": "Afmetingen", +"Vertical space": "Verticale ruimte", +"Horizontal space": "Horizontale ruimte", +"Border": "Rand", "Insert image": "Afbeelding invoegen", -"Zoom in": "Inzoomen", -"Contrast": "Contrast", -"Back": "Terug", -"Gamma": "Gamma", -"Flip horizontally": "Horizontaal spiegelen", -"Resize": "Formaat aanpassen", -"Sharpen": "Scherpte", -"Zoom out": "Uitzoomen", -"Image options": "Afbeelding opties", -"Apply": "Toepassen", -"Brightness": "Helderheid", -"Rotate clockwise": "Rechtsom draaien", +"Image": "Afbeelding", +"Image list": "Afbeeldingenlijst", "Rotate counterclockwise": "Linksom draaien", -"Edit image": "Bewerk afbeelding", -"Color levels": "Kleurniveau's", -"Crop": "Uitsnijden", -"Orientation": "Orientatie", +"Rotate clockwise": "Rechtsom draaien", "Flip vertically": "Verticaal spiegelen", +"Flip horizontally": "Horizontaal spiegelen", +"Edit image": "Bewerk afbeelding", +"Image options": "Afbeelding opties", +"Zoom in": "Inzoomen", +"Zoom out": "Uitzoomen", +"Crop": "Uitsnijden", +"Resize": "Formaat aanpassen", +"Orientation": "Orientatie", +"Brightness": "Helderheid", +"Sharpen": "Scherpte", +"Contrast": "Contrast", +"Color levels": "Kleurniveau's", +"Gamma": "Gamma", "Invert": "Omkeren", +"Apply": "Toepassen", +"Back": "Terug", "Insert date\/time": "Voeg datum\/tijd in", -"Remove link": "Link verwijderen", -"Url": "Url", -"Text to display": "Linktekst", -"Anchors": "Anker", +"Date\/time": "Datum\/tijd", "Insert link": "Hyperlink invoegen", -"New window": "Nieuw venster", -"None": "Geen", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "De ingegeven URL verwijst naar een extern adres. Wil je er \"http:\/\/\" aan toevoegen?", -"Target": "Doel", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "De ingegeven URL lijkt op een e-mailadres. Wil je er \"mailto:\" aan toevoegen?", "Insert\/edit link": "Hyperlink invoegen\/bewerken", -"Insert\/edit video": "Video invoegen\/bewerken", -"Poster": "Poster", -"Alternative source": "Alternatieve bron", -"Paste your embed code below:": "Plak u in te sluiten code hieronder:", +"Text to display": "Linktekst", +"Url": "Url", +"Target": "Doel", +"None": "Geen", +"New window": "Nieuw venster", +"Remove link": "Link verwijderen", +"Anchors": "Anker", +"Link": "Link", +"Paste or type a link": "Plak of typ een link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "De ingegeven URL lijkt op een e-mailadres. Wil je er \"mailto:\" aan toevoegen?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "De ingegeven URL verwijst naar een extern adres. Wil je er \"http:\/\/\" aan toevoegen?", +"Link list": "Linklijst", "Insert video": "Video invoegen", +"Insert\/edit video": "Video invoegen\/bewerken", +"Insert\/edit media": "Media invoegen\/bewerken", +"Alternative source": "Alternatieve bron", +"Poster": "Poster", +"Paste your embed code below:": "Plak u in te sluiten code hieronder:", "Embed": "Insluiten", +"Media": "Media", "Nonbreaking space": "Vaste spatie invoegen", "Page break": "Pagina einde", "Paste as text": "Plakken als tekst", "Preview": "Voorbeeld", "Print": "Print", "Save": "Opslaan", -"Could not find the specified string.": "Geen resultaten gevonden", -"Replace": "Vervangen", -"Next": "Volgende", -"Whole words": "Alleen hele woorden", -"Find and replace": "Zoek en vervang", -"Replace with": "Vervangen door", "Find": "Zoeken", +"Replace with": "Vervangen door", +"Replace": "Vervangen", "Replace all": "Alles vervangen", -"Match case": "Identieke hoofd\/kleine letters", "Prev": "Vorige", +"Next": "Volgende", +"Find and replace": "Zoek en vervang", +"Could not find the specified string.": "Geen resultaten gevonden", +"Match case": "Identieke hoofd\/kleine letters", +"Whole words": "Alleen hele woorden", "Spellcheck": "Spellingscontrole", -"Finish": "Einde", -"Ignore all": "Alles negeren", "Ignore": "Negeren", +"Ignore all": "Alles negeren", +"Finish": "Einde", "Add to Dictionary": "Toevoegen aan woordenlijst", -"Insert row before": "Voeg rij boven toe", -"Rows": "Rijen", -"Height": "Hoogte", -"Paste row after": "Plak rij onder", -"Alignment": "Uitlijning", -"Border color": "Randkleur", -"Column group": "Kolomgroep", -"Row": "Rij", -"Insert column before": "Voeg kolom in voor", -"Split cell": "Cel splitsen", -"Cell padding": "Ruimte binnen cel", -"Cell spacing": "Celruimte", -"Row type": "Rijtype", "Insert table": "Tabel invoegen", -"Body": "Body", -"Caption": "Onderschrift", -"Footer": "Voettekst", -"Delete row": "Verwijder rij", -"Paste row before": "Plak rij boven", -"Scope": "Bereik", -"Delete table": "Verwijder tabel", -"H Align": "Links uitlijnen", -"Top": "Bovenaan", -"Header cell": "Kopcel", -"Column": "Kolom", -"Row group": "Rijgroep", -"Cell": "Cel", -"Middle": "Centreren", -"Cell type": "Celtype", -"Copy row": "Kopieer rij", -"Row properties": "Rij eigenschappen", "Table properties": "Tabel eigenschappen", -"Bottom": "Onderaan", -"V Align": "Boven uitlijnen", -"Header": "Koptekst", -"Right": "Rechts", -"Insert column after": "Voeg kolom in na", -"Cols": "Kolommen", -"Insert row after": "Voeg rij onder toe", -"Width": "Breedte", +"Delete table": "Verwijder tabel", +"Cell": "Cel", +"Row": "Rij", +"Column": "Kolom", "Cell properties": "Cel eigenschappen", -"Left": "Links", -"Cut row": "Knip rij", -"Delete column": "Verwijder kolom", -"Center": "Midden", "Merge cells": "Cellen samenvoegen", +"Split cell": "Cel splitsen", +"Insert row before": "Voeg rij boven toe", +"Insert row after": "Voeg rij onder toe", +"Delete row": "Verwijder rij", +"Row properties": "Rij eigenschappen", +"Cut row": "Knip rij", +"Copy row": "Kopieer rij", +"Paste row before": "Plak rij boven", +"Paste row after": "Plak rij onder", +"Insert column before": "Voeg kolom in voor", +"Insert column after": "Voeg kolom in na", +"Delete column": "Verwijder kolom", +"Cols": "Kolommen", +"Rows": "Rijen", +"Width": "Breedte", +"Height": "Hoogte", +"Cell spacing": "Celruimte", +"Cell padding": "Ruimte binnen cel", +"Caption": "Onderschrift", +"Left": "Links", +"Center": "Midden", +"Right": "Rechts", +"Cell type": "Celtype", +"Scope": "Bereik", +"Alignment": "Uitlijning", +"H Align": "Links uitlijnen", +"V Align": "Boven uitlijnen", +"Top": "Bovenaan", +"Middle": "Centreren", +"Bottom": "Onderaan", +"Header cell": "Kopcel", +"Row group": "Rijgroep", +"Column group": "Kolomgroep", +"Row type": "Rijtype", +"Header": "Koptekst", +"Body": "Body", +"Footer": "Voettekst", +"Border color": "Randkleur", "Insert template": "Sjabloon invoegen", "Templates": "Sjablonen", +"Template": "Sjabloon", +"Text color": "Tekstkleur", "Background color": "Achtergrondkleur", "Custom...": "Eigen...", "Custom color": "Eigen kleur", "No color": "Geen kleur", -"Text color": "Tekstkleur", +"Table of Contents": "Inhoudsopgave", "Show blocks": "Blokken tonen", "Show invisible characters": "Onzichtbare karakters tonen", "Words: {0}": "Woorden: {0}", -"Insert": "Invoegen", +"{0} words": "{0} woorden", "File": "Bestand", "Edit": "Bewerken", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Druk ALT-F9 voor het menu. Druk ALT-F10 voor de toolbar. Druk ALT-0 voor help.", -"Tools": "Gereedschap", +"Insert": "Invoegen", "View": "Beeld", +"Format": "Opmaak", "Table": "Tabel", -"Format": "Opmaak" +"Tools": "Gereedschap", +"Powered by {0}": "Gemaakt door {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Druk ALT-F9 voor het menu. Druk ALT-F10 voor de toolbar. Druk ALT-0 voor help." }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/no.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/no.js deleted file mode 100644 index 69ded56dab..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/no.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({no:{common:{"more_colors":"Flere farger","invalid_data":"Feil: Ugyldig verdi er skrevet inn, disse er merket med r\u00f8dt","popup_blocked":"Beklager, men vi har registrert at din popup-sperrer har blokkert et vindu i nettleseren. Du m\u00e5 oppheve popup-sperren for at nettstedet skal f\u00e5 tilgang til dette verkt\u00f8yet","clipboard_no_support":"For tiden ikke st\u00f8ttet av din nettleser, bruk tastatursnarveier i stedet.","clipboard_msg":"Klipp ut / Kopier /Lim inn fungerer ikke i Mozilla og Firefox. Vil du vite mer om dette?","not_set":"--Ikke satt--","class_name":"Klasse",browse:"Bla gjennom",close:"Lukk",cancel:"Avbryt",update:"Oppdater",insert:"Sett inn",apply:"Bruk","edit_confirm":"Vil du bruke WYSIWYG-editoren for dette tekstfeltet?","invalid_data_number":"{#field} m\u00e5 v\u00e6re et nummer","invalid_data_min":"{#field} m\u00e5 v\u00e6re et nummber st\u00f8rre en {#min}","invalid_data_size":"{#field} m\u00e5 v\u00e6re et nummer eller prosent av",value:"(verdi)"},contextmenu:{full:"Full",right:"H\u00f8yre",center:"Midtstilt",left:"Venstre",align:"Justering"},insertdatetime:{"day_short":"S\u00f8n,Man,Tir,Ons,Tor,Fre,L\u00f8r,S\u00f8n","day_long":"s\u00f8ndag,mandag,tirsdag,onsdag,torsdag,fredag,l\u00f8rdag,s\u00f8ndag","months_short":"jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des","months_long":"januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember","inserttime_desc":"Sett inn tid","insertdate_desc":"Sett inn dato","time_fmt":"%H:%M:%S","date_fmt":"%d-%m-%Y"},print:{"print_desc":"Skriv ut"},preview:{"preview_desc":"Forh\u00e5ndsvisning"},directionality:{"rtl_desc":"Retning h\u00f8yre mot venstre","ltr_desc":"Retning venstre mot h\u00f8yre"},layer:{content:"Nytt lag ...","absolute_desc":"Sl\u00e5 p\u00e5/av absolutt plassering","backward_desc":"Flytt bakover","forward_desc":"Flytt fremover","insertlayer_desc":"Sett inn nytt lag"},save:{"save_desc":"Lagre","cancel_desc":"Kanseller alle endringer"},nonbreaking:{"nonbreaking_desc":"Sett inn tegn for hardt mellomrom"},iespell:{download:"ieSpell ikke funnet. \u00d8nsker du \u00e5 installere ieSpell?","iespell_desc":"Stavekontroll"},advhr:{"advhr_desc":"Horisontal linje","delta_height":"","delta_width":""},emotions:{"emotions_desc":"Hum\u00f8rfjes","delta_height":"","delta_width":""},searchreplace:{"replace_desc":"S\u00f8k/Erstatt","search_desc":"S\u00f8k","delta_width":"","delta_height":""},advimage:{"image_desc":"Sett inn / rediger bilde","delta_width":"","delta_height":""},advlink:{"link_desc":"Sett inn / rediger lenke","delta_height":"","delta_width":""},xhtmlxtras:{"attribs_desc":"Sett inn / rediger attributter","ins_desc":"Innsetting","del_desc":"Sletting","acronym_desc":"Akronym","abbr_desc":"Forkortelse","cite_desc":"Sitat","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"Rediger CSS-stil","delta_height":"","delta_width":""},paste:{"plaintext_mode":"Lim inn er n\u00e5 i vanlig tekst modus. Klikk igjen for \u00e5 bytte til vanlig innlimings modus.","plaintext_mode_sticky":"Lim inn er n\u00e5 i vanlig tekst modus. Klikk igjen for \u00e5 bytte til vanlig innlimings modus. Etter at du limer inn noe vil du g\u00e5 tilbake til ordin\u00e6r innliming.","selectall_desc":"Merk alt","paste_word_desc":"Lim inn fra Word","paste_text_desc":"Lim inn som ren tekst"},"paste_dlg":{"word_title":"Bruk CTRL+V p\u00e5 tastaturet for \u00e5 lime inn teksten i dette vinduet.","text_linebreaks":"Behold tekstbryting","text_title":"Bruk CTRL+V p\u00e5 tastaturet for \u00e5 lime inn teksten i dette vinduet."},table:{cell:"Celle",col:"Kolonne",row:"Rad",del:"Slett tabell","copy_row_desc":"Kopier rad","cut_row_desc":"Slett rad","paste_row_after_desc":"Lim inn rad etter","paste_row_before_desc":"Lim inn rad foran","props_desc":"Tabellegenskaper","cell_desc":"Celleegenskaper","row_desc":"Radegenskaper","merge_cells_desc":"Sl\u00e5 sammen celler","split_cells_desc":"Splitt sammensl\u00e5tte celler","delete_col_desc":"Slett kolonne","col_after_desc":"Sett inn kolonne etter","col_before_desc":"Sett inn kolonne foran","delete_row_desc":"Slett rad","row_after_desc":"Sett inn rad etter","row_before_desc":"Sett inn rad foran",desc:"Sett inn ny tabell","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"Hvis du gjenoppretter tidligere lagret innhold s\u00e5 vil du miste alt n\u00e5v\u00e6rende innhold i editoren.\n\nEr du sikker p\u00e5 at du vil gjenopprette tidligere lagret innhold?.","restore_content":"Gjenopprett autolagret innhold.","unload_msg":"Utf\u00f8rte endringer g\u00e5r tapt hvis du navigerer vekk fra denne siden!"},fullscreen:{desc:"Sl\u00e5 fullskjermsmodus av/p\u00e5"},media:{edit:"Rediger innebygd objekt",desc:"Sett inn / rediger innebygd objekt","delta_height":"","delta_width":""},fullpage:{desc:"Dokumentegenskaper","delta_width":"","delta_height":""},template:{desc:"Sett inn forh\u00e5ndsdefinert malinnhold"},visualchars:{desc:"Visuelle konktrolltegn p\u00e5/av"},spellchecker:{desc:"Stavekontroll p\u00e5/av",menu:"Oppsett stavekontroll","ignore_word":"Ignorer ord","ignore_words":"Ignorer alt",langs:"Spr\u00e5k",wait:"Vennligst vent ...",sug:"Forslag","no_sug":"Ingen forslag","no_mpell":"Ingen stavefeil funnet.","learn_word":"L\u00e6r ordet"},pagebreak:{desc:"Sett inn sideskift"},advlist:{types:"Types",def:"Standard","lower_alpha":"Sm\u00e5 alfanumerisk","lower_greek":"Sm\u00e5 gresk","lower_roman":"Sm\u00e5 roman","upper_alpha":"Store alfanumerisk","upper_roman":"Store roman",circle:"Sirkel",disc:"Plate",square:"Firkant"},colors:{"333300":"M\u00f8rk olivengr\u00f8nn","993300":"Brent oransje","000000":"Svart","003300":"M\u00f8rkegr\u00f8nn","003366":"M\u00f8rk asurbl\u00e5","000080":"Marinebl\u00e5","333399":"Indigobl\u00e5","333333":"M\u00f8rk m\u00f8rkegr\u00e5","800000":"R\u00f8dbrun",FF6600:"Oransje","808000":"Olivengr\u00f8nn","008000":"Gr\u00f8nn","008080":"M\u00f8rk gr\u00f8nnbl\u00e5","0000FF":"Bl\u00e5","666699":"Gr\u00e5bl\u00e5","808080":"Gr\u00e5",FF0000:"R\u00f8d",FF9900:"Amber","99CC00":"Gulgr\u00f8nn","339966":"Havgr\u00f8nn","33CCCC":"Turkis","3366FF":"Kongebl\u00e5","800080":"Purpur","999999":"Medium gr\u00e5",FF00FF:"Magentar\u00f8d",FFCC00:"Gull",FFFF00:"Gul","00FF00":"Limegr\u00f8nn","00FFFF":"Cyanbl\u00e5","00CCFF":"Himmelbl\u00e5","993366":"Brun",C0C0C0:"S\u00f8lv",FF99CC:"Rosa",FFCC99:"Fersken",FFFF99:"Lysgul",CCFFCC:"Lysegr\u00f8nn",CCFFFF:"Lys cyanbl\u00e5","99CCFF":"Lys himmelbl\u00e5",CC99FF:"Plomme",FFFFFF:"Hvit"},aria:{"rich_text_area":"Rikt tekstfelt"},wordcount:{words:"Ord:"}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pl.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pl.js index b80cb7bc95..92dc74dfa6 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pl.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pl.js @@ -1,219 +1,261 @@ tinymce.addI18n('pl',{ -"Cut": "Wytnij", -"Heading 5": "Nag\u0142\u00f3wek 5", -"Header 2": "Nag\u0142\u00f3wek 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Twoja przegl\u0105darka nie obs\u0142uguje bezpo\u015bredniego dost\u0119pu do schowka. U\u017cyj zamiast tego kombinacji klawiszy Ctrl+X\/C\/V.", -"Heading 4": "Nag\u0142\u00f3wek 4", -"Div": "Div", -"Heading 2": "Nag\u0142\u00f3wek 2", -"Paste": "Wklej", -"Close": "Zamknij", -"Font Family": "Kr\u00f3j fontu", -"Pre": "Sformatowany tekst", -"Align right": "Wyr\u00f3wnaj do prawej", -"New document": "Nowy dokument", -"Blockquote": "Blok cytatu", -"Numbered list": "Lista numerowana", -"Heading 1": "Nag\u0142\u00f3wek 1", -"Headings": "Nag\u0142\u00f3wki", -"Increase indent": "Zwi\u0119ksz wci\u0119cie", -"Formats": "Formaty", -"Headers": "Nag\u0142\u00f3wki", -"Select all": "Zaznacz wszystko", -"Header 3": "Nag\u0142\u00f3wek 3", -"Blocks": "Bloki", -"Undo": "Cofnij", -"Strikethrough": "Przekre\u015blenie", -"Bullet list": "Lista wypunktowana", -"Header 1": "Nag\u0142\u00f3wek 1", -"Superscript": "Indeks g\u00f3rny", -"Clear formatting": "Wyczy\u015b\u0107 formatowanie", -"Font Sizes": "Rozmiar fontu", -"Subscript": "Indeks dolny", -"Header 6": "Nag\u0142\u00f3wek 6", "Redo": "Pon\u00f3w", -"Paragraph": "Akapit", -"Ok": "Ok", -"Bold": "Pogrubienie", -"Code": "Kod \u017ar\u00f3d\u0142owy", -"Italic": "Kursywa", -"Align center": "Wyr\u00f3wnaj do \u015brodka", -"Header 5": "Nag\u0142\u00f3wek 5", -"Heading 6": "Nag\u0142\u00f3wek 6", -"Heading 3": "Nag\u0142\u00f3wek 3", -"Decrease indent": "Zmniejsz wci\u0119cie", -"Header 4": "Nag\u0142\u00f3wek 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Wklejanie jest w trybie tekstowym. Zawarto\u015b\u0107 zostanie wklejona jako zwyk\u0142y tekst dop\u00f3ki nie wy\u0142\u0105czysz tej opcji.", -"Underline": "Podkre\u015blenie", -"Cancel": "Anuluj", -"Justify": "Do lewej i prawej", -"Inline": "W tek\u015bcie", +"Undo": "Cofnij", +"Cut": "Wytnij", "Copy": "Kopiuj", -"Align left": "Wyr\u00f3wnaj do lewej", +"Paste": "Wklej", +"Select all": "Zaznacz wszystko", +"New document": "Nowy dokument", +"Ok": "Ok", +"Cancel": "Anuluj", "Visual aids": "Pomoce wizualne", -"Lower Greek": "Ma\u0142e greckie", -"Square": "Kwadrat", +"Bold": "Pogrubienie", +"Italic": "Kursywa", +"Underline": "Podkre\u015blenie", +"Strikethrough": "Przekre\u015blenie", +"Superscript": "Indeks g\u00f3rny", +"Subscript": "Indeks dolny", +"Clear formatting": "Wyczy\u015b\u0107 formatowanie", +"Align left": "Wyr\u00f3wnaj do lewej", +"Align center": "Wyr\u00f3wnaj do \u015brodka", +"Align right": "Wyr\u00f3wnaj do prawej", +"Justify": "Do lewej i prawej", +"Bullet list": "Lista wypunktowana", +"Numbered list": "Lista numerowana", +"Decrease indent": "Zmniejsz wci\u0119cie", +"Increase indent": "Zwi\u0119ksz wci\u0119cie", +"Close": "Zamknij", +"Formats": "Formaty", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Twoja przegl\u0105darka nie obs\u0142uguje bezpo\u015bredniego dost\u0119pu do schowka. U\u017cyj zamiast tego kombinacji klawiszy Ctrl+X\/C\/V.", +"Headers": "Nag\u0142\u00f3wki", +"Header 1": "Nag\u0142\u00f3wek 1", +"Header 2": "Nag\u0142\u00f3wek 2", +"Header 3": "Nag\u0142\u00f3wek 3", +"Header 4": "Nag\u0142\u00f3wek 4", +"Header 5": "Nag\u0142\u00f3wek 5", +"Header 6": "Nag\u0142\u00f3wek 6", +"Headings": "Nag\u0142\u00f3wki", +"Heading 1": "Nag\u0142\u00f3wek 1", +"Heading 2": "Nag\u0142\u00f3wek 2", +"Heading 3": "Nag\u0142\u00f3wek 3", +"Heading 4": "Nag\u0142\u00f3wek 4", +"Heading 5": "Nag\u0142\u00f3wek 5", +"Heading 6": "Nag\u0142\u00f3wek 6", +"Preformatted": "Sformatowany tekst", +"Div": "Div", +"Pre": "Sformatowany tekst", +"Code": "Kod \u017ar\u00f3d\u0142owy", +"Paragraph": "Akapit", +"Blockquote": "Blok cytatu", +"Inline": "W tek\u015bcie", +"Blocks": "Bloki", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Wklejanie jest w trybie tekstowym. Zawarto\u015b\u0107 zostanie wklejona jako zwyk\u0142y tekst dop\u00f3ki nie wy\u0142\u0105czysz tej opcji.", +"Font Family": "Kr\u00f3j fontu", +"Font Sizes": "Rozmiar fontu", +"Class": "Klasa", +"Browse for an image": "Przegl\u0105daj za zdj\u0119ciem", +"OR": "LUB", +"Drop an image here": "Upu\u015b\u0107 obraz tutaj", +"Upload": "Prze\u015blij", +"Block": "Zablokuj", +"Align": "Wyr\u00f3wnaj", "Default": "Domy\u015blne", -"Lower Alpha": "Ma\u0142e litery", "Circle": "K\u00f3\u0142ko", "Disc": "Dysk", +"Square": "Kwadrat", +"Lower Alpha": "Ma\u0142e litery", +"Lower Greek": "Ma\u0142e greckie", +"Lower Roman": "Ma\u0142e rzymskie", "Upper Alpha": "Wielkie litery", "Upper Roman": "Wielkie rzymskie", -"Lower Roman": "Ma\u0142e rzymskie", -"Name": "Nazwa", "Anchor": "Kotwica", +"Name": "Nazwa", +"Id": "Identyfikator", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Identyfikator powinien zaczyna\u0107 si\u0119 liter\u0105, dozwolone s\u0105 tylko litery, numery, uko\u015bniki, kropki, dwukropki i podkre\u015blniki - tzw. pod\u0142ogi", "You have unsaved changes are you sure you want to navigate away?": "Masz niezapisane zmiany. Czy na pewno chcesz opu\u015bci\u0107 stron\u0119?", "Restore last draft": "Przywr\u00f3\u0107 ostatni szkic", "Special character": "Znak specjalny", "Source code": "Kod \u017ar\u00f3d\u0142owy", -"B": "B", +"Insert\/Edit code sample": "Dodaj\/Edytuj przyk\u0142adowy kod", +"Language": "J\u0119zyk", +"Code sample": "Przyk\u0142ad kodu \u017ar\u00f3d\u0142owego", +"Color": "Kolor", "R": "R", "G": "G", -"Color": "Kolor", -"Right to left": "Od prawej do lewej", +"B": "B", "Left to right": "Od lewej do prawej", +"Right to left": "Od prawej do lewej", "Emoticons": "Ikony emocji", -"Robots": "Roboty", "Document properties": "W\u0142a\u015bciwo\u015bci dokumentu", "Title": "Tytu\u0142", "Keywords": "S\u0142owa kluczowe", -"Encoding": "Kodowanie", "Description": "Opis", +"Robots": "Roboty", "Author": "Autor", +"Encoding": "Kodowanie", "Fullscreen": "Pe\u0142ny ekran", +"Action": "Akcja", +"Shortcut": "Skr\u00f3t", +"Help": "Pomoc", +"Address": "Adres", +"Focus to menubar": "Skup si\u0119 na pasku menu", +"Focus to toolbar": "Skupi\u0107 si\u0119 na pasku", +"Focus to element path": "Skup si\u0119 na \u015bcie\u017cce elementu", +"Focus to contextual toolbar": "Skupi\u0107 si\u0119 na pasku narz\u0119dzi kontekstowych", +"Insert link (if link plugin activated)": "Wstaw \u0142\u0105cze (je\u015bli w\u0142\u0105czysz wtyczk\u0119 link\u00f3w)", +"Save (if save plugin activated)": "Zapisz (je\u015bli aktywowana jest wtyczka do zapisu)", +"Find (if searchreplace plugin activated)": "Znajd\u017a (je\u015bli w\u0142\u0105czysz wtyczk\u0119 do wyszukiwania)", +"Plugins installed ({0}):": "Zainstalowane wtyczki ({0}):", +"Premium plugins:": "Wtyczki Premium:", +"Learn more...": "Dowiedz si\u0119 wi\u0119cej...", +"You are using {0}": "U\u017cywasz {0}", +"Plugins": "Pluginy", +"Handy Shortcuts": "Przydatne skr\u00f3ty", "Horizontal line": "Pozioma linia", -"Horizontal space": "Odst\u0119p poziomy", "Insert\/edit image": "Wstaw\/edytuj obrazek", +"Image description": "Opis obrazka", +"Source": "\u0179r\u00f3d\u0142o", +"Dimensions": "Wymiary", +"Constrain proportions": "Zachowaj proporcje", "General": "Og\u00f3lne", "Advanced": "Zaawansowane", -"Source": "\u0179r\u00f3d\u0142o", -"Border": "Ramka", -"Constrain proportions": "Zachowaj proporcje", -"Vertical space": "Odst\u0119p pionowy", -"Image description": "Opis obrazka", "Style": "Styl", -"Dimensions": "Wymiary", +"Vertical space": "Odst\u0119p pionowy", +"Horizontal space": "Odst\u0119p poziomy", +"Border": "Ramka", "Insert image": "Wstaw obrazek", -"Zoom in": "Powi\u0119ksz", -"Contrast": "Kontrast", -"Back": "Cofnij", -"Gamma": "Gamma", -"Flip horizontally": "Przerzu\u0107 w poziomie", -"Resize": "Zmiana rozmiaru", -"Sharpen": "Wyostrz", -"Zoom out": "Pomniejsz", -"Image options": "Opcje obrazu", -"Apply": "Zaakceptuj", -"Brightness": "Jasno\u015b\u0107", -"Rotate clockwise": "Obr\u00f3\u0107 w prawo", +"Image": "Obraz", +"Image list": "Lista obrazk\u00f3w", "Rotate counterclockwise": "Obr\u00f3\u0107 w lewo", -"Edit image": "Edytuj obrazek", -"Color levels": "Poziom koloru", -"Crop": "Przytnij", -"Orientation": "Orientacja", +"Rotate clockwise": "Obr\u00f3\u0107 w prawo", "Flip vertically": "Przerzu\u0107 w pionie", +"Flip horizontally": "Przerzu\u0107 w poziomie", +"Edit image": "Edytuj obrazek", +"Image options": "Opcje obrazu", +"Zoom in": "Powi\u0119ksz", +"Zoom out": "Pomniejsz", +"Crop": "Przytnij", +"Resize": "Zmiana rozmiaru", +"Orientation": "Orientacja", +"Brightness": "Jasno\u015b\u0107", +"Sharpen": "Wyostrz", +"Contrast": "Kontrast", +"Color levels": "Poziom koloru", +"Gamma": "Gamma", "Invert": "Odwr\u00f3\u0107", +"Apply": "Zaakceptuj", +"Back": "Cofnij", "Insert date\/time": "Wstaw dat\u0119\/czas", -"Remove link": "Usu\u0144 \u0142\u0105cze", -"Url": "URL", -"Text to display": "Tekst do wy\u015bwietlenia", -"Anchors": "Kotwice", +"Date\/time": "Data\/Czas", "Insert link": "Wstaw \u0142\u0105cze", -"New window": "Nowe okno", -"None": "\u017baden", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL, kt\u00f3ry wprowadzi\u0142e\u015b wygl\u0105da na link zewn\u0119trzny. Czy chcesz doda\u0107 http:\/\/ jako prefiks?", -"Target": "Cel", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL, kt\u00f3ry wprowadzi\u0142e\u015b wygl\u0105da na adres e-mail. Czy chcesz doda\u0107 mailto: jako prefiks?", "Insert\/edit link": "Wstaw\/edytuj \u0142\u0105cze", -"Insert\/edit video": "Wstaw\/edytuj wideo", -"Poster": "Plakat", -"Alternative source": "Alternatywne \u017ar\u00f3d\u0142o", -"Paste your embed code below:": "Wklej tutaj kod do osadzenia:", +"Text to display": "Tekst do wy\u015bwietlenia", +"Url": "URL", +"Target": "Cel", +"None": "\u017baden", +"New window": "Nowe okno", +"Remove link": "Usu\u0144 \u0142\u0105cze", +"Anchors": "Kotwice", +"Link": "Adres \u0142\u0105cza", +"Paste or type a link": "Wklej lub wpisz adres \u0142\u0105cza", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL, kt\u00f3ry wprowadzi\u0142e\u015b wygl\u0105da na adres e-mail. Czy chcesz doda\u0107 mailto: jako prefiks?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL, kt\u00f3ry wprowadzi\u0142e\u015b wygl\u0105da na link zewn\u0119trzny. Czy chcesz doda\u0107 http:\/\/ jako prefiks?", +"Link list": "Lista link\u00f3w", "Insert video": "Wstaw wideo", +"Insert\/edit video": "Wstaw\/edytuj wideo", +"Insert\/edit media": "Wstaw\/Edytuj media", +"Alternative source": "Alternatywne \u017ar\u00f3d\u0142o", +"Poster": "Plakat", +"Paste your embed code below:": "Wklej tutaj kod do osadzenia:", "Embed": "Osad\u017a", +"Media": "Media", "Nonbreaking space": "Nie\u0142amliwa spacja", "Page break": "Podzia\u0142 strony", "Paste as text": "Wklej jako zwyk\u0142y tekst", "Preview": "Podgl\u0105d", "Print": "Drukuj", "Save": "Zapisz", -"Could not find the specified string.": "Nie znaleziono szukanego tekstu.", -"Replace": "Zamie\u0144", -"Next": "Nast.", -"Whole words": "Ca\u0142e s\u0142owa", -"Find and replace": "Znajd\u017a i zamie\u0144", -"Replace with": "Zamie\u0144 na", "Find": "Znajd\u017a", +"Replace with": "Zamie\u0144 na", +"Replace": "Zamie\u0144", "Replace all": "Zamie\u0144 wszystko", -"Match case": "Dopasuj wielko\u015b\u0107 liter", "Prev": "Poprz.", +"Next": "Nast.", +"Find and replace": "Znajd\u017a i zamie\u0144", +"Could not find the specified string.": "Nie znaleziono szukanego tekstu.", +"Match case": "Dopasuj wielko\u015b\u0107 liter", +"Whole words": "Ca\u0142e s\u0142owa", "Spellcheck": "Sprawdzanie pisowni", -"Finish": "Zako\u0144cz", -"Ignore all": "Ignoruj wszystko", "Ignore": "Ignoruj", +"Ignore all": "Ignoruj wszystko", +"Finish": "Zako\u0144cz", "Add to Dictionary": "Dodaj do s\u0142ownika", -"Insert row before": "Wstaw wiersz przed", -"Rows": "Wiersz.", -"Height": "Wysoko\u015b\u0107", -"Paste row after": "Wklej wiersz po", -"Alignment": "Wyr\u00f3wnanie", -"Border color": "Kolor ramki", -"Column group": "Grupa kolumn", -"Row": "Wiersz", -"Insert column before": "Wstaw kolumn\u0119 przed", -"Split cell": "Podziel kom\u00f3rk\u0119", -"Cell padding": "Dope\u0142nienie kom\u00f3rki", -"Cell spacing": "Odst\u0119py kom\u00f3rek", -"Row type": "Typ wiersza", "Insert table": "Wstaw tabel\u0119", -"Body": "Tre\u015b\u0107", -"Caption": "Tytu\u0142", -"Footer": "Stopka", -"Delete row": "Usu\u0144 wiersz", -"Paste row before": "Wklej wiersz przed", -"Scope": "Kontekst", -"Delete table": "Usu\u0144 tabel\u0119", -"H Align": "Wyr\u00f3wnanie w pionie", -"Top": "G\u00f3ra", -"Header cell": "Kom\u00f3rka nag\u0142\u00f3wka", -"Column": "Kolumna", -"Row group": "Grupa wierszy", -"Cell": "Kom\u00f3rka", -"Middle": "\u015arodek", -"Cell type": "Typ kom\u00f3rki", -"Copy row": "Kopiuj wiersz", -"Row properties": "W\u0142a\u015bciwo\u015bci wiersza", "Table properties": "W\u0142a\u015bciwo\u015bci tabeli", -"Bottom": "D\u00f3\u0142", -"V Align": "Wyr\u00f3wnanie w poziomie", -"Header": "Nag\u0142\u00f3wek", -"Right": "Prawo", -"Insert column after": "Wstaw kolumn\u0119 po", -"Cols": "Kol.", -"Insert row after": "Wstaw wiersz po", -"Width": "Szeroko\u015b\u0107", +"Delete table": "Usu\u0144 tabel\u0119", +"Cell": "Kom\u00f3rka", +"Row": "Wiersz", +"Column": "Kolumna", "Cell properties": "W\u0142a\u015bciwo\u015bci kom\u00f3rki", -"Left": "Lewo", -"Cut row": "Wytnij wiersz", -"Delete column": "Usu\u0144 kolumn\u0119", -"Center": "\u015arodek", "Merge cells": "\u0141\u0105cz kom\u00f3rki", +"Split cell": "Podziel kom\u00f3rk\u0119", +"Insert row before": "Wstaw wiersz przed", +"Insert row after": "Wstaw wiersz po", +"Delete row": "Usu\u0144 wiersz", +"Row properties": "W\u0142a\u015bciwo\u015bci wiersza", +"Cut row": "Wytnij wiersz", +"Copy row": "Kopiuj wiersz", +"Paste row before": "Wklej wiersz przed", +"Paste row after": "Wklej wiersz po", +"Insert column before": "Wstaw kolumn\u0119 przed", +"Insert column after": "Wstaw kolumn\u0119 po", +"Delete column": "Usu\u0144 kolumn\u0119", +"Cols": "Kol.", +"Rows": "Wiersz.", +"Width": "Szeroko\u015b\u0107", +"Height": "Wysoko\u015b\u0107", +"Cell spacing": "Odst\u0119py kom\u00f3rek", +"Cell padding": "Dope\u0142nienie kom\u00f3rki", +"Caption": "Tytu\u0142", +"Left": "Lewo", +"Center": "\u015arodek", +"Right": "Prawo", +"Cell type": "Typ kom\u00f3rki", +"Scope": "Kontekst", +"Alignment": "Wyr\u00f3wnanie", +"H Align": "Wyr\u00f3wnanie w pionie", +"V Align": "Wyr\u00f3wnanie w poziomie", +"Top": "G\u00f3ra", +"Middle": "\u015arodek", +"Bottom": "D\u00f3\u0142", +"Header cell": "Kom\u00f3rka nag\u0142\u00f3wka", +"Row group": "Grupa wierszy", +"Column group": "Grupa kolumn", +"Row type": "Typ wiersza", +"Header": "Nag\u0142\u00f3wek", +"Body": "Tre\u015b\u0107", +"Footer": "Stopka", +"Border color": "Kolor ramki", "Insert template": "Wstaw szablon", "Templates": "Szablony", +"Template": "Szablon", +"Text color": "Kolor tekstu", "Background color": "Kolor t\u0142a", "Custom...": "Niestandardowy...", "Custom color": "Kolor niestandardowy", "No color": "Bez koloru", -"Text color": "Kolor tekstu", +"Table of Contents": "Spis tre\u015bci", "Show blocks": "Poka\u017c bloki", "Show invisible characters": "Poka\u017c niewidoczne znaki", "Words: {0}": "S\u0142\u00f3w: {0}", -"Insert": "Wstaw", +"{0} words": "{0} s\u0142\u00f3w", "File": "Plik", "Edit": "Edycja", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Obszar Edycji. ALT-F9 - menu. ALT-F10 - pasek narz\u0119dzi. ALT-0 - pomoc", -"Tools": "Narz\u0119dzia", +"Insert": "Wstaw", "View": "Widok", +"Format": "Format", "Table": "Tabela", -"Format": "Format" +"Tools": "Narz\u0119dzia", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Obszar Edycji. ALT-F9 - menu. ALT-F10 - pasek narz\u0119dzi. ALT-0 - pomoc" }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt.js deleted file mode 100644 index 809f1c2d9b..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({pt:{common:{"more_colors":"Mais Cores","invalid_data":"Erro: Valores inv\u00e1lidos marcados em vermelho.","popup_blocked":"Detectamos que o seu bloqueador de popups bloqueou uma janela que \u00e9 essencial para a aplica\u00e7\u00e3o. Voc\u00ea precisa desativar o bloqueador de janelas de popups para utilizar esta ferramenta.","clipboard_no_support":"O seu browser n\u00e3o suporta esta fun\u00e7\u00e3o, use os atalhos do teclado.","clipboard_msg":"Copiar/recortar/colar n\u00e3o est\u00e1 dispon\u00edvel no Mozilla e Firefox. Deseja mais informa\u00e7\u00f5es sobre este problema?","not_set":"-- N/A --","class_name":"Classe",browse:"Procurar",close:"Fechar",cancel:"Cancelar",update:"Atualizar",insert:"Inserir",apply:"Aplicar","edit_confirm":"Deseja usar o modo de edi\u00e7\u00e3o avan\u00e7ado neste campo de texto?","invalid_data_number":"{#field} deve ser um n\u00famero","invalid_data_min":"{#field} deve ser um n\u00famero maior que {#min}","invalid_data_size":"{#field} deve ser um n\u00famero ou uma percentagem",value:"(valor)"},contextmenu:{full:"Justificado",right:"Direita",center:"Centro",left:"Esquerda",align:"Alinhamento"},insertdatetime:{"day_short":"Dom,Seg,Ter,Qua,Qui,Sex,Sab,Dom","day_long":"Domingo,Segunda-feira,Ter\u00e7a-feira,Quarta-feira,Quinta-feira,Sexta-feira,S\u00e1bado,Domingo","months_short":"Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez","months_long":"Janeiro,Fevereiro,Mar\u00e7o,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro","inserttime_desc":"Inserir hora","insertdate_desc":"Inserir data","time_fmt":"%H:%M:%S","date_fmt":"%d-%m-%Y"},print:{"print_desc":"Imprimir"},preview:{"preview_desc":"Pr\u00e9-visualizar"},directionality:{"rtl_desc":"Da direita para esquerda","ltr_desc":"Da esquerda para direita"},layer:{content:"Nova camada...","absolute_desc":"Alternar o posicionamento absoluto","backward_desc":"Mover para tr\u00e1s","forward_desc":"Mover para frente","insertlayer_desc":"Inserir nova camada"},save:{"save_desc":"Salvar","cancel_desc":"Cancelar todas as altera\u00e7\u00f5es"},nonbreaking:{"nonbreaking_desc":"Inserir um espa\u00e7o \"sem quebra\""},iespell:{download:"Plugin de ortografia n\u00e3o-detectado. Deseja instalar agora?","iespell_desc":"Verificar ortografia"},advhr:{"advhr_desc":"Separador horizontal","delta_height":"","delta_width":""},emotions:{"emotions_desc":"Emoticons","delta_height":"","delta_width":""},searchreplace:{"replace_desc":"Localizar/substituir","search_desc":"Localizar","delta_width":"","delta_height":""},advimage:{"image_desc":"Inserir/editar imagem","delta_width":"","delta_height":""},advlink:{"delta_width":"50","link_desc":"Inserir/editar hyperlink","delta_height":""},xhtmlxtras:{"attribs_desc":"Inserir/Editar atributos","ins_desc":"Inserir","del_desc":"Apagar","acronym_desc":"Acr\u00f4nimo","abbr_desc":"Abrevia\u00e7\u00e3o","cite_desc":"Cita\u00e7\u00e3o","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"Editar CSS","delta_height":"","delta_width":""},paste:{"plaintext_mode":"Comando colar est\u00e1 em modo texto simples. Clique novamente para voltar para o modo normal.","plaintext_mode_sticky":"Comando colar est\u00e1 em modo texto simples. Clique novamente para voltar para o modo normal. Depois de colar alguma coisa retornar\u00e1 para o modo normal.","selectall_desc":"Selecionar tudo","paste_word_desc":"Colar (copiado do WORD)","paste_text_desc":"Colar como texto simples"},"paste_dlg":{"word_title":"Use CTRL+V para colar o texto na janela.","text_linebreaks":"Manter quebras de linha","text_title":"Use CTRL+V para colar o texto na janela."},table:{cell:"C\u00e9lula",col:"Coluna",row:"Linha",del:"Apagar tabela","copy_row_desc":"Copiar linha","cut_row_desc":"Recortar linha","paste_row_after_desc":"Colar linha depois","paste_row_before_desc":"Colar linha antes","props_desc":"Propriedades da tabela","cell_desc":"Propriedades das c\u00e9lulas","row_desc":"Propriedades das linhas","merge_cells_desc":"Unir c\u00e9lulas","split_cells_desc":"Dividir c\u00e9lulas","delete_col_desc":"Remover coluna","col_after_desc":"Inserir coluna depois","col_before_desc":"Inserir coluna antes","delete_row_desc":"Apagar linha","row_after_desc":"Inserir linha depois","row_before_desc":"Inserir linha antes",desc:"Inserir nova tabela","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"Se restaurar o conte\u00fado, voc\u00ea ir\u00e1 perder tudo que est\u00e1 atualmente no editor.\n\nTem certeza que quer restaurar o conte\u00fado salvo?","restore_content":"Restaura conte\u00fado salvo automaticamente.","unload_msg":"As mudan\u00e7as efetuadas ser\u00e3o perdidas se sair desta p\u00e1gina."},fullscreen:{desc:"Tela Inteira"},media:{edit:"Editar m\u00eddia embutida",desc:"Inserir/Editar m\u00eddia embutida","delta_height":"","delta_width":""},fullpage:{desc:"Propriedades do Documento","delta_width":"","delta_height":""},template:{desc:"Inserir template"},visualchars:{desc:"Caracteres de controle visual ligado/desligado"},spellchecker:{desc:"Alternar verifica\u00e7\u00e3o ortogr\u00e1fica",menu:"Configura\u00e7\u00f5es de ortografia","ignore_word":"Ignorar palavra","ignore_words":"Ignorar tudo",langs:"Linguagens",wait:"Aguarde...",sug:"Sugest\u00f5es","no_sug":"Sem sugest\u00f5es","no_mpell":"N\u00e3o foram detectados erros de ortografia.","learn_word":"Aprender palavra"},pagebreak:{desc:"Inserir quebra de p\u00e1gina."},advlist:{types:"Tipos",def:"Padr\u00e3o","lower_alpha":"Alfabeto min\u00fasculo","lower_greek":"Alfabeto grego","lower_roman":"Num. romanos min\u00fasculos","upper_alpha":"Alfabeto mai\u00fasculos","upper_roman":"Num. romanos mai\u00fasculos",circle:"C\u00edrculo",disc:"Disco",square:"Quadrado"},colors:{"333300":"Oliva escuro","993300":"Laranja queimado","000000":"Preto","003300":"Verde escuro","003366":"Azul escuro","000080":"Azul marinho","333399":"\u00cdndigo","333333":"Cinza muito escuro","800000":"Marrom 1",FF6600:"Laranja","808000":"Oliva","008000":"Verde","008080":"Verde azulado","0000FF":"Azul","666699":"Azul acinzentado","808080":"Cinza",FF0000:"Vermelho",FF9900:"\u00c2mbar","99CC00":"Amarelo esverdeado","339966":"Verde mar","33CCCC":"Turquesa","3366FF":"Azul real","800080":"Roxo","999999":"Cinza m\u00e9dio",FF00FF:"Magenta",FFCC00:"Ouro",FFFF00:"Amarelo","00FF00":"Lima","00FFFF":"\u00c1gua","00CCFF":"Azul celeste","993366":"Marrom 2",C0C0C0:"Prata",FF99CC:"Rosa",FFCC99:"P\u00eassego",FFFF99:"Amarelo claro",CCFFCC:"Verde p\u00e1lido",CCFFFF:"Ciano p\u00e1lido","99CCFF":"Azul celeste claro",CC99FF:"Ameixa",FFFFFF:"Branco"},aria:{"rich_text_area":"\u00c1rea de texto rico"},wordcount:{words:"Palavras:"}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js new file mode 100644 index 0000000000..497043a9d4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js @@ -0,0 +1,261 @@ +tinymce.addI18n('pt_BR',{ +"Redo": "Refazer", +"Undo": "Desfazer", +"Cut": "Recortar", +"Copy": "Copiar", +"Paste": "Colar", +"Select all": "Selecionar tudo", +"New document": "Novo documento", +"Ok": "Ok", +"Cancel": "Cancelar", +"Visual aids": "Ajuda visual", +"Bold": "Negrito", +"Italic": "It\u00e1lico", +"Underline": "Sublinhar", +"Strikethrough": "Riscar", +"Superscript": "Sobrescrito", +"Subscript": "Subscrever", +"Clear formatting": "Limpar formata\u00e7\u00e3o", +"Align left": "Alinhar \u00e0 esquerda", +"Align center": "Centralizar", +"Align right": "Alinhar \u00e0 direita", +"Justify": "Justificar", +"Bullet list": "Lista n\u00e3o ordenada", +"Numbered list": "Lista ordenada", +"Decrease indent": "Diminuir recuo", +"Increase indent": "Aumentar recuo", +"Close": "Fechar", +"Formats": "Formatos", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Seu navegador n\u00e3o suporta acesso direto \u00e0 \u00e1rea de transfer\u00eancia. Por favor use os atalhos Ctrl+X - C - V do teclado", +"Headers": "Cabe\u00e7alhos", +"Header 1": "Cabe\u00e7alho 1", +"Header 2": "Cabe\u00e7alho 2", +"Header 3": "Cabe\u00e7alho 3", +"Header 4": "Cabe\u00e7alho 4", +"Header 5": "Cabe\u00e7alho 5", +"Header 6": "Cabe\u00e7alho 6", +"Headings": "Cabe\u00e7alhos", +"Heading 1": "Cabe\u00e7alho 1", +"Heading 2": "Cabe\u00e7alho 2", +"Heading 3": "Cabe\u00e7alho 3", +"Heading 4": "Cabe\u00e7alho 4", +"Heading 5": "Cabe\u00e7alho 5", +"Heading 6": "Cabe\u00e7alho 6", +"Preformatted": "Preformatado", +"Div": "Div", +"Pre": "Pre", +"Code": "C\u00f3digo", +"Paragraph": "Par\u00e1grafo", +"Blockquote": "Aspas", +"Inline": "Em linha", +"Blocks": "Blocos", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "O comando colar est\u00e1 agora em modo texto plano. O conte\u00fado ser\u00e1 colado como texto plano at\u00e9 voc\u00ea desligar esta op\u00e7\u00e3o.", +"Font Family": "Fonte", +"Font Sizes": "Tamanho", +"Class": "Classe", +"Browse for an image": "Procure uma imagem", +"OR": "OU", +"Drop an image here": "Arraste uma imagem aqui", +"Upload": "Carregar", +"Block": "Bloco", +"Align": "Alinhamento", +"Default": "Padr\u00e3o", +"Circle": "C\u00edrculo", +"Disc": "Disco", +"Square": "Quadrado", +"Lower Alpha": "a. b. c. ...", +"Lower Greek": "\u03b1. \u03b2. \u03b3. ...", +"Lower Roman": "i. ii. iii. ...", +"Upper Alpha": "A. B. C. ...", +"Upper Roman": "I. II. III. ...", +"Anchor": "\u00c2ncora", +"Name": "Nome", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id deve come\u00e7ar com uma letra, seguido apenas por letras, n\u00fameros, tra\u00e7os, pontos, dois pontos ou sublinhados.", +"You have unsaved changes are you sure you want to navigate away?": "Voc\u00ea tem mudan\u00e7as n\u00e3o salvas. Voc\u00ea tem certeza que deseja sair?", +"Restore last draft": "Restaurar \u00faltimo rascunho", +"Special character": "Caracteres especiais", +"Source code": "C\u00f3digo fonte", +"Insert\/Edit code sample": "Inserir\/Editar c\u00f3digo de exemplo", +"Language": "Idioma", +"Code sample": "Exemplo de c\u00f3digo", +"Color": "Cor", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Da esquerda para a direita", +"Right to left": "Da direita para a esquerda", +"Emoticons": "Emoticons", +"Document properties": "Propriedades do documento", +"Title": "T\u00edtulo", +"Keywords": "Palavras-chave", +"Description": "Descri\u00e7\u00e3o", +"Robots": "Rob\u00f4s", +"Author": "Autor", +"Encoding": "Codifica\u00e7\u00e3o", +"Fullscreen": "Tela cheia", +"Action": "A\u00e7\u00e3o", +"Shortcut": "Atalho", +"Help": "Ajuda", +"Address": "Endere\u00e7o", +"Focus to menubar": "Foco no menu", +"Focus to toolbar": "Foco na barra de ferramentas", +"Focus to element path": "Foco no caminho do elemento", +"Focus to contextual toolbar": "Foco na barra de ferramentas contextual", +"Insert link (if link plugin activated)": "Inserir link (se o plugin de link estiver ativado)", +"Save (if save plugin activated)": "Salvar (se o plugin de salvar estiver ativado)", +"Find (if searchreplace plugin activated)": "Procurar (se o plugin de procurar e substituir estiver ativado)", +"Plugins installed ({0}):": "Plugins instalados ({0}):", +"Premium plugins:": "Plugins premium:", +"Learn more...": "Saiba mais...", +"You are using {0}": "Voc\u00ea est\u00e1 usando {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Atalhos \u00fateis", +"Horizontal line": "Linha horizontal", +"Insert\/edit image": "Inserir\/editar imagem", +"Image description": "Inserir descri\u00e7\u00e3o", +"Source": "Endere\u00e7o da imagem", +"Dimensions": "Dimens\u00f5es", +"Constrain proportions": "Manter propor\u00e7\u00f5es", +"General": "Geral", +"Advanced": "Avan\u00e7ado", +"Style": "Estilo", +"Vertical space": "Espa\u00e7amento vertical", +"Horizontal space": "Espa\u00e7amento horizontal", +"Border": "Borda", +"Insert image": "Inserir imagem", +"Image": "Imagem", +"Image list": "Lista de Imagens", +"Rotate counterclockwise": "Girar em sentido hor\u00e1rio", +"Rotate clockwise": "Girar em sentido anti-hor\u00e1rio", +"Flip vertically": "Virar verticalmente", +"Flip horizontally": "Virar horizontalmente", +"Edit image": "Editar imagem", +"Image options": "Op\u00e7\u00f5es de Imagem", +"Zoom in": "Aumentar zoom", +"Zoom out": "Diminuir zoom", +"Crop": "Cortar", +"Resize": "Redimensionar", +"Orientation": "Orienta\u00e7\u00e3o", +"Brightness": "Brilho", +"Sharpen": "Aumentar nitidez", +"Contrast": "Contraste", +"Color levels": "N\u00edveis de cor", +"Gamma": "Gama", +"Invert": "Inverter", +"Apply": "Aplicar", +"Back": "Voltar", +"Insert date\/time": "Inserir data\/hora", +"Date\/time": "data\/hora", +"Insert link": "Inserir link", +"Insert\/edit link": "Inserir\/editar link", +"Text to display": "Texto para mostrar", +"Url": "Url", +"Target": "Alvo", +"None": "Nenhum", +"New window": "Nova janela", +"Remove link": "Remover link", +"Anchors": "\u00c2ncoras", +"Link": "Link", +"Paste or type a link": "Cole ou digite um Link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "A URL que voc\u00ea informou parece ser um link externo. Deseja incluir o prefixo http:\/\/?", +"Link list": "Lista de Links", +"Insert video": "Inserir v\u00eddeo", +"Insert\/edit video": "Inserir\/editar v\u00eddeo", +"Insert\/edit media": "Inserir\/editar imagem", +"Alternative source": "Fonte alternativa", +"Poster": "Autor", +"Paste your embed code below:": "Insira o c\u00f3digo de incorpora\u00e7\u00e3o abaixo:", +"Embed": "Incorporar", +"Media": "imagem", +"Nonbreaking space": "Espa\u00e7o n\u00e3o separ\u00e1vel", +"Page break": "Quebra de p\u00e1gina", +"Paste as text": "Colar como texto", +"Preview": "Pr\u00e9-visualizar", +"Print": "Imprimir", +"Save": "Salvar", +"Find": "Localizar", +"Replace with": "Substituir por", +"Replace": "Substituir", +"Replace all": "Substituir tudo", +"Prev": "Anterior", +"Next": "Pr\u00f3ximo", +"Find and replace": "Localizar e substituir", +"Could not find the specified string.": "N\u00e3o foi poss\u00edvel encontrar o termo especificado", +"Match case": "Diferenciar mai\u00fasculas e min\u00fasculas", +"Whole words": "Palavras inteiras", +"Spellcheck": "Corretor ortogr\u00e1fico", +"Ignore": "Ignorar", +"Ignore all": "Ignorar tudo", +"Finish": "Finalizar", +"Add to Dictionary": "Adicionar ao Dicion\u00e1rio", +"Insert table": "Inserir tabela", +"Table properties": "Propriedades da tabela", +"Delete table": "Excluir tabela", +"Cell": "C\u00e9lula", +"Row": "Linha", +"Column": "Coluna", +"Cell properties": "Propriedades da c\u00e9lula", +"Merge cells": "Agrupar c\u00e9lulas", +"Split cell": "Dividir c\u00e9lula", +"Insert row before": "Inserir linha antes", +"Insert row after": "Inserir linha depois", +"Delete row": "Excluir linha", +"Row properties": "Propriedades da linha", +"Cut row": "Recortar linha", +"Copy row": "Copiar linha", +"Paste row before": "Colar linha antes", +"Paste row after": "Colar linha depois", +"Insert column before": "Inserir coluna antes", +"Insert column after": "Inserir coluna depois", +"Delete column": "Excluir coluna", +"Cols": "Colunas", +"Rows": "Linhas", +"Width": "Largura", +"Height": "Altura", +"Cell spacing": "Espa\u00e7amento da c\u00e9lula", +"Cell padding": "Espa\u00e7amento interno da c\u00e9lula", +"Caption": "Legenda", +"Left": "Esquerdo", +"Center": "Centro", +"Right": "Direita", +"Cell type": "Tipo de c\u00e9lula", +"Scope": "Escopo", +"Alignment": "Alinhamento", +"H Align": "Alinhamento H", +"V Align": "Alinhamento V", +"Top": "Superior", +"Middle": "Meio", +"Bottom": "Inferior", +"Header cell": "C\u00e9lula cabe\u00e7alho", +"Row group": "Agrupar linha", +"Column group": "Agrupar coluna", +"Row type": "Tipo de linha", +"Header": "Cabe\u00e7alho", +"Body": "Corpo", +"Footer": "Rodap\u00e9", +"Border color": "Cor da borda", +"Insert template": "Inserir modelo", +"Templates": "Modelos", +"Template": "Modelo", +"Text color": "Cor do texto", +"Background color": "Cor do fundo", +"Custom...": "Personalizado...", +"Custom color": "Cor personalizada", +"No color": "Nenhuma cor", +"Table of Contents": "\u00edndice de Conte\u00fado", +"Show blocks": "Mostrar blocos", +"Show invisible characters": "Exibir caracteres invis\u00edveis", +"Words: {0}": "Palavras: {0}", +"{0} words": "{0} palavras", +"File": "Arquivo", +"Edit": "Editar", +"Insert": "Inserir", +"View": "Visualizar", +"Format": "Formatar", +"Table": "Tabela", +"Tools": "Ferramentas", +"Powered by {0}": "Distribu\u00eddo por {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u00c1rea de texto formatado. Pressione ALT-F9 para exibir o menu, ALT-F10 para exibir a barra de ferramentas ou ALT-0 para exibir a ajuda" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_PT.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_PT.js new file mode 100644 index 0000000000..0376e82429 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_PT.js @@ -0,0 +1,261 @@ +tinymce.addI18n('pt_PT',{ +"Redo": "Refazer", +"Undo": "Desfazer", +"Cut": "Cortar", +"Copy": "Copiar", +"Paste": "Colar", +"Select all": "Selecionar tudo", +"New document": "Novo documento", +"Ok": "Ok", +"Cancel": "Cancelar", +"Visual aids": "Ajuda visual", +"Bold": "Negrito", +"Italic": "It\u00e1lico", +"Underline": "Sublinhado", +"Strikethrough": "Rasurado", +"Superscript": "Superior \u00e0 linha", +"Subscript": "Inferior \u00e0 linha", +"Clear formatting": "Limpar formata\u00e7\u00e3o", +"Align left": "Alinhar \u00e0 esquerda", +"Align center": "Alinhar ao centro", +"Align right": "Alinhar \u00e0 direita", +"Justify": "Justificado", +"Bullet list": "Lista com marcadores", +"Numbered list": "Lista numerada", +"Decrease indent": "Diminuir avan\u00e7o", +"Increase indent": "Aumentar avan\u00e7o", +"Close": "Fechar", +"Formats": "Formatos", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "O seu navegador n\u00e3o suporta acesso direto \u00e0 \u00e1rea de transfer\u00eancia. Por favor use os atalhos Ctrl+X\/C\/V do seu teclado.", +"Headers": "Cabe\u00e7alhos", +"Header 1": "Cabe\u00e7alho 1", +"Header 2": "Cabe\u00e7alho 2", +"Header 3": "Cabe\u00e7alho 3", +"Header 4": "Cabe\u00e7alho 4", +"Header 5": "Cabe\u00e7alho 5", +"Header 6": "Cabe\u00e7alho 6", +"Headings": "T\u00edtulos", +"Heading 1": "T\u00edtulo 1", +"Heading 2": "T\u00edtulo 2", +"Heading 3": "T\u00edtulo 3", +"Heading 4": "T\u00edtulo 4", +"Heading 5": "T\u00edtulo 5", +"Heading 6": "T\u00edtulo 6", +"Preformatted": "Pr\u00e9-formatado", +"Div": "Div", +"Pre": "Pre", +"Code": "C\u00f3digo", +"Paragraph": "Par\u00e1grafo", +"Blockquote": "Cita\u00e7\u00e3o em bloco", +"Inline": "Na linha", +"Blocks": "Blocos", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "O comando colar est\u00e1 em modo de texto simples. O conte\u00fado ser\u00e1 colado como texto simples at\u00e9 desativar esta op\u00e7\u00e3o.", +"Font Family": "Fonte", +"Font Sizes": "Tamanhos", +"Class": "Classe", +"Browse for an image": "Procurar por uma imagem", +"OR": "Ou", +"Drop an image here": "Solte uma imagem aqui", +"Upload": "Carregar", +"Block": "Bloco", +"Align": "Alinhar", +"Default": "Padr\u00e3o", +"Circle": "C\u00edrculo", +"Disc": "Disco", +"Square": "Quadrado", +"Lower Alpha": "a. b. c. ...", +"Lower Greek": "\\u03b1. \\u03b2. \\u03b3. ...", +"Lower Roman": "i. ii. iii. ...", +"Upper Alpha": "A. B. C. ...", +"Upper Roman": "I. II. III. ...", +"Anchor": "\u00c2ncora", +"Name": "Nome", +"Id": "ID", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "O ID deve come\u00e7ar com uma letra, seguido apenas por letras, n\u00fameros, pontos, dois pontos, tra\u00e7os ou sobtra\u00e7os.", +"You have unsaved changes are you sure you want to navigate away?": "Existem altera\u00e7\u00f5es que ainda n\u00e3o foram guardadas. Tem a certeza que pretende sair?", +"Restore last draft": "Restaurar o \u00faltimo rascunho", +"Special character": "Car\u00e1cter especial", +"Source code": "C\u00f3digo fonte", +"Insert\/Edit code sample": "Inserir\/editar amostra de c\u00f3digo", +"Language": "Idioma", +"Code sample": "Amostra de c\u00f3digo", +"Color": "Cor", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Da esquerda para a direita", +"Right to left": "Da direita para a esquerda", +"Emoticons": "Emo\u00e7\u00f5es", +"Document properties": "Propriedades do documento", +"Title": "T\u00edtulo", +"Keywords": "Palavras-chave", +"Description": "Descri\u00e7\u00e3o", +"Robots": "Rob\u00f4s", +"Author": "Autor", +"Encoding": "Codifica\u00e7\u00e3o", +"Fullscreen": "Ecr\u00e3 completo", +"Action": "A\u00e7\u00e3o", +"Shortcut": "Atalho", +"Help": "Ajuda", +"Address": "Endere\u00e7o", +"Focus to menubar": "Foco na barra de menu", +"Focus to toolbar": "Foco na barra de ferramentas", +"Focus to element path": "Foco no caminho do elemento", +"Focus to contextual toolbar": "Foco na barra de contexto", +"Insert link (if link plugin activated)": "Inserir hiperliga\u00e7\u00e3o (se o plugin de liga\u00e7\u00f5es estiver ativado)", +"Save (if save plugin activated)": "Guardar (se o plugin de guardar estiver ativado)", +"Find (if searchreplace plugin activated)": "Pesquisar (se o plugin pesquisar e substituir estiver ativado)", +"Plugins installed ({0}):": "Plugins instalados ({0}):", +"Premium plugins:": "Plugins comerciais:", +"Learn more...": "Saiba mais...", +"You are using {0}": "Est\u00e1 a usar {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Atalhos \u00fateis", +"Horizontal line": "Linha horizontal", +"Insert\/edit image": "Inserir\/editar imagem", +"Image description": "Descri\u00e7\u00e3o da imagem", +"Source": "Localiza\u00e7\u00e3o", +"Dimensions": "Dimens\u00f5es", +"Constrain proportions": "Manter propor\u00e7\u00f5es", +"General": "Geral", +"Advanced": "Avan\u00e7ado", +"Style": "Estilo", +"Vertical space": "Espa\u00e7amento vertical", +"Horizontal space": "Espa\u00e7amento horizontal", +"Border": "Contorno", +"Insert image": "Inserir imagem", +"Image": "Imagem", +"Image list": "Lista de imagens", +"Rotate counterclockwise": "Rota\u00e7\u00e3o anti-hor\u00e1ria", +"Rotate clockwise": "Rota\u00e7\u00e3o hor\u00e1ria", +"Flip vertically": "Inverter verticalmente", +"Flip horizontally": "Inverter horizontalmente", +"Edit image": "Editar imagem", +"Image options": "Op\u00e7\u00f5es de imagem", +"Zoom in": "Mais zoom", +"Zoom out": "Menos zoom", +"Crop": "Recortar", +"Resize": "Redimensionar", +"Orientation": "Orienta\u00e7\u00e3o", +"Brightness": "Brilho", +"Sharpen": "Mais nitidez", +"Contrast": "Contraste", +"Color levels": "N\u00edveis de cor", +"Gamma": "Gama", +"Invert": "Inverter", +"Apply": "Aplicar", +"Back": "Voltar", +"Insert date\/time": "Inserir data\/hora", +"Date\/time": "Data\/hora", +"Insert link": "Inserir liga\u00e7\u00e3o", +"Insert\/edit link": "Inserir\/editar liga\u00e7\u00e3o", +"Text to display": "Texto a exibir", +"Url": "URL", +"Target": "Alvo", +"None": "Nenhum", +"New window": "Nova janela", +"Remove link": "Remover liga\u00e7\u00e3o", +"Anchors": "\u00c2ncora", +"Link": "Liga\u00e7\u00e3o", +"Paste or type a link": "Copiar ou escrever uma hiperliga\u00e7\u00e3o", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "O URL que indicou parece ser um endere\u00e7o de email. Quer adicionar o prefixo mailto: tal como necess\u00e1rio?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "O URL que indicou parece ser um endere\u00e7o web. Quer adicionar o prefixo http:\/\/ tal como necess\u00e1rio?", +"Link list": "Lista de liga\u00e7\u00f5es", +"Insert video": "Inserir v\u00eddeo", +"Insert\/edit video": "Inserir\/editar v\u00eddeo", +"Insert\/edit media": "Inserir\/editar media", +"Alternative source": "Localiza\u00e7\u00e3o alternativa", +"Poster": "Autor", +"Paste your embed code below:": "Colar c\u00f3digo para embeber:", +"Embed": "Embeber", +"Media": "Media", +"Nonbreaking space": "Espa\u00e7o n\u00e3o quebr\u00e1vel", +"Page break": "Quebra de p\u00e1gina", +"Paste as text": "Colar como texto", +"Preview": "Pr\u00e9-visualizar", +"Print": "Imprimir", +"Save": "Guardar", +"Find": "Pesquisar", +"Replace with": "Substituir por", +"Replace": "Substituir", +"Replace all": "Substituir tudo", +"Prev": "Anterior", +"Next": "Pr\u00f3ximo", +"Find and replace": "Pesquisar e substituir", +"Could not find the specified string.": "N\u00e3o foi poss\u00edvel localizar o termo especificado.", +"Match case": "Diferenciar mai\u00fasculas e min\u00fasculas", +"Whole words": "Palavras completas", +"Spellcheck": "Corretor ortogr\u00e1fico", +"Ignore": "Ignorar", +"Ignore all": "Ignorar tudo", +"Finish": "Concluir", +"Add to Dictionary": "Adicionar ao dicion\u00e1rio", +"Insert table": "Inserir tabela", +"Table properties": "Propriedades da tabela", +"Delete table": "Eliminar tabela", +"Cell": "C\u00e9lula", +"Row": "Linha", +"Column": "Coluna", +"Cell properties": "Propriedades da c\u00e9lula", +"Merge cells": "Unir c\u00e9lulas", +"Split cell": "Dividir c\u00e9lula", +"Insert row before": "Inserir linha antes", +"Insert row after": "Inserir linha depois", +"Delete row": "Eliminar linha", +"Row properties": "Propriedades da linha", +"Cut row": "Cortar linha", +"Copy row": "Copiar linha", +"Paste row before": "Colar linha antes", +"Paste row after": "Colar linha depois", +"Insert column before": "Inserir coluna antes", +"Insert column after": "Inserir coluna depois", +"Delete column": "Eliminar coluna", +"Cols": "Colunas", +"Rows": "Linhas", +"Width": "Largura", +"Height": "Altura", +"Cell spacing": "Espa\u00e7amento entre c\u00e9lulas", +"Cell padding": "Espa\u00e7amento interno da c\u00e9lula", +"Caption": "Legenda", +"Left": "Esquerda", +"Center": "Centro", +"Right": "Direita", +"Cell type": "Tipo de c\u00e9lula", +"Scope": "Escopo", +"Alignment": "Alinhamento", +"H Align": "Alinhamento H", +"V Align": "Alinhamento V", +"Top": "Superior", +"Middle": "Meio", +"Bottom": "Inferior", +"Header cell": "C\u00e9lula de cabe\u00e7alho", +"Row group": "Agrupar linha", +"Column group": "Agrupar coluna", +"Row type": "Tipo de linha", +"Header": "Cabe\u00e7alho", +"Body": "Corpo", +"Footer": "Rodap\u00e9", +"Border color": "Cor de contorno", +"Insert template": "Inserir modelo", +"Templates": "Modelos", +"Template": "Tema", +"Text color": "Cor do texto", +"Background color": "Cor de fundo", +"Custom...": "Personalizada...", +"Custom color": "Cor personalizada", +"No color": "Sem cor", +"Table of Contents": "\u00cdndice", +"Show blocks": "Mostrar blocos", +"Show invisible characters": "Mostrar caracteres invis\u00edveis", +"Words: {0}": "Palavras: {0}", +"{0} words": "{0} palavras", +"File": "Ficheiro", +"Edit": "Editar", +"Insert": "Inserir", +"View": "Ver", +"Format": "Formatar", +"Table": "Tabela", +"Tools": "Ferramentas", +"Powered by {0}": "Criado em {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Caixa de texto formatado. Pressione ALT-F9 para exibir o menu. Pressione ALT-F10 para exibir a barra de ferramentas. Pressione ALT-0 para exibir a ajuda" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js new file mode 100644 index 0000000000..0a6a2eadf0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js @@ -0,0 +1,230 @@ +tinymce.addI18n('ro',{ +"Cut": "Taie", +"Heading 5": "Titlu 5", +"Header 2": "Antet 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Browser-ul dumneavoastr\u0103 nu support\u0103 acces direct la clipboard. Folosi\u0163i combina\u0163ile de tastatur\u0103 Ctrl+X\/C\/V.", +"Heading 4": "Titlu 4", +"Div": "Div", +"Heading 2": "Titlu 2", +"Paste": "Insereaz\u0103", +"Close": "\u00cenchide", +"Font Family": "Familie font", +"Pre": "Pre", +"Align right": "Aliniere la dreapta", +"New document": "Document nou", +"Blockquote": "Citat", +"Numbered list": "List\u0103 ordonat\u0103", +"Heading 1": "Titlu 1", +"Headings": "Titluri", +"Increase indent": "Indenteaz\u0103", +"Formats": "Formate", +"Headers": "Antete", +"Select all": "Selecteaz\u0103 tot", +"Header 3": "Antet 3", +"Blocks": "Blocuri", +"Undo": "Ref\u0103 la loc", +"Strikethrough": "T\u0103iat", +"Bullet list": "List\u0103 neordonat\u0103", +"Header 1": "Antet 1", +"Superscript": "Superscript", +"Clear formatting": "\u015eterge format\u0103rile", +"Font Sizes": "Dimensiuni font", +"Subscript": "Subscript", +"Header 6": "Antet 6", +"Redo": "Execut\u0103 din nou", +"Paragraph": "Paragraf", +"Ok": "Ok", +"Bold": "\u00cengro\u015fat", +"Code": "Cod", +"Italic": "Italic", +"Align center": "Centrare", +"Header 5": "Antet 5", +"Heading 6": "Titlu 6", +"Heading 3": "Titlu 3", +"Decrease indent": "De-indenteaz\u0103", +"Header 4": "Antet 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Functia \"lipe\u015fte\" este acum \u00een modul text simplu. Continutul va fi acum inserat ca text simplu p\u00e2n\u0103 c\u00e2nd aceast\u0103 op\u021biune va fi dezactivat.", +"Underline": "Subliniat", +"Cancel": "Anuleaz\u0103", +"Justify": "Aliniere pe toat\u0103 l\u0103\u021bimea", +"Inline": "Inline", +"Copy": "Copiaz\u0103", +"Align left": "Aliniere la st\u00e2nga", +"Visual aids": "Ajutor vizual", +"Lower Greek": "Minuscule Grecesti", +"Square": "P\u0103trat", +"Default": "Implicit", +"Lower Alpha": "Minuscule Alfanumerice", +"Circle": "Cerc", +"Disc": "Disc", +"Upper Alpha": "Majuscule Alfanumerice", +"Upper Roman": "Majuscule Romane", +"Lower Roman": "Minuscule Romane", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id-ul trebuie s\u0103 inceap\u0103 cu o liter\u0103, urmat\u0103 exclusiv de litere, numere, cratime, puncte, punct \u0219i virgul\u0103 sau underscore-uri.", +"Name": "Nume", +"Anchor": "Ancor\u0103", +"Id": "Id", +"You have unsaved changes are you sure you want to navigate away?": "Ave\u021bi modific\u0103ri nesalvate! Sunte\u0163i sigur c\u0103 dori\u0163i s\u0103 ie\u015fiti?", +"Restore last draft": "Restaurare la ultima salvare", +"Special character": "Caractere speciale", +"Source code": "Codul surs\u0103", +"Language": "Limba", +"Insert\/Edit code sample": "Inserare\/Editare mostr\u0103 cod", +"B": "B", +"R": "R", +"G": "G", +"Color": "Culoare", +"Right to left": "Dreapta la st\u00e2nga", +"Left to right": "St\u00e2nga la dreapta", +"Emoticons": "Emoticoane", +"Robots": "Robo\u021bi", +"Document properties": "Propriet\u0103\u021bi document", +"Title": "Titlu", +"Keywords": "Cuvinte cheie", +"Encoding": "Codare", +"Description": "Descriere", +"Author": "Autor", +"Fullscreen": "Pe tot ecranul", +"Horizontal line": "Linie orizontal\u0103", +"Horizontal space": "Spa\u021biul orizontal", +"Insert\/edit image": "Inserare\/editarea imaginilor", +"General": "General", +"Advanced": "Avansat", +"Source": "Surs\u0103", +"Border": "Bordur\u0103", +"Constrain proportions": "Constr\u00e2nge propor\u021biile", +"Vertical space": "Spa\u021biul vertical", +"Image description": "Descrierea imaginii", +"Style": "Stil", +"Dimensions": "Dimensiuni", +"Insert image": "Inserare imagine", +"Image": "Imagine", +"Zoom in": "M\u0103rire", +"Contrast": "Contrast", +"Back": "\u00cenapoi", +"Gamma": "Gamma", +"Flip horizontally": "R\u0103sturn\u0103 orizontal", +"Resize": "Redimensionare", +"Sharpen": "Accentuare", +"Zoom out": "Mic\u015forare", +"Image options": "Op\u021biuni imagine", +"Apply": "Salveaz\u0103", +"Brightness": "Str\u0103lucire", +"Rotate clockwise": "Rotire \u00een sensul orar", +"Rotate counterclockwise": "Rotire \u00een sensul antiorar", +"Edit image": "Editare imagine", +"Color levels": "Niveluri de culoare", +"Crop": "Decupare", +"Orientation": "Orientare", +"Flip vertically": "R\u0103sturn\u0103 vertical", +"Invert": "Invers\u0103", +"Date\/time": "Data\/ora", +"Insert date\/time": "Insereaz\u0103 data\/ora", +"Remove link": "\u0218terge link-ul", +"Url": "Url", +"Text to display": "Text de afi\u0219at", +"Anchors": "Ancor\u0103", +"Insert link": "Inserare link", +"Link": "Link", +"New window": "Fereastr\u0103 nou\u0103", +"None": "Nici unul", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL-ul introdus pare s\u0103 fie o adres\u0103 web. Dori\u021bi s\u0103 ad\u0103uga\u021bi prefixul http:\/\/ ?", +"Paste or type a link": "Introduce\u021bi un link", +"Target": "\u021aint\u0103", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL-ul introdus pare s\u0103 fie o adres\u0103 de e-mail. Dori\u021bi s\u0103 ad\u0103uga\u021bi prefixul mailto: ?", +"Insert\/edit link": "Inserare\/editare link", +"Insert\/edit video": "Inserare\/editare video", +"Media": "Media", +"Alternative source": "Surs\u0103 alternativ\u0103", +"Paste your embed code below:": "Insera\u021bi codul:", +"Insert video": "Inserare video", +"Poster": "Poster", +"Insert\/edit media": "Inserare\/editare media", +"Embed": "Embed", +"Nonbreaking space": "Spa\u021biu neseparator", +"Page break": "\u00centrerupere de pagin\u0103", +"Paste as text": "Lipe\u015fte ca text", +"Preview": "Previzualizare", +"Print": "Tip\u0103re\u0219te", +"Save": "Salveaz\u0103", +"Could not find the specified string.": "Nu am putut g\u0103si \u0219irul specificat.", +"Replace": "\u00cenlocuie\u015fte", +"Next": "Precedent", +"Whole words": "Doar cuv\u00eentul \u00eentreg", +"Find and replace": "Caut\u0103 \u015fi \u00eenlocuie\u015fte", +"Replace with": "\u00cenlocuie\u015fte cu", +"Find": "Caut\u0103", +"Replace all": "\u00cenlocuie\u015fte toate", +"Match case": "Distinge majuscule\/minuscule", +"Prev": "Anterior", +"Spellcheck": "Verificarea ortografic\u0103", +"Finish": "Finalizeaz\u0103", +"Ignore all": "Ignor\u0103 toate", +"Ignore": "Ignor\u0103", +"Add to Dictionary": "Adaug\u0103 \u00een Dic\u021bionar", +"Insert row before": "Insereaz\u0103 \u00eenainte de linie", +"Rows": "Linii", +"Height": "\u00cen\u0103l\u0163ime", +"Paste row after": "Lipe\u015fte linie dup\u0103", +"Alignment": "Aliniament", +"Border color": "Culoare bordur\u0103", +"Column group": "Grup de coloane", +"Row": "Linie", +"Insert column before": "Insereaza \u00eenainte de coloan\u0103", +"Split cell": "\u00cemp\u0103r\u021birea celulelor", +"Cell padding": "Spa\u021biere", +"Cell spacing": "Spa\u021biere celule", +"Row type": "Tip de linie", +"Insert table": "Insereaz\u0103 tabel\u0103", +"Body": "Corp", +"Caption": "Titlu", +"Footer": "Subsol", +"Delete row": "\u0218terge linia", +"Paste row before": "Lipe\u015fte \u00eenainte de linie", +"Scope": "Domeniu", +"Delete table": "\u0218terge tabel\u0103", +"H Align": "Aliniere H", +"Top": "Sus", +"Header cell": "Antet celul\u0103", +"Column": "Coloan\u0103", +"Row group": "Grup de linii", +"Cell": "Celul\u0103", +"Middle": "Mijloc", +"Cell type": "Tip celul\u0103", +"Copy row": "Copiaz\u0103 linie", +"Row properties": "Propriet\u0103\u021bi linie", +"Table properties": "Propriet\u0103\u021bi tabel\u0103", +"Bottom": "Jos", +"V Align": "Aliniere V", +"Header": "Antet", +"Right": "Dreapta", +"Insert column after": "Insereaza dup\u0103 coloan\u0103", +"Cols": "Coloane", +"Insert row after": "Insereaz\u0103 dup\u0103 linie", +"Width": "L\u0103\u0163ime", +"Cell properties": "Propriet\u0103\u021bi celul\u0103", +"Left": "St\u00e2nga", +"Cut row": "Taie linie", +"Delete column": "\u0218terge coloana", +"Center": "Centru", +"Merge cells": "\u00cembinarea celulelor", +"Insert template": "Insereaz\u0103 \u0219ablon", +"Templates": "\u015eabloane", +"Background color": "Culoare fundal", +"Custom...": "Personalizat...", +"Custom color": "Culoare personalizat\u0103", +"No color": "F\u0103r\u0103 culoare", +"Text color": "Culoare text", +"Table of Contents": "Cuprins", +"Show blocks": "Afi\u0219are blocuri", +"Show invisible characters": "Afi\u0219are caractere invizibile", +"Words: {0}": "Cuvinte: {0}", +"Insert": "Insereaz\u0103", +"File": "Fil\u0103", +"Edit": "Editeaz\u0103", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zon\u0103 cu Rich Text. Apas\u0103 ALT-F9 pentru meniu. Apas\u0103 ALT-F10 pentru bara de unelte. Apas\u0103 ALT-0 pentru ajutor", +"Tools": "Unelte", +"View": "Vezi", +"Table": "Tabel\u0103", +"Format": "Formateaz\u0103" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ru.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ru.js index 110a978002..dfae77d955 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ru.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ru.js @@ -1,219 +1,261 @@ tinymce.addI18n('ru',{ -"Cut": "\u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c", -"Heading 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", -"Header 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", -"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0431\u0443\u0444\u0435\u0440\u0443 \u043e\u0431\u043c\u0435\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448: Ctrl+X\/C\/V.", -"Heading 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", -"Div": "\u0411\u043b\u043e\u043a", -"Heading 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", -"Paste": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c", -"Close": "\u0417\u0430\u043a\u0440\u044b\u0442\u044c", -"Font Family": "\u0428\u0440\u0438\u0444\u0442", -"Pre": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435", -"Align right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", -"New document": "\u041d\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442", -"Blockquote": "\u0426\u0438\u0442\u0430\u0442\u0430", -"Numbered list": "\u041d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", -"Heading 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", -"Headings": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", -"Increase indent": "\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c \u043e\u0442\u0441\u0442\u0443\u043f", -"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442", -"Headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", -"Select all": "\u0412\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0441\u0435", -"Header 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", -"Blocks": "\u0411\u043b\u043e\u043a\u0438", -"Undo": "\u0412\u0435\u0440\u043d\u0443\u0442\u044c", -"Strikethrough": "\u0417\u0430\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044b\u0439", -"Bullet list": "\u041c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", -"Header 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", -"Superscript": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0438\u043d\u0434\u0435\u043a\u0441", -"Clear formatting": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442", -"Font Sizes": "\u0420\u0430\u0437\u043c\u0435\u0440 \u0448\u0440\u0438\u0444\u0442\u0430", -"Subscript": "\u041d\u0438\u0436\u043d\u0438\u0439 \u0438\u043d\u0434\u0435\u043a\u0441", -"Header 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", "Redo": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c", -"Paragraph": "\u041f\u0430\u0440\u0430\u0433\u0440\u0430\u0444", -"Ok": "\u041e\u043a", -"Bold": "\u041f\u043e\u043b\u0443\u0436\u0438\u0440\u043d\u044b\u0439", -"Code": "\u041a\u043e\u0434", -"Italic": "\u041a\u0443\u0440\u0441\u0438\u0432", -"Align center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", -"Header 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", -"Heading 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", -"Heading 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", -"Decrease indent": "\u0423\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043e\u0442\u0441\u0442\u0443\u043f", -"Header 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", -"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u043e\u043f\u0446\u0438\u044e.", -"Underline": "\u041f\u043e\u0434\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044b\u0439", -"Cancel": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c", -"Justify": "\u041f\u043e \u0448\u0438\u0440\u0438\u043d\u0435", -"Inline": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435", +"Undo": "\u0412\u0435\u0440\u043d\u0443\u0442\u044c", +"Cut": "\u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c", "Copy": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c", -"Align left": "\u041f\u043e \u043b\u0435\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Paste": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c", +"Select all": "\u0412\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0441\u0435", +"New document": "\u041d\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442", +"Ok": "\u041e\u043a", +"Cancel": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c", "Visual aids": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0443\u0440\u044b", -"Lower Greek": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u0433\u0440\u0435\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b", -"Square": "\u041a\u0432\u0430\u0434\u0440\u0430\u0442\u044b", +"Bold": "\u041f\u043e\u043b\u0443\u0436\u0438\u0440\u043d\u044b\u0439", +"Italic": "\u041a\u0443\u0440\u0441\u0438\u0432", +"Underline": "\u041f\u043e\u0434\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044b\u0439", +"Strikethrough": "\u0417\u0430\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044b\u0439", +"Superscript": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0438\u043d\u0434\u0435\u043a\u0441", +"Subscript": "\u041d\u0438\u0436\u043d\u0438\u0439 \u0438\u043d\u0434\u0435\u043a\u0441", +"Clear formatting": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442", +"Align left": "\u041f\u043e \u043b\u0435\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Align center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Align right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Justify": "\u041f\u043e \u0448\u0438\u0440\u0438\u043d\u0435", +"Bullet list": "\u041c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Numbered list": "\u041d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Decrease indent": "\u0423\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043e\u0442\u0441\u0442\u0443\u043f", +"Increase indent": "\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c \u043e\u0442\u0441\u0442\u0443\u043f", +"Close": "\u0417\u0430\u043a\u0440\u044b\u0442\u044c", +"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0431\u0443\u0444\u0435\u0440\u0443 \u043e\u0431\u043c\u0435\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448: Ctrl+X\/C\/V.", +"Headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", +"Header 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Header 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Header 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Header 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Header 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Header 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Headings": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", +"Heading 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Heading 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Heading 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Heading 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Heading 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Heading 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Preformatted": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435", +"Div": "\u0411\u043b\u043e\u043a", +"Pre": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435", +"Code": "\u041a\u043e\u0434", +"Paragraph": "\u041f\u0430\u0440\u0430\u0433\u0440\u0430\u0444", +"Blockquote": "\u0426\u0438\u0442\u0430\u0442\u0430", +"Inline": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435", +"Blocks": "\u0411\u043b\u043e\u043a\u0438", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u043e\u043f\u0446\u0438\u044e.", +"Font Family": "\u0428\u0440\u0438\u0444\u0442", +"Font Sizes": "\u0420\u0430\u0437\u043c\u0435\u0440 \u0448\u0440\u0438\u0444\u0442\u0430", +"Class": "\u041a\u043b\u0430\u0441\u0441", +"Browse for an image": "\u0412\u044b\u0431\u043e\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", +"OR": "\u0418\u041b\u0418", +"Drop an image here": "\u041f\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u044e\u0434\u0430", +"Upload": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c", +"Block": "\u0411\u043b\u043e\u043a", +"Align": "\u0412\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", "Default": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439", -"Lower Alpha": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u043b\u0430\u0442\u0438\u043d\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b", "Circle": "\u041e\u043a\u0440\u0443\u0436\u043d\u043e\u0441\u0442\u0438", "Disc": "\u041a\u0440\u0443\u0433\u0438", +"Square": "\u041a\u0432\u0430\u0434\u0440\u0430\u0442\u044b", +"Lower Alpha": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u043b\u0430\u0442\u0438\u043d\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b", +"Lower Greek": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u0433\u0440\u0435\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b", +"Lower Roman": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u0440\u0438\u043c\u0441\u043a\u0438\u0435 \u0446\u0438\u0444\u0440\u044b", "Upper Alpha": "\u0417\u0430\u0433\u043b\u0430\u0432\u043d\u044b\u0435 \u043b\u0430\u0442\u0438\u043d\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b", "Upper Roman": "\u0417\u0430\u0433\u043b\u0430\u0432\u043d\u044b\u0435 \u0440\u0438\u043c\u0441\u043a\u0438\u0435 \u0446\u0438\u0444\u0440\u044b", -"Lower Roman": "\u0421\u0442\u0440\u043e\u0447\u043d\u044b\u0435 \u0440\u0438\u043c\u0441\u043a\u0438\u0435 \u0446\u0438\u0444\u0440\u044b", -"Name": "\u0418\u043c\u044f", "Anchor": "\u042f\u043a\u043e\u0440\u044c", +"Name": "\u0418\u043c\u044f", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 \u0431\u0443\u043a\u0432\u044b, \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0431\u0443\u043a\u0432\u044b, \u0446\u0438\u0444\u0440\u044b, \u0442\u0438\u0440\u0435, \u0442\u043e\u0447\u043a\u0438, \u0434\u0432\u043e\u0435\u0442\u043e\u0447\u0438\u044f \u0438\u043b\u0438 \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u0438\u0432\u0430\u043d\u0438\u044f.", "You have unsaved changes are you sure you want to navigate away?": "\u0423 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f. \u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0439\u0442\u0438?", "Restore last draft": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430", "Special character": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b", "Source code": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434", -"B": "B", +"Insert\/Edit code sample": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\/\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u0434\u0430", +"Language": "\u042f\u0437\u044b\u043a", +"Code sample": "\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u0434\u0430", +"Color": "\u0426\u0432\u0435\u0442", "R": "R", "G": "G", -"Color": "\u0426\u0432\u0435\u0442", -"Right to left": "\u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u043e", +"B": "B", "Left to right": "\u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e", +"Right to left": "\u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u043e", "Emoticons": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043c\u0430\u0439\u043b", -"Robots": "\u0420\u043e\u0431\u043e\u0442\u044b", "Document properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430", "Title": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", "Keywords": "\u041a\u043b\u044e\u0447\u0438\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430", -"Encoding": "\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430", "Description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435", +"Robots": "\u0420\u043e\u0431\u043e\u0442\u044b", "Author": "\u0410\u0432\u0442\u043e\u0440", +"Encoding": "\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430", "Fullscreen": "\u041f\u043e\u043b\u043d\u043e\u044d\u043a\u0440\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", +"Action": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435", +"Shortcut": "\u042f\u0440\u043b\u044b\u043a", +"Help": "\u041f\u043e\u043c\u043e\u0449\u044c", +"Address": "\u0410\u0434\u0440\u0435\u0441", +"Focus to menubar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u0435\u043d\u044e", +"Focus to toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432", +"Focus to element path": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0435 \u043f\u0443\u0442\u0438", +"Focus to contextual toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432", +"Insert link (if link plugin activated)": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 (\u0435\u0441\u043b\u0438 \u043f\u043b\u0430\u0433\u0438\u043d link \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d)", +"Save (if save plugin activated)": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c (\u0435\u0441\u043b\u0438 \u043f\u043b\u0430\u0433\u0438\u043d save \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d)", +"Find (if searchreplace plugin activated)": "\u041d\u0430\u0439\u0442\u0438 (\u0435\u0441\u043b\u0438 \u043f\u043b\u0430\u0433\u0438\u043d searchreplace \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d)", +"Plugins installed ({0}):": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u044b ({0}):", +"Premium plugins:": "\u041f\u0440\u0435\u043c\u0438\u0443\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u044b:", +"Learn more...": "\u0423\u0437\u043d\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435...", +"You are using {0}": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 {0}", +"Plugins": "\u041f\u043b\u0430\u0433\u0438\u043d\u044b", +"Handy Shortcuts": "\u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438", "Horizontal line": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u043b\u0438\u043d\u0438\u044f", -"Horizontal space": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b", "Insert\/edit image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", +"Image description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", +"Source": "\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a", +"Dimensions": "\u0420\u0430\u0437\u043c\u0435\u0440", +"Constrain proportions": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u0438", "General": "\u041e\u0431\u0449\u0435\u0435", "Advanced": "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435", -"Source": "\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a", -"Border": "\u0420\u0430\u043c\u043a\u0430", -"Constrain proportions": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u0438", -"Vertical space": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b", -"Image description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", "Style": "\u0421\u0442\u0438\u043b\u044c", -"Dimensions": "\u0420\u0430\u0437\u043c\u0435\u0440", +"Vertical space": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b", +"Horizontal space": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b", +"Border": "\u0420\u0430\u043c\u043a\u0430", "Insert image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", -"Zoom in": "\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u044c", -"Contrast": "\u041a\u043e\u043d\u0442\u0440\u0430\u0441\u0442", -"Back": "\u041d\u0430\u0437\u0430\u0434", -"Gamma": "\u0413\u0430\u043c\u043c\u0430", -"Flip horizontally": "\u041e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438", -"Resize": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440", -"Sharpen": "\u0427\u0435\u0442\u043a\u043e\u0441\u0442\u044c", -"Zoom out": "\u041e\u0442\u0434\u0430\u043b\u0438\u0442\u044c", -"Image options": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", -"Apply": "\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c", -"Brightness": "\u042f\u0440\u043a\u043e\u0441\u0442\u044c", -"Rotate clockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0435\u043b\u043a\u0435", +"Image": "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", +"Image list": "\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439", "Rotate counterclockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u0440\u043e\u0442\u0438\u0432 \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0435\u043b\u043a\u0438", -"Edit image": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", -"Color levels": "\u0426\u0432\u0435\u0442\u043e\u0432\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438", -"Crop": "\u041e\u0431\u0440\u0435\u0437\u0430\u0442\u044c", -"Orientation": "\u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f", +"Rotate clockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0435\u043b\u043a\u0435", "Flip vertically": "\u041e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438", +"Flip horizontally": "\u041e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438", +"Edit image": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", +"Image options": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", +"Zoom in": "\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u044c", +"Zoom out": "\u041e\u0442\u0434\u0430\u043b\u0438\u0442\u044c", +"Crop": "\u041e\u0431\u0440\u0435\u0437\u0430\u0442\u044c", +"Resize": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440", +"Orientation": "\u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f", +"Brightness": "\u042f\u0440\u043a\u043e\u0441\u0442\u044c", +"Sharpen": "\u0427\u0435\u0442\u043a\u043e\u0441\u0442\u044c", +"Contrast": "\u041a\u043e\u043d\u0442\u0440\u0430\u0441\u0442", +"Color levels": "\u0426\u0432\u0435\u0442\u043e\u0432\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438", +"Gamma": "\u0413\u0430\u043c\u043c\u0430", "Invert": "\u0418\u043d\u0432\u0435\u0440\u0441\u0438\u044f", +"Apply": "\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c", +"Back": "\u041d\u0430\u0437\u0430\u0434", "Insert date\/time": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u0442\u0443\/\u0432\u0440\u0435\u043c\u044f", -"Remove link": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", -"Url": "\u0410\u0434\u0440\u0435\u0441 \u0441\u0441\u044b\u043b\u043a\u0438", -"Text to display": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0439 \u0442\u0435\u043a\u0441\u0442", -"Anchors": "\u042f\u043a\u043e\u0440\u044f", +"Date\/time": "\u0414\u0430\u0442\u0430\/\u0432\u0440\u0435\u043c\u044f", "Insert link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", -"New window": "\u0412 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435", -"None": "\u041d\u0435\u0442", -"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0412\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439 URL \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0441\u044b\u043b\u043a\u043e\u0439. \u0412\u044b \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u00abhttp:\/\/\u00bb?", -"Target": "\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", -"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0412\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439 URL \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b. \u0412\u044b \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u00abmailto:\u00bb?", "Insert\/edit link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", -"Insert\/edit video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e", -"Poster": "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", -"Alternative source": "\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a", -"Paste your embed code below:": "\u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0438\u0436\u0435:", +"Text to display": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0439 \u0442\u0435\u043a\u0441\u0442", +"Url": "\u0410\u0434\u0440\u0435\u0441 \u0441\u0441\u044b\u043b\u043a\u0438", +"Target": "\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", +"None": "\u041d\u0435\u0442", +"New window": "\u0412 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435", +"Remove link": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443", +"Anchors": "\u042f\u043a\u043e\u0440\u044f", +"Link": "\u0421\u0441\u044b\u043b\u043a\u0430", +"Paste or type a link": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043b\u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0441\u044b\u043b\u043a\u0443", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0412\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439 URL \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b. \u0412\u044b \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u00abmailto:\u00bb?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0412\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439 URL \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0441\u044b\u043b\u043a\u043e\u0439. \u0412\u044b \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u00abhttp:\/\/\u00bb?", +"Link list": "\u0421\u043f\u0438\u0441\u043e\u043a \u0441\u0441\u044b\u043b\u043e\u043a", "Insert video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e", +"Insert\/edit video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e", +"Insert\/edit media": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e", +"Alternative source": "\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a", +"Poster": "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", +"Paste your embed code below:": "\u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0438\u0436\u0435:", "Embed": "\u041a\u043e\u0434 \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438", +"Media": "\u0412\u0438\u0434\u0435\u043e", "Nonbreaking space": "\u041d\u0435\u0440\u0430\u0437\u0440\u044b\u0432\u043d\u044b\u0439 \u043f\u0440\u043e\u0431\u0435\u043b", "Page break": "\u0420\u0430\u0437\u0440\u044b\u0432 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b", "Paste as text": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442", "Preview": "\u041f\u0440\u0435\u0434\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440", "Print": "\u041f\u0435\u0447\u0430\u0442\u044c", "Save": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", -"Could not find the specified string.": "\u0417\u0430\u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430", -"Replace": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c", -"Next": "\u0412\u043d\u0438\u0437", -"Whole words": "\u0421\u043b\u043e\u0432\u043e \u0446\u0435\u043b\u0438\u043a\u043e\u043c", -"Find and replace": "\u041f\u043e\u0438\u0441\u043a \u0438 \u0437\u0430\u043c\u0435\u043d\u0430", -"Replace with": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430", "Find": "\u041d\u0430\u0439\u0442\u0438", +"Replace with": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430", +"Replace": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c", "Replace all": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435", -"Match case": "\u0423\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0433\u0438\u0441\u0442\u0440", "Prev": "\u0412\u0432\u0435\u0440\u0445", +"Next": "\u0412\u043d\u0438\u0437", +"Find and replace": "\u041f\u043e\u0438\u0441\u043a \u0438 \u0437\u0430\u043c\u0435\u043d\u0430", +"Could not find the specified string.": "\u0417\u0430\u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430", +"Match case": "\u0423\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0433\u0438\u0441\u0442\u0440", +"Whole words": "\u0421\u043b\u043e\u0432\u043e \u0446\u0435\u043b\u0438\u043a\u043e\u043c", "Spellcheck": "\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435", -"Finish": "\u0417\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c", -"Ignore all": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435", "Ignore": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c", +"Ignore all": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435", +"Finish": "\u0417\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c", "Add to Dictionary": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c", -"Insert row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u0432\u0435\u0440\u0445\u0443", -"Rows": "\u0421\u0442\u0440\u043e\u043a\u0438", -"Height": "\u0412\u044b\u0441\u043e\u0442\u0430", -"Paste row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u043d\u0438\u0437\u0443", -"Alignment": "\u0412\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", -"Border color": "\u0426\u0432\u0435\u0442 \u0440\u0430\u043c\u043a\u0438", -"Column group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a", -"Row": "\u0421\u0442\u0440\u043e\u043a\u0430", -"Insert column before": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0441\u043b\u0435\u0432\u0430", -"Split cell": "\u0420\u0430\u0437\u0431\u0438\u0442\u044c \u044f\u0447\u0435\u0439\u043a\u0443", -"Cell padding": "\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043e\u0442\u0441\u0442\u0443\u043f", -"Cell spacing": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 \u043e\u0442\u0441\u0442\u0443\u043f", -"Row type": "\u0422\u0438\u043f \u0441\u0442\u0440\u043e\u043a\u0438", "Insert table": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443", -"Body": "\u0422\u0435\u043b\u043e", -"Caption": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", -"Footer": "\u041d\u0438\u0437", -"Delete row": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", -"Paste row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u0432\u0435\u0440\u0445\u0443", -"Scope": "Scope", -"Delete table": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443", -"H Align": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", -"Top": "\u041f\u043e \u0432\u0435\u0440\u0445\u0443", -"Header cell": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", -"Column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", -"Row group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0441\u0442\u0440\u043e\u043a", -"Cell": "\u042f\u0447\u0435\u0439\u043a\u0430", -"Middle": "\u041f\u043e \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435", -"Cell type": "\u0422\u0438\u043f \u044f\u0447\u0435\u0439\u043a\u0438", -"Copy row": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", -"Row properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u0442\u0440\u043e\u043a\u0438", "Table properties": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b", -"Bottom": "\u041f\u043e \u043d\u0438\u0437\u0443", -"V Align": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", -"Header": "\u0428\u0430\u043f\u043a\u0430", -"Right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", -"Insert column after": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0441\u043f\u0440\u0430\u0432\u0430", -"Cols": "\u0421\u0442\u043e\u043b\u0431\u0446\u044b", -"Insert row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u043d\u0438\u0437\u0443", -"Width": "\u0428\u0438\u0440\u0438\u043d\u0430", +"Delete table": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443", +"Cell": "\u042f\u0447\u0435\u0439\u043a\u0430", +"Row": "\u0421\u0442\u0440\u043e\u043a\u0430", +"Column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", "Cell properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u044f\u0447\u0435\u0439\u043a\u0438", -"Left": "\u041f\u043e \u043b\u0435\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", -"Cut row": "\u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", -"Delete column": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446", -"Center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", "Merge cells": "\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u044f\u0447\u0435\u0439\u043a\u0438", +"Split cell": "\u0420\u0430\u0437\u0431\u0438\u0442\u044c \u044f\u0447\u0435\u0439\u043a\u0443", +"Insert row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u0432\u0435\u0440\u0445\u0443", +"Insert row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u043d\u0438\u0437\u0443", +"Delete row": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", +"Row properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u0442\u0440\u043e\u043a\u0438", +"Cut row": "\u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", +"Copy row": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443", +"Paste row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u0432\u0435\u0440\u0445\u0443", +"Paste row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u043d\u0438\u0437\u0443", +"Insert column before": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0441\u043b\u0435\u0432\u0430", +"Insert column after": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0441\u043f\u0440\u0430\u0432\u0430", +"Delete column": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446", +"Cols": "\u0421\u0442\u043e\u043b\u0431\u0446\u044b", +"Rows": "\u0421\u0442\u0440\u043e\u043a\u0438", +"Width": "\u0428\u0438\u0440\u0438\u043d\u0430", +"Height": "\u0412\u044b\u0441\u043e\u0442\u0430", +"Cell spacing": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 \u043e\u0442\u0441\u0442\u0443\u043f", +"Cell padding": "\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043e\u0442\u0441\u0442\u0443\u043f", +"Caption": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Left": "\u041f\u043e \u043b\u0435\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Cell type": "\u0422\u0438\u043f \u044f\u0447\u0435\u0439\u043a\u0438", +"Scope": "Scope", +"Alignment": "\u0412\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", +"H Align": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", +"V Align": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435", +"Top": "\u041f\u043e \u0432\u0435\u0440\u0445\u0443", +"Middle": "\u041f\u043e \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435", +"Bottom": "\u041f\u043e \u043d\u0438\u0437\u0443", +"Header cell": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Row group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0441\u0442\u0440\u043e\u043a", +"Column group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a", +"Row type": "\u0422\u0438\u043f \u0441\u0442\u0440\u043e\u043a\u0438", +"Header": "\u0428\u0430\u043f\u043a\u0430", +"Body": "\u0422\u0435\u043b\u043e", +"Footer": "\u041d\u0438\u0437", +"Border color": "\u0426\u0432\u0435\u0442 \u0440\u0430\u043c\u043a\u0438", "Insert template": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d", "Templates": "\u0428\u0430\u0431\u043b\u043e\u043d\u044b", +"Template": "\u0428\u0430\u0431\u043b\u043e\u043d", +"Text color": "\u0426\u0432\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u0430", "Background color": "\u0426\u0432\u0435\u0442 \u0444\u043e\u043d\u0430", "Custom...": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c\u2026", "Custom color": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0446\u0432\u0435\u0442", "No color": "\u0411\u0435\u0437 \u0446\u0432\u0435\u0442\u0430", -"Text color": "\u0426\u0432\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u0430", +"Table of Contents": "\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435", "Show blocks": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0431\u043b\u043e\u043a\u0438", "Show invisible characters": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u0432\u0438\u0434\u0438\u043c\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b", "Words: {0}": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043b\u043e\u0432: {0}", -"Insert": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c", +"{0} words": "\u0441\u043b\u043e\u0432: {0}", "File": "\u0424\u0430\u0439\u043b", "Edit": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c", -"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0422\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 ALT-F9 \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u043d\u044e, ALT-F10 \u043f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, ALT-0 \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u043f\u043e\u043c\u043e\u0449\u0438.", -"Tools": "\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b", +"Insert": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c", "View": "\u0412\u0438\u0434", +"Format": "\u0424\u043e\u0440\u043c\u0430\u0442", "Table": "\u0422\u0430\u0431\u043b\u0438\u0446\u0430", -"Format": "\u0424\u043e\u0440\u043c\u0430\u0442" +"Tools": "\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b", +"Powered by {0}": "\u041f\u0440\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0435 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0422\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 ALT-F9 \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u043d\u044e, ALT-F10 \u043f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, ALT-0 \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u043f\u043e\u043c\u043e\u0449\u0438." }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js new file mode 100644 index 0000000000..2362a6dbc6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js @@ -0,0 +1,253 @@ +tinymce.addI18n('sk',{ +"Redo": "Znova", +"Undo": "Vr\u00e1ti\u0165", +"Cut": "Vystrihn\u00fa\u0165", +"Copy": "Kop\u00edrova\u0165", +"Paste": "Vlo\u017ei\u0165", +"Select all": "Ozna\u010di\u0165 v\u0161etko", +"New document": "Nov\u00fd dokument", +"Ok": "Ok", +"Cancel": "Zru\u0161i\u0165", +"Visual aids": "Vizu\u00e1lne pom\u00f4cky", +"Bold": "Tu\u010dn\u00e9", +"Italic": "Kurz\u00edva", +"Underline": "Pod\u010diarknut\u00e9", +"Strikethrough": "Pre\u010diarknut\u00e9", +"Superscript": "Horn\u00fd index", +"Subscript": "Spodn\u00fd index", +"Clear formatting": "Vymaza\u0165 form\u00e1tovanie", +"Align left": "Zarovna\u0165 v\u013eavo", +"Align center": "Zarovna\u0165 na stred", +"Align right": "Zarovna\u0165 vpravo", +"Justify": "Zarovna\u0165", +"Bullet list": "Odr\u00e1\u017eky", +"Numbered list": "\u010c\u00edslovan\u00fd zoznam", +"Decrease indent": "Zmen\u0161i\u0165 odsadenie", +"Increase indent": "Zv\u00e4\u010d\u0161i\u0165 odsadenie", +"Close": "Zatvori\u0165", +"Formats": "Form\u00e1ty", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "V\u00e1\u0161 prehliada\u010d nepodporuje priamy pr\u00edstup do schr\u00e1nky. Pou\u017eite kl\u00e1vesov\u00e9 skratky Ctrl+X\/C\/V.", +"Headers": "Nadpisy", +"Header 1": "Nadpis 1", +"Header 2": "Nadpis 2", +"Header 3": "Nadpis 3", +"Header 4": "Nadpis 4", +"Header 5": "Nadpis 5", +"Header 6": "Nadpis 6", +"Headings": "Nadpisy", +"Heading 1": "Nadpis 1", +"Heading 2": "Nadpis 2", +"Heading 3": "Nadpis 3", +"Heading 4": "Nadpis 4", +"Heading 5": "Nadpis 5", +"Heading 6": "Nadpis 6", +"Div": "Blok", +"Pre": "Preform\u00e1tovan\u00fd", +"Code": "K\u00f3d", +"Paragraph": "Odsek", +"Blockquote": "Cit\u00e1cia", +"Inline": "\u0160t\u00fdly", +"Blocks": "Bloky", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Vkladanie je v m\u00f3de neform\u00e1tovan\u00e9ho textu. Vkladan\u00fd obsah bude vlo\u017een\u00fd ako neform\u00e1tovan\u00fd, a\u017e pok\u00fdm t\u00fato mo\u017enos\u0165 nevypnete.", +"Font Family": "P\u00edsmo", +"Font Sizes": "Ve\u013ekos\u0165 p\u00edsma", +"Class": "Trieda", +"Browse for an image": "N\u00e1js\u0165 obr\u00e1zok", +"OR": "ALEBO", +"Drop an image here": "Pretiahnite obr\u00e1zok sem", +"Upload": "Nahra\u0165", +"Default": "V\u00fdchodzie", +"Circle": "Kruh", +"Disc": "Disk", +"Square": "\u0160tvorec", +"Lower Alpha": "Mal\u00e9 p\u00edsmen\u00e1", +"Lower Greek": "Mal\u00e9 gr\u00e9cke p\u00edsmen\u00e1", +"Lower Roman": "Mal\u00e9 r\u00edmske \u010d\u00edslice", +"Upper Alpha": "Ve\u013ek\u00e9 p\u00edsmen\u00e1", +"Upper Roman": "Ve\u013ek\u00e9 r\u00edmske \u010d\u00edslice", +"Anchor": "Odkaz", +"Name": "N\u00e1zov", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id by malo za\u010d\u00edna\u0165 p\u00edsmenom, nasledovan\u00e9 p\u00edsmenami, \u010d\u00edslami, pom\u013a\u010dkami, bodkami, dvojbodkami alebo podtr\u017en\u00edkmi.", +"You have unsaved changes are you sure you want to navigate away?": "M\u00e1te neulo\u017een\u00e9 zmeny, naozaj chcete opusti\u0165 str\u00e1nku?", +"Restore last draft": "Obnovi\u0165 posledn\u00fd koncept", +"Special character": "\u0160peci\u00e1lny znak", +"Source code": "Zdrojov\u00fd k\u00f3d", +"Insert\/Edit code sample": "Vlo\u017ei\u0165\/upravi\u0165 vzorku k\u00f3du", +"Language": "Jazyk", +"Color": "Farba", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Z\u013eava doprava", +"Right to left": "Sprava do\u013eava", +"Emoticons": "Smajl\u00edci", +"Document properties": "Vlastnosti dokumentu", +"Title": "Nadpis", +"Keywords": "K\u013e\u00fa\u010dov\u00e9 slov\u00e1", +"Description": "Popis", +"Robots": "Preh\u013ead\u00e1vacie roboty", +"Author": "Autor", +"Encoding": "K\u00f3dovanie", +"Fullscreen": "Na cel\u00fa obrazovku", +"Action": "Action", +"Shortcut": "Shortcut", +"Help": "Help", +"Address": "Address", +"Focus to menubar": "Focus to menubar", +"Focus to toolbar": "Focus to toolbar", +"Focus to element path": "Focus to element path", +"Focus to contextual toolbar": "Focus to contextual toolbar", +"Insert link (if link plugin activated)": "Insert link (if link plugin activated)", +"Save (if save plugin activated)": "Save (if save plugin activated)", +"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)", +"Plugins installed ({0}):": "Plugins installed ({0}):", +"Premium plugins:": "Premium plugins:", +"Learn more...": "Learn more...", +"You are using {0}": "You are using {0}", +"Horizontal line": "Horizont\u00e1lna \u010diara", +"Insert\/edit image": "Vlo\u017ei\u0165\/upravi\u0165 obr\u00e1zok", +"Image description": "Popis obr\u00e1zku", +"Source": "Zdroj", +"Dimensions": "Rozmery", +"Constrain proportions": "Vymedzen\u00e9 proporcie", +"General": "Hlavn\u00e9", +"Advanced": "Pokro\u010dil\u00e9", +"Style": "\u0160t\u00fdl", +"Vertical space": "Vertik\u00e1lny priestor", +"Horizontal space": "Horizont\u00e1lny priestor", +"Border": "Or\u00e1movanie", +"Insert image": "Vlo\u017ei\u0165 obr\u00e1zok", +"Image": "Obr\u00e1zok", +"Image list": "Zoznam obr\u00e1zkov", +"Rotate counterclockwise": "Oto\u010di\u0165 proti smeru hodinov\u00fdch ru\u010di\u010diek", +"Rotate clockwise": "Oto\u010di\u0165 v smere hodinov\u00fdch ru\u010di\u010diek", +"Flip vertically": "Preklopi\u0165 vertik\u00e1lne", +"Flip horizontally": "Preklopi\u0165 horizont\u00e1lne", +"Edit image": "Upravi\u0165 obr\u00e1zok", +"Image options": "Mo\u017enosti obr\u00e1zku", +"Zoom in": "Pribl\u00ed\u017ei\u0165", +"Zoom out": "Oddiali\u0165", +"Crop": "Vyreza\u0165", +"Resize": "Zmeni\u0165 ve\u013ekos\u0165", +"Orientation": "Orient\u00e1cia", +"Brightness": "Jas", +"Sharpen": "Zaostri\u0165", +"Contrast": "Kontrast", +"Color levels": "\u00darovne farieb", +"Gamma": "Gama", +"Invert": "Invertova\u0165", +"Apply": "Pou\u017ei\u0165", +"Back": "Sp\u00e4\u0165", +"Insert date\/time": "Vlo\u017ei\u0165 d\u00e1tum\/\u010das", +"Date\/time": "D\u00e1tum\/\u010das", +"Insert link": "Vlo\u017ei\u0165 odkaz", +"Insert\/edit link": "Vlo\u017ei\u0165\/upravi\u0165 odkaz", +"Text to display": "Zobrazen\u00fd text", +"Url": "Url", +"Target": "Cie\u013e", +"None": "\u017diadne", +"New window": "Nov\u00e9 okno", +"Remove link": "Odstr\u00e1ni\u0165 odkaz", +"Anchors": "Kotvy", +"Link": "Odkaz", +"Paste or type a link": "Prilepte alebo nap\u00ed\u0161te odkaz", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL, ktor\u00fa ste vlo\u017eili je pravdepodobne emailov\u00e1 adresa. \u017del\u00e1te si prida\u0165 vy\u017eadovan\u00fa mailto: predponu?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL adresa ktor\u00fa ste zadali vyzer\u00e1 ako extern\u00fd odkaz. Chcete prida\u0165 vy\u017eadovan\u00fa http:\/\/ predponu?", +"Link list": "Zoznam odkazov", +"Insert video": "Vlo\u017ei\u0165 video", +"Insert\/edit video": "Vlo\u017ei\u0165\/upravi\u0165 video", +"Insert\/edit media": "Vlo\u017ei\u0165\/upravi\u0165 m\u00e9di\u00e1", +"Alternative source": "Alternat\u00edvny zdroj", +"Poster": "Uk\u00e1\u017eka", +"Paste your embed code below:": "Vlo\u017ete k\u00f3d pre vlo\u017eenie na str\u00e1nku:", +"Embed": "Vlo\u017een\u00e9", +"Media": "M\u00e9di\u00e1", +"Nonbreaking space": "Nedelite\u013en\u00e1 medzera", +"Page break": "Zalomenie str\u00e1nky", +"Paste as text": "Vlo\u017ei\u0165 ako text", +"Preview": "N\u00e1h\u013ead", +"Print": "Tla\u010di\u0165", +"Save": "Ulo\u017ei\u0165", +"Find": "H\u013eada\u0165", +"Replace with": "Nahradi\u0165 za", +"Replace": "Nahradi\u0165", +"Replace all": "Nahradi\u0165 v\u0161etko", +"Prev": "Predch\u00e1dzaj\u00face", +"Next": "Nasleduj\u00face", +"Find and replace": "Vyh\u013eada\u0165 a nahradi\u0165", +"Could not find the specified string.": "Zadan\u00fd re\u0165azec sa nena\u0161iel.", +"Match case": "Rozli\u0161ova\u0165 ve\u013ek\u00e9\/mal\u00e9", +"Whole words": "Cel\u00e9 slov\u00e1", +"Spellcheck": "Kontrola pravopisu", +"Ignore": "Ignorova\u0165", +"Ignore all": "Ignorova\u0165 v\u0161etko", +"Finish": "Dokon\u010di\u0165", +"Add to Dictionary": "Prida\u0165 do slovn\u00edka", +"Insert table": "Vlo\u017ei\u0165 tabu\u013eku", +"Table properties": "Nastavenia tabu\u013eky", +"Delete table": "Zmaza\u0165 tabu\u013eku", +"Cell": "Bunka", +"Row": "Riadok", +"Column": "St\u013apec", +"Cell properties": "Vlastnosti bunky", +"Merge cells": "Spoji\u0165 bunky", +"Split cell": "Rozdeli\u0165 bunku", +"Insert row before": "Vlo\u017ei\u0165 nov\u00fd riadok pred", +"Insert row after": "Vlo\u017ei\u0165 nov\u00fd riadok za", +"Delete row": "Zmaza\u0165 riadok", +"Row properties": "Vlastnosti riadku", +"Cut row": "Vystrihn\u00fa\u0165 riadok", +"Copy row": "Kop\u00edrova\u0165 riadok", +"Paste row before": "Vlo\u017ei\u0165 riadok pred", +"Paste row after": "Vlo\u017ei\u0165 riadok za", +"Insert column before": "Prida\u0165 nov\u00fd st\u013apec pred", +"Insert column after": "Prida\u0165 nov\u00fd st\u013apec za", +"Delete column": "Vymaza\u0165 st\u013apec", +"Cols": "St\u013apce", +"Rows": "Riadky", +"Width": "\u0160\u00edrka", +"Height": "V\u00fd\u0161ka", +"Cell spacing": "Priestor medzi bunkami", +"Cell padding": "Odsadenie v bunk\u00e1ch", +"Caption": "Popisok", +"Left": "V\u013eavo", +"Center": "Na stred", +"Right": "Vpravo", +"Cell type": "Typ bunky", +"Scope": "Oblas\u0165", +"Alignment": "Zarovnanie", +"H Align": "Horizont\u00e1lne zarovnanie", +"V Align": "Vertik\u00e1lne zarovnanie", +"Top": "Vrch", +"Middle": "Stred", +"Bottom": "Spodok", +"Header cell": "Bunka z\u00e1hlavia", +"Row group": "Skupina riadkov", +"Column group": "Skupina st\u013apcov", +"Row type": "Typ riadku", +"Header": "Z\u00e1hlavie", +"Body": "Telo", +"Footer": "P\u00e4ti\u010dka", +"Border color": "Farba or\u00e1movania", +"Insert template": "Vlo\u017ei\u0165 \u0161abl\u00f3nu", +"Templates": "\u0160abl\u00f3ny", +"Template": "\u0160abl\u00f3na", +"Text color": "Farba textu", +"Background color": "Farba pozadia", +"Custom...": "Vlastn\u00e1...", +"Custom color": "Vlastn\u00e1 farba", +"No color": "Bez farby", +"Table of Contents": "Obsah", +"Show blocks": "Zobrazi\u0165 bloky", +"Show invisible characters": "Zobrazi\u0165 skryt\u00e9 znaky", +"Words: {0}": "Slov: {0}", +"File": "S\u00fabor", +"Edit": "Upravi\u0165", +"Insert": "Vlo\u017ei\u0165", +"View": "Zobrazi\u0165", +"Format": "Form\u00e1t", +"Table": "Tabu\u013eka", +"Tools": "N\u00e1stroje", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Textov\u00e9 pole. Stla\u010dte ALT-F9 pre zobrazenie menu, ALT-F10 pre zobrazenie panela n\u00e1strojov, ALT-0 pre n\u00e1povedu." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sl_SI.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sl_SI.js new file mode 100644 index 0000000000..ba6c89a584 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sl_SI.js @@ -0,0 +1,230 @@ +tinymce.addI18n('sl_SI',{ +"Cut": "Izre\u017ei", +"Heading 5": "Podnaslov 5", +"Header 2": "Naslov 2", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Varnostne nastavitve brskalnika ne dopu\u0161\u010dajo direktnega dostopa do odlo\u017ei\u0161\u010da. Uporabite kombinacijo tipk Ctrl+X\/C\/V na tipkovnici.", +"Heading 4": "Podnaslov 4", +"Div": "Div", +"Heading 2": "Podnaslov 2", +"Paste": "Prilepi", +"Close": "Zapri", +"Font Family": "Dru\u017eina pisav", +"Pre": "Predformat", +"Align right": "Desna poravnava", +"New document": "Nov dokument", +"Blockquote": "Zamik besedila", +"Numbered list": "O\u0161tevil\u010den seznam", +"Heading 1": "Podnaslov 1", +"Headings": "Podnaslovi", +"Increase indent": "Pove\u010daj zamik", +"Formats": "Oblika", +"Headers": "Naslovi", +"Select all": "Izberi vse", +"Header 3": "Naslov 3", +"Blocks": "Grupe", +"Undo": "Razveljavi", +"Strikethrough": "Pre\u010drtano", +"Bullet list": "Ozna\u010den seznam", +"Header 1": "Naslov 1", +"Superscript": "Nadpisano", +"Clear formatting": "Odstrani oblikovanje", +"Font Sizes": "Velikosti pisave", +"Subscript": "Podpisano", +"Header 6": "Naslov 6", +"Redo": "Ponovi", +"Paragraph": "Odstavek", +"Ok": "V redu", +"Bold": "Krepko", +"Code": "Koda", +"Italic": "Le\u017ee\u010de", +"Align center": "Sredinska poravnava", +"Header 5": "Naslov 5", +"Heading 6": "Podnaslov 6", +"Heading 3": "Podnaslov 3", +"Decrease indent": "Zmanj\u0161aj zamik", +"Header 4": "Naslov 4", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Odlagali\u0161\u010de je zdaj v tekstovnem na\u010dinu. Vsebina bo preslikana kot golo besedilo brez oblike, dokler te mo\u017enosti ne izklju\u010dite.", +"Underline": "Pod\u010drtano", +"Cancel": "Prekli\u010di", +"Justify": "Obojestranska poravnava", +"Inline": "Med besedilom", +"Copy": "Kopiraj", +"Align left": "Leva poravnava", +"Visual aids": "Vizualni pripomo\u010dki", +"Lower Greek": "Male gr\u0161ke \u010drke", +"Square": "Kvadratek", +"Default": "Privzeto", +"Lower Alpha": "Male tiskane \u010drke", +"Circle": "Pikica", +"Disc": "Kroglica", +"Upper Alpha": "Velike tiskane \u010drke", +"Upper Roman": "Velike rimske \u0161tevilke", +"Lower Roman": "Male rimske \u0161tevilke", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id se mora za\u010deti s \u010drko, sledijo samo \u010drke, \u0161tevilke, pomi\u0161ljaj, pike, dvopi\u010dje ali pod\u010drtaj.", +"Name": "Naziv zaznamka", +"Anchor": "Zaznamek", +"Id": "Id", +"You have unsaved changes are you sure you want to navigate away?": "Imate neshranjene spremembe. Ste prepri\u010dati, da \u017eelite zapustiti stran?", +"Restore last draft": "Obnovi zadnji osnutek", +"Special character": "Posebni znaki", +"Source code": "Izvorna koda", +"Language": "Jezik", +"Insert\/Edit code sample": "Vstavi\/Uredi vzor\u010dno kodo", +"B": "B", +"R": "R", +"G": "G", +"Color": "Barva", +"Right to left": "Od desne proti levi", +"Left to right": "Od leve proti desni", +"Emoticons": "Sme\u0161ki", +"Robots": "Robotki", +"Document properties": "Lastnosti dokumenta", +"Title": "Naslov", +"Keywords": "Klju\u010dne besede", +"Encoding": "Kodiranje", +"Description": "Opis", +"Author": "Avtor", +"Fullscreen": "\u010cez cel zaslon", +"Horizontal line": "Vodoravna \u010drta", +"Horizontal space": "Vodoravni prostor", +"Insert\/edit image": "Vstavi\/uredi sliko", +"General": "Splo\u0161no", +"Advanced": "Napredno", +"Source": "Pot", +"Border": "Obroba", +"Constrain proportions": "Obdr\u017ei razmerje", +"Vertical space": "Navpi\u010dni prostor", +"Image description": "Opis slike", +"Style": "Slog", +"Dimensions": "Dimenzije", +"Insert image": "Vnesi sliko", +"Image": "Slika", +"Zoom in": "Pove\u010daj", +"Contrast": "Kontrast", +"Back": "Nazaj", +"Gamma": "Gama", +"Flip horizontally": "Obrni vodoravno", +"Resize": "Spremeni velikost", +"Sharpen": "Izostri", +"Zoom out": "Pomanj\u0161aj", +"Image options": "Mo\u017enosti slike", +"Apply": "Uporabi", +"Brightness": "Svetlost", +"Rotate clockwise": "Zavrti v smeri urinega kazalca", +"Rotate counterclockwise": "Zavrti v nasprotni smeri urnega kazalca", +"Edit image": "Uredi sliko", +"Color levels": "Barvni nivo", +"Crop": "Obre\u017ei", +"Orientation": "Usmerjenost", +"Flip vertically": "Obrni navpi\u010dno", +"Invert": "Obrni", +"Date\/time": "Datum\/\u010das", +"Insert date\/time": "Vstavi datum\/\u010das", +"Remove link": "Odstrani povezavo", +"Url": "Povezava", +"Text to display": "Prikazno besedilo", +"Anchors": "Sidra", +"Insert link": "Vstavi povezavo", +"Link": "Povezava", +"New window": "Novo okno", +"None": "Brez", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Vne\u0161eni URL predstavlja zunanjo povezavo. Ali \u017eelite dodati \"http:\/\/\" predpono?", +"Paste or type a link": "Prilepite ali vnesite povezavo", +"Target": "Cilj", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Vne\u0161eni URL predstavlja e-po\u0161tni naslov. Ali \u017eelite dodati potrebno \"mailto:\" predpono?", +"Insert\/edit link": "Vstavi\/uredi povezavo", +"Insert\/edit video": "Vstavi\/uredi video", +"Media": "Medij", +"Alternative source": "Nadomestni vir", +"Paste your embed code below:": "Prilepite kodo za vdelavo:", +"Insert video": "Vstavi video", +"Poster": "Poster", +"Insert\/edit media": "Vstavi\/uredi medij", +"Embed": "Vdelaj", +"Nonbreaking space": "Nedeljivi presledek", +"Page break": "Prelom strani", +"Paste as text": "Vnesi kot besedilo", +"Preview": "Predogled", +"Print": "Natisni", +"Save": "Shrani", +"Could not find the specified string.": "Iskanje ni vrnilo rezultatov.", +"Replace": "Zamenjaj", +"Next": "Naprej", +"Whole words": "Cele besede", +"Find and replace": "Poi\u0161\u010di in zamenjaj", +"Replace with": "Zamenjaj z", +"Find": "I\u0161\u010di", +"Replace all": "Zamenjaj vse", +"Match case": "Ujemanje malih in velikih \u010drk", +"Prev": "Nazaj", +"Spellcheck": "Preverjanje \u010drkovanja", +"Finish": "Zaklju\u010di", +"Ignore all": "Prezri vse", +"Ignore": "Prezri", +"Add to Dictionary": "Dodaj v slovar", +"Insert row before": "Vstavi vrstico pred", +"Rows": "Vrstice", +"Height": "Vi\u0161ina", +"Paste row after": "Prilepi vrstico za", +"Alignment": "Poravnava", +"Border color": "Barva obrobe", +"Column group": "Grupiranje stolpcev", +"Row": "Vrstica", +"Insert column before": "Vstavi stolpec pred", +"Split cell": "Razdeli celico", +"Cell padding": "Polnilo med celicami", +"Cell spacing": "Razmik med celicami", +"Row type": "Tip vrstice", +"Insert table": "Vstavi tabelo", +"Body": "Vsebina", +"Caption": "Naslov", +"Footer": "Noga", +"Delete row": "Izbri\u0161i vrstico", +"Paste row before": "Prilepi vrstico pred", +"Scope": "Obseg", +"Delete table": "Izbri\u0161i tabelo", +"H Align": "Horizontalna poravnava", +"Top": "Vrh", +"Header cell": "Celica glave", +"Column": "Stolpec", +"Row group": "Grupiranje vrstic", +"Cell": "Celica", +"Middle": "Sredina", +"Cell type": "Tip celice", +"Copy row": "Kopiraj vrstico", +"Row properties": "Lastnosti vrstice", +"Table properties": "Lastnosti tabele", +"Bottom": "Dno", +"V Align": "Vertikalna poravnava", +"Header": "Glava", +"Right": "Desno", +"Insert column after": "Vstavi stolpec za", +"Cols": "Stolpci", +"Insert row after": "Vstavi vrstico za", +"Width": "\u0160irina", +"Cell properties": "Lastnosti celice", +"Left": "Levo", +"Cut row": "Izre\u017ei vrstico", +"Delete column": "Izbri\u0161i stolpec", +"Center": "Sredinsko", +"Merge cells": "Zdru\u017ei celice", +"Insert template": "Vstavi predlogo", +"Templates": "Predloge", +"Background color": "Barva ozadja", +"Custom...": "Po meri ...", +"Custom color": "Barva po meri", +"No color": "Brezbarvno", +"Text color": "Barva besedila", +"Table of Contents": "Kazalo", +"Show blocks": "Prika\u017ei bloke", +"Show invisible characters": "Prika\u017ei skrite znake", +"Words: {0}": "Besed: {0}", +"Insert": "Vstavi", +"File": "Datoteka", +"Edit": "Uredi", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Bogato besedilo. Pritisnite ALT-F9 za meni. Pritisnite ALT-F10 za orodno vrstico. Pritisnite ALT-0 za pomo\u010d", +"Tools": "Orodja", +"View": "Pogled", +"Table": "Tabela", +"Format": "Oblika" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js new file mode 100644 index 0000000000..9150c2ed82 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js @@ -0,0 +1,261 @@ +tinymce.addI18n('sr',{ +"Redo": "Napred", +"Undo": "Nazad", +"Cut": "Iseci", +"Copy": "Kopiraj", +"Paste": "Nalepi", +"Select all": "Obele\u017ei sve", +"New document": "Novi dokument", +"Ok": "Ok", +"Cancel": "Opozovi", +"Visual aids": "Vizuelna pomagala", +"Bold": "Podebljan", +"Italic": "isko\u0161en", +"Underline": "Podvu\u010den", +"Strikethrough": "Precrtan", +"Superscript": "Natpis", +"Subscript": "Potpisan", +"Clear formatting": "Brisanje formatiranja", +"Align left": "Poravnano levo", +"Align center": "Poravnano centar", +"Align right": "Poravnano desno", +"Justify": "Poravnanje", +"Bullet list": "Lista nabrajanja", +"Numbered list": "Numerisana lista", +"Decrease indent": "Smanji uvla\u010denje", +"Increase indent": "Pove\u0107aj uvla\u010denje", +"Close": "Zatvori", +"Formats": "Formatiraj", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Va\u0161 pretra\u017eiva\u010d nepodr\u017eava direktan pristup prenosu.Koristite Ctrl+X\/C\/V pre\u010dice na tastaturi", +"Headers": "Zaglavlje", +"Header 1": "Zaglavlje 1", +"Header 2": "Zaglavlje 2", +"Header 3": "Zaglavlje 3", +"Header 4": "Zaglavlje 4", +"Header 5": "Zaglavlje 5", +"Header 6": "Zaglavlje 6", +"Headings": "Naslovi", +"Heading 1": "Naslov 1", +"Heading 2": "Naslov 2", +"Heading 3": "Naslov 3", +"Heading 4": "Naslov 4", +"Heading 5": "Naslov 5", +"Heading 6": "Naslov 6", +"Preformatted": "Formatirano", +"Div": "Div", +"Pre": "Pre", +"Code": "Kod", +"Paragraph": "Paragraf", +"Blockquote": "Navodnici", +"Inline": "U liniji", +"Blocks": "Blokovi", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Nalepiti je sada u obi\u010dnom text modu.Sadr\u017eaj \u0107e biti nalepljen kao obi\u010dan tekst dok ne ugasite ovu opciju.", +"Font Family": "Vrsta fonta", +"Font Sizes": "Veli\u010dine fontova", +"Class": "Klasa", +"Browse for an image": "Prona\u0111i sliku", +"OR": "ili", +"Drop an image here": "Prevuci sliku ovde", +"Upload": "Po\u0161alji", +"Block": "Blok", +"Align": "Poravnaj", +"Default": "Podrazumevano", +"Circle": "Krug", +"Disc": "Disk", +"Square": "Kvadrat", +"Lower Alpha": "Donja Alpha", +"Lower Greek": "Ni\u017ei gr\u010dki", +"Lower Roman": "Donji Roman", +"Upper Alpha": "Gornji Alpha", +"Upper Roman": "Gornji Roman", +"Anchor": "Sidro", +"Name": "Ime", +"Id": "id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id treba da po\u010dinje slovom, pra\u0107eno samo slovima, brojevima, crticama, ta\u010dkama, dvota\u010dkom ili donjim crtama", +"You have unsaved changes are you sure you want to navigate away?": "Imate nesa\u010duvane promene dali ste sigurni da \u017eelite da iza\u0111ete?", +"Restore last draft": "Vrati poslednji nacrt", +"Special character": "Specijalni karakter", +"Source code": "Izvorni kod", +"Insert\/Edit code sample": "Dodaj\/Izmeni primer koda", +"Language": "Jezik", +"Code sample": "Primer koda", +"Color": "Boja", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Sa leve na desnu", +"Right to left": "Sa desne na levu", +"Emoticons": "Smajliji", +"Document properties": "Postavke dokumenta", +"Title": "Naslov", +"Keywords": "Klju\u010dne re\u010di", +"Description": "Opis", +"Robots": "Roboti", +"Author": "Autor", +"Encoding": "Kodiranje", +"Fullscreen": "Pun ekran", +"Action": "Akcija", +"Shortcut": "Pre\u010dica", +"Help": "Pomo\u0107", +"Address": "Adresa", +"Focus to menubar": "Fokus na meni", +"Focus to toolbar": "Fokus na traku sa alatima", +"Focus to element path": "Fokus na putanju elementa", +"Focus to contextual toolbar": "Fokus na kontekstualnu traku alata", +"Insert link (if link plugin activated)": "Dodaj link (ukoliko je link dodatak aktiviran)", +"Save (if save plugin activated)": "Sa\u010duvaj (ukoliko je sa\u010duvaj dodatak aktiviran)", +"Find (if searchreplace plugin activated)": "Prona\u0111i (ako je dodatak pretrage i zamene aktiviran)", +"Plugins installed ({0}):": "Dodataka instalirano ({0}):", +"Premium plugins:": "Premium dodaci", +"Learn more...": "Saznaj vi\u0161e", +"You are using {0}": "Koristite {0}", +"Plugins": "Dadaci", +"Handy Shortcuts": "Prakti\u010dne pre\u010dice", +"Horizontal line": "Horizontalna linija", +"Insert\/edit image": "Ubaci\/Promeni sliku", +"Image description": "Opis slike", +"Source": "Izvor", +"Dimensions": "Dimenzije", +"Constrain proportions": "Ograni\u010dene proporcije", +"General": "Op\u0161te", +"Advanced": "Napredno", +"Style": "Stil", +"Vertical space": "Vertikalni razmak", +"Horizontal space": "Horizontalni razmak", +"Border": "Okvir", +"Insert image": "Ubaci sliku", +"Image": "Slika", +"Image list": "Lista slika", +"Rotate counterclockwise": "Rotiraj levo", +"Rotate clockwise": "Rotiraj desno", +"Flip vertically": "Okreni vertikalno", +"Flip horizontally": "Okreni horizontalno", +"Edit image": "Izmeni sliku", +"Image options": "Opcije slike", +"Zoom in": "Uve\u0107aj", +"Zoom out": "Umanji", +"Crop": "Izeci", +"Resize": "Promeni veli\u010dinu", +"Orientation": "Orijentacija", +"Brightness": "Osvetljenje", +"Sharpen": "Izo\u0161travanje", +"Contrast": "Kontrast", +"Color levels": "Nivo boja", +"Gamma": "Gama", +"Invert": "Izokreni", +"Apply": "Primeni", +"Back": "Nazad", +"Insert date\/time": "Ubaci datum\/vreme", +"Date\/time": "Datum\/vreme", +"Insert link": "Ubaci vezu", +"Insert\/edit link": "Ubaci\/promeni vezu", +"Text to display": "Tekst za prikaz", +"Url": "Url", +"Target": "Meta", +"None": "Ni\u0161ta", +"New window": "Novi prozor", +"Remove link": "Ukloni link", +"Anchors": "sidro", +"Link": "Link", +"Paste or type a link": "Nalepi ili ukucaj link", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Izgleda da je URL koji ste uneli email adresa. Da li \u017eelite da dodate zahtevani mailto: prefiks?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Izgleda da je URL koji ste uneli spolja\u0161nja veza. Da li \u017eelite da dodate zahtevani http:\/\/ prefiks?", +"Link list": "Lista veza", +"Insert video": "Ubaci video", +"Insert\/edit video": "Ubaci\/promeni video", +"Insert\/edit media": "Ubaci\/izmeni mediju", +"Alternative source": "Alternativni izvor", +"Poster": "Poster", +"Paste your embed code below:": "Nalepite ugra\u0111eni kod ispod:", +"Embed": "Ugra\u0111eno", +"Media": "Medija", +"Nonbreaking space": "bez ramaka", +"Page break": "Lomljenje stranice", +"Paste as text": "Nalepi kao tekst", +"Preview": "Pregled", +"Print": "\u0160tampanje", +"Save": "Sa\u010duvati", +"Find": "Na\u0111i", +"Replace with": "Zameni sa", +"Replace": "Zameni", +"Replace all": "Zameni sve", +"Prev": "Prethodni", +"Next": "Slede\u0107i", +"Find and replace": "Na\u0111i i zameni", +"Could not find the specified string.": "Nije mogu\u0107e prona\u0107i navedeni niz.", +"Match case": "Predmet za upore\u0111ivanje", +"Whole words": "Cele re\u010di", +"Spellcheck": "Provera pravopisa", +"Ignore": "ignori\u0161i", +"Ignore all": "Ignori\u0161i sve", +"Finish": "Kraj", +"Add to Dictionary": "Dodaj u re\u010dnik", +"Insert table": "ubaci tabelu", +"Table properties": "Postavke tabele", +"Delete table": "Obri\u0161i tabelu", +"Cell": "\u0106elija", +"Row": "Red", +"Column": "Kolona", +"Cell properties": "Postavke \u0107elije", +"Merge cells": "Spoji \u0107elije", +"Split cell": "Razdvoji \u0107elije", +"Insert row before": "Ubaci red pre", +"Insert row after": "Ubaci red posle", +"Delete row": "Obri\u0161i red", +"Row properties": "Postavke reda", +"Cut row": "Iseci red", +"Copy row": "Kopiraj red", +"Paste row before": "Nalepi red pre", +"Paste row after": "Nalepi red posle", +"Insert column before": "Ubaci kolonu pre", +"Insert column after": "Ubaci kolonu posle", +"Delete column": "Obri\u0161i kolonu", +"Cols": "Kolone", +"Rows": "Redovi", +"Width": "\u0160irina", +"Height": "Visina", +"Cell spacing": "Prostor \u0107elije", +"Cell padding": "Razmak \u0107elije", +"Caption": "Natpis", +"Left": "Levo", +"Center": "Centar", +"Right": "Desno", +"Cell type": "Tip \u0107elije", +"Scope": "Obim", +"Alignment": "Svrstavanje", +"H Align": "Horizontalno poravnanje", +"V Align": "Vertikalno poravnanje", +"Top": "Vrh", +"Middle": "Sredina", +"Bottom": "Podno\u017eje", +"Header cell": "Visina \u0107elije", +"Row group": "Grupa reda", +"Column group": "Grupa kolone", +"Row type": "Tip reda", +"Header": "Zaglavlje", +"Body": "Telo", +"Footer": "Podno\u017eje", +"Border color": "Boja ivice", +"Insert template": "Ubaci \u0161ablon", +"Templates": "\u0160abloni", +"Template": "\u0160ablon", +"Text color": "Boja tekst", +"Background color": "Boja pozadine", +"Custom...": "Posebno...", +"Custom color": "Posebna boja", +"No color": "Bez boje", +"Table of Contents": "Tabela sadr\u017eaja", +"Show blocks": "Prikaz blokova", +"Show invisible characters": "Prika\u017ei nevidljive karaktere", +"Words: {0}": "Re\u010di:{0}", +"{0} words": "{0} re\u010di", +"File": "Datoteka", +"Edit": "Ure\u0111ivanje", +"Insert": "Umetni", +"View": "Prikaz", +"Format": "Format", +"Table": "Tabela", +"Tools": "Alatke", +"Powered by {0}": "Pokre\u0107e ga {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Oboga\u0107en tekst. Pritisni te ALT-F9 za meni.Pritisnite ALT-F10 za traku sa alatkama.Pritisnite ALT-0 za pomo\u0107" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv.js deleted file mode 100644 index a2a3d77ffd..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({sv:{common:{"more_colors":"Fler f\u00e4rger","invalid_data":"Fel: Inkorrekta v\u00e4rden har matats in, dessa \u00e4r markerade i r\u00f6tt.","popup_blocked":"Popup blockerare detekterad. St\u00e4ng av den s\u00e5 att dialogerna kan \u00f6ppnas.","clipboard_no_support":"Funktionen \u00e4r inte tillg\u00e4nglig i din webbl\u00e4sare, anv\u00e4nd tangentbordsgenv\u00e4garna i st\u00e4llet.","clipboard_msg":"Kopiera/klipp ut/klistra in \u00e4r inte tillg\u00e4ngligt i din webbl\u00e4sare.\nVill du veta mer?","not_set":"-- Inte satt --","class_name":"Klass",browse:"Bl\u00e4ddra",close:"St\u00e4ng",cancel:"Avbryt",update:"Uppdatera",insert:"Infoga",apply:"Applicera","edit_confirm":"Vill du anv\u00e4nda WYSIWYG f\u00f6r denna textarea?","invalid_data_number":"{#field} m\u00e5ste vara ett nummer","invalid_data_min":"{#field} m\u00e5ste vara ett nummer st\u00f6rren \u00e4n {#min}","invalid_data_size":"{#field} m\u00e5ste vara ett nummer eller i procent",value:"(V\u00e4rde)"},contextmenu:{full:"Utfyllnad",right:"H\u00f6ger",center:"Centrerad",left:"V\u00e4nster",align:"Justering"},insertdatetime:{"day_short":"S\u00f6n,M\u00e5n,Tis,Ons,Tors,Fre,L\u00f6r,S\u00f6n","day_long":"S\u00f6ndag,M\u00e5ndag,Tisdag,Onsdag,Torsdag,Fredag,L\u00f6rdag,S\u00f6ndag","months_short":"Jan,Feb,Mar,Apr,Maj,Jun,Jul,Aug,Sep,Okt,Nov,Dec","months_long":"Januari,Februari,Mars,April,Maj,Juni,Juli,Augusti,September,Oktober,November,December","inserttime_desc":"Infoga tid","insertdate_desc":"Infoga datum","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d "},print:{"print_desc":"Skriv ut"},preview:{"preview_desc":"F\u00f6rhandsgranska"},directionality:{"rtl_desc":"Skriftl\u00e4ge - h\u00f6ger till v\u00e4nster","ltr_desc":"Skriftl\u00e4ge - v\u00e4nster till h\u00f6ger"},layer:{content:"Nytt lager...","absolute_desc":"Sl\u00e5 av/p\u00e5 absolut positionering","backward_desc":"Flytta bak\u00e5t","forward_desc":"Flytta fram\u00e5t","insertlayer_desc":"Infoga nytt lager"},save:{"save_desc":"Spara","cancel_desc":"Hoppa \u00f6ver alla f\u00f6r\u00e4ndringar"},nonbreaking:{"nonbreaking_desc":"Infoga icke radbrytande mellanslag"},iespell:{download:"ieSpell kunde inte hittas, vill du installera denna nu?","iespell_desc":"R\u00e4ttstava"},advhr:{"advhr_desc":"Horisontell skiljelinje","delta_height":"","delta_width":""},emotions:{"emotions_desc":"Smileys","delta_height":"","delta_width":""},searchreplace:{"replace_desc":"S\u00f6k/ers\u00e4tt","search_desc":"S\u00f6k","delta_width":"","delta_height":""},advimage:{"image_desc":"Infoga/redigera bild","delta_width":"","delta_height":""},advlink:{"link_desc":"Infoga/redigera l\u00e4nk","delta_height":"","delta_width":""},xhtmlxtras:{"attribs_desc":"Redigera attribut","ins_desc":"Markera som tillagt","del_desc":"Markera som struket","acronym_desc":"Akronym","abbr_desc":"F\u00f6rkortning","cite_desc":"citat","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"Redigera inline CSS","delta_height":"","delta_width":""},paste:{"plaintext_mode":"Inklistring \u00e4r nu i textl\u00e4ge.","plaintext_mode_sticky":"Inklistring \u00e4r nu i textl\u00e4ge. Efter att du klistrat in kommer den att \u00e5terg\u00e5 till normall\u00e4ge.","selectall_desc":"Markera allt","paste_word_desc":"Klistra in fr\u00e5n Word","paste_text_desc":"Klistra in som text"},"paste_dlg":{"word_title":"Anv\u00e4nd ctrl-v p\u00e5 ditt tangentbord f\u00f6r att klistra in i detta f\u00f6nster.","text_linebreaks":"Spara radbrytningar","text_title":"Anv\u00e4nd ctrl-v p\u00e5 ditt tangentbord f\u00f6r att klistra in i detta f\u00f6nster."},table:{cell:"Cell",col:"Kolumn",row:"Rad",del:"Radera tabell","copy_row_desc":"Klistra in rad","cut_row_desc":"Klipp ut rad","paste_row_after_desc":"Klistra in rad efter","paste_row_before_desc":"Klistra in rad ovanf\u00f6r","props_desc":"Tabellinst\u00e4llningar","cell_desc":"Tabellcellsinst\u00e4llningar","row_desc":"Tabellradsinst\u00e4llningar","merge_cells_desc":"Sammanfoga celler","split_cells_desc":"Separera sammansatta celler","delete_col_desc":"Radera kolumn","col_after_desc":"Infoga kolumn efter","col_before_desc":"Infoga kolumn f\u00f6re","delete_row_desc":"Radera rad","row_after_desc":"Infoga ny rad efter","row_before_desc":"Infoga ny rad f\u00f6re",desc:"Infoga/redigera ny tabell","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"Om du \u00e5terskapar inneh\u00e5ll s\u00e5 kommer det nuvarande inneh\u00e5llet i f\u00e4ltet att raderas.\n\n\u00c4r du s\u00e4ker p\u00e5 att du vill g\u00f6ra detta?","restore_content":"\u00c5terskapa automatiskt sparat inneh\u00e5ll.","unload_msg":"De f\u00f6r\u00e4ndringar som du gjort kommer att g\u00e5 f\u00f6rlorade om du l\u00e4mnar sidan."},fullscreen:{desc:"Sl\u00e5 av/p\u00e5 fullsk\u00e4rmsl\u00e4ge"},media:{edit:"Redigera inb\u00e4ddad media",desc:"Infoga/redigera inb\u00e4ddad media","delta_height":"","delta_width":""},fullpage:{desc:"Dokumentinst\u00e4llningar","delta_width":"","delta_height":""},template:{desc:"Infoga en f\u00e4rdig mall"},visualchars:{desc:"Visa osynliga tecken"},spellchecker:{desc:"Sl\u00e5 av/p\u00e5 r\u00e4ttstavningskontroll",menu:"R\u00e4ttstavningsinst\u00e4llningar","ignore_word":"Ignorera ord","ignore_words":"Ignorera alla",langs:"Spr\u00e5k",wait:"Var god v\u00e4nta...",sug:"F\u00f6rslag","no_sug":"Inga f\u00f6rslag","no_mpell":"Inga felstavningar funna.","learn_word":"L\u00e4r ord"},pagebreak:{desc:"Infoga sidbrytning"},advlist:{types:"Typer",def:"Standard","lower_alpha":"Lower alpha","lower_greek":"Lower greek","lower_roman":"Lower roman","upper_alpha":"Upper alpha","upper_roman":"Upper roman",circle:"Cirkel",disc:"Disc",square:"Fyrkant"},colors:{"333300":"M\u00f6rkoliv","993300":"Br\u00e4ndorange","000000":"Svart","003300":"M\u00f6rkgr\u00f6n","003366":"M\u00f6rkazur","000080":"Marinbl\u00e5","333399":"Indigo","333333":"Mycket m\u00f6rkgr\u00e5","800000":"R\u00f6dbrun",FF6600:"Orange","808000":"Oliv","008000":"Gr\u00f6n","008080":"Kricka","0000FF":"Bl\u00e5","666699":"Gr\u00e5bl\u00e5","808080":"Gr\u00e5",FF0000:"R\u00f6d",FF9900:"B\u00e4rnsten","99CC00":"Gulgr\u00f6n","339966":"Havsbl\u00e5","33CCCC":"Turkos","3366FF":"Kungligtbl\u00e5tt","800080":"Lila","999999":"Medelgr\u00e5",FF00FF:"Magenta",FFCC00:"Guld",FFFF00:"Gul","00FF00":"Lime","00FFFF":"Vatten","00CCFF":"Himmelsbl\u00e5","993366":"Brun",C0C0C0:"Silver",FF99CC:"Rosa",FFCC99:"Periska",FFFF99:"Ljusgul",CCFFCC:"Blekgr\u00f6n",CCFFFF:"Blekcyan","99CCFF":"Ljus himmel",CC99FF:"Plommon",FFFFFF:"Vitt"},aria:{"rich_text_area":"Redigeringsarea"},wordcount:{words:"Ord:"}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js new file mode 100644 index 0000000000..83aaaef59d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js @@ -0,0 +1,261 @@ +tinymce.addI18n('sv_SE',{ +"Redo": "G\u00f6r om", +"Undo": "\u00c5ngra", +"Cut": "Klipp ut", +"Copy": "Kopiera", +"Paste": "Klistra in", +"Select all": "Markera allt", +"New document": "Nytt dokument", +"Ok": "Ok", +"Cancel": "Avbryt", +"Visual aids": "Visuella hj\u00e4lpmedel", +"Bold": "Fetstil", +"Italic": "Kursiv stil", +"Underline": "Understruken", +"Strikethrough": "Genomstruken", +"Superscript": "Upph\u00f6jd text", +"Subscript": "Neds\u00e4nkt text", +"Clear formatting": "Avformatera", +"Align left": "V\u00e4nsterst\u00e4ll", +"Align center": "Centrera", +"Align right": "H\u00f6gerst\u00e4ll", +"Justify": "Justera", +"Bullet list": "Punktlista", +"Numbered list": "Nummerlista", +"Decrease indent": "Minska indrag", +"Increase indent": "\u00d6ka indrag", +"Close": "St\u00e4ng", +"Formats": "Format", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Din browser st\u00f6djer inte direkt \u00e5tkomst till klippboken. V\u00e4nligen anv\u00e4nd kortkommandona Ctrl+X\/C\/V i st\u00e4llet.", +"Headers": "Rubriker", +"Header 1": "Rubrik 1", +"Header 2": "Rubrik 2", +"Header 3": "Rubrik 3", +"Header 4": "Rubrik 4", +"Header 5": "Rubrik 5", +"Header 6": "Rubrik 6", +"Headings": "Rubriker", +"Heading 1": "Rubrik 1", +"Heading 2": "Rubrik 2", +"Heading 3": "Rubrik 3", +"Heading 4": "Rubrik 4", +"Heading 5": "Rubrik 5", +"Heading 6": "Rubrik 6", +"Preformatted": "Preformaterad", +"Div": "Div", +"Pre": "F\u00f6rformaterad", +"Code": "Kod", +"Paragraph": "Br\u00f6dtext", +"Blockquote": "Blockcitat", +"Inline": "Inline", +"Blocks": "Block", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Klistra in \u00e4r nu i textl\u00e4ge. Inneh\u00e5ll kommer att konverteras till text tills du sl\u00e5r av detta l\u00e4ge.", +"Font Family": "Teckensnitt", +"Font Sizes": "Storlek", +"Class": "Klass", +"Browse for an image": "Bl\u00e4ddra efter en bild", +"OR": "ELLER", +"Drop an image here": "Sl\u00e4pp en bild h\u00e4r", +"Upload": "Ladda upp", +"Block": "Block", +"Align": "Justera", +"Default": "Original", +"Circle": "Cirkel", +"Disc": "Disk", +"Square": "Fyrkant", +"Lower Alpha": "Gemener", +"Lower Greek": "Grekiska gemener", +"Lower Roman": "Romerska gemener", +"Upper Alpha": "Versaler", +"Upper Roman": "Romerska versaler", +"Anchor": "Ankare", +"Name": "Namn", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id skall b\u00f6rja med en bokstav och f\u00f6ljande tecken ska vara bokst\u00e4ver, nummer, punkter, understr\u00e4ck eller kolon.", +"You have unsaved changes are you sure you want to navigate away?": "Du har f\u00f6r\u00e4ndringar som du inte har sparat. \u00c4r du s\u00e4ker p\u00e5 att du vill navigera vidare?", +"Restore last draft": "\u00c5terst\u00e4ll senaste utkast", +"Special character": "Specialtecken", +"Source code": "K\u00e4llkod", +"Insert\/Edit code sample": "Infoga\/Redigera k\u00e5d exempel", +"Language": "Spr\u00e5k", +"Code sample": "K\u00e5dexempel", +"Color": "F\u00e4rg", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "V\u00e4nster till h\u00f6ger", +"Right to left": "H\u00f6ger till v\u00e4nster", +"Emoticons": "Emoticons", +"Document properties": "Dokumentegenskaper", +"Title": "Titel", +"Keywords": "Nyckelord", +"Description": "Beskrivning", +"Robots": "Robotar", +"Author": "F\u00f6rfattare", +"Encoding": "Encoding", +"Fullscreen": "Fullsk\u00e4rm", +"Action": "H\u00e4ndelse", +"Shortcut": "Kortkommando", +"Help": "Hj\u00e4lp", +"Address": "Adress", +"Focus to menubar": "Fokusera p\u00e5 menyrad", +"Focus to toolbar": "Fokusera p\u00e5 verktygsrad", +"Focus to element path": "Fokusera p\u00e5 elements\u00f6kv\u00e4gsrad", +"Focus to contextual toolbar": "Fokusera p\u00e5 den kontextuella verktygsraden", +"Insert link (if link plugin activated)": "Infoga l\u00e4nk (om link-pluginet \u00e4r aktiverat)", +"Save (if save plugin activated)": "Spara (om save-pluginet \u00e4r aktiverat)", +"Find (if searchreplace plugin activated)": "S\u00f6k (om searchreplace-pluginet \u00e4r aktiverat)", +"Plugins installed ({0}):": "Installerade plugins ({0}):", +"Premium plugins:": "Premiumplugins:", +"Learn more...": "L\u00e4s mer...", +"You are using {0}": "Du anv\u00e4nder {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Kortkommandon", +"Horizontal line": "Horisontell linje", +"Insert\/edit image": "Infoga\/redigera bild", +"Image description": "Bildbeskrivning", +"Source": "K\u00e4lla", +"Dimensions": "Dimensioner", +"Constrain proportions": "Begr\u00e4nsa proportioner", +"General": "Generella", +"Advanced": "Avancerat", +"Style": "Stil", +"Vertical space": "Vertikaltutrymme", +"Horizontal space": "Horisontellt utrymme", +"Border": "Ram", +"Insert image": "Infoga bild", +"Image": "Bild", +"Image list": "Bildlista", +"Rotate counterclockwise": "Rotera moturs", +"Rotate clockwise": "Rotera medurs", +"Flip vertically": "Spegelv\u00e4nd vertikalt", +"Flip horizontally": "Spegelv\u00e4nd horisontellt", +"Edit image": "Redigera bild", +"Image options": "Bild inst\u00e4llningar", +"Zoom in": "Zooma in", +"Zoom out": "Zooma ut", +"Crop": "Besk\u00e4r", +"Resize": "Skala om", +"Orientation": "Orientera", +"Brightness": "Ljusstyrka", +"Sharpen": "Sk\u00e4rpa", +"Contrast": "Kontrast", +"Color levels": "F\u00e4rgniv\u00e5er", +"Gamma": "Gamma", +"Invert": "Invertera", +"Apply": "Applicera", +"Back": "Tillbaka", +"Insert date\/time": "Infoga datum\/tid", +"Date\/time": "Datum\/tid", +"Insert link": "Infoga l\u00e4nk", +"Insert\/edit link": "Infoga\/redigera l\u00e4nk", +"Text to display": "Text att visa", +"Url": "Url", +"Target": "M\u00e5l", +"None": "Ingen", +"New window": "Nytt f\u00f6nster", +"Remove link": "Ta bort l\u00e4nk", +"Anchors": "Bokm\u00e4rken", +"Link": "L\u00e4nk", +"Paste or type a link": "Klistra in eller skriv en l\u00e4nk", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Urlen du angav verkar vara en epost adress. Vill du l\u00e4gga till ett mailto: prefix?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Urlen du angav verkar vara en extern l\u00e4nk. Vill du l\u00e4gga till http:\/\/ prefixet?", +"Link list": "L\u00e4nklista", +"Insert video": "Infoga video", +"Insert\/edit video": "Infoga\/redigera video", +"Insert\/edit media": "Infoga\/redigera media", +"Alternative source": "Alternativ k\u00e4lla", +"Poster": "Affish", +"Paste your embed code below:": "Klistra in din inb\u00e4ddningskod nedan:", +"Embed": "Inb\u00e4ddning", +"Media": "Media", +"Nonbreaking space": "Avbrottsfritt mellanrum", +"Page break": "Sidbrytning", +"Paste as text": "Klistra in som text", +"Preview": "F\u00f6rhandsgranska", +"Print": "Skriv ut", +"Save": "Spara", +"Find": "S\u00f6k", +"Replace with": "Ers\u00e4tt med", +"Replace": "Ers\u00e4tt", +"Replace all": "Ers\u00e4tt alla", +"Prev": "F\u00f6reg\u00e5ende", +"Next": "N\u00e4sta", +"Find and replace": "S\u00f6k och ers\u00e4tt", +"Could not find the specified string.": "Kunde inte hitta den specifierade st\u00e4ngen.", +"Match case": "Matcha gemener\/versaler", +"Whole words": "Hela ord", +"Spellcheck": "R\u00e4ttstava", +"Ignore": "Ignorera", +"Ignore all": "Ignorera alla", +"Finish": "Avsluta", +"Add to Dictionary": "L\u00e4gg till i ordlista", +"Insert table": "Infoga tabell", +"Table properties": "Tabellegenskaper", +"Delete table": "Radera tabell", +"Cell": "Cell", +"Row": "Rad", +"Column": "Kolumn", +"Cell properties": "Cellegenskaper", +"Merge cells": "Sammanfoga celler", +"Split cell": "Bryt is\u00e4r celler", +"Insert row before": "Infoga rad f\u00f6re", +"Insert row after": "Infoga rad efter", +"Delete row": "Radera rad", +"Row properties": "Radegenskaper", +"Cut row": "Klipp ut rad", +"Copy row": "Kopiera rad", +"Paste row before": "Klista in rad f\u00f6re", +"Paste row after": "Klistra in rad efter", +"Insert column before": "Infoga kolumn f\u00f6re", +"Insert column after": "Infoga kolumn efter", +"Delete column": "Radera kolumn", +"Cols": "Kolumner", +"Rows": "Rader", +"Width": "Bredd", +"Height": "H\u00f6jd", +"Cell spacing": "Cellmellanrum", +"Cell padding": "Cellpaddning", +"Caption": "Rubrik", +"Left": "V\u00e4nster", +"Center": "Centrum", +"Right": "H\u00f6ger", +"Cell type": "Celltyp", +"Scope": "Omf\u00e5ng", +"Alignment": "Justering", +"H Align": "H-justering", +"V Align": "V-justering", +"Top": "Toppen", +"Middle": "Mitten", +"Bottom": "Botten", +"Header cell": "Huvudcell", +"Row group": "Radgrupp", +"Column group": "Kolumngrupp", +"Row type": "Radtyp", +"Header": "Huvud", +"Body": "Kropp", +"Footer": "Fot", +"Border color": "Ramf\u00e4rg", +"Insert template": "Infoga mall", +"Templates": "Mallar", +"Template": "Mall", +"Text color": "Textf\u00e4rg", +"Background color": "Bakgrundsf\u00e4rg", +"Custom...": "Anpassad...", +"Custom color": "Anpassad f\u00e4rg", +"No color": "Ingen f\u00e4rg", +"Table of Contents": "Inneh\u00e5llsf\u00f6rteckning", +"Show blocks": "Visa block", +"Show invisible characters": "Visa onsynliga tecken", +"Words: {0}": "Ord: {0}", +"{0} words": "{0} ord", +"File": "Fil", +"Edit": "Redigera", +"Insert": "Infoga", +"View": "Visa", +"Format": "Format", +"Table": "Tabell", +"Tools": "Verktyg", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Textredigerare. Tryck ALT-F9 f\u00f6r menyn. Tryck ALT-F10 f\u00f6r verktygsrader. Tryck ALT-0 f\u00f6r hj\u00e4lp." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta.js new file mode 100644 index 0000000000..6d0736cf72 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta.js @@ -0,0 +1,261 @@ +tinymce.addI18n('ta',{ +"Redo": "\u0bae\u0bc0\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Undo": "\u0b9a\u0bc6\u0baf\u0bb2\u0bcd\u0ba4\u0bb5\u0bbf\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Cut": "\u0bb5\u0bc6\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Copy": "\u0ba8\u0b95\u0bb2\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Paste": "\u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Select all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0ba4\u0bc7\u0bb0\u0bcd\u0bb5\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"New document": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf \u0b86\u0bb5\u0ba3\u0bae\u0bcd", +"Ok": "\u0b9a\u0bb0\u0bbf", +"Cancel": "\u0bb0\u0ba4\u0bcd\u0ba4\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Visual aids": "\u0b95\u0bbe\u0b9f\u0bcd\u0b9a\u0bbf\u0ba4\u0bcd \u0ba4\u0bc1\u0ba3\u0bc8\u0baf\u0ba9\u0bcd\u0b95\u0bb3\u0bcd", +"Bold": "\u0ba4\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Italic": "\u0b9a\u0bbe\u0baf\u0bcd\u0bb5\u0bc1", +"Underline": "\u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bcb\u0b9f\u0bc1", +"Strikethrough": "\u0ba8\u0b9f\u0bc1\u0b95\u0bcd\u0b95\u0bcb\u0b9f\u0bc1", +"Superscript": "\u0bae\u0bc7\u0bb2\u0bcd\u0b92\u0b9f\u0bcd\u0b9f\u0bc1", +"Subscript": "\u0b95\u0bc0\u0bb4\u0bcd\u0b92\u0b9f\u0bcd\u0b9f\u0bc1", +"Clear formatting": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b85\u0bb4\u0bbf\u0b95\u0bcd\u0b95", +"Align left": "\u0b87\u0b9f\u0ba4\u0bc1 \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Align center": "\u0bae\u0bc8\u0baf \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Align right": "\u0bb5\u0bb2\u0ba4\u0bc1 \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Justify": "\u0ba8\u0bc7\u0bb0\u0bcd\u0ba4\u0bcd\u0ba4\u0bbf \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Bullet list": "\u0baa\u0bca\u0b9f\u0bcd\u0b9f\u0bbf\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Numbered list": "\u0b8e\u0ba3\u0bcd\u0ba3\u0bbf\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Decrease indent": "\u0b89\u0bb3\u0bcd\u0ba4\u0bb3\u0bcd\u0bb3\u0bc1\u0ba4\u0bb2\u0bc8 \u0b95\u0bc1\u0bb1\u0bc8\u0b95\u0bcd\u0b95", +"Increase indent": "\u0b89\u0bb3\u0bcd\u0ba4\u0bb3\u0bcd\u0bb3\u0bc1\u0ba4\u0bb2\u0bc8 \u0b85\u0ba4\u0bbf\u0b95\u0bb0\u0bbf\u0b95\u0bcd\u0b95", +"Close": "\u0bae\u0bc2\u0b9f\u0bc1\u0b95", +"Formats": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0ba8\u0b95\u0bb2\u0b95\u0ba4\u0bcd\u0ba4\u0bbf\u0bb1\u0bcd\u0b95\u0bc1 \u0ba8\u0bc7\u0bb0\u0b9f\u0bbf \u0b85\u0ba3\u0bc1\u0b95\u0bb2\u0bc8 \u0ba4\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb2\u0bbe\u0bb5\u0bbf \u0b86\u0ba4\u0bb0\u0bbf\u0b95\u0bcd\u0b95\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8. \u0b86\u0b95\u0bb5\u0bc7 \u0bb5\u0bbf\u0b9a\u0bc8\u0baa\u0bcd\u0baa\u0bb2\u0b95\u0bc8 \u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf\u0b95\u0bb3\u0bbe\u0ba9 Ctrl+X\/C\/V \u0b87\u0bb5\u0bb1\u0bcd\u0bb1\u0bc8 \u0ba4\u0baf\u0bb5\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0ba4\u0bc1 \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95.", +"Headers": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Header 1": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 1", +"Header 2": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 2", +"Header 3": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 3", +"Header 4": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 4", +"Header 5": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 5", +"Header 6": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 6", +"Headings": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Heading 1": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 1", +"Heading 2": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 2", +"Heading 3": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 3", +"Heading 4": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 4", +"Heading 5": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 5", +"Heading 6": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 6", +"Preformatted": "\u0bae\u0bc1\u0ba9\u0bcd\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1", +"Div": "\u0baa\u0bbf\u0bb0\u0bbf\u0bb5\u0bc1 (Div)", +"Pre": "\u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1 (Pre)", +"Code": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1", +"Paragraph": "\u0baa\u0ba4\u0bcd\u0ba4\u0bbf", +"Blockquote": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf \u0bae\u0bc7\u0bb1\u0bcd\u0b95\u0bcb\u0bb3\u0bcd", +"Inline": "\u0b89\u0bb3\u0bcd\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Blocks": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf\u0b95\u0bb3\u0bcd", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0b87\u0baf\u0bb2\u0bcd\u0baa\u0bc1 \u0b89\u0bb0\u0bc8 \u0bae\u0bc1\u0bb1\u0bc8\u0bae\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0ba4\u0bb1\u0bcd\u0baa\u0bcb\u0ba4\u0bc1 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0ba4\u0bb2\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0ba4\u0bc1. \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b87\u0ba8\u0bcd\u0ba4 \u0bb5\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc8 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0bae\u0bcd \u0bb5\u0bb0\u0bc8 \u0b89\u0bb3\u0bcd\u0bb3\u0b9f\u0b95\u0bcd\u0b95\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b87\u0baf\u0bb2\u0bcd\u0baa\u0bc1 \u0b89\u0bb0\u0bc8\u0baf\u0bbe\u0b95 \u0b92\u0b9f\u0bcd\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd.", +"Font Family": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1 \u0b95\u0bc1\u0b9f\u0bc1\u0bae\u0bcd\u0baa\u0bae\u0bcd", +"Font Sizes": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1 \u0b85\u0bb3\u0bb5\u0bc1\u0b95\u0bb3\u0bcd", +"Class": "Class", +"Browse for an image": "\u0b92\u0bb0\u0bc1 \u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bc1 \u0b89\u0bb2\u0bbe\u0bb5\u0bc1\u0b95", +"OR": "\u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1", +"Drop an image here": "\u0b92\u0bb0\u0bc1 \u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc8 \u0b87\u0b99\u0bcd\u0b95\u0bc1 \u0b87\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0baa\u0bcd \u0baa\u0bcb\u0b9f\u0bb5\u0bc1\u0bae\u0bcd", +"Upload": "\u0baa\u0ba4\u0bbf\u0bb5\u0bc7\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Block": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf", +"Align": "\u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Default": "\u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc1", +"Circle": "\u0bb5\u0b9f\u0bcd\u0b9f\u0bae\u0bcd", +"Disc": "\u0bb5\u0b9f\u0bcd\u0b9f\u0bc1", +"Square": "\u0b9a\u0ba4\u0bc1\u0bb0\u0bae\u0bcd", +"Lower Alpha": "\u0b95\u0bc0\u0bb4\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Lower Greek": "\u0b95\u0bc0\u0bb4\u0bcd \u0b95\u0bbf\u0bb0\u0bc7\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Lower Roman": "\u0b95\u0bc0\u0bb4\u0bcd \u0bb0\u0bcb\u0bae\u0bbe\u0ba9\u0bbf\u0baf\u0bae\u0bcd", +"Upper Alpha": "\u0bae\u0bc7\u0bb2\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Upper Roman": "\u0bae\u0bc7\u0bb2\u0bcd \u0bb0\u0bcb\u0bae\u0bbe\u0ba9\u0bbf\u0baf\u0bae\u0bcd", +"Anchor": "\u0ba8\u0b99\u0bcd\u0b95\u0bc2\u0bb0\u0bae\u0bcd", +"Name": "\u0baa\u0bc6\u0baf\u0bb0\u0bcd", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u0b86\u0ba9\u0ba4\u0bc1 \u0b92\u0bb0\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0bb2\u0bcd \u0ba4\u0bca\u0b9f\u0b99\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd; \u0b87\u0ba4\u0ba9\u0bc8\u0ba4\u0bcd \u0ba4\u0bca\u0b9f\u0bb0\u0bcd\u0ba8\u0bcd\u0ba4\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bcd, \u0b8e\u0ba3\u0bcd\u0b95\u0bb3\u0bcd, \u0b87\u0b9f\u0bc8\u0b95\u0bcd\u0b95\u0bc7\u0bbe\u0b9f\u0bc1\u0b95\u0bb3\u0bcd (-), \u0baa\u0bc1\u0bb3\u0bcd\u0bb3\u0bbf\u0b95\u0bb3\u0bcd (.), \u0bae\u0bc1\u0b95\u0bcd\u0b95\u0bbe\u0bb1\u0bcd\u0baa\u0bc1\u0bb3\u0bcd\u0bb3\u0bbf\u0b95\u0bb3\u0bcd (:) \u0bae\u0bb1\u0bcd\u0bb1\u0bc1\u0bae\u0bcd \u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc7\u0bbe\u0b9f\u0bc1\u0b95\u0bb3\u0bcd (_) \u0bae\u0b9f\u0bcd\u0b9f\u0bc1\u0bae\u0bc7 \u0b87\u0bb0\u0bc1\u0ba4\u0bcd\u0ba4\u0bb2\u0bcd \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd.", +"You have unsaved changes are you sure you want to navigate away?": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bbe\u0ba4 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0ba9; \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb1\u0bc1\u0ba4\u0bbf\u0baf\u0bbe\u0b95 \u0bb5\u0bc6\u0bb3\u0bbf\u0baf\u0bc7\u0bb1 \u0bb5\u0bbf\u0bb0\u0bc1\u0bae\u0bcd\u0baa\u0bc1\u0b95\u0bbf\u0bb1\u0bc0\u0bb0\u0bcd\u0b95\u0bbe\u0bb3\u0bbe?", +"Restore last draft": "\u0b95\u0b9f\u0ba8\u0bcd\u0ba4 \u0bb5\u0bb0\u0bc8\u0bb5\u0bc8 \u0bae\u0bc0\u0b9f\u0bcd\u0b9f\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Special character": "\u0b9a\u0bbf\u0bb1\u0baa\u0bcd\u0baa\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1", +"Source code": "\u0bae\u0bc2\u0bb2 \u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1", +"Insert\/Edit code sample": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1 \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Language": "\u0bae\u0bca\u0bb4\u0bbf", +"Code sample": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1 \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf", +"Color": "\u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0b87\u0b9f\u0bae\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc1 \u0bb5\u0bb2\u0bae\u0bcd", +"Right to left": "\u0bb5\u0bb2\u0bae\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc1 \u0b87\u0b9f\u0bae\u0bcd", +"Emoticons": "\u0b89\u0ba3\u0bb0\u0bcd\u0b9a\u0bcd\u0b9a\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bbf\u0bae\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Document properties": "\u0b86\u0bb5\u0ba3\u0ba4\u0bcd\u0ba4\u0bbf\u0ba9\u0bcd \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Title": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Keywords": "\u0bae\u0bc1\u0ba4\u0ba9\u0bcd\u0bae\u0bc8\u0b9a\u0bcd\u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"Description": "\u0bb5\u0bbf\u0bb5\u0bb0\u0bae\u0bcd", +"Robots": "\u0baa\u0bca\u0bb1\u0bbf\u0baf\u0ba9\u0bcd\u0b95\u0bb3\u0bcd (Robots)", +"Author": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbe\u0bb3\u0bb0\u0bcd", +"Encoding": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bbe\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Fullscreen": "\u0bae\u0bc1\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0bb0\u0bc8", +"Action": "\u0b9a\u0bc6\u0baf\u0bb2\u0bcd", +"Shortcut": "\u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf", +"Help": "\u0b89\u0ba4\u0bb5\u0bbf", +"Address": "\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf", +"Focus to menubar": "\u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to toolbar": "\u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to element path": "\u0bae\u0bc2\u0bb2\u0b95\u0baa\u0bcd \u0baa\u0bbe\u0ba4\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to contextual toolbar": "\u0b9a\u0bc2\u0bb4\u0bcd\u0ba8\u0bbf\u0bb2\u0bc8 \u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Insert link (if link plugin activated)": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95 (\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Save (if save plugin activated)": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95 (\u0b9a\u0bc7\u0bae\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Find (if searchreplace plugin activated)": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95 (\u0ba4\u0bc7\u0b9f\u0bbf\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bb2\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Plugins installed ({0}):": "\u0ba8\u0bbf\u0bb1\u0bc1\u0bb5\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc1\u0bb3\u0bcd\u0bb3 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd ({0}):", +"Premium plugins:": "\u0b89\u0baf\u0bb0\u0bcd\u0bae\u0ba4\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd:", +"Learn more...": "\u0bae\u0bc7\u0bb2\u0bc1\u0bae\u0bcd \u0b85\u0bb1\u0bbf\u0b95...", +"You are using {0}": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb5\u0ba4\u0bc1 {0}", +"Plugins": "\u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd", +"Handy Shortcuts": "\u0b8e\u0bb3\u0bbf\u0ba4\u0bbf\u0bb2\u0bcd \u0b95\u0bc8\u0baf\u0bbe\u0bb3\u0b95\u0bcd\u0b95\u0bc2\u0b9f\u0bbf\u0baf \u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf\u0b95\u0bb3\u0bcd", +"Horizontal line": "\u0b95\u0bbf\u0b9f\u0bc8 \u0b95\u0bcb\u0b9f\u0bc1", +"Insert\/edit image": "\u0baa\u0b9f\u0bae\u0bcd \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Image description": "\u0baa\u0b9f \u0bb5\u0bbf\u0bb5\u0bb0\u0bae\u0bcd", +"Source": "\u0bae\u0bc2\u0bb2\u0bae\u0bcd", +"Dimensions": "\u0baa\u0bb0\u0bbf\u0bae\u0bbe\u0ba3\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Constrain proportions": "\u0bb5\u0bbf\u0b95\u0bbf\u0ba4\u0bbe\u0b9a\u0bcd\u0b9a\u0bbe\u0bb0\u0ba4\u0bcd\u0ba4\u0bbf\u0bb2\u0bcd \u0b95\u0b9f\u0bcd\u0b9f\u0bc1\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"General": "\u0baa\u0bca\u0ba4\u0bc1", +"Advanced": "\u0bae\u0bc7\u0bae\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1", +"Style": "\u0baa\u0bbe\u0ba3\u0bbf", +"Vertical space": "\u0ba8\u0bc6\u0b9f\u0bc1\u0ba4\u0bb3 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Horizontal space": "\u0b95\u0bbf\u0b9f\u0bc8\u0bae\u0b9f\u0bcd\u0b9f \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Border": "\u0b95\u0bb0\u0bc8", +"Insert image": "\u0baa\u0b9f\u0bae\u0bcd \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Image": "\u0baa\u0b9f\u0bae\u0bcd", +"Image list": "\u0baa\u0b9f\u0baa\u0bcd \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Rotate counterclockwise": "\u0b95\u0b9f\u0bbf\u0b95\u0bbe\u0bb0 \u0b8e\u0ba4\u0bbf\u0bb0\u0bcd\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc1\u0bb4\u0bb1\u0bcd\u0bb1\u0bc1", +"Rotate clockwise": "\u0b95\u0b9f\u0bbf\u0b95\u0bbe\u0bb0\u0ba4\u0bcd\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc1\u0bb4\u0bb1\u0bcd\u0bb1\u0bc1", +"Flip vertically": "\u0b9a\u0bc6\u0b99\u0bcd\u0b95\u0bc1\u0ba4\u0bcd\u0ba4\u0bbe\u0b95 \u0baa\u0bc1\u0bb0\u0b9f\u0bcd\u0b9f\u0bc1", +"Flip horizontally": "\u0b95\u0bbf\u0b9f\u0bc8\u0bae\u0b9f\u0bcd\u0b9f\u0bae\u0bbe\u0b95 \u0baa\u0bc1\u0bb0\u0b9f\u0bcd\u0b9f\u0bc1", +"Edit image": "\u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc8 \u0ba4\u0bca\u0b95\u0bc1", +"Image options": "\u0baa\u0b9f \u0bb5\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Zoom in": "\u0baa\u0bc6\u0bb0\u0bbf\u0ba4\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Zoom out": "\u0b9a\u0bbf\u0bb1\u0bbf\u0ba4\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Crop": "\u0b9a\u0bc6\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bc1", +"Resize": "\u0bae\u0bb1\u0bc1\u0b85\u0bb3\u0bb5\u0bbf\u0b9f\u0bc1", +"Orientation": "\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bae\u0bc8\u0bb5\u0bc1", +"Brightness": "\u0b92\u0bb3\u0bbf\u0bb0\u0bcd\u0bb5\u0bc1", +"Sharpen": "\u0b95\u0bc2\u0bb0\u0bcd\u0bae\u0bc8\u0baf\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Contrast": "\u0ba8\u0bbf\u0bb1\u0bae\u0bbe\u0bb1\u0bc1\u0baa\u0bbe\u0b9f\u0bc1", +"Color levels": "\u0bb5\u0ba3\u0bcd\u0ba3 \u0ba8\u0bbf\u0bb2\u0bc8\u0b95\u0bb3\u0bcd", +"Gamma": "Gamma", +"Invert": "\u0ba8\u0bc7\u0bb0\u0bcd\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1", +"Apply": "\u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Back": "\u0baa\u0bbf\u0ba9\u0bcd", +"Insert date\/time": "\u0ba4\u0bc7\u0ba4\u0bbf\/\u0ba8\u0bc7\u0bb0\u0bae\u0bcd \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Date\/time": "\u0ba4\u0bc7\u0ba4\u0bbf\/\u0ba8\u0bc7\u0bb0\u0bae\u0bcd", +"Insert link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert\/edit link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Text to display": "\u0b95\u0bbe\u0b9f\u0bcd\u0b9a\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bbf\u0baf \u0b89\u0bb0\u0bc8", +"Url": "\u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf", +"Target": "\u0b87\u0bb2\u0b95\u0bcd\u0b95\u0bc1", +"None": "\u0b8f\u0ba4\u0bc1\u0bae\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8", +"New window": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf \u0b9a\u0bbe\u0bb3\u0bb0\u0bae\u0bcd", +"Remove link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc8 \u0b85\u0b95\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Anchors": "\u0ba8\u0b99\u0bcd\u0b95\u0bc2\u0bb0\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Paste or type a link": "\u0b92\u0bb0\u0bc1 \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95 \u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1 \u0ba4\u0b9f\u0bcd\u0b9f\u0b9a\u0bcd\u0b9a\u0bbf\u0b9f\u0bc1\u0b95", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0b9f\u0bcd\u0b9f \u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf (URL) \u0b92\u0bb0\u0bc1 \u0bae\u0bbf\u0ba9\u0bcd-\u0b85\u0b9e\u0bcd\u0b9a\u0bb2\u0bcd \u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf \u0baa\u0bcb\u0bb2\u0bcd \u0ba4\u0bcb\u0ba9\u0bcd\u0bb1\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1. \u0ba4\u0bc7\u0bb5\u0bc8\u0baf\u0bbe\u0ba9 mailto: \u0bae\u0bc1\u0ba9\u0bcd-\u0b92\u0b9f\u0bcd\u0b9f\u0bc8\u0ba4\u0bcd (prefix) \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bbe?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0b9f\u0bcd\u0b9f \u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf (URL) \u0b92\u0bb0\u0bc1 \u0bb5\u0bc6\u0bb3\u0bbf\u0baa\u0bcd\u0baa\u0bc1\u0bb1 \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 (external link) \u0baa\u0bcb\u0bb2\u0bcd \u0ba4\u0bcb\u0ba9\u0bcd\u0bb1\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1. \u0ba4\u0bc7\u0bb5\u0bc8\u0baf\u0bbe\u0ba9 http:\/\/ \u0bae\u0bc1\u0ba9\u0bcd-\u0b92\u0b9f\u0bcd\u0b9f\u0bc8\u0ba4\u0bcd (prefix) \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bbe?", +"Link list": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0baa\u0bcd \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Insert video": "\u0b95\u0bbe\u0ba3\u0bca\u0bb3\u0bbf \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert\/edit video": "\u0b95\u0bbe\u0ba3\u0bca\u0bb3\u0bbf \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Insert\/edit media": "\u0b8a\u0b9f\u0b95\u0bae\u0bcd \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Alternative source": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1 \u0bae\u0bc2\u0bb2\u0bae\u0bcd", +"Poster": "\u0b9a\u0bc1\u0bb5\u0bb0\u0bca\u0b9f\u0bcd\u0b9f\u0bbf", +"Paste your embed code below:": "\u0ba4\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0b9f\u0bcd\u0baa\u0bc6\u0bbe\u0ba4\u0bbf \u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bcd\u0b9f\u0bc8 \u0b95\u0bc0\u0bb4\u0bc7 \u0b92\u0b9f\u0bcd\u0b9f\u0bb5\u0bc1\u0bae\u0bcd:", +"Embed": "\u0b89\u0b9f\u0bcd\u0baa\u0bca\u0ba4\u0bbf", +"Media": "\u0b8a\u0b9f\u0b95\u0bae\u0bcd", +"Nonbreaking space": "\u0baa\u0bbf\u0bb0\u0bbf\u0baf\u0bbe\u0ba4 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Page break": "\u0baa\u0b95\u0bcd\u0b95 \u0baa\u0bbf\u0bb0\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Paste as text": "\u0b89\u0bb0\u0bc8\u0baf\u0bbe\u0b95 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Preview": "\u0bae\u0bc1\u0ba9\u0bcd\u0ba8\u0bcb\u0b95\u0bcd\u0b95\u0bc1", +"Print": "\u0b85\u0b9a\u0bcd\u0b9a\u0bbf\u0b9f\u0bc1\u0b95", +"Save": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95", +"Find": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95", +"Replace with": "\u0b87\u0ba4\u0ba9\u0bc1\u0b9f\u0ba9\u0bcd \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Replace": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Replace all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Prev": "\u0bae\u0bc1\u0ba8\u0bcd\u0ba4\u0bc8\u0baf", +"Next": "\u0b85\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4", +"Find and replace": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0ba4\u0bcd\u0ba4\u0bc1 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Could not find the specified string.": "\u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bbf\u0b9f\u0bcd\u0b9f \u0b9a\u0bb0\u0bae\u0bcd \u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95 \u0bae\u0bc1\u0b9f\u0bbf\u0baf\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8", +"Match case": "\u0bb5\u0b9f\u0bbf\u0bb5\u0ba4\u0bcd\u0ba4\u0bc8 \u0baa\u0bca\u0bb0\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Whole words": "\u0bae\u0bc1\u0bb4\u0bc1 \u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"Spellcheck": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0baa\u0bcd\u0baa\u0bbf\u0bb4\u0bc8\u0baf\u0bc8 \u0b9a\u0bb0\u0bbf\u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Ignore": "\u0baa\u0bc1\u0bb1\u0b95\u0bcd\u0b95\u0ba3\u0bbf\u0b95\u0bcd\u0b95", +"Ignore all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0baa\u0bc1\u0bb1\u0b95\u0bcd\u0b95\u0ba3\u0bbf\u0b95\u0bcd\u0b95", +"Finish": "\u0bae\u0bc1\u0b9f\u0bbf\u0b95\u0bcd\u0b95", +"Add to Dictionary": "\u0b85\u0b95\u0bb0\u0bbe\u0ba4\u0bbf\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Insert table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Table properties": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Delete table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Cell": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8", +"Row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Column": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Cell properties": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Merge cells": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Split cell": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0baa\u0bbf\u0bb0\u0bbf\u0b95\u0bcd\u0b95", +"Insert row before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert row after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Delete row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Row properties": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Cut row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0bb5\u0bc6\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Copy row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0b95\u0bb2\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Paste row before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Paste row after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Insert column before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert column after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Delete column": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Cols": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8\u0b95\u0bb3\u0bcd", +"Rows": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8\u0b95\u0bb3\u0bcd", +"Width": "\u0b85\u0b95\u0bb2\u0bae\u0bcd", +"Height": "\u0b89\u0baf\u0bb0\u0bae\u0bcd", +"Cell spacing": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Cell padding": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0ba8\u0bbf\u0bb0\u0baa\u0bcd\u0baa\u0bb2\u0bcd", +"Caption": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Left": "\u0b87\u0b9f\u0bae\u0bcd", +"Center": "\u0bae\u0bc8\u0baf\u0bae\u0bcd", +"Right": "\u0bb5\u0bb2\u0bae\u0bcd", +"Cell type": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0bb5\u0b95\u0bc8", +"Scope": "\u0bb5\u0bb0\u0bc8\u0baf\u0bc6\u0bb2\u0bcd\u0bb2\u0bc8", +"Alignment": "\u0b9a\u0bc0\u0bb0\u0bae\u0bc8\u0bb5\u0bc1", +"H Align": "\u0b95\u0bbf (H) \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"V Align": "\u0b9a\u0bc6 (V) \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Top": "\u0bae\u0bc7\u0bb2\u0bcd", +"Middle": "\u0ba8\u0b9f\u0bc1", +"Bottom": "\u0b95\u0bc0\u0bb4\u0bcd", +"Header cell": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8", +"Row group": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b95\u0bc1\u0bb4\u0bc1", +"Column group": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b95\u0bc1\u0bb4\u0bc1", +"Row type": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0bb5\u0b95\u0bc8", +"Header": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Body": "\u0b89\u0b9f\u0bb2\u0bcd", +"Footer": "\u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Border color": "\u0b95\u0bb0\u0bc8\u0baf\u0bbf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Insert template": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Templates": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bcd", +"Template": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1", +"Text color": "\u0b89\u0bb0\u0bc8\u0baf\u0bbf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Background color": "\u0baa\u0bbf\u0ba9\u0bcd\u0ba9\u0ba3\u0bbf \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Custom...": "\u0ba4\u0ba9\u0bbf\u0baa\u0bcd\u0baa\u0baf\u0ba9\u0bcd...", +"Custom color": "\u0ba4\u0ba9\u0bbf\u0baa\u0bcd\u0baa\u0baf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"No color": "\u0ba8\u0bbf\u0bb1\u0bae\u0bcd \u0b87\u0bb2\u0bcd\u0bb2\u0bc8", +"Table of Contents": "\u0baa\u0bca\u0bb0\u0bc1\u0bb3\u0b9f\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Show blocks": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf\u0b95\u0bb3\u0bc8 \u0b95\u0bbe\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Show invisible characters": "\u0b95\u0ba3\u0bcd\u0ba3\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0ba4\u0bcd \u0ba4\u0bc6\u0bb0\u0bbf\u0baf\u0bbe\u0ba4 \u0b89\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bc8 \u0b95\u0bbe\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Words: {0}": "\u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd: {0}", +"{0} words": "{0} \u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"File": "\u0b95\u0bcb\u0baa\u0bcd\u0baa\u0bc1", +"Edit": "\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Insert": "\u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"View": "\u0ba8\u0bcb\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Format": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8", +"Tools": "\u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0b95\u0bb3\u0bcd", +"Powered by {0}": "\u0bb5\u0bb2\u0bc1\u0bb5\u0bb3\u0bbf\u0baa\u0bcd\u0baa\u0ba4\u0bc1 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0b89\u0baf\u0bb0\u0bcd \u0b89\u0bb0\u0bc8 \u0baa\u0b95\u0bc1\u0ba4\u0bbf. \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc1 ALT-F9 , \u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0b95\u0bcd\u0b95\u0bc1 ALT-F10 , \u0b89\u0ba4\u0bb5\u0bbf\u0b95\u0bcd\u0b95\u0bc1 ALT-0" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta_IN.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta_IN.js new file mode 100644 index 0000000000..faa20ef916 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ta_IN.js @@ -0,0 +1,261 @@ +tinymce.addI18n('ta_IN',{ +"Redo": "\u0bae\u0bc0\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Undo": "\u0b9a\u0bc6\u0baf\u0bb2\u0bcd\u0ba4\u0bb5\u0bbf\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Cut": "\u0bb5\u0bc6\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Copy": "\u0ba8\u0b95\u0bb2\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Paste": "\u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Select all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0ba4\u0bc7\u0bb0\u0bcd\u0bb5\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"New document": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf \u0b86\u0bb5\u0ba3\u0bae\u0bcd", +"Ok": "\u0b9a\u0bb0\u0bbf", +"Cancel": "\u0bb0\u0ba4\u0bcd\u0ba4\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Visual aids": "\u0b95\u0bbe\u0b9f\u0bcd\u0b9a\u0bbf\u0ba4\u0bcd \u0ba4\u0bc1\u0ba3\u0bc8\u0baf\u0ba9\u0bcd\u0b95\u0bb3\u0bcd", +"Bold": "\u0ba4\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Italic": "\u0b9a\u0bbe\u0baf\u0bcd\u0bb5\u0bc1", +"Underline": "\u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bcb\u0b9f\u0bc1", +"Strikethrough": "\u0ba8\u0b9f\u0bc1\u0b95\u0bcd\u0b95\u0bcb\u0b9f\u0bc1", +"Superscript": "\u0bae\u0bc7\u0bb2\u0bcd\u0b92\u0b9f\u0bcd\u0b9f\u0bc1", +"Subscript": "\u0b95\u0bc0\u0bb4\u0bcd\u0b92\u0b9f\u0bcd\u0b9f\u0bc1", +"Clear formatting": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b85\u0bb4\u0bbf\u0b95\u0bcd\u0b95", +"Align left": "\u0b87\u0b9f\u0ba4\u0bc1 \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Align center": "\u0bae\u0bc8\u0baf \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Align right": "\u0bb5\u0bb2\u0ba4\u0bc1 \u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Justify": "\u0ba8\u0bc7\u0bb0\u0bcd\u0ba4\u0bcd\u0ba4\u0bbf \u0b9a\u0bc6\u0baf\u0bcd\u0b95", +"Bullet list": "\u0baa\u0bca\u0b9f\u0bcd\u0b9f\u0bbf\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Numbered list": "\u0b8e\u0ba3\u0bcd\u0ba3\u0bbf\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Decrease indent": "\u0b89\u0bb3\u0bcd\u0ba4\u0bb3\u0bcd\u0bb3\u0bc1\u0ba4\u0bb2\u0bc8 \u0b95\u0bc1\u0bb1\u0bc8\u0b95\u0bcd\u0b95", +"Increase indent": "\u0b89\u0bb3\u0bcd\u0ba4\u0bb3\u0bcd\u0bb3\u0bc1\u0ba4\u0bb2\u0bc8 \u0b85\u0ba4\u0bbf\u0b95\u0bb0\u0bbf\u0b95\u0bcd\u0b95", +"Close": "\u0bae\u0bc2\u0b9f\u0bc1\u0b95", +"Formats": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0ba8\u0b95\u0bb2\u0b95\u0ba4\u0bcd\u0ba4\u0bbf\u0bb1\u0bcd\u0b95\u0bc1 \u0ba8\u0bc7\u0bb0\u0b9f\u0bbf \u0b85\u0ba3\u0bc1\u0b95\u0bb2\u0bc8 \u0ba4\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb2\u0bbe\u0bb5\u0bbf \u0b86\u0ba4\u0bb0\u0bbf\u0b95\u0bcd\u0b95\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8. \u0b86\u0b95\u0bb5\u0bc7 \u0bb5\u0bbf\u0b9a\u0bc8\u0baa\u0bcd\u0baa\u0bb2\u0b95\u0bc8 \u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf\u0b95\u0bb3\u0bbe\u0ba9 Ctrl+X\/C\/V \u0b87\u0bb5\u0bb1\u0bcd\u0bb1\u0bc8 \u0ba4\u0baf\u0bb5\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0ba4\u0bc1 \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95.", +"Headers": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Header 1": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 1", +"Header 2": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 2", +"Header 3": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 3", +"Header 4": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 4", +"Header 5": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 5", +"Header 6": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 6", +"Headings": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Heading 1": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 1", +"Heading 2": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 2", +"Heading 3": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 3", +"Heading 4": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 4", +"Heading 5": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 5", +"Heading 6": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 6", +"Preformatted": "\u0bae\u0bc1\u0ba9\u0bcd\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1", +"Div": "\u0baa\u0bbf\u0bb0\u0bbf\u0bb5\u0bc1 (Div)", +"Pre": "\u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1 (Pre)", +"Code": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1", +"Paragraph": "\u0baa\u0ba4\u0bcd\u0ba4\u0bbf", +"Blockquote": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf \u0bae\u0bc7\u0bb1\u0bcd\u0b95\u0bcb\u0bb3\u0bcd", +"Inline": "\u0b89\u0bb3\u0bcd\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Blocks": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf\u0b95\u0bb3\u0bcd", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0b87\u0baf\u0bb2\u0bcd\u0baa\u0bc1 \u0b89\u0bb0\u0bc8 \u0bae\u0bc1\u0bb1\u0bc8\u0bae\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0ba4\u0bb1\u0bcd\u0baa\u0bcb\u0ba4\u0bc1 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0ba4\u0bb2\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0ba4\u0bc1. \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b87\u0ba8\u0bcd\u0ba4 \u0bb5\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc8 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0bae\u0bcd \u0bb5\u0bb0\u0bc8 \u0b89\u0bb3\u0bcd\u0bb3\u0b9f\u0b95\u0bcd\u0b95\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b87\u0baf\u0bb2\u0bcd\u0baa\u0bc1 \u0b89\u0bb0\u0bc8\u0baf\u0bbe\u0b95 \u0b92\u0b9f\u0bcd\u0b9f\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd.", +"Font Family": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1 \u0b95\u0bc1\u0b9f\u0bc1\u0bae\u0bcd\u0baa\u0bae\u0bcd", +"Font Sizes": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1 \u0b85\u0bb3\u0bb5\u0bc1\u0b95\u0bb3\u0bcd", +"Class": "Class", +"Browse for an image": "\u0b92\u0bb0\u0bc1 \u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bc1 \u0b89\u0bb2\u0bbe\u0bb5\u0bc1\u0b95", +"OR": "\u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1", +"Drop an image here": "\u0b92\u0bb0\u0bc1 \u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc8 \u0b87\u0b99\u0bcd\u0b95\u0bc1 \u0b87\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0baa\u0bcd \u0baa\u0bcb\u0b9f\u0bb5\u0bc1\u0bae\u0bcd", +"Upload": "\u0baa\u0ba4\u0bbf\u0bb5\u0bc7\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Block": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf", +"Align": "\u0b9a\u0bc0\u0bb0\u0bae\u0bc8", +"Default": "\u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc1", +"Circle": "\u0bb5\u0b9f\u0bcd\u0b9f\u0bae\u0bcd", +"Disc": "\u0bb5\u0b9f\u0bcd\u0b9f\u0bc1", +"Square": "\u0b9a\u0ba4\u0bc1\u0bb0\u0bae\u0bcd", +"Lower Alpha": "\u0b95\u0bc0\u0bb4\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Lower Greek": "\u0b95\u0bc0\u0bb4\u0bcd \u0b95\u0bbf\u0bb0\u0bc7\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Lower Roman": "\u0b95\u0bc0\u0bb4\u0bcd \u0bb0\u0bcb\u0bae\u0bbe\u0ba9\u0bbf\u0baf\u0bae\u0bcd", +"Upper Alpha": "\u0bae\u0bc7\u0bb2\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Upper Roman": "\u0bae\u0bc7\u0bb2\u0bcd \u0bb0\u0bcb\u0bae\u0bbe\u0ba9\u0bbf\u0baf\u0bae\u0bcd", +"Anchor": "\u0ba8\u0b99\u0bcd\u0b95\u0bc2\u0bb0\u0bae\u0bcd", +"Name": "\u0baa\u0bc6\u0baf\u0bb0\u0bcd", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u0b86\u0ba9\u0ba4\u0bc1 \u0b92\u0bb0\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0bb2\u0bcd \u0ba4\u0bca\u0b9f\u0b99\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd; \u0b87\u0ba4\u0ba9\u0bc8\u0ba4\u0bcd \u0ba4\u0bca\u0b9f\u0bb0\u0bcd\u0ba8\u0bcd\u0ba4\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bcd, \u0b8e\u0ba3\u0bcd\u0b95\u0bb3\u0bcd, \u0b87\u0b9f\u0bc8\u0b95\u0bcd\u0b95\u0bc7\u0bbe\u0b9f\u0bc1\u0b95\u0bb3\u0bcd (-), \u0baa\u0bc1\u0bb3\u0bcd\u0bb3\u0bbf\u0b95\u0bb3\u0bcd (.), \u0bae\u0bc1\u0b95\u0bcd\u0b95\u0bbe\u0bb1\u0bcd\u0baa\u0bc1\u0bb3\u0bcd\u0bb3\u0bbf\u0b95\u0bb3\u0bcd (:) \u0bae\u0bb1\u0bcd\u0bb1\u0bc1\u0bae\u0bcd \u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc7\u0bbe\u0b9f\u0bc1\u0b95\u0bb3\u0bcd (_) \u0bae\u0b9f\u0bcd\u0b9f\u0bc1\u0bae\u0bc7 \u0b87\u0bb0\u0bc1\u0ba4\u0bcd\u0ba4\u0bb2\u0bcd \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd.", +"You have unsaved changes are you sure you want to navigate away?": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bbe\u0ba4 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0ba9; \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb1\u0bc1\u0ba4\u0bbf\u0baf\u0bbe\u0b95 \u0bb5\u0bc6\u0bb3\u0bbf\u0baf\u0bc7\u0bb1 \u0bb5\u0bbf\u0bb0\u0bc1\u0bae\u0bcd\u0baa\u0bc1\u0b95\u0bbf\u0bb1\u0bc0\u0bb0\u0bcd\u0b95\u0bbe\u0bb3\u0bbe?", +"Restore last draft": "\u0b95\u0b9f\u0ba8\u0bcd\u0ba4 \u0bb5\u0bb0\u0bc8\u0bb5\u0bc8 \u0bae\u0bc0\u0b9f\u0bcd\u0b9f\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Special character": "\u0b9a\u0bbf\u0bb1\u0baa\u0bcd\u0baa\u0bc1 \u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb0\u0bc1", +"Source code": "\u0bae\u0bc2\u0bb2 \u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1", +"Insert\/Edit code sample": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1 \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Language": "\u0bae\u0bca\u0bb4\u0bbf", +"Code sample": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bc1 \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf", +"Color": "\u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0b87\u0b9f\u0bae\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc1 \u0bb5\u0bb2\u0bae\u0bcd", +"Right to left": "\u0bb5\u0bb2\u0bae\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc1 \u0b87\u0b9f\u0bae\u0bcd", +"Emoticons": "\u0b89\u0ba3\u0bb0\u0bcd\u0b9a\u0bcd\u0b9a\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bbf\u0bae\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Document properties": "\u0b86\u0bb5\u0ba3\u0ba4\u0bcd\u0ba4\u0bbf\u0ba9\u0bcd \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Title": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Keywords": "\u0bae\u0bc1\u0ba4\u0ba9\u0bcd\u0bae\u0bc8\u0b9a\u0bcd\u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"Description": "\u0bb5\u0bbf\u0bb5\u0bb0\u0bae\u0bcd", +"Robots": "\u0baa\u0bca\u0bb1\u0bbf\u0baf\u0ba9\u0bcd\u0b95\u0bb3\u0bcd (Robots)", +"Author": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbe\u0bb3\u0bb0\u0bcd", +"Encoding": "\u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bbe\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Fullscreen": "\u0bae\u0bc1\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0bb0\u0bc8", +"Action": "\u0b9a\u0bc6\u0baf\u0bb2\u0bcd", +"Shortcut": "\u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf", +"Help": "\u0b89\u0ba4\u0bb5\u0bbf", +"Address": "\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf", +"Focus to menubar": "\u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to toolbar": "\u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to element path": "\u0bae\u0bc2\u0bb2\u0b95\u0baa\u0bcd \u0baa\u0bbe\u0ba4\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Focus to contextual toolbar": "\u0b9a\u0bc2\u0bb4\u0bcd\u0ba8\u0bbf\u0bb2\u0bc8 \u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b95\u0bb5\u0ba9\u0bae\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Insert link (if link plugin activated)": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bc6\u0bb0\u0bc1\u0b95\u0bc1\u0b95 (\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Save (if save plugin activated)": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95 (\u0b9a\u0bc7\u0bae\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Find (if searchreplace plugin activated)": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95 (\u0ba4\u0bc7\u0b9f\u0bbf\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bb2\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf \u0b9a\u0bc6\u0baf\u0bb2\u0bbe\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf\u0baf\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe\u0bb2\u0bcd)", +"Plugins installed ({0}):": "\u0ba8\u0bbf\u0bb1\u0bc1\u0bb5\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc1\u0bb3\u0bcd\u0bb3 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd ({0}):", +"Premium plugins:": "\u0b89\u0baf\u0bb0\u0bcd\u0bae\u0ba4\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd:", +"Learn more...": "\u0bae\u0bc7\u0bb2\u0bc1\u0bae\u0bcd \u0b85\u0bb1\u0bbf\u0b95...", +"You are using {0}": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0bb5\u0ba4\u0bc1 {0}", +"Plugins": "\u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bbf\u0b95\u0bb3\u0bcd", +"Handy Shortcuts": "\u0b8e\u0bb3\u0bbf\u0ba4\u0bbf\u0bb2\u0bcd \u0b95\u0bc8\u0baf\u0bbe\u0bb3\u0b95\u0bcd\u0b95\u0bc2\u0b9f\u0bbf\u0baf \u0b95\u0bc1\u0bb1\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bb5\u0bb4\u0bbf\u0b95\u0bb3\u0bcd", +"Horizontal line": "\u0b95\u0bbf\u0b9f\u0bc8 \u0b95\u0bcb\u0b9f\u0bc1", +"Insert\/edit image": "\u0baa\u0b9f\u0bae\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Image description": "\u0baa\u0b9f \u0bb5\u0bbf\u0bb5\u0bb0\u0bae\u0bcd", +"Source": "\u0bae\u0bc2\u0bb2\u0bae\u0bcd", +"Dimensions": "\u0baa\u0bb0\u0bbf\u0bae\u0bbe\u0ba3\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Constrain proportions": "\u0bb5\u0bbf\u0b95\u0bbf\u0ba4\u0bbe\u0b9a\u0bcd\u0b9a\u0bbe\u0bb0\u0ba4\u0bcd\u0ba4\u0bbf\u0bb2\u0bcd \u0b95\u0b9f\u0bcd\u0b9f\u0bc1\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"General": "\u0baa\u0bca\u0ba4\u0bc1", +"Advanced": "\u0bae\u0bc7\u0bae\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1", +"Style": "\u0baa\u0bbe\u0ba3\u0bbf", +"Vertical space": "\u0ba8\u0bc6\u0b9f\u0bc1\u0ba4\u0bb3 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Horizontal space": "\u0b95\u0bbf\u0b9f\u0bc8\u0bae\u0b9f\u0bcd\u0b9f \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Border": "\u0b95\u0bb0\u0bc8", +"Insert image": "\u0baa\u0b9f\u0bae\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Image": "\u0baa\u0b9f\u0bae\u0bcd", +"Image list": "\u0baa\u0b9f\u0baa\u0bcd \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Rotate counterclockwise": "\u0b95\u0b9f\u0bbf\u0b95\u0bbe\u0bb0 \u0b8e\u0ba4\u0bbf\u0bb0\u0bcd\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc1\u0bb4\u0bb1\u0bcd\u0bb1\u0bc1", +"Rotate clockwise": "\u0b95\u0b9f\u0bbf\u0b95\u0bbe\u0bb0\u0ba4\u0bcd\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc1\u0bb4\u0bb1\u0bcd\u0bb1\u0bc1", +"Flip vertically": "\u0b9a\u0bc6\u0b99\u0bcd\u0b95\u0bc1\u0ba4\u0bcd\u0ba4\u0bbe\u0b95 \u0baa\u0bc1\u0bb0\u0b9f\u0bcd\u0b9f\u0bc1", +"Flip horizontally": "\u0b95\u0bbf\u0b9f\u0bc8\u0bae\u0b9f\u0bcd\u0b9f\u0bae\u0bbe\u0b95 \u0baa\u0bc1\u0bb0\u0b9f\u0bcd\u0b9f\u0bc1", +"Edit image": "\u0baa\u0b9f\u0ba4\u0bcd\u0ba4\u0bc8 \u0ba4\u0bca\u0b95\u0bc1", +"Image options": "\u0baa\u0b9f \u0bb5\u0bbf\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Zoom in": "\u0baa\u0bc6\u0bb0\u0bbf\u0ba4\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Zoom out": "\u0b9a\u0bbf\u0bb1\u0bbf\u0ba4\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Crop": "\u0b9a\u0bc6\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bc1", +"Resize": "\u0bae\u0bb1\u0bc1\u0b85\u0bb3\u0bb5\u0bbf\u0b9f\u0bc1", +"Orientation": "\u0ba4\u0bbf\u0b9a\u0bc8\u0baf\u0bae\u0bc8\u0bb5\u0bc1", +"Brightness": "\u0b92\u0bb3\u0bbf\u0bb0\u0bcd\u0bb5\u0bc1", +"Sharpen": "\u0b95\u0bc2\u0bb0\u0bcd\u0bae\u0bc8\u0baf\u0bbe\u0b95\u0bcd\u0b95\u0bc1", +"Contrast": "\u0ba8\u0bbf\u0bb1\u0bae\u0bbe\u0bb1\u0bc1\u0baa\u0bbe\u0b9f\u0bc1", +"Color levels": "\u0bb5\u0ba3\u0bcd\u0ba3 \u0ba8\u0bbf\u0bb2\u0bc8\u0b95\u0bb3\u0bcd", +"Gamma": "Gamma", +"Invert": "\u0ba8\u0bc7\u0bb0\u0bcd\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1", +"Apply": "\u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1", +"Back": "\u0baa\u0bbf\u0ba9\u0bcd", +"Insert date\/time": "\u0ba4\u0bc7\u0ba4\u0bbf\/\u0ba8\u0bc7\u0bb0\u0bae\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Date\/time": "\u0ba4\u0bc7\u0ba4\u0bbf\/\u0ba8\u0bc7\u0bb0\u0bae\u0bcd", +"Insert link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert\/edit link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Text to display": "\u0b95\u0bbe\u0b9f\u0bcd\u0b9a\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bbf\u0baf \u0b89\u0bb0\u0bc8", +"Url": "\u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf", +"Target": "\u0b87\u0bb2\u0b95\u0bcd\u0b95\u0bc1", +"None": "\u0b8f\u0ba4\u0bc1\u0bae\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8", +"New window": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf \u0b9a\u0bbe\u0bb3\u0bb0\u0bae\u0bcd", +"Remove link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc8 \u0b85\u0b95\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Anchors": "\u0ba8\u0b99\u0bcd\u0b95\u0bc2\u0bb0\u0b99\u0bcd\u0b95\u0bb3\u0bcd", +"Link": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Paste or type a link": "\u0b92\u0bb0\u0bc1 \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95 \u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1 \u0ba4\u0b9f\u0bcd\u0b9f\u0b9a\u0bcd\u0b9a\u0bbf\u0b9f\u0bc1\u0b95", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0b9f\u0bcd\u0b9f \u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf (URL) \u0b92\u0bb0\u0bc1 \u0bae\u0bbf\u0ba9\u0bcd-\u0b85\u0b9e\u0bcd\u0b9a\u0bb2\u0bcd \u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf \u0baa\u0bcb\u0bb2\u0bcd \u0ba4\u0bcb\u0ba9\u0bcd\u0bb1\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1. \u0ba4\u0bc7\u0bb5\u0bc8\u0baf\u0bbe\u0ba9 mailto: \u0bae\u0bc1\u0ba9\u0bcd-\u0b92\u0b9f\u0bcd\u0b9f\u0bc8\u0ba4\u0bcd (prefix) \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bbe?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0bb3\u0bcd\u0bb3\u0bbf\u0b9f\u0bcd\u0b9f \u0b87\u0ba3\u0bc8\u0baf\u0bae\u0bc1\u0b95\u0bb5\u0bb0\u0bbf (URL) \u0b92\u0bb0\u0bc1 \u0bb5\u0bc6\u0bb3\u0bbf\u0baa\u0bcd\u0baa\u0bc1\u0bb1 \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 (external link) \u0baa\u0bcb\u0bb2\u0bcd \u0ba4\u0bcb\u0ba9\u0bcd\u0bb1\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1. \u0ba4\u0bc7\u0bb5\u0bc8\u0baf\u0bbe\u0ba9 http:\/\/ \u0bae\u0bc1\u0ba9\u0bcd-\u0b92\u0b9f\u0bcd\u0b9f\u0bc8\u0ba4\u0bcd (prefix) \u0ba4\u0bbe\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95 \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bbe?", +"Link list": "\u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0baa\u0bcd \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0baf\u0bb2\u0bcd", +"Insert video": "\u0b95\u0bbe\u0ba3\u0bca\u0bb3\u0bbf \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert\/edit video": "\u0b95\u0bbe\u0ba3\u0bca\u0bb3\u0bbf \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Insert\/edit media": "\u0b8a\u0b9f\u0b95\u0bae\u0bcd \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95\/\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Alternative source": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1 \u0bae\u0bc2\u0bb2\u0bae\u0bcd", +"Poster": "\u0b9a\u0bc1\u0bb5\u0bb0\u0bca\u0b9f\u0bcd\u0b9f\u0bbf", +"Paste your embed code below:": "\u0ba4\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b89\u0b9f\u0bcd\u0baa\u0bc6\u0bbe\u0ba4\u0bbf \u0b95\u0bc1\u0bb1\u0bbf\u0baf\u0bc0\u0b9f\u0bcd\u0b9f\u0bc8 \u0b95\u0bc0\u0bb4\u0bc7 \u0b92\u0b9f\u0bcd\u0b9f\u0bb5\u0bc1\u0bae\u0bcd:", +"Embed": "\u0b89\u0b9f\u0bcd\u0baa\u0bca\u0ba4\u0bbf", +"Media": "\u0b8a\u0b9f\u0b95\u0bae\u0bcd", +"Nonbreaking space": "\u0baa\u0bbf\u0bb0\u0bbf\u0baf\u0bbe\u0ba4 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Page break": "\u0baa\u0b95\u0bcd\u0b95 \u0baa\u0bbf\u0bb0\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Paste as text": "\u0b89\u0bb0\u0bc8\u0baf\u0bbe\u0b95 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Preview": "\u0bae\u0bc1\u0ba9\u0bcd\u0ba8\u0bcb\u0b95\u0bcd\u0b95\u0bc1", +"Print": "\u0b85\u0b9a\u0bcd\u0b9a\u0bbf\u0b9f\u0bc1\u0b95", +"Save": "\u0b9a\u0bc7\u0bae\u0bbf\u0b95\u0bcd\u0b95", +"Find": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95", +"Replace with": "\u0b87\u0ba4\u0ba9\u0bc1\u0b9f\u0ba9\u0bcd \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Replace": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Replace all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Prev": "\u0bae\u0bc1\u0ba8\u0bcd\u0ba4\u0bc8\u0baf", +"Next": "\u0b85\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4", +"Find and replace": "\u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0ba4\u0bcd\u0ba4\u0bc1 \u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1\u0b95", +"Could not find the specified string.": "\u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bbf\u0b9f\u0bcd\u0b9f \u0b9a\u0bb0\u0bae\u0bcd \u0b95\u0ba3\u0bcd\u0b9f\u0bc1\u0baa\u0bbf\u0b9f\u0bbf\u0b95\u0bcd\u0b95 \u0bae\u0bc1\u0b9f\u0bbf\u0baf\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8", +"Match case": "\u0bb5\u0b9f\u0bbf\u0bb5\u0ba4\u0bcd\u0ba4\u0bc8 \u0baa\u0bca\u0bb0\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0b95", +"Whole words": "\u0bae\u0bc1\u0bb4\u0bc1 \u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"Spellcheck": "\u0b8e\u0bb4\u0bc1\u0ba4\u0bcd\u0ba4\u0bc1\u0baa\u0bcd\u0baa\u0bbf\u0bb4\u0bc8\u0baf\u0bc8 \u0b9a\u0bb0\u0bbf\u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Ignore": "\u0baa\u0bc1\u0bb1\u0b95\u0bcd\u0b95\u0ba3\u0bbf\u0b95\u0bcd\u0b95", +"Ignore all": "\u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0baa\u0bc1\u0bb1\u0b95\u0bcd\u0b95\u0ba3\u0bbf\u0b95\u0bcd\u0b95", +"Finish": "\u0bae\u0bc1\u0b9f\u0bbf\u0b95\u0bcd\u0b95", +"Add to Dictionary": "\u0b85\u0b95\u0bb0\u0bbe\u0ba4\u0bbf\u0baf\u0bbf\u0bb2\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Insert table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Table properties": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Delete table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Cell": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8", +"Row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Column": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8", +"Cell properties": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Merge cells": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8\u0b95\u0bb3\u0bcd \u0b9a\u0bc7\u0bb0\u0bcd\u0b95\u0bcd\u0b95", +"Split cell": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0baa\u0bbf\u0bb0\u0bbf\u0b95\u0bcd\u0b95", +"Insert row before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert row after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Delete row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Row properties": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0baa\u0ba3\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd", +"Cut row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0bb5\u0bc6\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Copy row": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0b95\u0bb2\u0bc6\u0b9f\u0bc1\u0b95\u0bcd\u0b95", +"Paste row before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Paste row after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b92\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Insert column before": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0bae\u0bc1\u0ba9\u0bcd \u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Insert column after": "\u0b87\u0ba4\u0bb1\u0bcd\u0b95\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd \u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Delete column": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0ba8\u0bc0\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Cols": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8\u0b95\u0bb3\u0bcd", +"Rows": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8\u0b95\u0bb3\u0bcd", +"Width": "\u0b85\u0b95\u0bb2\u0bae\u0bcd", +"Height": "\u0b89\u0baf\u0bb0\u0bae\u0bcd", +"Cell spacing": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0b87\u0b9f\u0bc8\u0bb5\u0bc6\u0bb3\u0bbf", +"Cell padding": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0ba8\u0bbf\u0bb0\u0baa\u0bcd\u0baa\u0bb2\u0bcd", +"Caption": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Left": "\u0b87\u0b9f\u0bae\u0bcd", +"Center": "\u0bae\u0bc8\u0baf\u0bae\u0bcd", +"Right": "\u0bb5\u0bb2\u0bae\u0bcd", +"Cell type": "\u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8 \u0bb5\u0b95\u0bc8", +"Scope": "\u0bb5\u0bb0\u0bc8\u0baf\u0bc6\u0bb2\u0bcd\u0bb2\u0bc8", +"Alignment": "\u0b9a\u0bc0\u0bb0\u0bae\u0bc8\u0bb5\u0bc1", +"H Align": "\u0b95\u0bbf (H) \u0b87\u0b9a\u0bc8\u0bb5\u0bc1", +"V Align": "\u0b9a\u0bc6 (V) \u0b87\u0b9a\u0bc8\u0bb5\u0bc1", +"Top": "\u0bae\u0bc7\u0bb2\u0bcd", +"Middle": "\u0ba8\u0b9f\u0bc1", +"Bottom": "\u0bae\u0bc7\u0bb2\u0bcd", +"Header cell": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0b9a\u0bbf\u0bb1\u0bcd\u0bb1\u0bb1\u0bc8", +"Row group": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b95\u0bc1\u0bb4\u0bc1", +"Column group": "\u0ba8\u0bc6\u0b9f\u0bc1\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0b95\u0bc1\u0bb4\u0bc1", +"Row type": "\u0bb5\u0bb0\u0bbf\u0b9a\u0bc8 \u0bb5\u0b95\u0bc8", +"Header": "\u0ba4\u0bb2\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Body": "\u0b89\u0b9f\u0bb2\u0bcd", +"Footer": "\u0b85\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bc1", +"Border color": "\u0b95\u0bb0\u0bc8\u0baf\u0bbf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Insert template": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1 \u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"Templates": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bcd", +"Template": "\u0bb5\u0bbe\u0bb0\u0bcd\u0baa\u0bcd\u0baa\u0bc1\u0bb0\u0bc1", +"Text color": "\u0b89\u0bb0\u0bc8\u0baf\u0bbf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Background color": "\u0baa\u0bbf\u0ba9\u0bcd\u0ba9\u0ba3\u0bbf \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"Custom...": "\u0ba4\u0ba9\u0bbf\u0baa\u0bcd\u0baa\u0baf\u0ba9\u0bcd...", +"Custom color": "\u0ba4\u0ba9\u0bbf\u0baa\u0bcd\u0baa\u0baf\u0ba9\u0bcd \u0ba8\u0bbf\u0bb1\u0bae\u0bcd", +"No color": "\u0ba8\u0bbf\u0bb1\u0bae\u0bcd \u0b87\u0bb2\u0bcd\u0bb2\u0bc8", +"Table of Contents": "\u0baa\u0bca\u0bb0\u0bc1\u0bb3\u0b9f\u0b95\u0bcd\u0b95\u0bae\u0bcd", +"Show blocks": "\u0ba4\u0bca\u0b95\u0bc1\u0ba4\u0bbf\u0b95\u0bb3\u0bc8 \u0b95\u0bbe\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Show invisible characters": "\u0b95\u0ba3\u0bcd\u0ba3\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0ba4\u0bcd \u0ba4\u0bc6\u0bb0\u0bbf\u0baf\u0bbe\u0ba4 \u0b89\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bb3\u0bc8 \u0b95\u0bbe\u0b9f\u0bcd\u0b9f\u0bc1\u0b95", +"Words: {0}": "\u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd: {0}", +"{0} words": "{0} \u0b9a\u0bca\u0bb1\u0bcd\u0b95\u0bb3\u0bcd", +"File": "\u0b95\u0bcb\u0baa\u0bcd\u0baa\u0bc1", +"Edit": "\u0ba4\u0bca\u0b95\u0bc1\u0b95\u0bcd\u0b95", +"Insert": "\u0b9a\u0bca\u0bb0\u0bc1\u0b95\u0bc1\u0b95", +"View": "\u0ba8\u0bcb\u0b95\u0bcd\u0b95\u0bc1\u0b95", +"Format": "\u0bb5\u0b9f\u0bbf\u0bb5\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1", +"Table": "\u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8", +"Tools": "\u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0b95\u0bb3\u0bcd", +"Powered by {0}": "\u0bb5\u0bb2\u0bc1\u0bb5\u0bb3\u0bbf\u0baa\u0bcd\u0baa\u0ba4\u0bc1 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0b89\u0baf\u0bb0\u0bcd \u0b89\u0bb0\u0bc8 \u0baa\u0b95\u0bc1\u0ba4\u0bbf. \u0baa\u0b9f\u0bcd\u0b9f\u0bbf\u0b95\u0bcd\u0b95\u0bc1 ALT-F9 , \u0b95\u0bb0\u0bc1\u0bb5\u0bbf\u0baa\u0bcd\u0baa\u0b9f\u0bcd\u0b9f\u0bc8\u0b95\u0bcd\u0b95\u0bc1 ALT-F10 , \u0b89\u0ba4\u0bb5\u0bbf\u0b95\u0bcd\u0b95\u0bc1 ALT-0" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/th_TH.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/th_TH.js new file mode 100644 index 0000000000..9e42a9243a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/th_TH.js @@ -0,0 +1,261 @@ +tinymce.addI18n('th_TH',{ +"Redo": "\u0e17\u0e4d\u0e32\u0e0b\u0e49\u0e33", +"Undo": "\u0e40\u0e25\u0e34\u0e01\u0e17\u0e33", +"Cut": "\u0e15\u0e31\u0e14", +"Copy": "\u0e04\u0e31\u0e14\u0e25\u0e2d\u0e01", +"Paste": "\u0e27\u0e32\u0e07", +"Select all": "\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14", +"New document": "\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23\u0e43\u0e2b\u0e21\u0e48", +"Ok": "\u0e15\u0e01\u0e25\u0e07", +"Cancel": "\u0e22\u0e01\u0e40\u0e25\u0e34\u0e01", +"Visual aids": "\u0e17\u0e31\u0e28\u0e19\u0e39\u0e1b\u0e01\u0e23\u0e13\u0e4c", +"Bold": "\u0e15\u0e31\u0e27\u0e2b\u0e19\u0e32", +"Italic": "\u0e15\u0e31\u0e27\u0e40\u0e2d\u0e35\u0e22\u0e07", +"Underline": "\u0e02\u0e35\u0e14\u0e40\u0e2a\u0e49\u0e19\u0e43\u0e15\u0e49", +"Strikethrough": "\u0e02\u0e35\u0e14\u0e17\u0e31\u0e1a", +"Superscript": "\u0e15\u0e31\u0e27\u0e22\u0e01", +"Subscript": "\u0e15\u0e31\u0e27\u0e2b\u0e49\u0e2d\u0e22", +"Clear formatting": "\u0e25\u0e49\u0e32\u0e07\u0e01\u0e32\u0e23\u0e08\u0e31\u0e14\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a", +"Align left": "\u0e08\u0e31\u0e14\u0e0a\u0e34\u0e14\u0e0b\u0e49\u0e32\u0e22", +"Align center": "\u0e08\u0e31\u0e14\u0e01\u0e36\u0e48\u0e07\u0e01\u0e25\u0e32\u0e07", +"Align right": "\u0e08\u0e31\u0e14\u0e0a\u0e34\u0e14\u0e02\u0e27\u0e32", +"Justify": "\u0e40\u0e15\u0e47\u0e21\u0e41\u0e19\u0e27", +"Bullet list": "\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e2a\u0e31\u0e0d\u0e25\u0e31\u0e01\u0e29\u0e13\u0e4c\u0e2b\u0e31\u0e27\u0e02\u0e49\u0e2d\u0e22\u0e48\u0e2d\u0e22", +"Numbered list": "\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e25\u0e33\u0e14\u0e31\u0e1a\u0e40\u0e25\u0e02", +"Decrease indent": "\u0e25\u0e14\u0e01\u0e32\u0e23\u0e40\u0e22\u0e37\u0e49\u0e2d\u0e07", +"Increase indent": "\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e01\u0e32\u0e23\u0e40\u0e22\u0e37\u0e49\u0e2d\u0e07", +"Close": "\u0e1b\u0e34\u0e14", +"Formats": "\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0e40\u0e1a\u0e23\u0e32\u0e27\u0e4c\u0e40\u0e0b\u0e2d\u0e23\u0e4c\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e44\u0e21\u0e48\u0e2a\u0e19\u0e31\u0e1a\u0e2a\u0e19\u0e38\u0e19\u0e01\u0e32\u0e23\u0e40\u0e02\u0e49\u0e32\u0e16\u0e36\u0e07\u0e42\u0e14\u0e22\u0e15\u0e23\u0e07\u0e44\u0e1b\u0e22\u0e31\u0e07\u0e04\u0e25\u0e34\u0e1b\u0e1a\u0e2d\u0e23\u0e4c\u0e14 \u0e01\u0e23\u0e38\u0e13\u0e32\u0e43\u0e0a\u0e49\u0e41\u0e1b\u0e49\u0e19\u0e1e\u0e34\u0e21\u0e1e\u0e4c\u0e25\u0e31\u0e14 Ctrl+X\/C\/V \u0e41\u0e17\u0e19", +"Headers": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27", +"Header 1": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 1", +"Header 2": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 2", +"Header 3": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 3", +"Header 4": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 4", +"Header 5": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 5", +"Header 6": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27 6", +"Headings": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07", +"Heading 1": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 1", +"Heading 2": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 2", +"Heading 3": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 3", +"Heading 4": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 4", +"Heading 5": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 5", +"Heading 6": "\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 6", +"Preformatted": "\u0e08\u0e31\u0e14\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a", +"Div": "Div", +"Pre": "\u0e01\u0e48\u0e2d\u0e19", +"Code": "\u0e42\u0e04\u0e49\u0e14", +"Paragraph": "\u0e22\u0e48\u0e2d\u0e2b\u0e19\u0e49\u0e32", +"Blockquote": "\u0e22\u0e01\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e17\u0e31\u0e49\u0e07\u0e22\u0e48\u0e2d\u0e2b\u0e19\u0e49\u0e32", +"Inline": "\u0e41\u0e1a\u0e1a\u0e2d\u0e34\u0e19\u0e44\u0e25\u0e19\u0e4c", +"Blocks": "\u0e1a\u0e25\u0e47\u0e2d\u0e01", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0e01\u0e32\u0e23\u0e27\u0e32\u0e07\u0e15\u0e2d\u0e19\u0e19\u0e35\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e42\u0e2b\u0e21\u0e14\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e18\u0e23\u0e23\u0e21\u0e14\u0e32 \u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e08\u0e30\u0e16\u0e39\u0e01\u0e27\u0e32\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e18\u0e23\u0e23\u0e21\u0e14\u0e32\u0e08\u0e19\u0e01\u0e27\u0e48\u0e32\u0e04\u0e38\u0e13\u0e08\u0e30\u0e1b\u0e34\u0e14\u0e15\u0e31\u0e27\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e19\u0e35\u0e49", +"Font Family": "\u0e15\u0e23\u0e30\u0e01\u0e39\u0e25\u0e41\u0e1a\u0e1a\u0e2d\u0e31\u0e01\u0e29\u0e23", +"Font Sizes": "\u0e02\u0e19\u0e32\u0e14\u0e41\u0e1a\u0e1a\u0e2d\u0e31\u0e01\u0e29\u0e23", +"Class": "\u0e04\u0e25\u0e32\u0e2a", +"Browse for an image": "\u0e40\u0e23\u0e35\u0e22\u0e01\u0e14\u0e39\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e", +"OR": "\u0e2b\u0e23\u0e37\u0e2d", +"Drop an image here": "\u0e27\u0e32\u0e07\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e\u0e17\u0e35\u0e48\u0e19\u0e35\u0e48", +"Upload": "\u0e2d\u0e31\u0e1b\u0e42\u0e2b\u0e25\u0e14", +"Block": "\u0e1a\u0e25\u0e47\u0e2d\u0e01", +"Align": "\u0e01\u0e32\u0e23\u0e08\u0e31\u0e14\u0e41\u0e19\u0e27", +"Default": "\u0e04\u0e48\u0e32\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19", +"Circle": "\u0e27\u0e07\u0e01\u0e25\u0e21", +"Disc": "\u0e14\u0e34\u0e2a\u0e01\u0e4c", +"Square": "\u0e08\u0e31\u0e15\u0e38\u0e23\u0e31\u0e2a", +"Lower Alpha": "\u0e2d\u0e31\u0e25\u0e1f\u0e32\u0e17\u0e35\u0e48\u0e15\u0e48\u0e33\u0e01\u0e27\u0e48\u0e32", +"Lower Greek": "\u0e01\u0e23\u0e35\u0e01\u0e17\u0e35\u0e48\u0e15\u0e48\u0e33\u0e01\u0e27\u0e48\u0e32", +"Lower Roman": "\u0e42\u0e23\u0e21\u0e31\u0e19\u0e17\u0e35\u0e48\u0e15\u0e48\u0e33\u0e01\u0e27\u0e48\u0e32", +"Upper Alpha": "\u0e2d\u0e31\u0e25\u0e1f\u0e32\u0e17\u0e35\u0e48\u0e2a\u0e39\u0e07\u0e01\u0e27\u0e48\u0e32", +"Upper Roman": "\u0e42\u0e23\u0e21\u0e31\u0e19\u0e17\u0e35\u0e48\u0e2a\u0e39\u0e07\u0e01\u0e27\u0e48\u0e32", +"Anchor": "\u0e08\u0e38\u0e14\u0e22\u0e36\u0e14", +"Name": "\u0e0a\u0e37\u0e48\u0e2d", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id \u0e04\u0e27\u0e23\u0e08\u0e30\u0e02\u0e36\u0e49\u0e19\u0e15\u0e49\u0e19\u0e14\u0e49\u0e27\u0e22\u0e15\u0e31\u0e27\u0e2d\u0e31\u0e01\u0e29\u0e23 \u0e15\u0e32\u0e21\u0e14\u0e49\u0e27\u0e22\u0e15\u0e31\u0e27\u0e2d\u0e31\u0e01\u0e29\u0e23 \u0e15\u0e31\u0e27\u0e40\u0e25\u0e02 \u0e02\u0e35\u0e14\u0e01\u0e25\u0e32\u0e07 \u0e08\u0e38\u0e14 \u0e2d\u0e31\u0e12\u0e20\u0e32\u0e04 \u0e2b\u0e23\u0e37\u0e2d \u0e02\u0e35\u0e14\u0e25\u0e48\u0e32\u0e07", +"You have unsaved changes are you sure you want to navigate away?": "\u0e04\u0e38\u0e13\u0e21\u0e35\u0e01\u0e32\u0e23\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19\u0e41\u0e1b\u0e25\u0e07\u0e17\u0e35\u0e48\u0e44\u0e21\u0e48\u0e44\u0e14\u0e49\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01 \u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e17\u0e35\u0e48\u0e08\u0e30\u0e2d\u0e2d\u0e01\u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48?", +"Restore last draft": "\u0e04\u0e37\u0e19\u0e04\u0e48\u0e32\u0e41\u0e1a\u0e1a\u0e23\u0e48\u0e32\u0e07\u0e25\u0e48\u0e32\u0e2a\u0e38\u0e14", +"Special character": "\u0e2d\u0e31\u0e01\u0e02\u0e23\u0e30\u0e1e\u0e34\u0e40\u0e28\u0e29", +"Source code": "\u0e42\u0e04\u0e49\u0e14\u0e15\u0e49\u0e19\u0e09\u0e1a\u0e31\u0e1a", +"Insert\/Edit code sample": "\u0e41\u0e17\u0e23\u0e01\/\u0e41\u0e01\u0e49\u0e44\u0e02\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e42\u0e04\u0e49\u0e14", +"Language": "\u0e20\u0e32\u0e29\u0e32", +"Code sample": "\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e42\u0e04\u0e49\u0e14", +"Color": "\u0e2a\u0e35", +"R": "\u0e41\u0e14\u0e07", +"G": "\u0e40\u0e02\u0e35\u0e22\u0e27", +"B": "\u0e19\u0e49\u0e33\u0e40\u0e07\u0e34\u0e19", +"Left to right": "\u0e0b\u0e49\u0e32\u0e22\u0e44\u0e1b\u0e02\u0e27\u0e32", +"Right to left": "\u0e02\u0e27\u0e32\u0e44\u0e1b\u0e0b\u0e49\u0e32\u0e22", +"Emoticons": "\u0e2d\u0e34\u0e42\u0e21\u0e15\u0e34\u0e04\u0e2d\u0e19", +"Document properties": "\u0e04\u0e38\u0e13\u0e2a\u0e21\u0e1a\u0e31\u0e15\u0e34\u0e02\u0e2d\u0e07\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23", +"Title": "\u0e0a\u0e37\u0e48\u0e2d\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07", +"Keywords": "\u0e04\u0e33\u0e2a\u0e33\u0e04\u0e31\u0e0d", +"Description": "\u0e04\u0e33\u0e2d\u0e18\u0e34\u0e1a\u0e32\u0e22", +"Robots": "\u0e2b\u0e38\u0e48\u0e19\u0e22\u0e19\u0e15\u0e4c", +"Author": "\u0e1c\u0e39\u0e49\u0e40\u0e02\u0e35\u0e22\u0e19", +"Encoding": "\u0e01\u0e32\u0e23\u0e40\u0e02\u0e49\u0e32\u0e23\u0e2b\u0e31\u0e2a", +"Fullscreen": "\u0e40\u0e15\u0e47\u0e21\u0e08\u0e2d", +"Action": "\u0e01\u0e32\u0e23\u0e01\u0e23\u0e30\u0e17\u0e33", +"Shortcut": "\u0e17\u0e32\u0e07\u0e25\u0e31\u0e14", +"Help": "\u0e0a\u0e48\u0e27\u0e22\u0e40\u0e2b\u0e25\u0e37\u0e2d", +"Address": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48", +"Focus to menubar": "\u0e42\u0e1f\u0e01\u0e31\u0e2a\u0e44\u0e1b\u0e17\u0e35\u0e48\u0e40\u0e21\u0e19\u0e39\u0e1a\u0e32\u0e23\u0e4c", +"Focus to toolbar": "\u0e42\u0e1f\u0e01\u0e31\u0e2a\u0e44\u0e1b\u0e17\u0e35\u0e48\u0e41\u0e16\u0e1a\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d", +"Focus to element path": "\u0e42\u0e1f\u0e01\u0e31\u0e2a\u0e44\u0e1b\u0e17\u0e35\u0e48\u0e40\u0e2a\u0e49\u0e19\u0e17\u0e32\u0e07\u0e02\u0e2d\u0e07\u0e2d\u0e07\u0e04\u0e4c\u0e1b\u0e23\u0e30\u0e01\u0e2d\u0e1a", +"Focus to contextual toolbar": "\u0e42\u0e1f\u0e01\u0e31\u0e2a\u0e44\u0e1b\u0e17\u0e35\u0e48\u0e41\u0e16\u0e1a\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d\u0e15\u0e32\u0e21\u0e1a\u0e23\u0e34\u0e1a\u0e17", +"Insert link (if link plugin activated)": "\u0e41\u0e17\u0e23\u0e01\u0e25\u0e34\u0e07\u0e01\u0e4c (\u0e2b\u0e32\u0e01\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19\u0e25\u0e34\u0e07\u0e01\u0e4c)", +"Save (if save plugin activated)": "\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01 (\u0e2b\u0e32\u0e01\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01)", +"Find (if searchreplace plugin activated)": "\u0e04\u0e49\u0e19\u0e2b\u0e32 (\u0e2b\u0e32\u0e01\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19 searchreplace)", +"Plugins installed ({0}):": "\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19\u0e17\u0e35\u0e48\u0e15\u0e34\u0e14\u0e15\u0e31\u0e49\u0e07\u0e41\u0e25\u0e49\u0e27 ({0}):", +"Premium plugins:": "\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19\u0e1e\u0e23\u0e35\u0e40\u0e21\u0e35\u0e22\u0e21:", +"Learn more...": "\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e39\u0e49\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e40\u0e15\u0e34\u0e21...", +"You are using {0}": "\u0e04\u0e38\u0e13\u0e01\u0e33\u0e25\u0e31\u0e07\u0e43\u0e0a\u0e49 {0}", +"Plugins": "\u0e1b\u0e25\u0e31\u0e4a\u0e01\u0e2d\u0e34\u0e19", +"Handy Shortcuts": "\u0e17\u0e32\u0e07\u0e25\u0e31\u0e14\u0e14\u0e49\u0e27\u0e22\u0e21\u0e37\u0e2d", +"Horizontal line": "\u0e40\u0e2a\u0e49\u0e19\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19", +"Insert\/edit image": "\u0e41\u0e17\u0e23\u0e01\/\u0e41\u0e01\u0e49\u0e44\u0e02\u0e23\u0e39\u0e1b", +"Image description": "\u0e04\u0e33\u0e2d\u0e18\u0e34\u0e1a\u0e32\u0e22\u0e23\u0e39\u0e1b", +"Source": "\u0e41\u0e2b\u0e25\u0e48\u0e07\u0e17\u0e35\u0e48\u0e21\u0e32", +"Dimensions": "\u0e02\u0e19\u0e32\u0e14", +"Constrain proportions": "\u0e08\u0e33\u0e01\u0e31\u0e14\u0e2a\u0e31\u0e14\u0e2a\u0e48\u0e27\u0e19", +"General": "\u0e17\u0e31\u0e48\u0e27\u0e44\u0e1b", +"Advanced": "\u0e02\u0e31\u0e49\u0e19\u0e2a\u0e39\u0e07", +"Style": "\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a", +"Vertical space": "\u0e0a\u0e48\u0e2d\u0e07\u0e27\u0e48\u0e32\u0e07\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07", +"Horizontal space": "\u0e0a\u0e48\u0e2d\u0e07\u0e27\u0e48\u0e32\u0e07\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19", +"Border": "\u0e40\u0e2a\u0e49\u0e19\u0e02\u0e2d\u0e1a", +"Insert image": "\u0e41\u0e17\u0e23\u0e01\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e", +"Image": "\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e", +"Image list": "\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e", +"Rotate counterclockwise": "\u0e2b\u0e21\u0e38\u0e19\u0e17\u0e27\u0e19\u0e40\u0e02\u0e47\u0e21\u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32", +"Rotate clockwise": "\u0e2b\u0e21\u0e38\u0e19\u0e15\u0e32\u0e21\u0e40\u0e02\u0e47\u0e21\u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32", +"Flip vertically": "\u0e1e\u0e25\u0e34\u0e01\u0e15\u0e32\u0e21\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07", +"Flip horizontally": "\u0e1e\u0e25\u0e34\u0e01\u0e15\u0e32\u0e21\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19", +"Edit image": "\u0e41\u0e01\u0e49\u0e44\u0e02\u0e23\u0e39\u0e1b", +"Image options": "\u0e15\u0e31\u0e27\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e", +"Zoom in": "\u0e02\u0e22\u0e32\u0e22\u0e40\u0e02\u0e49\u0e32", +"Zoom out": "\u0e22\u0e48\u0e2d\u0e2d\u0e2d\u0e01", +"Crop": "\u0e04\u0e23\u0e2d\u0e1b\u0e15\u0e31\u0e14", +"Resize": "\u0e1b\u0e23\u0e31\u0e1a\u0e02\u0e19\u0e32\u0e14", +"Orientation": "\u0e01\u0e32\u0e23\u0e08\u0e31\u0e14\u0e27\u0e32\u0e07", +"Brightness": "\u0e04\u0e27\u0e32\u0e21\u0e2a\u0e27\u0e48\u0e32\u0e07", +"Sharpen": "\u0e04\u0e27\u0e32\u0e21\u0e04\u0e21", +"Contrast": "\u0e04\u0e27\u0e32\u0e21\u0e40\u0e1b\u0e23\u0e35\u0e22\u0e1a\u0e15\u0e48\u0e32\u0e07", +"Color levels": "\u0e23\u0e30\u0e14\u0e31\u0e1a\u0e2a\u0e35", +"Gamma": "\u0e41\u0e01\u0e21\u0e21\u0e32", +"Invert": "\u0e22\u0e49\u0e2d\u0e19\u0e01\u0e25\u0e31\u0e1a", +"Apply": "\u0e19\u0e33\u0e44\u0e1b\u0e43\u0e0a\u0e49", +"Back": "\u0e01\u0e25\u0e31\u0e1a", +"Insert date\/time": "\u0e41\u0e17\u0e23\u0e01\u0e27\u0e31\u0e19\u0e17\u0e35\u0e48\/\u0e40\u0e27\u0e25\u0e32", +"Date\/time": "\u0e27\u0e31\u0e19\u0e17\u0e35\u0e48\/\u0e40\u0e27\u0e25\u0e32", +"Insert link": "\u0e41\u0e17\u0e23\u0e01\u0e25\u0e34\u0e07\u0e01\u0e4c", +"Insert\/edit link": "\u0e41\u0e17\u0e23\u0e01\/\u0e41\u0e01\u0e49\u0e44\u0e02\u0e25\u0e34\u0e07\u0e01\u0e4c", +"Text to display": "\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e17\u0e35\u0e48\u0e08\u0e30\u0e41\u0e2a\u0e14\u0e07", +"Url": "URL", +"Target": "\u0e40\u0e1b\u0e49\u0e32\u0e2b\u0e21\u0e32\u0e22", +"None": "\u0e44\u0e21\u0e48\u0e21\u0e35", +"New window": "\u0e40\u0e1b\u0e34\u0e14\u0e2b\u0e19\u0e49\u0e32\u0e15\u0e48\u0e32\u0e07\u0e43\u0e2b\u0e21\u0e48", +"Remove link": "\u0e40\u0e2d\u0e32\u0e25\u0e34\u0e07\u0e01\u0e4c\u0e2d\u0e2d\u0e01", +"Anchors": "\u0e08\u0e38\u0e14\u0e22\u0e36\u0e14", +"Link": "\u0e25\u0e34\u0e07\u0e01\u0e4c", +"Paste or type a link": "\u0e27\u0e32\u0e07\u0e2b\u0e23\u0e37\u0e2d\u0e1b\u0e49\u0e2d\u0e19\u0e25\u0e34\u0e07\u0e01\u0e4c", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL \u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e23\u0e30\u0e1a\u0e38\u0e14\u0e39\u0e40\u0e2b\u0e21\u0e37\u0e2d\u0e19\u0e27\u0e48\u0e32\u0e40\u0e1b\u0e47\u0e19\u0e2d\u0e35\u0e40\u0e21\u0e25\u0e41\u0e2d\u0e14\u0e40\u0e14\u0e23\u0e2a \u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e43\u0e2a\u0e48 mailto: \u0e19\u0e33\u0e2b\u0e19\u0e49\u0e32\u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL \u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e23\u0e30\u0e1a\u0e38\u0e14\u0e39\u0e40\u0e2b\u0e21\u0e37\u0e2d\u0e19\u0e27\u0e48\u0e32\u0e40\u0e1b\u0e47\u0e19\u0e25\u0e34\u0e07\u0e01\u0e4c\u0e20\u0e32\u0e22\u0e19\u0e2d\u0e01 \u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e43\u0e2a\u0e48 http:\/\/ \u0e19\u0e33\u0e2b\u0e19\u0e49\u0e32\u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48", +"Link list": "\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e25\u0e34\u0e07\u0e01\u0e4c", +"Insert video": "\u0e41\u0e17\u0e23\u0e01\u0e27\u0e34\u0e14\u0e35\u0e42\u0e2d", +"Insert\/edit video": "\u0e41\u0e17\u0e23\u0e01\/\u0e41\u0e01\u0e49\u0e44\u0e02\u0e27\u0e34\u0e14\u0e35\u0e42\u0e2d", +"Insert\/edit media": "\u0e41\u0e17\u0e23\u0e01\/\u0e41\u0e01\u0e49\u0e44\u0e02\u0e2a\u0e37\u0e48\u0e2d", +"Alternative source": "\u0e41\u0e2b\u0e25\u0e48\u0e07\u0e17\u0e35\u0e48\u0e21\u0e32\u0e2a\u0e33\u0e23\u0e2d\u0e07", +"Poster": "\u0e42\u0e1b\u0e2a\u0e40\u0e15\u0e2d\u0e23\u0e4c", +"Paste your embed code below:": "\u0e27\u0e32\u0e07\u0e42\u0e04\u0e49\u0e14\u0e1d\u0e31\u0e07\u0e15\u0e31\u0e27\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07:", +"Embed": "\u0e1d\u0e31\u0e07", +"Media": "\u0e2a\u0e37\u0e48\u0e2d", +"Nonbreaking space": "\u0e0a\u0e48\u0e2d\u0e07\u0e27\u0e48\u0e32\u0e07\u0e44\u0e21\u0e48\u0e41\u0e22\u0e01", +"Page break": "\u0e15\u0e31\u0e27\u0e41\u0e1a\u0e48\u0e07\u0e2b\u0e19\u0e49\u0e32", +"Paste as text": "\u0e27\u0e32\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21", +"Preview": "\u0e41\u0e2a\u0e14\u0e07\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07", +"Print": "\u0e1e\u0e34\u0e21\u0e1e\u0e4c", +"Save": "\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01", +"Find": "\u0e04\u0e49\u0e19\u0e2b\u0e32", +"Replace with": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48\u0e14\u0e49\u0e27\u0e22", +"Replace": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48", +"Replace all": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14", +"Prev": "\u0e01\u0e48\u0e2d\u0e19\u0e2b\u0e19\u0e49\u0e32", +"Next": "\u0e16\u0e31\u0e14\u0e44\u0e1b", +"Find and replace": "\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e41\u0e25\u0e30\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48", +"Could not find the specified string.": "\u0e44\u0e21\u0e48\u0e1e\u0e1a\u0e2a\u0e15\u0e23\u0e34\u0e07\u0e17\u0e35\u0e48\u0e23\u0e30\u0e1a\u0e38", +"Match case": "\u0e15\u0e23\u0e07\u0e15\u0e32\u0e21\u0e15\u0e31\u0e27\u0e1e\u0e34\u0e21\u0e1e\u0e4c\u0e43\u0e2b\u0e0d\u0e48-\u0e40\u0e25\u0e47\u0e01", +"Whole words": "\u0e17\u0e31\u0e49\u0e07\u0e04\u0e33", +"Spellcheck": "\u0e15\u0e23\u0e27\u0e08\u0e01\u0e32\u0e23\u0e2a\u0e30\u0e01\u0e14", +"Ignore": "\u0e25\u0e30\u0e40\u0e27\u0e49\u0e19", +"Ignore all": "\u0e25\u0e30\u0e40\u0e27\u0e49\u0e19\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14", +"Finish": "\u0e40\u0e2a\u0e23\u0e47\u0e08\u0e2a\u0e34\u0e49\u0e19", +"Add to Dictionary": "\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e43\u0e19\u0e1e\u0e08\u0e19\u0e32\u0e19\u0e38\u0e01\u0e23\u0e21", +"Insert table": "\u0e41\u0e17\u0e23\u0e01\u0e15\u0e32\u0e23\u0e32\u0e07", +"Table properties": "\u0e04\u0e38\u0e13\u0e2a\u0e21\u0e1a\u0e31\u0e15\u0e34\u0e02\u0e2d\u0e07\u0e15\u0e32\u0e23\u0e32\u0e07", +"Delete table": "\u0e25\u0e1a\u0e15\u0e32\u0e23\u0e32\u0e07", +"Cell": "\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Row": "\u0e41\u0e16\u0e27", +"Column": "\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c", +"Cell properties": "\u0e04\u0e38\u0e13\u0e2a\u0e21\u0e1a\u0e31\u0e15\u0e34\u0e02\u0e2d\u0e07\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Merge cells": "\u0e1c\u0e2a\u0e32\u0e19\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Split cell": "\u0e41\u0e22\u0e01\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Insert row before": "\u0e41\u0e17\u0e23\u0e01\u0e41\u0e16\u0e27\u0e14\u0e49\u0e32\u0e19\u0e1a\u0e19", +"Insert row after": "\u0e41\u0e17\u0e23\u0e01\u0e41\u0e16\u0e27\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07", +"Delete row": "\u0e25\u0e1a\u0e41\u0e16\u0e27", +"Row properties": "\u0e04\u0e38\u0e13\u0e2a\u0e21\u0e1a\u0e31\u0e15\u0e34\u0e02\u0e2d\u0e07\u0e41\u0e16\u0e27", +"Cut row": "\u0e15\u0e31\u0e14\u0e41\u0e16\u0e27", +"Copy row": "\u0e04\u0e31\u0e14\u0e25\u0e2d\u0e01\u0e41\u0e16\u0e27", +"Paste row before": "\u0e27\u0e32\u0e07\u0e41\u0e16\u0e27\u0e14\u0e49\u0e32\u0e19\u0e1a\u0e19", +"Paste row after": "\u0e27\u0e32\u0e07\u0e41\u0e16\u0e27\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07", +"Insert column before": "\u0e41\u0e17\u0e23\u0e01\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e02\u0e49\u0e32\u0e07\u0e2b\u0e19\u0e49\u0e32", +"Insert column after": "\u0e41\u0e17\u0e23\u0e01\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e02\u0e49\u0e32\u0e07\u0e2b\u0e25\u0e31\u0e07", +"Delete column": "\u0e25\u0e1a\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c", +"Cols": "\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c", +"Rows": "\u0e41\u0e16\u0e27", +"Width": "\u0e04\u0e27\u0e32\u0e21\u0e01\u0e27\u0e49\u0e32\u0e07", +"Height": "\u0e04\u0e27\u0e32\u0e21\u0e2a\u0e39\u0e07", +"Cell spacing": "\u0e0a\u0e48\u0e2d\u0e07\u0e27\u0e48\u0e32\u0e07\u0e23\u0e30\u0e2b\u0e27\u0e48\u0e32\u0e07\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Cell padding": "\u0e0a\u0e48\u0e2d\u0e07\u0e27\u0e48\u0e32\u0e07\u0e20\u0e32\u0e22\u0e43\u0e19\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Caption": "\u0e1b\u0e49\u0e32\u0e22\u0e04\u0e33\u0e2d\u0e18\u0e34\u0e1a\u0e32\u0e22", +"Left": "\u0e0b\u0e49\u0e32\u0e22", +"Center": "\u0e01\u0e36\u0e48\u0e07\u0e01\u0e25\u0e32\u0e07", +"Right": "\u0e02\u0e27\u0e32", +"Cell type": "\u0e0a\u0e19\u0e34\u0e14\u0e02\u0e2d\u0e07\u0e40\u0e0b\u0e25\u0e25\u0e4c", +"Scope": "\u0e02\u0e2d\u0e1a\u0e40\u0e02\u0e15", +"Alignment": "\u0e01\u0e32\u0e23\u0e08\u0e31\u0e14\u0e41\u0e19\u0e27", +"H Align": "\u0e01\u0e32\u0e23\u0e40\u0e23\u0e35\u0e22\u0e07\u0e43\u0e19\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19", +"V Align": "\u0e01\u0e32\u0e23\u0e40\u0e23\u0e35\u0e22\u0e07\u0e43\u0e19\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07", +"Top": "\u0e1a\u0e19", +"Middle": "\u0e01\u0e25\u0e32\u0e07", +"Bottom": "\u0e25\u0e48\u0e32\u0e07", +"Header cell": "\u0e40\u0e0b\u0e25\u0e25\u0e4c\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27", +"Row group": "\u0e01\u0e25\u0e38\u0e48\u0e21\u0e41\u0e16\u0e27", +"Column group": "\u0e01\u0e25\u0e38\u0e48\u0e21\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c", +"Row type": "\u0e0a\u0e19\u0e34\u0e14\u0e02\u0e2d\u0e07\u0e41\u0e16\u0e27", +"Header": "\u0e2a\u0e48\u0e27\u0e19\u0e2b\u0e31\u0e27", +"Body": "\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21", +"Footer": "\u0e2a\u0e48\u0e27\u0e19\u0e17\u0e49\u0e32\u0e22", +"Border color": "\u0e2a\u0e35\u0e02\u0e2d\u0e1a", +"Insert template": "\u0e41\u0e17\u0e23\u0e01\u0e41\u0e21\u0e48\u0e41\u0e1a\u0e1a", +"Templates": "\u0e41\u0e21\u0e48\u0e41\u0e1a\u0e1a", +"Template": "\u0e41\u0e21\u0e48\u0e41\u0e1a\u0e1a", +"Text color": "\u0e2a\u0e35\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21", +"Background color": "\u0e2a\u0e35\u0e1e\u0e37\u0e49\u0e19\u0e2b\u0e25\u0e31\u0e07", +"Custom...": "\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e40\u0e2d\u0e07", +"Custom color": "\u0e2a\u0e35\u0e17\u0e35\u0e48\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e40\u0e2d\u0e07", +"No color": "\u0e44\u0e21\u0e48\u0e21\u0e35\u0e2a\u0e35", +"Table of Contents": "\u0e2a\u0e32\u0e23\u0e1a\u0e31\u0e0d", +"Show blocks": "\u0e41\u0e2a\u0e14\u0e07\u0e1a\u0e25\u0e47\u0e2d\u0e01", +"Show invisible characters": "\u0e41\u0e2a\u0e14\u0e07\u0e15\u0e31\u0e27\u0e2d\u0e31\u0e01\u0e29\u0e23\u0e17\u0e35\u0e48\u0e21\u0e2d\u0e07\u0e44\u0e21\u0e48\u0e40\u0e2b\u0e47\u0e19", +"Words: {0}": "\u0e04\u0e33: {0}", +"{0} words": "{0} \u0e04\u0e33", +"File": "\u0e44\u0e1f\u0e25\u0e4c", +"Edit": "\u0e41\u0e01\u0e49\u0e44\u0e02", +"Insert": "\u0e41\u0e17\u0e23\u0e01", +"View": "\u0e21\u0e38\u0e21\u0e21\u0e2d\u0e07", +"Format": "\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a", +"Table": "\u0e15\u0e32\u0e23\u0e32\u0e07", +"Tools": "\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d", +"Powered by {0}": "\u0e02\u0e31\u0e1a\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e42\u0e14\u0e22 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48 Rich Text \u0e01\u0e14 ALT-F9 \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e21\u0e19\u0e39 \u0e01\u0e14 ALT-F10 \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e41\u0e16\u0e1a\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d \u0e01\u0e14 ALT-0 \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e04\u0e27\u0e32\u0e21\u0e0a\u0e48\u0e27\u0e22\u0e40\u0e2b\u0e25\u0e37\u0e2d" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js new file mode 100644 index 0000000000..7b69596402 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js @@ -0,0 +1,261 @@ +tinymce.addI18n('tr',{ +"Redo": "Yinele", +"Undo": "Geri al", +"Cut": "Kes", +"Copy": "Kopyala", +"Paste": "Yap\u0131\u015ft\u0131r", +"Select all": "T\u00fcm\u00fcn\u00fc se\u00e7", +"New document": "Yeni dok\u00fcman", +"Ok": "Tamam", +"Cancel": "\u0130ptal", +"Visual aids": "G\u00f6rsel ara\u00e7lar", +"Bold": "Kal\u0131n", +"Italic": "\u0130talik", +"Underline": "Alt\u0131 \u00e7izili", +"Strikethrough": "\u00dcst\u00fc \u00e7izili", +"Superscript": "\u00dcst simge", +"Subscript": "Alt simge", +"Clear formatting": "Bi\u00e7imi temizle", +"Align left": "Sola hizala", +"Align center": "Ortala", +"Align right": "Sa\u011fa hizala", +"Justify": "\u0130ki yana yasla", +"Bullet list": "\u0130\u015faretli liste", +"Numbered list": "Numaral\u0131 liste ", +"Decrease indent": "Girintiyi azalt", +"Increase indent": "Girintiyi art\u0131r", +"Close": "Kapat", +"Formats": "Bi\u00e7imler", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Taray\u0131c\u0131n\u0131z panoya do\u011frudan eri\u015fimi desteklemiyor. L\u00fctfen Ctrl+X\\\/C\\\/V klavye k\u0131sayollar\u0131n\u0131 kullan\u0131n\u0131z.", +"Headers": "Ba\u015fl\u0131klar", +"Header 1": "Ba\u015fl\u0131k 1", +"Header 2": "Ba\u015fl\u0131k 2", +"Header 3": "Ba\u015fl\u0131k 3", +"Header 4": "Ba\u015fl\u0131k 4", +"Header 5": "Ba\u015fl\u0131k 5", +"Header 6": "Ba\u015fl\u0131k 6", +"Headings": "Ba\u015fl\u0131klar", +"Heading 1": "Ba\u015fl\u0131k 1", +"Heading 2": "Ba\u015fl\u0131k 2", +"Heading 3": "Ba\u015fl\u0131k 3", +"Heading 4": "Ba\u015fl\u0131k 4", +"Heading 5": "Ba\u015fl\u0131k 5", +"Heading 6": "Ba\u015fl\u0131k 6", +"Preformatted": "\u00d6nceden bi\u00e7imlendirilmi\u015f", +"Div": "Div", +"Pre": "Pre", +"Code": "Kod", +"Paragraph": "Paragraf", +"Blockquote": "Al\u0131nt\u0131", +"Inline": "Sat\u0131r i\u00e7i", +"Blocks": "Bloklar", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "D\u00fcz metin modunda yap\u0131\u015ft\u0131r. Bu se\u00e7ene\u011fi kapatana kadar i\u00e7erikler d\u00fcz metin olarak yap\u0131\u015ft\u0131r\u0131l\u0131r.", +"Font Family": "Yaz\u0131 Tipleri", +"Font Sizes": "Yaz\u0131 Boyutlar\u0131", +"Class": "Class", +"Browse for an image": "G\u00f6rsel se\u00e7", +"OR": "ya da", +"Drop an image here": "G\u00f6rseli buraya s\u00fcr\u00fckleyin", +"Upload": "Y\u00fckle", +"Block": "Blok", +"Align": "Hizala", +"Default": "Varsay\u0131lan", +"Circle": "Daire", +"Disc": "Disk", +"Square": "Kare", +"Lower Alpha": "K\u00fc\u00e7\u00fck Harf", +"Lower Greek": "K\u00fc\u00e7\u00fck Yunan Harfleri", +"Lower Roman": "K\u00fc\u00e7\u00fck Roman Harfleri ", +"Upper Alpha": "B\u00fcy\u00fck Harf", +"Upper Roman": "B\u00fcy\u00fck Roman Harfleri ", +"Anchor": "\u00c7apa", +"Name": "\u0130sim", +"Id": "Kimlik", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id bir harf ile ba\u015flamal\u0131d\u0131r ve harf, rakam, \u00e7izgi, nokta, iki nokta \u00fcst\u00fcste veya alt \u00e7izgi kullan\u0131labilir.", +"You have unsaved changes are you sure you want to navigate away?": "Kaydedilmemi\u015f de\u011fi\u015fiklikler var, sayfadan ayr\u0131lmak istedi\u011finize emin misiniz?", +"Restore last draft": "Son tasla\u011f\u0131 geri y\u00fckle", +"Special character": "\u00d6zel karakter", +"Source code": "Kaynak kodu", +"Insert\/Edit code sample": "\u00d6rnek kod ekle\/d\u00fczenle", +"Language": "Dil", +"Code sample": "Code sample", +"Color": "Renk", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Soldan sa\u011fa", +"Right to left": "Sa\u011fdan sola", +"Emoticons": "\u0130fadeler", +"Document properties": "Dok\u00fcman \u00f6zellikleri", +"Title": "Ba\u015fl\u0131k", +"Keywords": "Anahtar kelimeler", +"Description": "A\u00e7\u0131klama", +"Robots": "Robotlar", +"Author": "Yazar", +"Encoding": "Kodlama", +"Fullscreen": "Tam ekran", +"Action": "Eylem", +"Shortcut": "K\u0131sayol", +"Help": "Yard\u0131m", +"Address": "Adres", +"Focus to menubar": "Men\u00fcye odaklan", +"Focus to toolbar": "Ara\u00e7 tak\u0131m\u0131na odaklan", +"Focus to element path": "\u00d6\u011fe yoluna odaklan", +"Focus to contextual toolbar": "Ba\u011flamsal ara\u00e7 tak\u0131m\u0131na odaklan", +"Insert link (if link plugin activated)": "Ba\u011flant\u0131 ekle (Ba\u011flant\u0131 eklentisi aktif ise)", +"Save (if save plugin activated)": "Kaydet (Kay\u0131t eklentisi aktif ise)", +"Find (if searchreplace plugin activated)": "Bul (Bul\/De\u011fi\u015ftir eklentisi aktif ise)", +"Plugins installed ({0}):": "Eklentiler y\u00fcklendi ({0}):", +"Premium plugins:": "Premium eklentiler:", +"Learn more...": "Detayl\u0131 bilgi...", +"You are using {0}": "\u015eu an {0} kullan\u0131yorsunuz", +"Plugins": "Plugins", +"Handy Shortcuts": "Handy Shortcuts", +"Horizontal line": "Yatay \u00e7izgi", +"Insert\/edit image": "Resim ekle\/d\u00fczenle", +"Image description": "Resim a\u00e7\u0131klamas\u0131", +"Source": "Kaynak", +"Dimensions": "Boyutlar", +"Constrain proportions": "Oranlar\u0131 koru", +"General": "Genel", +"Advanced": "Geli\u015fmi\u015f", +"Style": "Stil", +"Vertical space": "Dikey bo\u015fluk", +"Horizontal space": "Yatay bo\u015fluk", +"Border": "Kenarl\u0131k", +"Insert image": "Resim ekle", +"Image": "Resim", +"Image list": "G\u00f6rsel listesi", +"Rotate counterclockwise": "Saatin tersi y\u00f6n\u00fcnde d\u00f6nd\u00fcr", +"Rotate clockwise": "Saat y\u00f6n\u00fcnde d\u00f6nd\u00fcr", +"Flip vertically": "Dikine \u00e7evir", +"Flip horizontally": "Enine \u00e7evir", +"Edit image": "Resmi d\u00fczenle", +"Image options": "Resim ayarlar\u0131", +"Zoom in": "Yak\u0131nla\u015ft\u0131r", +"Zoom out": "Uzakla\u015ft\u0131r", +"Crop": "K\u0131rp", +"Resize": "Yeniden Boyutland\u0131r", +"Orientation": "Oryantasyon", +"Brightness": "Parlakl\u0131k", +"Sharpen": "Keskinle\u015ftir", +"Contrast": "Kontrast", +"Color levels": "Renk d\u00fczeyleri", +"Gamma": "Gama", +"Invert": "Ters \u00c7evir", +"Apply": "Uygula", +"Back": "Geri", +"Insert date\/time": "Tarih\/saat ekle", +"Date\/time": "Tarih\/saat", +"Insert link": "Ba\u011flant\u0131 ekle", +"Insert\/edit link": "Ba\u011flant\u0131 ekle\/d\u00fczenle", +"Text to display": "Yaz\u0131y\u0131 g\u00f6r\u00fcnt\u00fcle", +"Url": "Url", +"Target": "Hedef", +"None": "Hi\u00e7biri", +"New window": "Yeni pencere", +"Remove link": "Ba\u011flant\u0131y\u0131 kald\u0131r", +"Anchors": "\u00c7apalar", +"Link": "Ba\u011flant\u0131", +"Paste or type a link": "Bir ba\u011flant\u0131 yaz\u0131n yada yap\u0131\u015ft\u0131r\u0131n", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Girdi\u011finiz URL bir e-posta adresi gibi g\u00f6r\u00fcn\u00fcyor. Gerekli olan mailto: \u00f6nekini eklemek ister misiniz?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Girdi\u011finiz URL bir d\u0131\u015f ba\u011flant\u0131 gibi g\u00f6r\u00fcn\u00fcyor. Gerekli olan http:\/\/ \u00f6nekini eklemek ister misiniz?", +"Link list": "Ba\u011flant\u0131 listesi", +"Insert video": "Video ekle", +"Insert\/edit video": "Video ekle\/d\u00fczenle", +"Insert\/edit media": "Medya ekle\/d\u00fczenle", +"Alternative source": "Alternatif kaynak", +"Poster": "Poster", +"Paste your embed code below:": "Video g\u00f6mme kodunu a\u015fa\u011f\u0131ya yap\u0131\u015ft\u0131r\u0131n\u0131z:", +"Embed": "G\u00f6mme", +"Media": "Medya", +"Nonbreaking space": "B\u00f6l\u00fcnemez bo\u015fluk", +"Page break": "Sayfa sonu", +"Paste as text": "Metin olarak yap\u0131\u015ft\u0131r", +"Preview": "\u00d6nizleme", +"Print": "Yazd\u0131r", +"Save": "Kaydet", +"Find": "Bul", +"Replace with": "Bununla de\u011fi\u015ftir", +"Replace": "De\u011fi\u015ftir", +"Replace all": "T\u00fcm\u00fcn\u00fc de\u011fi\u015ftir", +"Prev": "\u00d6nceki", +"Next": "Sonraki", +"Find and replace": "Bul ve de\u011fi\u015ftir", +"Could not find the specified string.": "Herhangi bir sonu\u00e7 bulunamad\u0131.", +"Match case": "B\u00fcy\u00fck\/k\u00fc\u00e7\u00fck harf duyarl\u0131", +"Whole words": "Tam kelimeler", +"Spellcheck": "Yaz\u0131m denetimi", +"Ignore": "Yoksay", +"Ignore all": "T\u00fcm\u00fcn\u00fc yoksay", +"Finish": "Bitir", +"Add to Dictionary": "S\u00f6zl\u00fc\u011fe Ekle", +"Insert table": "Tablo ekle", +"Table properties": "Tablo \u00f6zellikleri", +"Delete table": "Tablo sil", +"Cell": "H\u00fccre", +"Row": "Sat\u0131r", +"Column": "S\u00fctun", +"Cell properties": "H\u00fccre \u00f6zellikleri", +"Merge cells": "H\u00fccreleri birle\u015ftir", +"Split cell": "H\u00fccre b\u00f6l", +"Insert row before": "\u00dcste sat\u0131r ekle", +"Insert row after": "Alta sat\u0131r ekle ", +"Delete row": "Sat\u0131r sil", +"Row properties": "Sat\u0131r \u00f6zellikleri", +"Cut row": "Sat\u0131r\u0131 kes", +"Copy row": "Sat\u0131r\u0131 kopyala", +"Paste row before": "\u00dcste sat\u0131r yap\u0131\u015ft\u0131r", +"Paste row after": "Alta sat\u0131r yap\u0131\u015ft\u0131r", +"Insert column before": "Sola s\u00fctun ekle", +"Insert column after": "Sa\u011fa s\u00fctun ekle", +"Delete column": "S\u00fctun sil", +"Cols": "S\u00fctunlar", +"Rows": "Sat\u0131rlar", +"Width": "Geni\u015flik", +"Height": "Y\u00fckseklik", +"Cell spacing": "H\u00fccre aral\u0131\u011f\u0131", +"Cell padding": "H\u00fccre dolgusu", +"Caption": "Ba\u015fl\u0131k", +"Left": "Sol", +"Center": "Orta", +"Right": "Sa\u011f", +"Cell type": "H\u00fccre tipi", +"Scope": "Kapsam", +"Alignment": "Hizalama", +"H Align": "Yatay Hizalama", +"V Align": "Dikey Hizalama", +"Top": "\u00dcst", +"Middle": "Orta", +"Bottom": "Alt", +"Header cell": "Ba\u015fl\u0131k h\u00fccresi", +"Row group": "Sat\u0131r grubu", +"Column group": "S\u00fctun grubu", +"Row type": "Sat\u0131r tipi", +"Header": "Ba\u015fl\u0131k", +"Body": "G\u00f6vde", +"Footer": "Alt", +"Border color": "Kenarl\u0131k rengi", +"Insert template": "\u015eablon ekle", +"Templates": "\u015eablonlar", +"Template": "Taslak", +"Text color": "Yaz\u0131 rengi", +"Background color": "Arka plan rengi", +"Custom...": "\u00d6zel...", +"Custom color": "\u00d6zel renk", +"No color": "Renk yok", +"Table of Contents": "\u0130\u00e7erik tablosu", +"Show blocks": "Bloklar\u0131 g\u00f6ster", +"Show invisible characters": "G\u00f6r\u00fcnmez karakterleri g\u00f6ster", +"Words: {0}": "Kelime: {0}", +"{0} words": "{0} words", +"File": "Dosya", +"Edit": "D\u00fczenle", +"Insert": "Ekle", +"View": "G\u00f6r\u00fcn\u00fcm", +"Format": "Bi\u00e7im", +"Table": "Tablo", +"Tools": "Ara\u00e7lar", +"Powered by {0}": "Powered by {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zengin Metin Alan\u0131. Men\u00fc i\u00e7in ALT-F9 tu\u015funa bas\u0131n\u0131z. Ara\u00e7 \u00e7ubu\u011fu i\u00e7in ALT-F10 tu\u015funa bas\u0131n\u0131z. Yard\u0131m i\u00e7in ALT-0 tu\u015funa bas\u0131n\u0131z." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr_TR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr_TR.js new file mode 100644 index 0000000000..ea89bc44c3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr_TR.js @@ -0,0 +1,261 @@ +tinymce.addI18n('tr_TR',{ +"Redo": "Yinele", +"Undo": "Geri Al", +"Cut": "Kes", +"Copy": "Kopyala", +"Paste": "Yap\u0131\u015ft\u0131r", +"Select all": "T\u00fcm\u00fcn\u00fc se\u00e7", +"New document": "Yeni dok\u00fcman", +"Ok": "Tamam", +"Cancel": "\u0130ptal", +"Visual aids": "G\u00f6rsel ara\u00e7lar", +"Bold": "Kal\u0131n", +"Italic": "\u0130talik", +"Underline": "Alt\u0131 \u00e7izili", +"Strikethrough": "\u00dcst\u00fc \u00e7izili", +"Superscript": "\u00dcst simge", +"Subscript": "Alt simge", +"Clear formatting": "Bi\u00e7imi temizle", +"Align left": "Sola hizala", +"Align center": "Ortala", +"Align right": "Sa\u011fa hizala", +"Justify": "\u0130ki yana yasla", +"Bullet list": "S\u0131ras\u0131z liste", +"Numbered list": "S\u0131ral\u0131 liste", +"Decrease indent": "Girintiyi azalt", +"Increase indent": "Girintiyi art\u0131r", +"Close": "Kapat", +"Formats": "Bi\u00e7imler", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Taray\u0131c\u0131n\u0131z panoya direk eri\u015fimi desteklemiyor. L\u00fctfen Ctrl+X\/C\/V klavye k\u0131sayollar\u0131n\u0131 kullan\u0131n.", +"Headers": "Ba\u015fl\u0131klar", +"Header 1": "Ba\u015fl\u0131k 1", +"Header 2": "Ba\u015fl\u0131k 2", +"Header 3": "Ba\u015fl\u0131k 3", +"Header 4": "Ba\u015fl\u0131k 4", +"Header 5": "Ba\u015fl\u0131k 5", +"Header 6": "Ba\u015fl\u0131k 6", +"Headings": "Ba\u015fl\u0131klar", +"Heading 1": "Ba\u015fl\u0131k 1", +"Heading 2": "Ba\u015fl\u0131k 2", +"Heading 3": "Ba\u015fl\u0131k 3", +"Heading 4": "Ba\u015fl\u0131k 4", +"Heading 5": "Ba\u015fl\u0131k 5", +"Heading 6": "Ba\u015fl\u0131k 6", +"Preformatted": "\u00d6nceden bi\u00e7imlendirilmi\u015f", +"Div": "Div", +"Pre": "\u00d6n", +"Code": "Kod", +"Paragraph": "Paragraf", +"Blockquote": "Al\u0131nt\u0131", +"Inline": "Sat\u0131r i\u00e7i", +"Blocks": "Bloklar", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "D\u00fcz metin modunda yap\u0131\u015ft\u0131r. Bu se\u00e7ene\u011fi kapatana kadar i\u00e7erikler d\u00fcz metin olarak yap\u0131\u015ft\u0131r\u0131l\u0131r.", +"Font Family": "Yaz\u0131tipi Ailesi", +"Font Sizes": "Yaz\u0131tipi B\u00fcy\u00fckl\u00fc\u011f\u00fc", +"Class": "S\u0131n\u0131f", +"Browse for an image": "Bir resim aray\u0131n", +"OR": "ya da", +"Drop an image here": "Buraya bir resim koy", +"Upload": "Y\u00fckle", +"Block": "Blok", +"Align": "Hizala", +"Default": "Varsay\u0131lan", +"Circle": "Daire", +"Disc": "Disk", +"Square": "Kare", +"Lower Alpha": "K\u00fc\u00e7\u00fck ABC", +"Lower Greek": "K\u00fc\u00e7\u00fck Yunan alfabesi", +"Lower Roman": "K\u00fc\u00e7\u00fck Roman alfabesi", +"Upper Alpha": "B\u00fcy\u00fck ABC", +"Upper Roman": "B\u00fcy\u00fck Roman alfabesi", +"Anchor": "\u00c7apa", +"Name": "\u0130sim", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id bir harf ile ba\u015flamal\u0131d\u0131r ve sadece harfleri, rakamlar\u0131, \u00e7izgileri, noktalar\u0131, virg\u00fclleri veya alt \u00e7izgileri i\u00e7ermelidir.", +"You have unsaved changes are you sure you want to navigate away?": "Kaydedilmemi\u015f de\u011fi\u015fiklikler var, sayfadan ayr\u0131lmak istedi\u011finize emin misiniz?", +"Restore last draft": "Son tasla\u011f\u0131 kurtar", +"Special character": "\u00d6zel karakter", +"Source code": "Kaynak kodu", +"Insert\/Edit code sample": "Kod \u00f6rne\u011fini Kaydet\/D\u00fczenle", +"Language": "Dil", +"Code sample": "Kod \u00f6rne\u011fi", +"Color": "Renk", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Soldan sa\u011fa", +"Right to left": "Sa\u011fdan sola", +"Emoticons": "G\u00fcl\u00fcc\u00fckler", +"Document properties": "Dok\u00fcman \u00f6zellikleri", +"Title": "Ba\u015fl\u0131k", +"Keywords": "Anahtar kelimeler", +"Description": "A\u00e7\u0131klama", +"Robots": "Robotlar", +"Author": "Yazar", +"Encoding": "Kodlama", +"Fullscreen": "Tam ekran", +"Action": "Eylem", +"Shortcut": "K\u0131sayol", +"Help": "Yard\u0131m", +"Address": "Adres", +"Focus to menubar": "Men\u00fc \u00e7ubu\u011funa odaklan.", +"Focus to toolbar": "Ara\u00e7 \u00e7ubu\u011funa odaklan.", +"Focus to element path": "Eleman yoluna odaklan", +"Focus to contextual toolbar": "Ba\u011flamsal ara\u00e7 \u00e7ubu\u011funa odaklan", +"Insert link (if link plugin activated)": "Link ekle (Link eklentisi aktif ise)", +"Save (if save plugin activated)": "Kaydet (Kay\u0131t eklentisi aktif ise)", +"Find (if searchreplace plugin activated)": "Bul (SearchReplace eklentisi aktif ise)", +"Plugins installed ({0}):": "Y\u00fckl\u00fc eklenti say\u0131s\u0131 : ({0}):", +"Premium plugins:": "Premium eklentileri", +"Learn more...": "Daha fazla bilgi edinin.", +"You are using {0}": "{0} kullan\u0131yorsun.", +"Plugins": "Eklentiler", +"Handy Shortcuts": "Kullan\u0131\u015fl\u0131 K\u0131sayollar", +"Horizontal line": "Yatay \u00e7izgi", +"Insert\/edit image": "Resim ekle\/d\u00fczenle", +"Image description": "Resim a\u00e7\u0131klamas\u0131", +"Source": "Kaynak", +"Dimensions": "Boyutlar", +"Constrain proportions": "En - Boy oran\u0131n\u0131 koru", +"General": "Genel", +"Advanced": "Geli\u015fmi\u015f", +"Style": "Stil", +"Vertical space": "Dikey bo\u015fluk", +"Horizontal space": "Yatay bo\u015fluk", +"Border": "\u00c7er\u00e7eve", +"Insert image": "Resim ekle", +"Image": "Resim", +"Image list": "Resim listesi", +"Rotate counterclockwise": "Saat y\u00f6n\u00fcn\u00fcn tersine d\u00f6nd\u00fcr", +"Rotate clockwise": "Saat y\u00f6n\u00fcnde d\u00f6nd\u00fcr", +"Flip vertically": "Dikey \u00e7evir", +"Flip horizontally": "Yatay \u00e7evir", +"Edit image": "G\u00f6r\u00fcnt\u00fcy\u00fc d\u00fczenle", +"Image options": "G\u00f6r\u00fcnt\u00fc se\u00e7enekleri", +"Zoom in": "Yak\u0131nla\u015ft\u0131r", +"Zoom out": "Uzakla\u015ft\u0131r", +"Crop": "Kes", +"Resize": "Yeniden Boyutland\u0131r", +"Orientation": "Y\u00f6n\u00fcn\u00fc Belirle", +"Brightness": "Parlakl\u0131k", +"Sharpen": "Keskinle\u015ftir", +"Contrast": "Kontrast", +"Color levels": "Renk seviyesi", +"Gamma": "Gama", +"Invert": "Tersine \u00e7evir", +"Apply": "Uygula", +"Back": "Geri", +"Insert date\/time": "Tarih \/ Zaman ekle", +"Date\/time": "Tarih\/zaman", +"Insert link": "Ba\u011flant\u0131 ekle", +"Insert\/edit link": "Ba\u011flant\u0131 ekle\/d\u00fczenle", +"Text to display": "G\u00f6r\u00fcnen yaz\u0131", +"Url": "Url", +"Target": "Hedef", +"None": "Hi\u00e7biri", +"New window": "Yeni pencere", +"Remove link": "Ba\u011flant\u0131y\u0131 kald\u0131r", +"Anchors": "\u00c7apalar", +"Link": "Ba\u011flant\u0131", +"Paste or type a link": "Bir ba\u011flant\u0131 yap\u0131\u015ft\u0131r\u0131n yada yaz\u0131n.", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Girdi\u011finiz URL bir eposta adresi gibi g\u00f6z\u00fck\u00fcyor. Gerekli olan mailto: \u00f6nekini eklemek ister misiniz?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Girdi\u011finiz URL bir d\u0131\u015f ba\u011flant\u0131 gibi g\u00f6z\u00fck\u00fcyor. Gerekli olan http:\/\/ \u00f6nekini eklemek ister misiniz?", +"Link list": "Link listesi", +"Insert video": "Video ekle", +"Insert\/edit video": "Video ekle\/d\u00fczenle", +"Insert\/edit media": "Medya ekle\/d\u00fczenle", +"Alternative source": "Alternatif kaynak", +"Poster": "Poster", +"Paste your embed code below:": "Medya g\u00f6mme kodunu buraya yap\u0131\u015ft\u0131r:", +"Embed": "G\u00f6mme", +"Media": "Medya", +"Nonbreaking space": "B\u00f6l\u00fcnemez bo\u015fluk", +"Page break": "Sayfa sonu", +"Paste as text": "Metin olarak yap\u0131\u015ft\u0131r", +"Preview": "\u00d6nizleme", +"Print": "Yazd\u0131r", +"Save": "Kaydet", +"Find": "Bul", +"Replace with": "Bununla de\u011fi\u015ftir", +"Replace": "De\u011fi\u015ftir", +"Replace all": "T\u00fcm\u00fcn\u00fc de\u011fi\u015ftir", +"Prev": "\u00d6nceki", +"Next": "Sonraki", +"Find and replace": "Bul ve de\u011fi\u015ftir", +"Could not find the specified string.": "Herhangi bir sonu\u00e7 bulunamad\u0131.", +"Match case": "B\u00fcy\u00fck \/ K\u00fc\u00e7\u00fck harfe duyarl\u0131", +"Whole words": "Tam s\u00f6zc\u00fckler", +"Spellcheck": "Yaz\u0131m denetimi", +"Ignore": "Yoksay", +"Ignore all": "T\u00fcm\u00fcn\u00fc yoksay", +"Finish": "Bitir", +"Add to Dictionary": "S\u00f6zl\u00fc\u011fe ekle", +"Insert table": "Tablo ekle", +"Table properties": "Tablo \u00f6zellikleri", +"Delete table": "Tabloyu sil", +"Cell": "H\u00fccre", +"Row": "Sat\u0131r", +"Column": "S\u00fctun", +"Cell properties": "H\u00fccre \u00f6zellikleri", +"Merge cells": "H\u00fccreleri birle\u015ftir", +"Split cell": "H\u00fccreleri ay\u0131r", +"Insert row before": "\u00d6ncesine yeni sat\u0131r ekle", +"Insert row after": "Sonras\u0131na yeni sat\u0131r ekle", +"Delete row": "Sat\u0131r\u0131 sil", +"Row properties": "Sat\u0131r \u00f6zellikleri", +"Cut row": "Sat\u0131r\u0131 kes", +"Copy row": "Sat\u0131r\u0131 kopyala", +"Paste row before": "\u00d6ncesine sat\u0131r yap\u0131\u015ft\u0131r", +"Paste row after": "Sonras\u0131na sat\u0131r yap\u0131\u015ft\u0131r", +"Insert column before": "\u00d6ncesine yeni s\u00fctun ekle", +"Insert column after": "Sonras\u0131na yeni s\u00fctun ekle", +"Delete column": "S\u00fctunu sil", +"Cols": "S\u00fctunlar", +"Rows": "Sat\u0131rlar", +"Width": "Geni\u015flik", +"Height": "Y\u00fckseklik", +"Cell spacing": "H\u00fccre aral\u0131\u011f\u0131", +"Cell padding": "H\u00fccre i\u00e7 bo\u015flu\u011fu", +"Caption": "Ba\u015fl\u0131k", +"Left": "Sol", +"Center": "Orta", +"Right": "Sa\u011f", +"Cell type": "H\u00fccre tipi", +"Scope": "Kapsam", +"Alignment": "Hizalama", +"H Align": "Yatay Hizalama", +"V Align": "Dikey Hizalama", +"Top": "\u00dcst", +"Middle": "Orta", +"Bottom": "Alt", +"Header cell": "Ba\u015fl\u0131k h\u00fccresi", +"Row group": "Sat\u0131r grubu", +"Column group": "S\u00fctun grubu", +"Row type": "Sat\u0131r tipi", +"Header": "Ba\u015fl\u0131k", +"Body": "G\u00f6vde", +"Footer": "Alt", +"Border color": "Kenarl\u0131k Rengi", +"Insert template": "\u015eablon ekle", +"Templates": "\u015eablonlar", +"Template": "Tema", +"Text color": "Yaz\u0131 rengi", +"Background color": "Arkaplan rengi", +"Custom...": "\u00d6zel", +"Custom color": "\u00d6zel Renk", +"No color": "Renk Yok", +"Table of Contents": "\u0130\u00e7indekiler", +"Show blocks": "Bloklar\u0131 g\u00f6r\u00fcnt\u00fcle", +"Show invisible characters": "G\u00f6r\u00fcnmez karakterleri g\u00f6ster", +"Words: {0}": "Kelime: {0}", +"{0} words": "{0} kelime", +"File": "Dosya", +"Edit": "D\u00fczenle", +"Insert": "Ekle", +"View": "G\u00f6r\u00fcnt\u00fcle", +"Format": "Bi\u00e7im", +"Table": "Tablo", +"Tools": "Ara\u00e7lar", +"Powered by {0}": "{0} taraf\u0131ndan yap\u0131lm\u0131\u015ft\u0131r ", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zengin Metin Alan\u0131. Men\u00fc i\u00e7in ALT-F9 k\u0131sayolunu kullan\u0131n. Ara\u00e7 \u00e7ubu\u011fu i\u00e7in ALT-F10 k\u0131sayolunu kullan\u0131n. Yard\u0131m i\u00e7in ALT-0 k\u0131sayolunu kullan\u0131n." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ug.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ug.js new file mode 100644 index 0000000000..55fe840b13 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ug.js @@ -0,0 +1,260 @@ +tinymce.addI18n('ug',{ +"Redo": "\u0642\u0627\u064a\u062a\u0627 \u0642\u0649\u0644\u0649\u0634", +"Undo": "\u0626\u0627\u0631\u0642\u0649\u063a\u0627 \u064a\u06d0\u0646\u0649\u0634", +"Cut": "\u0643\u06d0\u0633\u0649\u0634", +"Copy": "\u0643\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Paste": "\u0686\u0627\u067e\u0644\u0627\u0634", +"Select all": "\u06be\u06d5\u0645\u0645\u0649\u0646\u0649 \u062a\u0627\u0644\u0644\u0627\u0634", +"New document": "\u064a\u06d0\u06ad\u0649 \u067e\u06c8\u062a\u06c8\u0643", +"Ok": "\u062c\u06d5\u0632\u0649\u0645\u0644\u06d5\u0634", +"Cancel": "\u0642\u0627\u0644\u062f\u06c7\u0631\u06c7\u0634", +"Visual aids": "\u0626\u06d5\u0633\u0643\u06d5\u0631\u062a\u0649\u0634", +"Bold": "\u062a\u0648\u0645", +"Italic": "\u064a\u0627\u0646\u062a\u06c7", +"Underline": "\u0626\u0627\u0633\u062a\u0649 \u0633\u0649\u0632\u0649\u0642", +"Strikethrough": "\u0626\u06c6\u0686\u06c8\u0631\u06c8\u0634 \u0633\u0649\u0632\u0649\u0642\u0649", +"Superscript": "\u0626\u06c8\u0633\u062a\u06c8\u0646\u0643\u0649 \u0628\u06d5\u0644\u06af\u06d5", +"Subscript": "\u0626\u0627\u0633\u062a\u0649\u0646\u0642\u0649 \u0628\u06d5\u0644\u06af\u06d5", +"Clear formatting": "\u0641\u0648\u0631\u0645\u0627\u062a\u0646\u0649 \u062a\u0627\u0632\u0644\u0627\u0634", +"Align left": "\u0633\u0648\u0644\u063a\u0627 \u062a\u0648\u063a\u0631\u0649\u0644\u0627\u0634", +"Align center": "\u0645\u06d5\u0631\u0643\u06d5\u0632\u06af\u06d5 \u062a\u0648\u063a\u06c7\u0631\u0644\u0627\u0634", +"Align right": "\u0626\u0648\u06ad\u063a\u0627 \u062a\u0648\u063a\u06c7\u0631\u0644\u0627\u0634", +"Justify": "\u0626\u0649\u0643\u0643\u0649 \u064a\u0627\u0646\u063a\u0627 \u062a\u0648\u063a\u06c7\u0631\u0644\u0627\u0634", +"Bullet list": "\u0628\u06d5\u0644\u06af\u06d5 \u062a\u0649\u0632\u0649\u0645\u0644\u0649\u0643", +"Numbered list": "\u0633\u0627\u0646\u0644\u0649\u0642 \u062a\u0649\u0632\u0649\u0645\u0644\u0649\u0643", +"Decrease indent": "\u0626\u0627\u0644\u062f\u0649\u063a\u0627 \u0633\u06c8\u0631\u06c8\u0634", +"Increase indent": "\u0643\u06d5\u064a\u0646\u0649\u06af\u06d5 \u0633\u06c8\u0631\u06c8\u0634", +"Close": "\u062a\u0627\u0642\u0627\u0634", +"Formats": "\u0641\u0648\u0631\u0645\u0627\u062a", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0633\u0649\u0632\u0646\u0649\u06ad \u062a\u0648\u0631 \u0643\u06c6\u0631\u06af\u06c8\u0686\u0649\u06ad\u0649\u0632 \u0642\u0649\u064a\u0649\u067e \u0686\u0627\u067e\u0644\u0627\u0634 \u062a\u0627\u062e\u062a\u0649\u0633\u0649 \u0632\u0649\u064a\u0627\u0631\u06d5\u062a \u0642\u0649\u0644\u0649\u0634\u0646\u0649 \u0642\u0648\u0644\u0644\u0649\u0645\u0627\u064a\u062f\u06c7. Ctrl+X\/C\/V \u062a\u06d0\u0632\u0644\u06d5\u062a\u0645\u06d5 \u0643\u06c7\u0646\u06c7\u067e\u0643\u0649\u0633\u0649 \u0626\u0627\u0631\u0642\u0649\u0644\u0649\u0642 \u0643\u06d0\u0633\u0649\u067e \u0686\u0627\u067e\u0644\u0627\u0634 \u0645\u06d5\u0634\u063a\u06c7\u0644\u0627\u062a\u0649 \u0642\u0649\u0644\u0649\u06ad.", +"Headers": "\u0628\u06d0\u0634\u0649", +"Header 1": "\u062a\u06d0\u0645\u0627 1", +"Header 2": "\u062a\u06d0\u0645\u0627 2", +"Header 3": "\u062a\u06d0\u0645\u0627 3", +"Header 4": "\u062a\u06d0\u0645\u0627 4", +"Header 5": "\u062a\u06d0\u0645\u0627 5", +"Header 6": "\u062a\u06d0\u0645\u0627 6", +"Headings": "\u0645\u0627\u06cb\u0632\u06c7", +"Heading 1": "1 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Heading 2": "2 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Heading 3": "3 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Heading 4": "4 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Heading 5": "5 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Heading 6": "6 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0645\u0627\u06cb\u0632\u06c7", +"Div": "Div", +"Pre": "Pre", +"Code": "\u0643\u0648\u062f", +"Paragraph": "\u067e\u0627\u0631\u0627\u06af\u0649\u0631\u0627 \u0641", +"Blockquote": "\u0626\u06d5\u0633\u0643\u06d5\u0631\u062a\u0649\u0634", +"Inline": "\u0626\u0649\u0686\u0643\u0649", +"Blocks": "\u0631\u0627\u064a\u0648\u0646", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u06be\u0627\u0632\u0649\u0631 \u0686\u0627\u067e\u0644\u0649\u0633\u0649\u06ad\u0649\u0632 \u0633\u0627\u067e \u062a\u06d0\u0643\u0649\u0634 \u0645\u06d5\u0632\u0645\u06c7\u0646\u0649 \u0686\u0627\u067e\u0644\u0649\u0646\u0649\u062f\u06c7. \u062a\u06d0\u0643\u0649\u0634 \u0634\u06d5\u0643\u0644\u0649\u062f\u06d5 \u0686\u0627\u067e\u0644\u0627\u0634 \u062a\u06d5\u06ad\u0634\u0649\u0643\u0649\u0646\u0649 \u062a\u0627\u0642\u0649\u06cb\u06d5\u062a\u0643\u06d5\u0646\u06af\u06d5 \u0642\u06d5\u062f\u06d5\u0631.", +"Font Family": "\u062e\u06d5\u062a \u0646\u06c7\u0633\u062e\u0649\u0633\u0649", +"Font Sizes": "\u062e\u06d5\u062a \u0686\u0648\u06ad\u0644\u06c7\u0642\u0649", +"Class": "\u062a\u06c8\u0631", +"Browse for an image": "\u0631\u06d5\u0633\u0649\u0645\u0646\u0649 \u0643\u06c6\u0631\u0633\u0649\u062a\u0649\u0634", +"OR": "\u064a\u0627\u0643\u0649", +"Drop an image here": "\u0628\u06c7 \u064a\u06d5\u0631\u062f\u0649\u0643\u0649 \u0631\u06d5\u0633\u0649\u0645\u0646\u0649 \u0626\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Upload": "\u0686\u0649\u0642\u0649\u0631\u0649\u0634", +"Block": "\u067e\u0627\u0631\u0686\u06d5", +"Align": "\u062a\u0648\u063a\u0631\u0649\u0644\u0649\u0646\u0649\u0634\u0649", +"Default": "\u0633\u06c8\u0643\u06c8\u062a", +"Circle": "\u0686\u06d5\u0645\u0628\u06d5\u0631", +"Disc": "\u062f\u06d0\u0633\u0643\u0627", +"Square": "\u0643\u06cb\u0627\u062f\u0631\u0627\u062a", +"Lower Alpha": "\u0626\u0649\u0646\u06af\u0649\u0644\u0649\u0632\u0686\u06d5 \u0643\u0649\u0686\u0649\u0643 \u064a\u06d0\u0632\u0649\u0644\u0649\u0634\u0649", +"Lower Greek": "\u06af\u0631\u06d0\u062a\u0633\u0649\u064a\u0649\u0686\u06d5 \u0643\u0649\u0686\u0649\u0643 \u064a\u06d0\u0632\u0649\u0644\u0649\u0634\u0649", +"Lower Roman": "\u0631\u0649\u0645\u0686\u06d5 \u0643\u0649\u0686\u0649\u0643 \u064a\u06d0\u0632\u0649\u0644\u0649\u0634\u0649", +"Upper Alpha": "\u0626\u0649\u0646\u06af\u0649\u0644\u0649\u0632\u0686\u06d5 \u0686\u0648\u06ad \u064a\u06d0\u0632\u0649\u0644\u0649\u0634\u0649", +"Upper Roman": "\u0631\u0649\u0645\u0686\u06d5 \u0686\u0648\u06ad \u064a\u06d0\u0632\u0649\u0644\u0649\u0634\u0649", +"Anchor": "\u0626\u06c7\u0644\u0627\u0646\u0645\u0627", +"Name": "\u0646\u0627\u0645\u0649", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID \u0686\u0648\u0642\u06c7\u0645 \u06be\u06d5\u0631\u0649\u067e \u0628\u0649\u0644\u06d5\u0646 \u0628\u0627\u0634\u0644\u0649\u0646\u0649\u0634\u0649 \u0643\u06d0\u0631\u06d5\u0643 \u060c \u0626\u0627\u0631\u0642\u0649\u0633\u0649 \u067e\u06d5\u0642\u06d5\u062a \u06be\u06d5\u0631\u0649\u067e \u060c \u0633\u0627\u0646 \u060c \u0626\u0627\u064a\u0631\u0649\u0634 \u0628\u06d5\u0644\u06af\u0649\u0633\u0649 \u060c \u0686\u0649\u0643\u0649\u062a \u06cb\u06d5 \u0626\u0627\u0633\u062a\u0649 \u0633\u0649\u0632\u0649\u0642\u0649 \u062f\u0649\u0646 \u0626\u0649\u0628\u0627\u0631\u06d5\u062a .", +"You have unsaved changes are you sure you want to navigate away?": "\u0633\u0649\u0632 \u062a\u06d0\u062e\u0649 \u0645\u06d5\u0632\u0645\u06c7\u0646\u0646\u0649 \u0633\u0627\u0642\u0644\u0649\u0645\u0649\u062f\u0649\u06ad\u0649\u0632\u060c \u0626\u0627\u064a\u0631\u0649\u0644\u0627\u0645\u0633\u0649\u0632\u061f", +"Restore last draft": "\u0626\u0627\u062e\u0649\u0631\u0642\u0649 \u0643\u06c7\u067e\u0649\u064a\u0649\u06af\u06d5 \u0642\u0627\u064a\u062a\u0649\u0634", +"Special character": "\u0626\u0627\u0644\u0627\u06be\u0649\u062f\u06d5 \u0628\u06d5\u0644\u06af\u0649\u0644\u06d5\u0631", +"Source code": "\u0626\u06d5\u0633\u0644\u0649 \u0643\u0648\u062f\u0649", +"Insert\/Edit code sample": "\u0643\u0648\u062f \u0645\u0649\u0633\u0627\u0644\u0649\\\u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Language": "\u062a\u0649\u0644", +"Code sample": "\u0643\u0648\u062f \u0645\u0649\u0633\u0627\u0644\u0649", +"Color": "\u0631\u06d5\u06ad", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0633\u0648\u0644\u062f\u0649\u0646 \u0626\u0648\u06ad\u063a\u0627 ", +"Right to left": "\u0626\u0648\u06ad\u062f\u0649\u0646 \u0633\u0648\u0644\u063a\u0627", +"Emoticons": "\u0686\u0649\u0631\u0627\u064a \u0626\u0649\u067e\u0627\u062f\u06d5", +"Document properties": "\u06be\u06c6\u062c\u062c\u06d5\u062a \u062e\u0627\u0633\u0644\u0649\u0642\u0649", +"Title": "\u062a\u06d0\u0645\u0627", +"Keywords": "\u06be\u0627\u0644\u0642\u0649\u0644\u0649\u0642 \u0633\u06c6\u0632", +"Description": "\u062a\u06d5\u0633\u0649\u06cb\u0649\u0631", +"Robots": "\u0645\u0627\u0634\u0649\u0646\u0627 \u0626\u0627\u062f\u06d5\u0645", +"Author": "\u0626\u06c7\u0644\u0627\u0646\u0645\u0627", +"Encoding": "\u0643\u0648\u062f\u0644\u0627\u0634", +"Fullscreen": "\u067e\u06c8\u062a\u06c8\u0646 \u0626\u06d0\u0643\u0631\u0627\u0646", +"Action": "\u06be\u06d5\u0631\u0649\u0643\u06d5\u062a", +"Shortcut": "\u0642\u0649\u0633\u0642\u0627 \u064a\u0648\u0644", +"Help": "\u064a\u0627\u0631\u062f\u06d5\u0645", +"Address": "\u0626\u0627\u062f\u0649\u0631\u0649\u0633", +"Focus to menubar": "\u062a\u0649\u0632\u0649\u0645\u0644\u0649\u0643 \u0633\u0649\u062a\u0648\u0646\u0649\u063a\u0627 \u062f\u0649\u0642\u06d5\u062a", +"Focus to toolbar": "\u0642\u06c7\u0631\u0627\u0644 \u0633\u0649\u062a\u0648\u0646\u0649\u063a\u0627 \u062f\u0649\u0642\u06d5\u062a", +"Focus to element path": "\u0626\u06d0\u0644\u0649\u0645\u0649\u0646\u062a\u0644\u0627\u0631 \u064a\u0648\u0644\u0649\u063a\u0627 \u062f\u0649\u0642\u06d5\u062a", +"Focus to contextual toolbar": "\u0643\u0648\u0646\u062a\u06d0\u0643\u0649\u0633\u062a \u0642\u0648\u0631\u0627\u0644 \u0626\u0649\u0633\u062a\u0648\u0646\u0649\u063a\u0627 \u062f\u06d0\u0642\u06d5\u062a", +"Insert link (if link plugin activated)": "\u0626\u06c7\u0644\u0627\u0646\u0645\u0627 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u06ad (\u0626\u06c7\u0644\u0627\u0646\u0645\u0627 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634 \u0642\u0649\u0633\u062a\u06c7\u0631\u0645\u0649\u0633\u0649\u0646\u0649 \u0642\u0648\u0632\u063a\u0627\u062a\u0642\u0627\u0646 \u0626\u06d5\u06be\u06cb\u0627\u0644\u062f\u0627)", +"Save (if save plugin activated)": "\u0633\u0627\u0642\u0644\u0627\u0634 (\u0633\u0627\u0642\u0644\u0627\u0634 \u0642\u0649\u0633\u062a\u06c7\u0631\u0645\u0649\u0633\u0649\u0646\u0649 \u0642\u0648\u0632\u063a\u0627\u062a\u0642\u0627\u0646 \u0626\u06d5\u06be\u06cb\u0627\u0644\u062f\u0627)", +"Find (if searchreplace plugin activated)": "\u0626\u0649\u0632\u062f\u06d5\u0634 (\u0626\u0649\u0632\u062f\u06d5\u0634 \u0642\u0649\u0633\u062a\u06c7\u0631\u0645\u0649\u0633\u0649 \u0642\u0648\u0632\u063a\u0649\u062a\u0649\u0644\u063a\u0627\u0646 \u0626\u06d5\u06be\u06cb\u0627\u0644\u062f\u0627)", +"Plugins installed ({0}):": "\u0642\u0627\u0686\u0649\u0644\u0627\u0646\u063a\u0627\u0646 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0644\u0645\u0627 ({0}):", +"Premium plugins:": "\u064a\u06c7\u0642\u0649\u0631\u0649 \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0643 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0644\u0645\u0627 :", +"Learn more...": "\u062a\u06d0\u062e\u0649\u0645\u06c7 \u0686\u06c8\u0634\u0649\u0646\u0649\u0634 ...", +"You are using {0}": "\u0626\u0649\u0634\u0644\u0649\u062a\u0649\u06cb\u0627\u062a\u0642\u0649\u0646\u0649\u06ad\u0649\u0632 {0}", +"Plugins": "\u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0644\u0645\u0627", +"Handy Shortcuts": "\u0642\u0648\u0644\u0627\u064a\u0644\u0649\u0642 \u0642\u0649\u0633\u0642\u0627 \u064a\u0648\u0644", +"Horizontal line": "\u06af\u0648\u0631\u0632\u0649\u0646\u062a\u0627\u0644 \u0642\u06c7\u0631", +"Insert\/edit image": "\u0631\u06d5\u0633\u0649\u0645 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634 \u064a\u0627\u0643\u0649 \u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Image description": "\u0631\u06d5\u0633\u0649\u0645 \u062a\u06d5\u0633\u06cb\u0649\u0631\u0649", +"Source": "\u0645\u06d5\u0646\u0628\u06d5", +"Dimensions": "\u0686\u0648\u06ad-\u0643\u0649\u0686\u0649\u0643", +"Constrain proportions": "\u0626\u06d0\u06af\u0649\u0632\u0644\u0649\u0643-\u0643\u06d5\u06ad\u0644\u0649\u0643 \u0646\u0649\u0633\u067e\u0649\u062a\u0649\u0646\u0649 \u0633\u0627\u0642\u0644\u0627\u0634", +"General": "\u0626\u0627\u062f\u06d5\u062a\u062a\u0649\u0643\u0649", +"Advanced": "\u0626\u0627\u0644\u0627\u06be\u0649\u062f\u06d5", +"Style": "\u0626\u06c7\u0633\u0644\u06c7\u067e", +"Vertical space": "\u06cb\u06d0\u0631\u062a\u0649\u0643\u0627\u0644 \u0628\u0648\u0634\u0644\u06c7\u0642", +"Horizontal space": "\u06af\u0648\u0631\u0632\u0649\u0646\u062a\u0627\u0644 \u0628\u0648\u0634\u0644\u06c7\u0642", +"Border": "\u064a\u0627\u0642\u0627", +"Insert image": "\u0631\u06d5\u0633\u0649\u0645 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Image": "\u0631\u06d5\u0633\u0649\u0645", +"Image list": "\u0631\u06d5\u0633\u0649\u0645 \u062a\u0649\u0632\u0649\u0645\u0644\u0649\u0643\u0649", +"Rotate counterclockwise": "\u200f\u200f\u0633\u0627\u0626\u06d5\u062a\u0643\u06d5 \u0642\u0627\u0631\u0634\u0649 \u0686\u06c6\u0631\u06c8\u0634", +"Rotate clockwise": "\u200f\u200f\u0633\u0627\u0626\u06d5\u062a \u064a\u06c6\u0646\u0649\u0644\u0649\u0634\u0649\u062f\u06d5 \u0686\u06c6\u0631\u06c8\u0634", +"Flip vertically": "\u06cb\u06d0\u0631\u062a\u0649\u0643\u0627\u0644 \u0626\u06c6\u0631\u06c8\u0634", +"Flip horizontally": "\u06af\u0648\u0631\u0649\u0632\u0648\u0646\u062a\u0627\u0644 \u0626\u06c6\u0631\u06c8\u0634", +"Edit image": "\u0631\u06d5\u0633\u0649\u0645 \u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Image options": "\u0631\u06d5\u0633\u0649\u0645 \u062a\u0627\u0644\u0644\u0627\u0646\u0645\u0649\u0644\u0649\u0631\u0649", +"Zoom in": "\u064a\u06d0\u0642\u0649\u0646\u0644\u0627\u062a\u0645\u0627\u0642", +"Zoom out": "\u064a\u0649\u0631\u0627\u0642\u0644\u0627\u062a\u0645\u0627\u0642", +"Crop": "\u0642\u0649\u064a\u0649\u0634", +"Resize": "\u0686\u0648\u06ad\u0644\u06c7\u0642\u0649\u0646\u0649 \u0626\u06c6\u0632\u06af\u06d5\u0631\u062a\u0649\u0634", +"Orientation": "\u064a\u06c6\u0646\u0649\u0644\u0649\u0634", +"Brightness": "\u064a\u0648\u0631\u06c7\u0642\u0644\u06c7\u0642\u0649", +"Sharpen": "\u0626\u06c6\u062a\u0643\u06c8\u0631\u0644\u06d5\u0634\u062a\u06c8\u0631\u06c8\u0634", +"Contrast": "\u0633\u06d0\u0644\u0649\u0634\u062a\u06c7\u0631\u0645\u0627", +"Color levels": "\u0631\u06d5\u06ad \u062f\u06d5\u0631\u0649\u062c\u0649\u0644\u0649\u0631\u0649", +"Gamma": "\u06af\u0627\u0645\u0645\u0627", +"Invert": "\u062a\u06d5\u062a\u06c8\u0631", +"Apply": "\u0642\u0648\u0644\u0644\u0649\u0646\u0649\u0634", +"Back": "\u0642\u0627\u064a\u062a\u0649\u0634", +"Insert date\/time": "\u0686\u0649\u0633\u0644\u0627\/\u06cb\u0627\u0642\u0649\u062a \u0643\u0649\u0631\u06af\u06c8\u0632\u06c8\u0634", +"Date\/time": "\u0686\u06d0\u0633\u0644\u0627\\\u06cb\u0627\u0642\u0649\u062a", +"Insert link": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Insert\/edit link": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634 \u0642\u06c7\u0633\u062a\u06c7\u0631\u06c7\u0634\/\u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Text to display": "\u0643\u06c6\u0631\u06c8\u0646\u0649\u062f\u0649\u063a\u0627\u0646 \u0645\u06d5\u0632\u0645\u06c7\u0646", +"Url": "\u0626\u0627\u062f\u0631\u0649\u0633", +"Target": "\u0646\u0649\u0634\u0627\u0646", +"None": "\u064a\u0648\u0642", +"New window": "\u064a\u06d0\u06ad\u0649 \u0643\u06c6\u0632\u0646\u06d5\u0643", +"Remove link": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634 \u0626\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Anchors": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634", +"Link": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634", +"Paste or type a link": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634 \u0686\u0627\u067e\u0644\u0627\u06ad \u064a\u0627\u0643\u0649 \u0643\u0649\u0631\u06af\u06c8\u0632\u06c8\u06ad", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0633\u0649\u0632 \u0643\u0649\u0631\u06af\u06c8\u0632\u06af\u06d5\u0646 URL \u0628\u0649\u0631 \u0626\u06d0\u0644\u062e\u06d5\u062a \u0626\u0627\u062f\u0631\u06d0\u0633\u0649\u062f\u06d5\u0643 \u0642\u0649\u0644\u0649\u067e \u062a\u06c7\u0631\u0649\u062f\u06c7\u060c\u062a\u06d5\u0644\u06d5\u067e \u0642\u0649\u0644\u0649\u0646\u063a\u0627\u0646 mailto \u0646\u0649 \u0642\u06c7\u0634\u0627\u0645\u0633\u0649\u0632\u061f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0633\u0649\u0632 \u0643\u0649\u0631\u06af\u06c8\u0632\u06af\u06d5\u0646 \u062a\u0648\u0631 \u0626\u0627\u062f\u0631\u06d0\u0633\u0649 \u0633\u0649\u0631\u062a\u0642\u0649 \u0626\u06c7\u0644\u0627\u0646\u0645\u0649\u062f\u06d5\u0643 \u0642\u0649\u0644\u0649\u067e \u062a\u06c7\u0631\u0649\u062f\u06c7 \u060c\u062a\u06d5\u0644\u06d5\u067e \u0642\u0649\u0644\u0649\u0646\u063a\u0627\u0646 http:\/\/ \u0646\u0649 \u0642\u0648\u0634\u0627\u0645\u0633\u0649\u0632\u061f", +"Link list": "\u0626\u06c7\u0644\u0649\u0646\u0649\u0634 \u062a\u06c8\u0631\u0649", +"Insert video": "\u0633\u0649\u0646 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Insert\/edit video": "\u0633\u0649\u0646 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634\/\u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Insert\/edit media": "\u0645\u06d0\u062f\u0649\u064a\u0627 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634\/\u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Alternative source": "\u062a\u06d5\u0633\u06cb\u0649\u0631\u0649", +"Poster": "\u064a\u0648\u0644\u0644\u0649\u063a\u06c7\u0686\u0649", +"Paste your embed code below:": "\u0642\u0649\u0633\u062a\u06c7\u0631\u0645\u0627\u0642\u0686\u0649 \u0628\u0648\u0644\u063a\u0627\u0646 \u0643\u0648\u062f\u0646\u0649 \u0686\u0627\u067e\u0644\u0627\u06ad", +"Embed": "\u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Media": "\u0645\u06d0\u062f\u0649\u064a\u0627", +"Nonbreaking space": "\u0628\u0648\u0634\u0644\u06c7\u0642", +"Page break": "\u0628\u06d5\u062a \u0626\u0627\u062e\u0649\u0631\u0644\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Paste as text": "\u062a\u06d0\u0643\u0649\u0634 \u0634\u06d5\u0643\u0644\u0649\u062f\u06d5 \u0686\u0627\u067e\u0644\u0627\u0634", +"Preview": "\u0643\u06c6\u0631\u06c8\u0634", +"Print": "\u0628\u06d0\u0633\u0649\u0634", +"Save": "\u0633\u0627\u0642\u0644\u0627\u0634", +"Find": "\u0626\u0649\u0632\u062f\u06d5\u0634", +"Replace with": "\u0626\u0627\u0644\u0645\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Replace": "\u0626\u0627\u0644\u0645\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Replace all": "\u06be\u06d5\u0645\u0645\u0649\u0646\u0649 \u0626\u0627\u0644\u0645\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Prev": "\u0626\u0627\u0644\u062f\u0649\u0646\u0642\u0649\u0633\u0649", +"Next": "\u0643\u06d0\u064a\u0649\u0646\u0643\u0649\u0633\u0649", +"Find and replace": "\u0626\u0649\u0632\u062f\u06d5\u0634 \u06cb\u06d5 \u0626\u0627\u0644\u0645\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Could not find the specified string.": "\u0626\u0649\u0632\u062f\u0649\u0645\u06d5\u0643\u0686\u0649 \u0628\u0648\u0644\u063a\u0627\u0646 \u0645\u06d5\u0632\u0645\u06c7\u0646\u0646\u0649 \u062a\u0627\u067e\u0627\u0644\u0645\u0649\u062f\u0649.", +"Match case": "\u0686\u0648\u06ad \u0643\u0649\u0686\u0649\u0643 \u06be\u06d5\u0631\u0649\u067e\u0646\u0649 \u067e\u06d5\u0631\u0649\u0642\u0644\u06d5\u0646\u062f\u06c8\u0631\u06c8\u0634", +"Whole words": "\u062a\u0648\u0644\u06c7\u0642 \u0645\u0627\u0633\u0644\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Spellcheck": "\u0626\u0649\u0645\u0644\u0627 \u062a\u06d5\u0643\u0634\u06c8\u0631\u06c8\u0634", +"Ignore": "\u0626\u06c6\u062a\u0643\u06c8\u0632\u06c8\u0634", +"Ignore all": "\u06be\u06d5\u0645\u0645\u0649\u0646\u0649 \u0626\u06c6\u062a\u0643\u06c8\u0632\u06c8\u0634", +"Finish": "\u0626\u0627\u062e\u0649\u0631\u0644\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634", +"Add to Dictionary": "\u0644\u06c7\u063a\u06d5\u062a \u0642\u0648\u0634\u06c7\u0634", +"Insert table": "\u062c\u06d5\u062f\u06cb\u06d5\u0644 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Table properties": "\u062c\u06d5\u062f\u06cb\u06d5\u0644 \u062e\u0627\u0633\u0644\u0649\u0642\u0649", +"Delete table": "\u062c\u06d5\u062f\u06cb\u06d5\u0644 \u0626\u06c6\u0686\u06c8\u0631\u0634", +"Cell": "\u0643\u0627\u062a\u06d5\u0643", +"Row": "\u0642\u06c7\u0631", +"Column": "\u0631\u06d5\u062a", +"Cell properties": "\u0643\u0627\u062a\u06d5\u0643 \u062e\u0627\u0633\u0644\u0649\u0642\u0649", +"Merge cells": "\u0643\u0627\u062a\u06d5\u0643 \u0628\u0649\u0631\u0644\u06d5\u0634\u062a\u06c8\u0631\u06c8\u0634", +"Split cell": "\u0643\u0627\u062a\u06d5\u0643 \u067e\u0627\u0631\u0686\u0649\u0644\u0627\u0634", +"Insert row before": "\u0626\u0627\u0644\u062f\u0649\u063a\u0627 \u0642\u06c7\u0631 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Insert row after": "\u0626\u0627\u0631\u0642\u0649\u063a\u0627 \u0642\u06c7\u0631 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Delete row": "\u0642\u06c7\u0631 \u0626\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Row properties": "\u0642\u06c7\u0631 \u062e\u0627\u0633\u0644\u0649\u0642\u0649", +"Cut row": "\u0642\u06c7\u0631 \u0643\u06d0\u0633\u0649\u0634", +"Copy row": "\u0642\u06c7\u0631 \u0643\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Paste row before": "\u0642\u06c7\u0631 \u0626\u0627\u0644\u062f\u0649\u063a\u0627 \u0686\u0627\u067e\u0644\u0627\u0634", +"Paste row after": "\u0642\u06c7\u0631 \u0643\u06d5\u064a\u0646\u0649\u06af\u06d5 \u0686\u0627\u067e\u0644\u0627\u0634", +"Insert column before": "\u0631\u06d5\u062a \u0626\u0627\u0644\u062f\u0649\u063a\u0627 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Insert column after": "\u0631\u06d5\u062a \u0643\u06d5\u064a\u0646\u0649\u06af\u06d5 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Delete column": "\u0631\u06d5\u062a \u0626\u06c6\u0686\u06c8\u0631\u06c8\u0634", +"Cols": "\u0631\u06d5\u062a", +"Rows": "\u0642\u06c7\u0631", +"Width": "\u0643\u06d5\u06ad\u0644\u0649\u0643\u0649", +"Height": "\u0626\u06d0\u06af\u0649\u0632\u0644\u0649\u0643\u0649", +"Cell spacing": "\u0643\u0627\u062a\u06d5\u0643 \u0633\u0649\u0631\u062a\u0642\u0649 \u0626\u0627\u0631\u0649\u0644\u0649\u0642\u0649", +"Cell padding": "\u0643\u0627\u062a\u06d5\u0643 \u0626\u0649\u0686\u0643\u0649 \u0626\u0627\u0631\u0649\u0644\u0649\u0642\u0649", +"Caption": "\u0686\u06c8\u0634\u06d5\u0646\u062f\u06c8\u0631\u06c8\u0634", +"Left": "\u0633\u0648\u0644", +"Center": "\u0645\u06d5\u0631\u0643\u06d5\u0632", +"Right": "\u0626\u0648\u06ad", +"Cell type": "\u0643\u0627\u062a\u06d5\u0643 \u062a\u0649\u067e\u0649", +"Scope": "\u062f\u0627\u0626\u0649\u0631\u06d5", +"Alignment": "\u064a\u06c6\u0644\u0649\u0646\u0649\u0634\u0649", +"H Align": "\u06af\u0648\u0631\u0632\u0649\u0646\u062a\u0627\u0644 \u062a\u0648\u063a\u0631\u0649\u0644\u0627\u0634", +"V Align": "\u06cb\u06d0\u0631\u062a\u0649\u0643\u0627\u0644 \u062a\u0648\u063a\u0631\u0649\u0644\u0627\u0634", +"Top": "\u0626\u06c8\u0633\u062a\u0649", +"Middle": "\u0626\u0648\u062a\u062a\u06c7\u0631\u0633\u0649", +"Bottom": "\u0626\u0627\u0633\u062a\u0649", +"Header cell": "\u0628\u0627\u0634 \u0643\u0627\u062a\u06d5\u0643", +"Row group": "\u0642\u06c7\u0631 \u06af\u06c7\u0631\u06c7\u067e\u067e\u0649\u0633\u0649", +"Column group": "\u0631\u06d5\u062a \u06af\u06c7\u0631\u06c7\u067e\u067e\u0649\u0633\u0649", +"Row type": "\u0642\u06c7\u0631 \u062a\u0649\u067e\u0649", +"Header": "\u0628\u06d0\u0634\u0649", +"Body": "\u0628\u06d5\u062f\u0649\u0646\u0649", +"Footer": "\u067e\u06c7\u062a\u0649", +"Border color": "\u0631\u0627\u0645\u0643\u0627 \u0631\u06d5\u06ad\u06af\u0649", +"Insert template": "\u0626\u06c8\u0644\u06af\u06d5 \u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"Templates": "\u0626\u06c8\u0644\u06af\u0649\u0644\u06d5\u0631", +"Template": "\u0626\u06c8\u0644\u06af\u0649\u0644\u06d5\u0631", +"Text color": "\u062e\u06d5\u062a \u0631\u06d5\u06ad\u06af\u0649", +"Background color": "\u0626\u0627\u0631\u0642\u0627 \u0631\u06d5\u06ad\u06af\u0649", +"Custom...": "\u0626\u0649\u062e\u062a\u0649\u064a\u0627\u0631\u0649", +"Custom color": "\u0626\u0649\u062e\u062a\u0649\u064a\u0627\u0631\u0649 \u0631\u06d5\u06ad", +"No color": "\u0631\u06d5\u06ad \u064a\u0648\u0642", +"Table of Contents": "\u062c\u06d5\u062f\u06d5\u0644\u0646\u0649\u06ad \u0645\u06d5\u0632\u0645\u06c7\u0646\u0649", +"Show blocks": "\u0631\u0627\u064a\u0648\u0646 \u0643\u06c6\u0631\u0633\u0649\u062a\u0649\u0634", +"Show invisible characters": "\u0643\u06c6\u0631\u06c8\u0646\u0645\u06d5\u064a\u062f\u0649\u063a\u0627\u0646 \u06be\u06d5\u0631\u0649\u067e\u0644\u06d5\u0631\u0646\u0649 \u0643\u06c6\u0631\u0633\u0649\u062a\u0649\u0634", +"Words: {0}": "\u0633\u06c6\u0632: {0}", +"{0} words": "{0} \u0633\u06c6\u0632", +"File": "\u06be\u06c6\u062c\u062c\u06d5\u062a", +"Edit": "\u062a\u06d5\u06be\u0631\u0649\u0631\u0644\u06d5\u0634", +"Insert": "\u0642\u0649\u0633\u062a\u06c7\u0631\u06c7\u0634", +"View": "\u0643\u06c6\u0631\u06c8\u0634", +"Format": "\u0641\u0648\u0631\u0645\u0627\u062a", +"Table": "\u062c\u06d5\u062f\u06cb\u06d5\u0644", +"Tools": "\u0642\u06c7\u0631\u0627\u0644", +"Powered by {0}": "\u062a\u06d0\u062e\u0646\u0649\u0643\u0649\u062f\u0627 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0645\u0648\u0644 \u0645\u06d5\u0632\u0645\u06c7\u0646\u0644\u06c7\u0642 \u062a\u06d0\u0643\u06d0\u0633\u0649\u062a \u0631\u0627\u0645\u0643\u0649\u0633\u0649 \u0631\u0627\u064a\u0648\u0646\u0649\u062f\u0627 \u062a\u0649\u0632\u0649\u0645\u0644\u0649\u0643 \u0626\u06c8\u0686\u06c8\u0646 ALT-F9 \u0646\u0649\u060c \u0642\u0648\u0631\u0627\u0644 \u0628\u0627\u0644\u062f\u0649\u0642\u0649 \u0626\u06c8\u0686\u06c8\u0646 ALT-F10 \u0646\u0649\u060c \u064a\u0627\u0631\u062f\u06d5\u0645 \u0626\u06c8\u0686\u06c8\u0646 ALT-0 \u0646\u0649 \u0628\u06d0\u0633\u0649\u06ad" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk.js new file mode 100644 index 0000000000..976ca53ace --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk.js @@ -0,0 +1,261 @@ +tinymce.addI18n('uk',{ +"Redo": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438", +"Undo": "\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438", +"Cut": "\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438", +"Copy": "\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438", +"Paste": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438", +"Select all": "\u0412\u0438\u0434\u0456\u043b\u0438\u0442\u0438 \u0432\u0441\u0435", +"New document": "\u041d\u043e\u0432\u0438\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442", +"Ok": "\u0413\u0430\u0440\u0430\u0437\u0434", +"Cancel": "\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438", +"Visual aids": "\u041d\u0430\u043e\u0447\u043d\u0456 \u043f\u0440\u0438\u043b\u0430\u0434\u0434\u044f", +"Bold": "\u0416\u0438\u0440\u043d\u0438\u0439", +"Italic": "\u041a\u0443\u0440\u0441\u0438\u0432", +"Underline": "\u041f\u0456\u0434\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u0438\u0439", +"Strikethrough": "\u0417\u0430\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u0438\u0439", +"Superscript": "\u0412\u0435\u0440\u0445\u043d\u0456\u0439 \u0456\u043d\u0434\u0435\u043a\u0441", +"Subscript": "\u041d\u0438\u0436\u043d\u0456\u0439 \u0456\u043d\u0434\u0435\u043a\u0441", +"Clear formatting": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0443\u0432\u0430\u043d\u043d\u044f", +"Align left": "\u041f\u043e \u043b\u0456\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Align center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Align right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Justify": "\u041f\u043e \u0448\u0438\u0440\u0438\u043d\u0456", +"Bullet list": "\u041d\u0435\u043d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Numbered list": "\u041d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Decrease indent": "\u0417\u043c\u0435\u043d\u0448\u0438\u0442\u0438\u0442\u0438 \u0432\u0456\u0434\u0441\u0442\u0443\u043f", +"Increase indent": "\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438 \u0432\u0456\u0434\u0441\u0442\u0443\u043f", +"Close": "\u0417\u0430\u043a\u0440\u0438\u0442\u0438", +"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442\u0438", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0454 \u043f\u0440\u044f\u043c\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043e \u0431\u0443\u0444\u0435\u0440\u0443 \u043e\u0431\u043c\u0456\u043d\u0443. \u0411\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u0441\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044f \u043a\u043b\u0430\u0432\u0456\u0448 Ctrl+C\/V\/X.", +"Headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", +"Header 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Header 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Header 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Header 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Header 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Header 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Headings": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Heading 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Heading 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Heading 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Heading 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Heading 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Heading 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Preformatted": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044c\u043e \u0432\u0456\u0434\u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432\u0430\u043d\u0438\u0439", +"Div": "\u0411\u043b\u043e\u043a", +"Pre": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u0454 \u0444\u043e\u0440\u043c\u0430\u0442\u0443\u0432\u0430\u043d\u043d\u044f", +"Code": "\u041a\u043e\u0434", +"Paragraph": "\u041f\u0430\u0440\u0430\u0433\u0440\u0430\u0444", +"Blockquote": "\u0426\u0438\u0442\u0430\u0442\u0430", +"Inline": "\u0412\u0431\u0443\u0434\u043e\u0432\u0430\u043d\u0456", +"Blocks": "\u0411\u043b\u043e\u043a\u0438", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u0437\u0434\u0456\u0439\u0441\u043d\u044e\u0454\u0442\u044c\u0441\u044f \u0443 \u0432\u0438\u0433\u043b\u044f\u0434\u0456 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0443, \u043f\u043e\u043a\u0438 \u043d\u0435 \u0432\u0456\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0434\u0430\u043d\u0443 \u043e\u043f\u0446\u0456\u044e.", +"Font Family": "\u0422\u0438\u043f \u0448\u0440\u0438\u0444\u0442\u0443", +"Font Sizes": "\u0420\u043e\u0437\u043c\u0456\u0440 \u0448\u0440\u0438\u0444\u0442\u0443", +"Class": "\u041a\u043b\u0430\u0441", +"Browse for an image": "\u0412\u0438\u0431\u0456\u0440 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"OR": "\u0410\u0411\u041e", +"Drop an image here": "\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0456\u0442\u044c \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u0441\u044e\u0434\u0438", +"Upload": "\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438", +"Block": "\u0411\u043b\u043e\u043a", +"Align": "\u0412\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"Default": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0438\u0439", +"Circle": "\u041e\u043a\u0440\u0443\u0436\u043d\u043e\u0441\u0442\u0456", +"Disc": "\u041a\u0440\u0443\u0433\u0438", +"Square": "\u041a\u0432\u0430\u0434\u0440\u0430\u0442\u0438", +"Lower Alpha": "\u041c\u0430\u043b\u0456 \u043b\u0430\u0442\u0438\u043d\u0441\u044c\u043a\u0456 \u0431\u0443\u043a\u0432\u0438", +"Lower Greek": "\u041c\u0430\u043b\u0456 \u0433\u0440\u0435\u0446\u044c\u043a\u0456 \u0431\u0443\u043a\u0432\u0438", +"Lower Roman": "\u041c\u0430\u043b\u0456 \u0440\u0438\u043c\u0441\u044c\u043a\u0456 \u0446\u0438\u0444\u0440\u0438", +"Upper Alpha": "\u0412\u0435\u043b\u0438\u043a\u0456 \u043b\u0430\u0442\u0438\u043d\u0441\u044c\u043a\u0456 \u0431\u0443\u043a\u0432\u0438", +"Upper Roman": "\u0420\u0438\u043c\u0441\u044c\u043a\u0456 \u0446\u0438\u0444\u0440\u0438", +"Anchor": "\u042f\u043a\u0456\u0440", +"Name": "\u041d\u0430\u0437\u0432\u0430", +"Id": "\u041a\u043e\u0434", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u041a\u043e\u0434 \u043c\u0430\u0454 \u043f\u043e\u0447\u0438\u043d\u0430\u0442\u0438\u0441\u044f \u0437 \u043b\u0456\u0442\u0435\u0440\u0438 \u0456 \u043c\u043e\u0436\u0435 \u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u043b\u0438\u0448\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u0438 \u043b\u0456\u0442\u0435\u0440, \u0446\u0438\u0444\u0440, \u0434\u0435\u0444\u0456\u0441\u0443, \u043a\u0440\u0430\u043f\u043a\u0438, \u043a\u043e\u043c\u0438 \u0430\u0431\u043e \u043d\u0438\u0436\u043d\u044c\u043e\u0433\u043e \u043f\u0456\u0434\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u043d\u044f.", +"You have unsaved changes are you sure you want to navigate away?": "\u0423 \u0412\u0430\u0441 \u0454 \u043d\u0435\u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u0456 \u0437\u043c\u0456\u043d\u0438. \u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u043f\u0456\u0442\u0438?", +"Restore last draft": "\u0412\u0456\u0434\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u043e\u0441\u0442\u0430\u043d\u043d\u044c\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0443", +"Special character": "\u0421\u043f\u0435\u0446\u0456\u0430\u043b\u044c\u043d\u0456 \u0441\u0438\u043c\u0432\u043e\u043b\u0438", +"Source code": "\u0412\u0438\u0445\u0456\u0434\u043d\u0438\u0439 \u043a\u043e\u0434", +"Insert\/Edit code sample": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0437\u043c\u0456\u043d\u0438\u0442\u0438 \u043f\u0440\u0438\u043a\u043b\u0430\u0434 \u043a\u043e\u0434\u0443", +"Language": "\u041c\u043e\u0432\u0430", +"Code sample": "\u041f\u0440\u0438\u043a\u043b\u0430\u0434 \u043a\u043e\u0434\u0443", +"Color": "\u043a\u043e\u043b\u0456\u0440", +"R": "\u0427", +"G": "\u0417", +"B": "\u0411", +"Left to right": "\u0417\u043b\u0456\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e", +"Right to left": "\u0421\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0456\u0432\u043e", +"Emoticons": "\u0415\u043c\u043e\u0446\u0456\u0457", +"Document properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430", +"Title": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Keywords": "\u041a\u043b\u044e\u0447\u043e\u0432\u0456 \u0441\u043b\u043e\u0432\u0430", +"Description": "\u041e\u043f\u0438\u0441", +"Robots": "\u0420\u043e\u0431\u043e\u0442\u0438", +"Author": "\u0410\u0432\u0442\u043e\u0440", +"Encoding": "\u041a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f", +"Fullscreen": "\u041f\u043e\u0432\u043d\u043e\u0435\u043a\u0440\u0430\u043d\u043d\u0438\u0439 \u0440\u0435\u0436\u0438\u043c", +"Action": "\u0414\u0456\u044f", +"Shortcut": "\u042f\u0440\u043b\u0438\u043a", +"Help": "\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430", +"Address": "\u0410\u0434\u0440\u0435\u0441\u0430", +"Focus to menubar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043c\u0435\u043d\u044e", +"Focus to toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0456\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445", +"Focus to element path": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0448\u043b\u044f\u0445\u0443", +"Focus to contextual toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0456", +"Insert link (if link plugin activated)": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f (\u044f\u043a\u0449\u043e \u043f\u043b\u0430\u0433\u0456\u043d \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u044c \u0430\u043a\u0442\u0438\u0432\u043e\u0432\u0430\u043d\u0438\u0439)", +"Save (if save plugin activated)": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 (\u044f\u043a\u0449\u043e \u043f\u043b\u0430\u0433\u0456\u043d \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f \u0430\u043a\u0442\u0438\u0432\u043e\u0432\u0430\u043d\u043e)", +"Find (if searchreplace plugin activated)": "\u0417\u043d\u0430\u0439\u0442\u0438 (\u044f\u043a\u0449\u043e \u043f\u043b\u0430\u0433\u0456\u043d \u043f\u043e\u0448\u0443\u043a\u0443 \u0430\u043a\u0442\u0438\u0432\u043e\u0432\u0430\u043d\u043e)", +"Plugins installed ({0}):": "\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0456 \u043f\u043b\u0430\u0433\u0456\u043d\u0438 ({0}):", +"Premium plugins:": "\u041f\u0440\u0435\u043c\u0456\u0443\u043c \u043f\u043b\u0430\u0433\u0456\u043d\u0438:", +"Learn more...": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u043e...", +"You are using {0}": "\u0423 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u0430\u043d\u043d\u0456 {0}", +"Plugins": "\u041f\u043b\u0430\u0433\u0456\u043d\u0438", +"Handy Shortcuts": "\u041a\u043b\u0430\u0432\u0456\u0430\u0442\u0443\u0440\u043d\u0456 \u0441\u043a\u043e\u0440\u043e\u0447\u0435\u043d\u043d\u044f", +"Horizontal line": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0430 \u043b\u0456\u043d\u0456\u044f", +"Insert\/edit image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0437\u043c\u0456\u043d\u0438\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image description": "\u041e\u043f\u0438\u0441 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Source": "\u0414\u0436\u0435\u0440\u0435\u043b\u043e", +"Dimensions": "\u0420\u043e\u0437\u043c\u0456\u0440", +"Constrain proportions": "\u0417\u0431\u0435\u0440\u0456\u0433\u0430\u0442\u0438 \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0456\u0457", +"General": "\u0417\u0430\u0433\u0430\u043b\u044c\u043d\u0456", +"Advanced": "\u0420\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u0456", +"Style": "\u0421\u0442\u0438\u043b\u044c", +"Vertical space": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0438\u0439 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b", +"Horizontal space": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0438\u0439 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b", +"Border": "\u041c\u0435\u0436\u0430", +"Insert image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image": "\u0417\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image list": "\u041f\u0435\u0440\u0435\u043b\u0456\u043a \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044c", +"Rotate counterclockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u043f\u0440\u043e\u0442\u0438 \u0433\u043e\u0434\u0438\u043d\u043d\u0438\u043a\u043e\u0432\u043e\u0457 \u0441\u0442\u0440\u0456\u043b\u043a\u0438", +"Rotate clockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0437\u0430 \u0433\u043e\u0434\u0438\u043d\u043d\u0438\u043a\u043e\u0432\u043e\u044e \u0441\u0442\u0440\u0456\u043b\u043a\u043e\u044e", +"Flip vertically": "\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0456", +"Flip horizontally": "\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0456", +"Edit image": "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image options": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Zoom in": "\u041d\u0430\u0431\u043b\u0438\u0437\u0438\u0442\u0438", +"Zoom out": "\u0412\u0456\u0434\u0434\u0430\u043b\u0438\u0442\u0438", +"Crop": "\u041e\u0431\u0440\u0456\u0437\u0430\u0442\u0438", +"Resize": "\u0417\u043c\u0456\u043d\u0438\u0442\u0438 \u0440\u043e\u0437\u043c\u0456\u0440", +"Orientation": "\u041e\u0440\u0456\u0454\u043d\u0442\u0430\u0446\u0456\u044f", +"Brightness": "\u042f\u0441\u043a\u0440\u0430\u0432\u0456\u0441\u0442\u044c", +"Sharpen": "\u0427\u0456\u0442\u043a\u0456\u0441\u0442\u044c", +"Contrast": "\u041a\u043e\u043d\u0442\u0440\u0430\u0441\u0442", +"Color levels": "\u0420\u0456\u0432\u043d\u0456 \u043a\u043e\u043b\u044c\u043e\u0440\u0456\u0432", +"Gamma": "\u0413\u0430\u043c\u043c\u0430", +"Invert": "\u0406\u043d\u0432\u0435\u0440\u0441\u0456\u044f", +"Apply": "\u0417\u0430\u0441\u0442\u043e\u0441\u0443\u0432\u0430\u0442\u0438", +"Back": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438\u0441\u044f", +"Insert date\/time": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0434\u0430\u0442\u0443\/\u0447\u0430\u0441", +"Date\/time": "\u0414\u0430\u0442\u0430\/\u0447\u0430\u0441", +"Insert link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Insert\/edit link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Text to display": "\u0422\u0435\u043a\u0441\u0442 \u0434\u043b\u044f \u0432\u0456\u0434\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Url": "\u0410\u0434\u0440\u0435\u0441\u0430 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Target": "\u0412\u0456\u0434\u043a\u0440\u0438\u0432\u0430\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"None": "\u041d\u0456", +"New window": "\u0423 \u043d\u043e\u0432\u043e\u043c\u0443 \u0432\u0456\u043a\u043d\u0456", +"Remove link": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Anchors": "\u042f\u043a\u043e\u0440\u0456", +"Link": "\u041f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Paste or type a link": "\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u0438 \u0430\u0431\u043e \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0421\u0445\u043e\u0436\u0435, \u0449\u043e \u0432\u0438 \u0432\u0432\u0435\u043b\u0438 \u0430\u0434\u0440\u0435\u0441\u0443 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438. \u0412\u0438 \u0431\u0430\u0436\u0430\u0454\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 mailto: \u043f\u0440\u0435\u0444\u0456\u043a\u0441?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0421\u0445\u043e\u0436\u0435, \u0449\u043e \u0432\u0438 \u0432\u0432\u0435\u043b\u0438 \u0437\u043e\u0432\u043d\u0456\u0448\u043d\u0454 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f. \u0412\u0438 \u0431\u0430\u0436\u0430\u0454\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 http:\/\/ \u043f\u0440\u0435\u0444\u0456\u043a\u0441?", +"Link list": "\u041f\u0435\u0440\u0435\u043b\u0456\u043a \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u044c", +"Insert video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0432\u0456\u0434\u0435\u043e", +"Insert\/edit video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0432\u0456\u0434\u0435\u043e", +"Insert\/edit media": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0430\u0443\u0434\u0456\u043e", +"Alternative source": "\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u0435 \u0434\u0436\u0435\u0440\u0435\u043b\u043e", +"Poster": "\u0417\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Paste your embed code below:": "\u0412\u0441\u0442\u0430\u0432\u0442\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0438\u0436\u0447\u0435:", +"Embed": "\u041a\u043e\u0434 \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438", +"Media": "\u041c\u0435\u0434\u0456\u0430\u0434\u0430\u043d\u0456", +"Nonbreaking space": "\u041d\u0435\u0440\u043e\u0437\u0440\u0438\u0432\u043d\u0438\u0439 \u043f\u0440\u043e\u0431\u0456\u043b", +"Page break": "\u0420\u043e\u0437\u0440\u0438\u0432 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438", +"Paste as text": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u044f\u043a \u0442\u0435\u043a\u0441\u0442", +"Preview": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u0456\u0439 \u043f\u0435\u0440\u0435\u0433\u043b\u044f\u0434", +"Print": "\u0414\u0440\u0443\u043a\u0443\u0432\u0430\u0442\u0438", +"Save": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438", +"Find": "\u0417\u043d\u0430\u0439\u0442\u0438", +"Replace with": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u043d\u0430", +"Replace": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438", +"Replace all": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u0432\u0441\u0435", +"Prev": "\u0412\u0433\u043e\u0440\u0443", +"Next": "\u0412\u043d\u0438\u0437", +"Find and replace": "\u041f\u043e\u0448\u0443\u043a \u0456 \u0437\u0430\u043c\u0456\u043d\u0430", +"Could not find the specified string.": "\u0412\u043a\u0430\u0437\u0430\u043d\u0438\u0439 \u0440\u044f\u0434\u043e\u043a \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e", +"Match case": "\u0412\u0440\u0430\u0445\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0440\u0435\u0433\u0456\u0441\u0442\u0440", +"Whole words": "\u0426\u0456\u043b\u0456 \u0441\u043b\u043e\u0432\u0430", +"Spellcheck": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u043e\u0440\u0444\u043e\u0433\u0440\u0430\u0444\u0456\u0457", +"Ignore": "\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438", +"Ignore all": "\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0432\u0441\u0435", +"Finish": "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438", +"Add to Dictionary": "\u0414\u043e\u0434\u0430\u0442\u0438 \u0434\u043e \u0421\u043b\u043e\u0432\u043d\u0438\u043a\u0430", +"Insert table": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044e", +"Table properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0442\u0430\u0431\u043b\u0438\u0446\u0456", +"Delete table": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044e", +"Cell": "\u041a\u043e\u043c\u0456\u0440\u043a\u0430", +"Row": "\u0420\u044f\u0434\u043e\u043a", +"Column": "\u0421\u0442\u043e\u0432\u043f\u0435\u0446\u044c", +"Cell properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Merge cells": "\u041e\u0431'\u0454\u0434\u043d\u0430\u0442\u0438 \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Split cell": "\u0420\u043e\u0437\u0431\u0438\u0442\u0438 \u043a\u043e\u043c\u0456\u0440\u043a\u0443", +"Insert row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u0439 \u0440\u044f\u0434\u043e\u043a \u0437\u0432\u0435\u0440\u0445\u0443", +"Insert row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u0439 \u0440\u044f\u0434\u043e\u043a \u0437\u043d\u0438\u0437\u0443", +"Delete row": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Row properties": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0440\u044f\u0434\u043a\u0430", +"Cut row": "\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Copy row": "\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Paste row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a \u0437\u0432\u0435\u0440\u0445\u0443", +"Paste row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a \u0437\u043d\u0438\u0437\u0443", +"Insert column before": "\u0414\u043e\u0434\u0430\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c \u043b\u0456\u0432\u043e\u0440\u0443\u0447", +"Insert column after": "\u0414\u043e\u0434\u0430\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c \u043f\u0440\u0430\u0432\u043e\u0440\u0443\u0447", +"Delete column": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c", +"Cols": "\u0421\u0442\u043e\u0432\u043f\u0446\u0456", +"Rows": "\u0420\u044f\u0434\u043a\u0438", +"Width": "\u0428\u0438\u0440\u0438\u043d\u0430", +"Height": "\u0412\u0438\u0441\u043e\u0442\u0430", +"Cell spacing": "\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u044c \u043c\u0456\u0436 \u043a\u043e\u043c\u0456\u0440\u043a\u0430\u043c\u0438", +"Cell padding": "\u041f\u043e\u043b\u044f \u043a\u043e\u043c\u0456\u0440\u043e\u043a", +"Caption": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Left": "\u041f\u043e \u043b\u0456\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Right": "\u041f\u043e \u043f\u0440\u0430\u0432\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Cell type": "\u0422\u0438\u043f \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Scope": "\u0421\u0444\u0435\u0440\u0430", +"Alignment": "\u0412\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"H Align": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0435 \u0432\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"V Align": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0435 \u0432\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"Top": "\u041f\u043e \u0432\u0435\u0440\u0445\u043d\u044c\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Middle": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Bottom": "\u041f\u043e \u043d\u0438\u0436\u043d\u044c\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Header cell": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Row group": "\u0413\u0440\u0443\u043f\u0430 \u0440\u044f\u0434\u043a\u0456\u0432", +"Column group": "\u0413\u0440\u0443\u043f\u0430 \u0441\u0442\u043e\u0432\u043f\u0446\u0456\u0432", +"Row type": "\u0422\u0438\u043f \u0440\u044f\u0434\u043a\u0430", +"Header": "\u0412\u0435\u0440\u0445\u043d\u0456\u0439 \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b", +"Body": "\u0422\u0456\u043b\u043e", +"Footer": "\u041d\u0438\u0436\u043d\u0456\u0439 \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b", +"Border color": "\u043a\u043e\u043b\u0456\u0440 \u0440\u0430\u043c\u043a\u0438", +"Insert template": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0448\u0430\u0431\u043b\u043e\u043d", +"Templates": "\u0428\u0430\u0431\u043b\u043e\u043d\u0438", +"Template": "\u0428\u0430\u0431\u043b\u043e\u043d", +"Text color": "\u041a\u043e\u043b\u0456\u0440 \u0442\u0435\u043a\u0441\u0442\u0443", +"Background color": "\u041a\u043e\u043b\u0456\u0440 \u0444\u043e\u043d\u0443", +"Custom...": "\u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0446\u044c\u043a\u0438\u0439", +"Custom color": "\u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0446\u044c\u043a\u0438\u0439 \u043a\u043e\u043b\u0456\u0440", +"No color": "\u0431\u0435\u0437 \u043a\u043e\u043b\u044c\u043e\u0440\u0443", +"Table of Contents": "\u0417\u043c\u0456\u0441\u0442", +"Show blocks": "\u041f\u043e\u043a\u0430\u0437\u0443\u0432\u0430\u0442\u0438 \u0431\u043b\u043e\u043a\u0438", +"Show invisible characters": "\u041f\u043e\u043a\u0430\u0437\u0443\u0432\u0430\u0442\u0438 \u043d\u0435\u0432\u0438\u0434\u0438\u043c\u0456 \u0441\u0438\u043c\u0432\u043e\u043b\u0438", +"Words: {0}": "\u041a\u0456\u043b\u044c\u043a\u0456\u0441\u0442\u044c \u0441\u043b\u0456\u0432: {0}", +"{0} words": "{0} \u0441\u043b\u0456\u0432", +"File": "\u0424\u0430\u0439\u043b", +"Edit": "\u0417\u043c\u0456\u043d\u0438\u0442\u0438", +"Insert": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438", +"View": "\u0412\u0438\u0433\u043b\u044f\u0434", +"Format": "\u0424\u043e\u0440\u043c\u0430\u0442", +"Table": "\u0422\u0430\u0431\u043b\u0438\u0446\u044f", +"Tools": "\u0406\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438", +"Powered by {0}": "\u041f\u0440\u0430\u0446\u044e\u0454 \u043d\u0430 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u0422\u0435\u043a\u0441\u0442\u043e\u0432\u0435 \u043f\u043e\u043b\u0435. \u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ALT-F9 \u0449\u043e\u0431 \u0432\u0438\u043a\u043b\u0438\u043a\u0430\u0442\u0438 \u043c\u0435\u043d\u044e, ALT-F10 \u043f\u0430\u043d\u0435\u043b\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0456\u0432, ALT-0 \u0434\u043b\u044f \u0432\u0438\u043a\u043b\u0438\u043a\u0443 \u0434\u043e\u043f\u043e\u043c\u043e\u0433\u0438." +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk_UA.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk_UA.js new file mode 100644 index 0000000000..b2d3bae286 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uk_UA.js @@ -0,0 +1,261 @@ +tinymce.addI18n('uk_UA',{ +"Redo": "\u0412\u0456\u0434\u043d\u043e\u0432\u0438\u0442\u0438", +"Undo": "\u0412\u0456\u0434\u043c\u0456\u043d\u0438\u0442\u0438", +"Cut": "\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438", +"Copy": "\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438", +"Paste": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438", +"Select all": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0443\u0441\u0435", +"New document": "\u041d\u043e\u0432\u0438\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442", +"Ok": "Ok", +"Cancel": "\u0412\u0456\u0434\u043c\u0456\u043d\u0438\u0442\u0438", +"Visual aids": "\u0412\u0456\u0437\u0443\u0430\u043b\u044c\u043d\u0456 \u0437\u0430\u0441\u043e\u0431\u0438", +"Bold": "\u0416\u0438\u0440\u043d\u0438\u0439", +"Italic": "\u041a\u0443\u0440\u0441\u0438\u0432", +"Underline": "\u041f\u0456\u0434\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u0438\u0439", +"Strikethrough": "\u041f\u0435\u0440\u0435\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u0438\u0439", +"Superscript": "\u0412\u0435\u0440\u0445\u043d\u0456\u0439 \u0456\u043d\u0434\u0435\u043a\u0441", +"Subscript": "\u0406\u043d\u0434\u0435\u043a\u0441", +"Clear formatting": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0443\u0432\u0430\u043d\u043d\u044f", +"Align left": "\u041b\u0456\u0432\u043e\u0440\u0443\u0447", +"Align center": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Align right": "\u041f\u0440\u0430\u0432\u043e\u0440\u0443\u0447", +"Justify": "\u0412\u0438\u0440\u0456\u0432\u043d\u044f\u0442\u0438", +"Bullet list": "\u041c\u0430\u0440\u043a\u0456\u0440\u043e\u0432\u0430\u043d\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Numbered list": "\u041f\u0440\u043e\u043d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", +"Decrease indent": "\u0417\u043c\u0435\u043d\u0448\u0438\u0442\u0438 \u0432\u0456\u0434\u0441\u0442\u0443\u043f", +"Increase indent": "\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438 \u0432\u0456\u0434\u0441\u0442\u0443\u043f", +"Close": "\u0417\u0430\u043a\u0440\u0438\u0442\u0438", +"Formats": "\u0424\u043e\u0440\u043c\u0430\u0442\u0438", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0454 \u043f\u0440\u044f\u043c\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043e \u0431\u0443\u0444\u0435\u0440\u0430 \u043e\u0431\u043c\u0456\u043d\u0443. \u0417\u0430\u043c\u0456\u0441\u0442\u044c \u0446\u044c\u043e\u0433\u043e \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u043f\u043e\u0454\u0434\u043d\u0430\u043d\u043d\u044f \u043a\u043b\u0430\u0432\u0456\u0448 Ctrl + X\/C\/V.", +"Headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", +"Header 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Header 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Header 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Header 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Header 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Header 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Headings": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", +"Heading 1": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 1", +"Heading 2": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 2", +"Heading 3": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 3", +"Heading 4": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 4", +"Heading 5": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 5", +"Heading 6": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a 6", +"Preformatted": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044c\u043e \u0432\u0456\u0434\u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432\u0430\u043d\u0438\u0439", +"Div": "Div", +"Pre": "Pre", +"Code": "\u041a\u043e\u0434", +"Paragraph": "\u0410\u0431\u0437\u0430\u0446", +"Blockquote": "\u0426\u0438\u0442\u0430\u0442\u0430", +"Inline": "\u0412\u0431\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0439", +"Blocks": "\u0411\u043b\u043e\u043a\u0438", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u0437\u0430\u0440\u0430\u0437 \u0432 \u0440\u0435\u0436\u0438\u043c\u0456 \u0437\u0432\u0438\u0447\u0430\u0439\u043d\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0443. \u0417\u043c\u0456\u0441\u0442 \u0431\u0443\u0434\u0435 \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u044f\u043a \u043f\u0440\u043e\u0441\u0442\u0438\u0439 \u0442\u0435\u043a\u0441\u0442, \u043f\u043e\u043a\u0438 \u0412\u0438 \u043d\u0435 \u0432\u0438\u043c\u043a\u043d\u0435\u0442\u0435 \u0446\u044e \u043e\u043f\u0446\u0456\u044e.", +"Font Family": "\u0428\u0440\u0438\u0444\u0442", +"Font Sizes": "\u0420\u043e\u0437\u043c\u0456\u0440 \u0448\u0440\u0438\u0444\u0442\u0430", +"Class": "\u041a\u043b\u0430\u0441", +"Browse for an image": "\u0412\u0438\u0431\u0456\u0440 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"OR": "\u0410\u0411\u041e", +"Drop an image here": "\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0456\u0442\u044c \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u0441\u044e\u0434\u0438", +"Upload": "\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438", +"Block": "\u0411\u043b\u043e\u043a", +"Align": "\u0412\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"Default": "\u0423\u043c\u043e\u0432\u0447\u0430\u043d\u043d\u044f", +"Circle": "\u041a\u043e\u043b\u043e", +"Disc": "\u0414\u0438\u0441\u043a", +"Square": "\u041a\u0432\u0430\u0434\u0440\u0430\u0442", +"Lower Alpha": "\u041d\u0438\u0436\u043d\u0456\u0439 \u0440\u0435\u0433\u0456\u0441\u0442\u0440", +"Lower Greek": "\u041c\u0430\u043b\u0456 \u0433\u0440\u0435\u0446\u044c\u043a\u0456 \u043b\u0456\u0442\u0435\u0440\u0438", +"Lower Roman": "\u0420\u0438\u043c\u0441\u044c\u043a\u0456 \u0446\u0438\u0444\u0440\u0438 \u0443 \u043d\u0438\u0436\u043d\u044c\u043e\u043c\u0443 \u0440\u0435\u0433\u0456\u0441\u0442\u0440\u0456", +"Upper Alpha": "\u0412\u0435\u0440\u0445\u043d\u0456\u0439 \u0440\u0435\u0433\u0456\u0441\u0442\u0440", +"Upper Roman": "\u0420\u0438\u043c\u0441\u044c\u043a\u0456 \u0446\u0438\u0444\u0440\u0438 \u0443 \u0432\u0435\u0440\u0445\u043d\u044c\u043e\u043c\u0443 \u0440\u0435\u0433\u0456\u0441\u0442\u0440\u0456", +"Anchor": "\u041f\u0440\u0438\u0432'\u044f\u0437\u043a\u0430", +"Name": "\u0406\u043c'\u044f", +"Id": "\u041a\u043e\u0434", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u041a\u043e\u0434 \u043c\u0430\u0454 \u043f\u043e\u0447\u0438\u043d\u0430\u0442\u0438\u0441\u044f \u0437 \u043b\u0456\u0442\u0435\u0440\u0438 \u0456 \u043c\u043e\u0436\u0435 \u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u043b\u0438\u0448\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u0438 \u043b\u0456\u0442\u0435\u0440, \u0446\u0438\u0444\u0440, \u0434\u0435\u0444\u0456\u0441\u0443, \u043a\u0440\u0430\u043f\u043a\u0438, \u043a\u043e\u043c\u0438 \u0430\u0431\u043e \u043d\u0438\u0436\u043d\u044c\u043e\u0433\u043e \u043f\u0456\u0434\u043a\u0440\u0435\u0441\u043b\u0435\u043d\u043d\u044f.", +"You have unsaved changes are you sure you want to navigate away?": "\u0423 \u0432\u0430\u0441 \u0454 \u043d\u0435\u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u0456 \u0437\u043c\u0456\u043d\u0438. \u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u043f\u0456\u0442\u0438 ?", +"Restore last draft": "\u0412\u0456\u0434\u043d\u043e\u0432\u0438\u0442\u0438 \u043e\u0441\u0442\u0430\u043d\u043d\u0456\u0439 \u043f\u0440\u043e\u0435\u043a\u0442", +"Special character": "\u0421\u043f\u0435\u0446\u0456\u0430\u043b\u044c\u043d\u0438\u0439 \u0441\u0438\u043c\u0432\u043e\u043b", +"Source code": "\u0414\u0436\u0435\u0440\u0435\u043b\u043e", +"Insert\/Edit code sample": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u0438 \u043f\u0440\u0438\u043a\u043b\u0430\u0434 \u043a\u043e\u0434\u0443", +"Language": "\u041c\u043e\u0432\u0430", +"Code sample": "\u041f\u0440\u0438\u043a\u043b\u0430\u0434 \u043a\u043e\u0434\u0443", +"Color": "\u041a\u043e\u043b\u0456\u0440", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u0417\u043b\u0456\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e", +"Right to left": "\u0421\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0456\u0432\u043e", +"Emoticons": "\u0421\u043c\u0430\u0439\u043b\u0438", +"Document properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443", +"Title": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Keywords": "\u041a\u043b\u044e\u0447\u043e\u0432\u0456 \u0441\u043b\u043e\u0432\u0430", +"Description": "\u041e\u043f\u0438\u0441", +"Robots": "\u0420\u043e\u0431\u043e\u0442\u0438", +"Author": "\u0410\u0432\u0442\u043e\u0440", +"Encoding": "\u041a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f", +"Fullscreen": "\u041d\u0430 \u0432\u0435\u0441\u044c \u0435\u043a\u0440\u0430\u043d", +"Action": "\u0414\u0456\u044f", +"Shortcut": "\u042f\u0440\u043b\u0438\u043a", +"Help": "\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430", +"Address": "\u0410\u0434\u0440\u0435\u0441\u0430", +"Focus to menubar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043c\u0435\u043d\u044e", +"Focus to toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0456\u043d\u0441\u0442\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445", +"Focus to element path": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0448\u043b\u044f\u0445\u0443", +"Focus to contextual toolbar": "\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442", +"Insert link (if link plugin activated)": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f (\u044f\u043a\u0449\u043e \u0434\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u043e)", +"Save (if save plugin activated)": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 (\u044f\u043a\u0449\u043e \u0434\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u043e)", +"Find (if searchreplace plugin activated)": "\u0417\u043d\u0430\u0439\u0442\u0438 (\u044f\u043a\u0449\u043e \u0434\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u043e)", +"Plugins installed ({0}):": "\u041d\u0430\u044f\u0432\u043d\u0456 \u0434\u043e\u0434\u0430\u0442\u043a\u0438 ({0}):", +"Premium plugins:": "\u041f\u0440\u0435\u043c\u0456\u0430\u043b\u044c\u043d\u0456 \u0434\u043e\u0434\u0430\u0442\u043a\u0438:", +"Learn more...": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u043e...", +"You are using {0}": "\u0423 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u0430\u043d\u043d\u0456 {0}", +"Plugins": "\u041f\u043b\u0430\u0433\u0456\u043d\u0438", +"Handy Shortcuts": "\u041a\u043e\u0440\u0438\u0441\u043d\u0456 \u044f\u0440\u043b\u0438\u043a\u0438", +"Horizontal line": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0430 \u043b\u0456\u043d\u0456\u044f", +"Insert\/edit image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image description": "\u041e\u043f\u0438\u0441 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Source": "\u0414\u0436\u0435\u0440\u0435\u043b\u043e", +"Dimensions": "\u0420\u043e\u0437\u043c\u0456\u0440", +"Constrain proportions": "\u0417\u0431\u0435\u0440\u0456\u0433\u0430\u0442\u0438 \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0456\u0457", +"General": "\u0417\u0430\u0433\u0430\u043b\u044c\u043d\u0435", +"Advanced": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u043e", +"Style": "\u0421\u0442\u0438\u043b\u044c", +"Vertical space": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0438\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a", +"Horizontal space": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0438\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a", +"Border": "\u041c\u0435\u0436\u0430", +"Insert image": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image": "\u0417\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image list": "\u0421\u043f\u0438\u0441\u043e\u043a \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044c", +"Rotate counterclockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u043f\u0440\u043e\u0442\u0438 \u0433\u043e\u0434\u0438\u043d\u043d\u0438\u043a\u043e\u0432\u043e\u0457 \u0441\u0442\u0440\u0456\u043b\u043a\u0438", +"Rotate clockwise": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0437\u0430 \u0433\u043e\u0434\u0438\u043d\u043d\u0438\u043a\u043e\u0432\u043e\u044e \u0441\u0442\u0440\u0456\u043b\u043a\u043e\u044e", +"Flip vertically": "\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0456", +"Flip horizontally": "\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0456", +"Edit image": "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Image options": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Zoom in": "\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438", +"Zoom out": "\u0417\u043c\u0435\u043d\u0448\u0438\u0442\u0438", +"Crop": "\u041e\u0431\u0440\u0456\u0437\u0430\u0442\u0438", +"Resize": "\u0417\u043c\u0456\u043d\u0438\u0442\u0438 \u0440\u043e\u0437\u043c\u0456\u0440", +"Orientation": "\u041e\u0440\u0456\u0454\u043d\u0442\u0430\u0446\u0456\u044f", +"Brightness": "\u042f\u0441\u043a\u0440\u0430\u0432\u0456\u0441\u0442\u044c", +"Sharpen": "\u0427\u0456\u0442\u043a\u0456\u0441\u0442\u044c", +"Contrast": "\u041a\u043e\u043d\u0442\u0440\u0430\u0441\u0442", +"Color levels": "\u0420\u0456\u0432\u043d\u0456 \u043a\u043e\u043b\u044c\u043e\u0440\u0456\u0432", +"Gamma": "\u0413\u0430\u043c\u043c\u0430", +"Invert": "\u0406\u043d\u0432\u0435\u0440\u0441\u0456\u044f", +"Apply": "\u0417\u0430\u0441\u0442\u043e\u0441\u0443\u0432\u0430\u0442\u0438", +"Back": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438\u0441\u044f", +"Insert date\/time": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0434\u0430\u0442\u0443\/\u0447\u0430\u0441", +"Date\/time": "\u0414\u0430\u0442\u0430\/\u0447\u0430\u0441", +"Insert link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Insert\/edit link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Text to display": "\u0422\u0435\u043a\u0441\u0442 \u0434\u043b\u044f \u0432\u0456\u0434\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f", +"Url": "URL", +"Target": "\u041c\u0435\u0442\u0430", +"None": "\u041d\u0456", +"New window": "\u041d\u043e\u0432\u0435 \u0432\u0456\u043a\u043d\u043e", +"Remove link": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Anchors": "\u042f\u043a\u043e\u0440\u044f", +"Link": "\u041f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"Paste or type a link": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0430\u0431\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u0438 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u0421\u0445\u043e\u0436\u0435, \u0449\u043e \u0432\u0438 \u0432\u0432\u0435\u043b\u0438 \u0430\u0434\u0440\u0435\u0441\u0443 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438. \u0412\u0438 \u0431\u0430\u0436\u0430\u0454\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 \u043f\u0440\u0435\u0444\u0456\u043a\u0441 mailto:?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u0421\u0445\u043e\u0436\u0435, \u0449\u043e \u0432\u0438 \u0432\u0432\u0435\u043b\u0438 \u0437\u043e\u0432\u043d\u0456\u0448\u043d\u0454 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f. \u0412\u0438 \u0431\u0430\u0436\u0430\u0454\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 \u043f\u0440\u0435\u0444\u0456\u043a\u0441 http:\/\/?", +"Link list": "\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u044c", +"Insert video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0432\u0456\u0434\u0435\u043e", +"Insert\/edit video": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0432\u0456\u0434\u0435\u043e", +"Insert\/edit media": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438\/\u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043c\u0435\u0434\u0456\u0430\u0434\u0430\u043d\u0456", +"Alternative source": "\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u0435 \u0434\u0436\u0435\u0440\u0435\u043b\u043e", +"Poster": "\u041f\u043b\u0430\u043a\u0430\u0442", +"Paste your embed code below:": "\u0412\u0441\u0442\u0430\u0432\u0442\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0438\u0436\u0447\u0435:", +"Embed": "\u0412\u043f\u0440\u043e\u0432\u0430\u0434\u0438\u0442\u0438", +"Media": "\u041c\u0435\u0434\u0456\u0430\u0434\u0430\u043d\u0456", +"Nonbreaking space": "\u041d\u0435\u0440\u043e\u0437\u0440\u0438\u0432\u043d\u0438\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a", +"Page break": "\u0420\u043e\u0437\u0440\u0438\u0432 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438", +"Paste as text": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u044f\u043a \u0442\u0435\u043a\u0441\u0442", +"Preview": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u0456\u0439 \u043f\u0435\u0440\u0435\u0433\u043b\u044f\u0434", +"Print": "\u0414\u0440\u0443\u043a", +"Save": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438", +"Find": "\u0417\u043d\u0430\u0439\u0442\u0438", +"Replace with": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u043d\u0430", +"Replace": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438", +"Replace all": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u0432\u0441\u0435", +"Prev": "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u0456\u0439", +"Next": "\u041d\u0430\u0441\u0442\u0443\u043f\u043d\u0438\u0439", +"Find and replace": "\u0417\u043d\u0430\u0439\u0442\u0438 \u0456 \u0437\u0430\u043c\u0456\u043d\u0438\u0442\u0438", +"Could not find the specified string.": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u043d\u0430\u0439\u0442\u0438 \u0437\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0440\u044f\u0434\u043e\u043a.", +"Match case": "\u0417 \u0443\u0440\u0430\u0445\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0440\u0435\u0433\u0456\u0441\u0442\u0440\u0443", +"Whole words": "\u0426\u0456\u043b\u0456 \u0441\u043b\u043e\u0432\u0430", +"Spellcheck": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u043e\u0440\u0444\u043e\u0433\u0440\u0430\u0444\u0456\u0457", +"Ignore": "\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438", +"Ignore all": "\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0432\u0441\u0435", +"Finish": "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438", +"Add to Dictionary": "\u0414\u043e\u0434\u0430\u0442\u0438 \u0432 \u0441\u043b\u043e\u0432\u043d\u0438\u043a", +"Insert table": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044e", +"Table properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0442\u0430\u0431\u043b\u0438\u0446\u0456", +"Delete table": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044e", +"Cell": "\u041a\u043e\u043c\u0456\u0440\u043a\u0430", +"Row": "\u0420\u044f\u0434\u043e\u043a", +"Column": "\u0421\u0442\u043e\u0432\u043f\u0435\u0446\u044c", +"Cell properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Merge cells": "\u041e\u0431'\u0454\u0434\u043d\u0430\u0442\u0438 \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Split cell": "\u0420\u043e\u0437\u0431\u0438\u0442\u0438 \u043a\u043e\u043c\u0456\u0440\u043a\u0443", +"Insert row before": "\u0412\u0441\u0442\u0430\u0432\u0442\u0435 \u0440\u044f\u0434\u043e\u043a \u043f\u0435\u0440\u0435\u0434", +"Insert row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a \u043f\u0456\u0441\u043b\u044f", +"Delete row": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Row properties": "\u0412\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0440\u044f\u0434\u043a\u0430", +"Cut row": "\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Copy row": "\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438 \u0440\u044f\u0434\u043e\u043a", +"Paste row before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a \u043f\u0435\u0440\u0435\u0434", +"Paste row after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0440\u044f\u0434\u043e\u043a \u043f\u0456\u0441\u043b\u044f", +"Insert column before": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c \u043f\u0435\u0440\u0435\u0434", +"Insert column after": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c \u043f\u0456\u0441\u043b\u044f", +"Delete column": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0441\u0442\u043e\u0432\u043f\u0435\u0446\u044c", +"Cols": "\u0421\u0442\u043e\u0432\u043f\u0446\u0456", +"Rows": "\u0420\u044f\u0434\u043a\u0438", +"Width": "\u0428\u0438\u0440\u0438\u043d\u0430", +"Height": "\u0412\u0438\u0441\u043e\u0442\u0430", +"Cell spacing": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043c\u0456\u0436 \u043a\u043e\u043c\u0456\u0440\u043a\u0430\u043c\u0438", +"Cell padding": "\u0417\u0430\u043f\u043e\u0432\u043d\u0435\u043d\u043d\u044f \u043a\u043e\u043c\u0456\u0440\u043e\u043a", +"Caption": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", +"Left": "\u041b\u0456\u0432\u043e\u0440\u0443\u0447", +"Center": "\u0426\u0435\u043d\u0442\u0440", +"Right": "\u041f\u0440\u0430\u0432\u043e\u0440\u0443\u0447", +"Cell type": "\u0422\u0438\u043f \u043a\u043e\u043c\u0456\u0440\u043a\u0438", +"Scope": "\u0423 \u043c\u0435\u0436\u0430\u0445", +"Alignment": "\u0412\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"H Align": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0435 \u0432\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"V Align": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0435 \u0432\u0438\u0440\u0456\u0432\u043d\u044e\u0432\u0430\u043d\u043d\u044f", +"Top": "\u041f\u043e \u0432\u0435\u0440\u0445\u043d\u044c\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Middle": "\u041f\u043e \u0446\u0435\u043d\u0442\u0440\u0443", +"Bottom": "\u041f\u043e \u043d\u0438\u0436\u043d\u044c\u043e\u043c\u0443 \u043a\u0440\u0430\u044e", +"Header cell": "\u041a\u043e\u043c\u0456\u0440\u043a\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0443", +"Row group": "\u0413\u0440\u0443\u043f\u0430 \u0440\u044f\u0434\u043a\u0456\u0432", +"Column group": "\u0413\u0440\u0443\u043f\u0430 \u0441\u0442\u043e\u0432\u043f\u0446\u0456\u0432", +"Row type": "\u0422\u0438\u043f \u0440\u044f\u0434\u043a\u0430", +"Header": "\u0412\u0435\u0440\u0445\u043d\u0456\u0439 \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b", +"Body": "\u0422\u0456\u043b\u043e", +"Footer": "\u041d\u0438\u0436\u043d\u0456\u0439 \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b", +"Border color": "\u041a\u043e\u043b\u0456\u0440 \u043c\u0435\u0436\u0456", +"Insert template": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0448\u0430\u0431\u043b\u043e\u043d", +"Templates": "\u0428\u0430\u0431\u043b\u043e\u043d\u0438", +"Template": "\u0428\u0430\u0431\u043b\u043e\u043d", +"Text color": "\u041a\u043e\u043b\u0456\u0440 \u0442\u0435\u043a\u0441\u0442\u0443", +"Background color": "\u041a\u043e\u043b\u0456\u0440 \u0444\u043e\u043d\u0443", +"Custom...": "\u0406\u043d\u0448\u0438\u0439...", +"Custom color": "\u041a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0446\u044c\u043a\u0438\u0439 \u043a\u043e\u043b\u0456\u0440", +"No color": "\u0411\u0435\u0437 \u043a\u043e\u043b\u044c\u043e\u0440\u0443", +"Table of Contents": "\u0417\u043c\u0456\u0441\u0442", +"Show blocks": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0431\u043b\u043e\u043a\u0438", +"Show invisible characters": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043d\u0435\u0432\u0438\u0434\u0438\u043c\u0456 \u0441\u0438\u043c\u0432\u043e\u043b\u0438", +"Words: {0}": "\u0421\u043b\u043e\u0432\u0430: {0}", +"{0} words": "{0} \u0441\u043b\u0456\u0432", +"File": "\u0424\u0430\u0439\u043b", +"Edit": "\u041f\u0440\u0430\u0432\u043a\u0430", +"Insert": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438", +"View": "\u0412\u0438\u0434", +"Format": "\u0424\u043e\u0440\u043c\u0430\u0442", +"Table": "\u0422\u0430\u0431\u043b\u0438\u0446\u044f", +"Tools": "\u0406\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438", +"Powered by {0}": "\u0417\u0430 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0438\u043a\u0438 {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c Rich \u0442\u0435\u043a\u0441\u0442\u0443. \u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ALT-F9 - \u043c\u0435\u043d\u044e. \u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ALT-F10 - \u043f\u0430\u043d\u0435\u043b\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0456\u0432. \u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ALT-0 - \u0434\u043e\u0432\u0456\u0434\u043a\u0430" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uz.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uz.js new file mode 100644 index 0000000000..5e977a0825 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/uz.js @@ -0,0 +1,260 @@ +tinymce.addI18n('uz',{ +"Redo": "Bekor qilish", +"Undo": "Orqaga qaytarish", +"Cut": "Kesib olish", +"Copy": "Nusxa olish", +"Paste": "Qo\u2018yish", +"Select all": "Barchasini belgilash", +"New document": "Yangi hujjat", +"Ok": "Bajarish", +"Cancel": "Bekor qilish", +"Visual aids": "Ko\u2018rgazmali o\u2018quv qurollar", +"Bold": "Yo'g'on", +"Italic": "Yotiq", +"Underline": "Tagi chizilgan", +"Strikethrough": "O'chirilgan yozuv", +"Superscript": "Yuqori yozuv", +"Subscript": "Quyi yozuv", +"Clear formatting": "Formatlashni tozalash", +"Align left": "Chapga tekislash", +"Align center": "Markazga tekislash", +"Align right": "O'ngga tekislash", +"Justify": "Ikki tomondan tekislash", +"Bullet list": "Nuqtali ro\u2018yxat", +"Numbered list": "Raqamli ro\u2018yxat", +"Decrease indent": "Satr boshini kamaytirish", +"Increase indent": "Satr boshini oshirish", +"Close": "Yopish", +"Formats": "Formatlar", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Sizning brauzeringiz buferga to\u2018g\u2018ridan-to\u2018g\u2018ri kirish qo\u2018llab-quvvatlamaydi. O\u2018rniga klaviaturaning Ctrl+X\/C\/V qisqartirishlarni foydalaning.", +"Headers": "Sarlavhalar", +"Header 1": "Sarlavha 1", +"Header 2": "Sarlavha 2", +"Header 3": "Sarlavha 3", +"Header 4": "Sarlavha 4", +"Header 5": "Sarlavha 5", +"Header 6": "Sarlavha 6", +"Headings": "Sarlavhalar", +"Heading 1": "Sarlavha 1", +"Heading 2": "Sarlavha 2", +"Heading 3": "Sarlavha 3", +"Heading 4": "Sarlavha 4", +"Heading 5": "Sarlavha 5", +"Heading 6": "Sarlavha 6", +"Div": "Div", +"Pre": "Pre", +"Code": "Kod", +"Paragraph": "Paragraf", +"Blockquote": "Matn blok parchasi", +"Inline": "Bir qator ketma-ketlikda", +"Blocks": "Bloklar", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Qo'shish oddiy matn rejimida amalga oshiriladi. Ushbu hususiyatni o'chirmaguningizcha, kontent oddiy matn sifatida qo'shiladi.", +"Font Family": "Srift turi", +"Font Sizes": "Shrift kattaligi", +"Class": "Klass", +"Browse for an image": "Rasmni yuklash", +"OR": "YOKI", +"Drop an image here": "Bu erga rasmni olib o'ting", +"Upload": "Yuklash", +"Block": "Blok", +"Align": "Saflamoq", +"Default": "Standart", +"Circle": "Doira", +"Disc": "Disk", +"Square": "Kvadrat", +"Lower Alpha": "Kichik lotincha", +"Lower Greek": "Pastki yunon", +"Lower Roman": "Kichik kirilcha", +"Upper Alpha": "Katta lotincha", +"Upper Roman": "Katta kirilcha", +"Anchor": "Langar", +"Name": "Nomi", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id faqat harf bilan boshlanishi lozim, o'z ichiga faqat harflar, sonlar, tire, nuqtalar, pastgi chiziqlardan iborat bo'lishi mumkin", +"You have unsaved changes are you sure you want to navigate away?": "Sizda saqlanmagan o'zgartirishlar bor. Boshqa yerga chiqib ketish uchun ishonchingiz komilmi?", +"Restore last draft": "Oxirgi ", +"Special character": "Maxsus belgilar", +"Source code": "Manba kodi", +"Insert\/Edit code sample": "Kod namunasini qo'shish \/ tahrirlash", +"Language": "Til", +"Code sample": "Kod namunasi", +"Color": "Rang", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Chapdan o'ngga", +"Right to left": "O'ngdan chapga", +"Emoticons": "Hissiyot ikonkalari", +"Document properties": "Hujjatning xususiyatlari", +"Title": "Nomi", +"Keywords": "Kalit so'zlar", +"Description": "Tavsif", +"Robots": "Robotlar", +"Author": "Muallif", +"Encoding": "Kodlash", +"Fullscreen": "Butun ekran rejimi", +"Action": "Harakat", +"Shortcut": "Yorliq", +"Help": "Yordam", +"Address": "Manzil", +"Focus to menubar": "Menubarga e'tibor qaratish", +"Focus to toolbar": "Vositalar paneliga e'tibor qaratish", +"Focus to element path": "Elementlar manziliga e'tibor qaratish", +"Focus to contextual toolbar": "Kontekstli vositalar paneliga e'tibor qaratish", +"Insert link (if link plugin activated)": "Havolani qo'shish (havola plagini o'rnatilgan bo'lsa)", +"Save (if save plugin activated)": "Saqlash (saqlash plagini o'rnatilgan bo'lsa)", +"Find (if searchreplace plugin activated)": "Qidirish (qidirish plagini o'rnatilgan bo'lsa)", +"Plugins installed ({0}):": "O'rnatilgan plaginlar ({0})", +"Premium plugins:": "Premium plaginlar:", +"Learn more...": "Batafsil ma'lumot...", +"You are using {0}": "Siz {0} ishlatmoqdasiz", +"Plugins": "Plaginlar", +"Handy Shortcuts": "Foydalanadigan yorliqlar", +"Horizontal line": "Gorizontal chiziq", +"Insert\/edit image": "Rasmni qo'shish \/ tahrirlash", +"Image description": "Rasm tavsifi", +"Source": "Manba", +"Dimensions": "O'lchamlari", +"Constrain proportions": "Nisbatlarni cheklash", +"General": "Umumiy", +"Advanced": "Ilg'or", +"Style": "Uslub", +"Vertical space": "Vertikal o'lchov", +"Horizontal space": "Gorizontal o'lchov", +"Border": "Chegara", +"Insert image": "Rasm qo'shish", +"Image": "Rasm", +"Image list": "Rasmlar ro'yhati", +"Rotate counterclockwise": "Soatga qarshi yo'nalishda aylantirish", +"Rotate clockwise": "Soat yo'nalishda aylantirish", +"Flip vertically": "Vertikal o'girish", +"Flip horizontally": "Gorizontal o'girish", +"Edit image": "Rasmni tahrirlash", +"Image options": "Rasm imkoniyatlari", +"Zoom in": "Yaqinlashtirish", +"Zoom out": "Uzoqlashtirish", +"Crop": "Kesib olish", +"Resize": "O'lchamini o'zgartirish", +"Orientation": "Orientatsiya", +"Brightness": "Yorqinligi", +"Sharpen": "Keskinligi", +"Contrast": "Ravshanligi", +"Color levels": "Rang sathi", +"Gamma": "Gamma", +"Invert": "Ranglarni ag'darish", +"Apply": "Qo'llash", +"Back": "Ortga qaytish", +"Insert date\/time": "Kun \/ vaqtni qo'shish", +"Date\/time": "Kun\/vaqt", +"Insert link": "Havola qo'shish", +"Insert\/edit link": "Havola qo'shish \/ tahrirlash", +"Text to display": "Ko'rsatiladigan matn", +"Url": "URL", +"Target": "Nishon", +"None": "Hech bir", +"New window": "Yangi oyna", +"Remove link": "Havolani olib tashlash", +"Anchors": "Langarlar", +"Link": "Havola", +"Paste or type a link": "Havolani joylashtirish yoki kiritish", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "Siz kiritgan URL elektron pochta manziliga oxshayapti. \"mailto:\" prefiksi qo'shilsinmi?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "Siz kiritgan URL tashqi havolaga oxshayapti. \"http:\/\/\" prefiksi qo'shilsinmi?", +"Link list": "Havolalar ro'yhati", +"Insert video": "Video qo'shish", +"Insert\/edit video": "Videoni qo'shish \/ tahrirlash", +"Insert\/edit media": "Media qo'shish \/ tahrirlash", +"Alternative source": "Muqobil manba", +"Poster": "Poster", +"Paste your embed code below:": "Kodni quyiga joylashtiring:", +"Embed": "Ichiga olgan", +"Media": "Media", +"Nonbreaking space": "Buzilmas bo'sh joy", +"Page break": "Yangi bet", +"Paste as text": "Tekst qo'shish", +"Preview": "Tahrirni avvaldan ko'rish", +"Print": "Chop etish", +"Save": "Saqlash", +"Find": "Qidirish", +"Replace with": "bilan almashtirish", +"Replace": "Almashtirish", +"Replace all": "Barchasini almashtirish", +"Prev": "Avvalgisi", +"Next": "Keyingisi", +"Find and replace": "Topib almashtirish", +"Could not find the specified string.": "Belgilangan satr topilmadi.", +"Match case": "O'xshashliklar", +"Whole words": "Butun so'z", +"Spellcheck": "Imloni tekshirish", +"Ignore": "E'tiborsiz qoldirish", +"Ignore all": "Barchasini e'tiborsiz qoldirish", +"Finish": "Tugatish", +"Add to Dictionary": "Lug'atga qo'shish", +"Insert table": "Jadvalni qo'shish", +"Table properties": "Jadval xususiyatlari", +"Delete table": "Jadvalni o'chirib tashlash", +"Cell": "Katak", +"Row": "Satr", +"Column": "Ustun", +"Cell properties": "Katak hususiyatlari", +"Merge cells": "Kataklarni birlashtirish", +"Split cell": "Kataklarni bo'lish", +"Insert row before": "Yuqorisiga satr qo'shish", +"Insert row after": "Ketidan satr qo'shish", +"Delete row": "Satrni olib tashlash", +"Row properties": "Satr hususiyatlari", +"Cut row": "Satrni kesib olish", +"Copy row": "Satrdan nusxa ko'chirish", +"Paste row before": "Yuqorisiga satrni joylashtirish", +"Paste row after": "Ketidan satrni joylashtirish", +"Insert column before": "Ustunni oldi tomoniga qo'shish", +"Insert column after": "Ustunni ketidan qo'shish", +"Delete column": "Ustunni olib tashlash", +"Cols": "Ustunlar", +"Rows": "Satrlar", +"Width": "Kengligi", +"Height": "Balandligi", +"Cell spacing": "Kataklar orasi", +"Cell padding": "Kataklar chegarasidan bo'sh joy", +"Caption": "Taglavha", +"Left": "Chapga", +"Center": "Markazga", +"Right": "O'ngga", +"Cell type": "Katak turi", +"Scope": "Muhit", +"Alignment": "Tekislash", +"H Align": "Gorizontal tekislash", +"V Align": "Vertikal tekislash", +"Top": "Yuqoriga", +"Middle": "Markaziga", +"Bottom": "Tagiga", +"Header cell": "Sarlavha katagi", +"Row group": "Satrlar guruhi", +"Column group": "Ustunlar guruhi", +"Row type": "Satr turi", +"Header": "Sarlavha", +"Body": "Tanasi", +"Footer": "Tag qismi", +"Border color": "Chegara rangi", +"Insert template": "Andozani qo'shish", +"Templates": "Andozalar", +"Template": "Andoza", +"Text color": "Matn rangi", +"Background color": "Orqa fon rangi", +"Custom...": "O'zgacha...", +"Custom color": "O'zgacha rang", +"No color": "Rangsiz", +"Table of Contents": "Mundarija", +"Show blocks": "Bloklarni ko'rsatish", +"Show invisible characters": "Ko'rinmas belgilarni ko'rsatish", +"Words: {0}": "So'zlar soni: {0}", +"{0} words": "{0} so`z", +"File": "Fayl", +"Edit": "Tahrirlash", +"Insert": "Qo'shish", +"View": "Ko'rish", +"Format": "Shakllar", +"Table": "Jadval", +"Tools": "Vositalar", +"Powered by {0}": "{0} bilan ishlaydi", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Murakkab matn maydoni. Menyu uchun ALT-F9 tugmalarini bosing. Vositalar paneli uchun ALT-F10 tugmasini bosing. Yordamni chaqirish uchun ALT-0-ni bosing" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/vi_VN.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/vi_VN.js new file mode 100644 index 0000000000..3c0a859d63 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/vi_VN.js @@ -0,0 +1,260 @@ +tinymce.addI18n('vi_VN',{ +"Redo": "Ho\u00e0n t\u00e1t", +"Undo": "Hu\u1ef7 thao t\u00e1c", +"Cut": "C\u1eaft", +"Copy": "Ch\u00e9p", +"Paste": "D\u00e1n", +"Select all": "Ch\u1ecdn t\u1ea5t c\u1ea3", +"New document": "T\u1ea1o t\u00e0i li\u1ec7u m\u1edbi", +"Ok": "OK", +"Cancel": "Hu\u1ef7", +"Visual aids": "Hi\u1ec7n khung so\u1ea1n th\u1ea3o", +"Bold": "T\u00f4 \u0111\u1eadm", +"Italic": "In nghi\u00eang", +"Underline": "G\u1ea1ch d\u01b0\u1edbi", +"Strikethrough": "G\u1ea1ch ngang", +"Superscript": "Tr\u00ean d\u00f2ng", +"Subscript": "D\u01b0\u1edbi d\u00f2ng", +"Clear formatting": "Xo\u00e1 \u0111\u1ecbnh d\u1ea1ng", +"Align left": "Canh tr\u00e1i", +"Align center": "Canh gi\u1eefa", +"Align right": "Canh ph\u1ea3i", +"Justify": "Canh \u0111\u1ec1u hai b\u00ean", +"Bullet list": "D\u1ea5u \u0111\u1ea7u d\u00f2ng", +"Numbered list": "Danh s\u00e1ch s\u1ed1", +"Decrease indent": "L\u00f9i ra", +"Increase indent": "L\u00f9i v\u00e0o", +"Close": "\u0110\u00f3ng", +"Formats": "\u0110\u1ecbnh d\u1ea1ng", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Tr\u00ecnh duy\u1ec7t c\u1ee7a b\u1ea1n kh\u00f4ng h\u1ed7 tr\u1ee3 truy c\u1eadp clipboard, vui l\u00f2ng s\u1eed d\u1ee5ng c\u00e1c t\u1ed5 h\u1ee3p Ctrl + X, C, V.", +"Headers": "\u0110\u1ea7u trang", +"Header 1": "Ti\u00eau \u0111\u1ec1 1", +"Header 2": "Ti\u00eau \u0111\u1ec1 2", +"Header 3": "Ti\u00eau \u0111\u1ec1 3", +"Header 4": "Ti\u00eau \u0111\u1ec1 4", +"Header 5": "Ti\u00eau \u0111\u1ec1 5", +"Header 6": "Ti\u00eau \u0111\u1ec1 6", +"Headings": "Ti\u00eau \u0111\u1ec1", +"Heading 1": "Ti\u00eau \u0111\u1ec1 1", +"Heading 2": "Ti\u00eau \u0111\u1ec1 2", +"Heading 3": "Ti\u00eau \u0111\u1ec1 3", +"Heading 4": "Ti\u00eau \u0111\u1ec1 4", +"Heading 5": "Ti\u00eau \u0111\u1ec1 5", +"Heading 6": "Ti\u00eau \u0111\u1ec1 6", +"Div": "Khung", +"Pre": "\u0110\u1ecbnh d\u1ea1ng", +"Code": "M\u00e3", +"Paragraph": "\u0110o\u1ea1n v\u0103n", +"Blockquote": "Tr\u00edch", +"Inline": "C\u00f9ng d\u00f2ng", +"Blocks": "Bao", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "D\u00e1n b\u00e2y gi\u1edd l\u00e0 \u1edf ch\u1ebf \u0111\u1ed9 v\u0103n b\u1ea3n \u0111\u01a1n gi\u1ea3n. N\u1ed9i dung s\u1ebd \u0111\u01b0\u1ee3c d\u00e1n nh\u01b0 \u0111\u1ed3ng b\u1eb1ng v\u0103n b\u1ea3n cho \u0111\u1ebfn khi b\u1ea1n chuy\u1ec3n \u0111\u1ed5i t\u00f9y ch\u1ecdn n\u00e0y.", +"Font Family": "Ph\u00f4ng", +"Font Sizes": "K\u00edch th\u01b0\u1edbc ph\u00f4ng", +"Class": "L\u1edbp", +"Browse for an image": "Duy\u1ec7t \u1ea3nh", +"OR": "HO\u1eb6C", +"Drop an image here": "Th\u1ea3 h\u00ecnh \u1ea3nh \u1edf \u0111\u00e2y", +"Upload": "T\u1ea3i l\u00ean", +"Block": "Kh\u1ed1i", +"Align": "C\u0103n ch\u1ec9nh", +"Default": "Ng\u1ea7m \u0111\u1ecbnh", +"Circle": "H\u00ecnh tr\u00f2n", +"Disc": "H\u00ecnh tr\u00f2n m\u1ecfng", +"Square": "\u00d4 vu\u00f4ng", +"Lower Alpha": "K\u00fd t\u1ef1 th\u01b0\u1eddng", +"Lower Greek": "S\u1ed1 hy l\u1ea1p th\u01b0\u1eddng", +"Lower Roman": "S\u1ed1 la m\u00e3 th\u01b0\u1eddng", +"Upper Alpha": "K\u00fd t\u1ef1 hoa", +"Upper Roman": "S\u1ed1 la m\u00e3 hoa", +"Anchor": "Neo", +"Name": "T\u00ean", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id ph\u1ea3i b\u1eaft \u0111\u1ea7u b\u1eb1ng m\u1ed9t ch\u1eef c\u00e1i, ch\u1ec9 theo sau b\u1edfi c\u00e1c ch\u1eef c\u00e1i, s\u1ed1, d\u1ea5u g\u1ea1ch ngang, d\u1ea5u ch\u1ea5m, d\u1ea5u hai ch\u1ea5m ho\u1eb7c d\u1ea5u g\u1ea1ch d\u01b0\u1edbi.", +"You have unsaved changes are you sure you want to navigate away?": "B\u1ea1n ch\u01b0a l\u01b0u c\u00e1c thay \u0111\u1ed5i, b\u1ea1n c\u00f3 th\u1eadt s\u1ef1 mu\u1ed1n \u0111\u00f3ng ?", +"Restore last draft": "Ph\u1ee5c h\u1ed3i b\u1ea3n l\u01b0u g\u1ea7n nh\u1ea5t", +"Special character": "K\u00fd t\u1ef1 \u0111\u1eb7c bi\u1ec7t", +"Source code": "M\u00e3 ngu\u1ed3n", +"Insert\/Edit code sample": "Ch\u00e8n\/Ch\u1ec9nh s\u1eeda m\u1eabu", +"Language": "Ng\u00f4n ng\u1eef", +"Code sample": "\u0110o\u1ea1n m\u00e3 m\u1eabu", +"Color": "M\u00e0u", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "Tr\u00e1i sang ph\u1ea3i", +"Right to left": "Ph\u1ea3i sang tr\u00e1i", +"Emoticons": "Bi\u1ec3u t\u01b0\u1ee3ng c\u1ea3m x\u00fac", +"Document properties": "Thu\u1ed9c t\u00ednh t\u00e0i li\u1ec7u", +"Title": "Ti\u00eau \u0111\u1ec1", +"Keywords": "T\u1eeb kho\u00e1", +"Description": "Mi\u00eau t\u1ea3", +"Robots": "Robots", +"Author": "Neo", +"Encoding": "M\u00e3 ho\u00e1", +"Fullscreen": "\u0110\u1ea7y m\u00e0n h\u00ecnh", +"Action": "H\u00e0nh \u0111\u1ed9ng", +"Shortcut": "L\u1ed1i t\u1eaft", +"Help": "Tr\u1ee3 gi\u00fap", +"Address": "\u0110\u1ecba ch\u1ec9", +"Focus to menubar": "G\u1eafn l\u00ean thanh tr\u00ecnh \u0111\u01a1n", +"Focus to toolbar": "G\u1eafn l\u00ean thanh c\u00f4ng c\u1ee5", +"Focus to element path": "G\u1eafn v\u00e0o \u0111\u01b0\u1eddng d\u1eabn", +"Focus to contextual toolbar": "G\u1eafn v\u00e0o thanh c\u00f4ng c\u1ee5 ng\u1eef c\u1ea3nh", +"Insert link (if link plugin activated)": "Ch\u00e8n li\u00ean k\u1ebft (n\u1ebfu plugin li\u00ean k\u1ebft \u0111\u1ea3 k\u00edch ho\u1ea1t)", +"Save (if save plugin activated)": "L\u01b0u (n\u1ebfu plugin l\u01b0u \u0111\u1ea3 k\u00edch ho\u1ea1t)", +"Find (if searchreplace plugin activated)": "T\u00ecm (n\u1ebfu plugin t\u00ecm v\u00e0 thay th\u1ebf \u0111\u1ea3 k\u00edch ho\u1ea1t)", +"Plugins installed ({0}):": "Plugin \u0111\u00e3 c\u00e0i \u0111\u1eb7t ({0}):", +"Premium plugins:": "C\u00e1c Plugin tr\u1ea3 ph\u00ed:", +"Learn more...": "T\u00ecm hi\u1ec3u th\u00eam...", +"You are using {0}": "B\u1ea1n \u0111ang s\u1eed d\u1ee5ng {0}", +"Plugins": "Plugins", +"Handy Shortcuts": "Ph\u00edm t\u1eaft ti\u1ec7n d\u1ee5ng", +"Horizontal line": "G\u1ea1ch ngang", +"Insert\/edit image": "Th\u00eam \/ s\u1eeda h\u00ecnh \u1ea3nh", +"Image description": "Mi\u00eau t\u1ea3 h\u00ecnh \u1ea3nh", +"Source": "Ngu\u1ed3n", +"Dimensions": "K\u00edch th\u01b0\u1edbc", +"Constrain proportions": "H\u1ea1n ch\u1ebf t\u1ef7 l\u1ec7", +"General": "T\u1ed5ng h\u1ee3p", +"Advanced": "N\u00e2ng cao", +"Style": "Ki\u1ec3u", +"Vertical space": "Kho\u1ea3ng c\u00e1ch d\u1ecdc", +"Horizontal space": "Kho\u1ea3ng c\u00e1ch ngang", +"Border": "\u0110\u01b0\u1eddng vi\u1ec1n", +"Insert image": "Ch\u00e8n \u1ea3nh", +"Image": "H\u00ecnh \u1ea3nh", +"Image list": "Danh s\u00e1ch \u1ea3nh", +"Rotate counterclockwise": "Xoay ng\u01b0\u1ee3c chi\u1ec1u kim \u0111\u1ed3ng", +"Rotate clockwise": "Xoay theo chi\u1ec1u kim \u0111\u1ed3ng h\u1ed3", +"Flip vertically": "L\u1eadt d\u1ecdc", +"Flip horizontally": "L\u1eadt ngang", +"Edit image": "S\u1eeda \u1ea3nh", +"Image options": "T\u00f9y ch\u1ecdn h\u00ecnh \u1ea3nh", +"Zoom in": "Ph\u00f3ng to", +"Zoom out": "Thu nh\u1ecf", +"Crop": "X\u00e9n", +"Resize": "Thay \u0111\u1ed5i k\u00edch th\u01b0\u1edbc", +"Orientation": "\u0110\u1ecbnh h\u01b0\u1edbng", +"Brightness": "\u0110\u1ed9 s\u00e1ng", +"Sharpen": "\u0110\u1ed9 s\u1eafc n\u00e9t", +"Contrast": "\u0110\u1ed9 t\u01b0\u01a1ng ph\u1ea3n", +"Color levels": "M\u1ee9c \u0111\u1ed9 m\u00e0u s\u1eafc", +"Gamma": "M\u00e0u Gamma", +"Invert": "\u0110\u1ea3o ng\u01b0\u1ee3c", +"Apply": "\u00c1p d\u1ee5ng", +"Back": "Tr\u1edf l\u1ea1i", +"Insert date\/time": "Th\u00eam ng\u00e0y \/ gi\u1edd", +"Date\/time": "Ng\u00e0y\/gi\u1edd", +"Insert link": "Th\u00eam li\u00ean k\u1ebft", +"Insert\/edit link": "Th\u00eam \/ s\u1eeda li\u00ean k\u1ebft", +"Text to display": "Ch\u1eef hi\u1ec3n th\u1ecb", +"Url": "Li\u00ean k\u1ebft", +"Target": "M\u1ee5c ti\u00eau", +"None": "Kh\u00f4ng", +"New window": "C\u1eeda s\u1ed5 m\u1edbi", +"Remove link": "Xo\u00e1 li\u00ean k\u1ebft", +"Anchors": "Ghim", +"Link": "Li\u00ean k\u1ebft", +"Paste or type a link": "D\u00e1n ho\u1eb7c nh\u1eadp li\u00ean k\u1ebft", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "URL b\u1ea1n nh\u1eadp v\u00e0o c\u00f3 v\u1ebb l\u00e0 m\u1ed9t \u0111\u1ecba ch\u1ec9 email. B\u1ea1n c\u00f3 mu\u1ed1n th\u00eam c\u00e1c y\u00eau c\u1ea7u mailto: ti\u1ec1n t\u1ed1?", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "URL b\u1ea1n nh\u1eadp v\u00e0o c\u00f3 v\u1ebb l\u00e0 m\u1ed9t li\u00ean k\u1ebft b\u00ean ngo\u00e0i. B\u1ea1n c\u00f3 mu\u1ed1n th\u00eam ti\u1ec1n t\u1ed1 http:\/\/ c\u1ea7n thi\u1ebft?", +"Link list": "Danh s\u00e1ch li\u00ean k\u1ebft", +"Insert video": "Th\u00eam video", +"Insert\/edit video": "Th\u00eam \/ s\u1eeda video", +"Insert\/edit media": "Ch\u00e8n\/ch\u1ec9nh s\u1eeda ph\u01b0\u01a1ng ti\u1ec7n truy\u1ec1n th\u00f4ng", +"Alternative source": "Ngu\u1ed3n thay th\u1ebf", +"Poster": "Ng\u01b0\u1eddi \u0111\u0103ng", +"Paste your embed code below:": "D\u00e1n m\u00e3 embed v\u00e0o:", +"Embed": "Embed", +"Media": "Ph\u01b0\u01a1ng ti\u1ec7n truy\u1ec1n th\u00f4ng", +"Nonbreaking space": "Kh\u00f4ng ng\u1eaft kho\u1ea3ng", +"Page break": "Ng\u1eaft trang", +"Paste as text": "D\u00e1n nh\u01b0 v\u0103n b\u1ea3n", +"Preview": "Xem tr\u01b0\u1edbc", +"Print": "In", +"Save": "L\u01b0u", +"Find": "T\u00ecm", +"Replace with": "Thay th\u1ebf b\u1eb1ng", +"Replace": "Thay th\u1ebf", +"Replace all": "Thay th\u1ebf t\u1ea5t c\u1ea3", +"Prev": "Tr\u01b0\u1edbc", +"Next": "Sau", +"Find and replace": "T\u00ecm v\u00e0 thay th\u1ebf", +"Could not find the specified string.": "Kh\u00f4ng t\u00ecm th\u1ea5y chu\u1ed7i y\u00eau c\u1ea7u", +"Match case": "Ph\u00e2n bi\u1ec7t hoa th\u01b0\u1eddng", +"Whole words": "T\u1ea5t c\u1ea3 \u0111o\u1ea1n", +"Spellcheck": "Ki\u1ec3m tra ch\u00ednh t\u1ea3", +"Ignore": "L\u1edd qua", +"Ignore all": "L\u1edd t\u1ea5t c\u1ea3", +"Finish": "Ho\u00e0n t\u1ea5t", +"Add to Dictionary": "Th\u00eam v\u00e0o t\u1eeb \u0111i\u1ec3n", +"Insert table": "Th\u00eam b\u1ea3ng", +"Table properties": "Thu\u1ed9c t\u00ednh b\u1ea3ng", +"Delete table": "Xo\u00e1 b\u1ea3ng", +"Cell": "\u00d4", +"Row": "D\u00f2ng", +"Column": "C\u1ed9t", +"Cell properties": "Thu\u1ed9c t\u00ednh \u00f4", +"Merge cells": "N\u1ed1i \u00f4", +"Split cell": "Chia \u00f4", +"Insert row before": "Th\u00eam d\u00f2ng ph\u00eda tr\u00ean", +"Insert row after": "Th\u00eam d\u00f2ng ph\u00eda d\u01b0\u1edbi", +"Delete row": "Xo\u00e1 d\u00f2ng", +"Row properties": "Thu\u1ed9c t\u00ednh d\u00f2ng", +"Cut row": "C\u1eaft d\u00f2ng", +"Copy row": "Ch\u00e9p d\u00f2ng", +"Paste row before": "D\u00e1n v\u00e0o ph\u00eda tr\u01b0\u1edbc, tr\u00ean", +"Paste row after": "D\u00e1n v\u00e0o ph\u00eda sau, d\u01b0\u1edbi", +"Insert column before": "Th\u00eam c\u1ed9t b\u00ean tr\u00e1i", +"Insert column after": "Th\u00eam c\u1ed9t b\u00ean ph\u1ea3i", +"Delete column": "Xo\u00e1 c\u1ed9t", +"Cols": "C\u1ed9t", +"Rows": "D\u00f2ng", +"Width": "R\u1ed9ng", +"Height": "Cao", +"Cell spacing": "Kho\u1ea3ng c\u00e1ch \u00f4", +"Cell padding": "Kho\u1ea3ng c\u00e1ch trong \u00f4", +"Caption": "Ti\u00eau \u0111\u1ec1", +"Left": "Tr\u00e1i", +"Center": "Gi\u1eefa", +"Right": "Ph\u1ea3i", +"Cell type": "Lo\u1ea1i \u00f4", +"Scope": "Quy\u1ec1n", +"Alignment": "Canh ch\u1ec9nh", +"H Align": "X\u1ebfp ngang", +"V Align": "X\u1ebfp d\u1ecdc", +"Top": "\u0110\u1ec9nh", +"Middle": "Gi\u1eefa", +"Bottom": "\u0110\u00e1y", +"Header cell": "Ti\u00eau \u0111\u1ec1 \u00f4", +"Row group": "Nh\u00f3m d\u00f2ng", +"Column group": "Nh\u00f3m c\u1ed9t", +"Row type": "Lo\u1ea1i d\u00f2ng", +"Header": "Ti\u00eau \u0111\u1ec1", +"Body": "N\u1ed9i dung", +"Footer": "Ch\u00e2n", +"Border color": "M\u00e0u vi\u1ec1n", +"Insert template": "Th\u00eam m\u1eabu", +"Templates": "M\u1eabu", +"Template": "B\u1ea3n m\u1eabu", +"Text color": "M\u00e0u ch\u1eef", +"Background color": "M\u00e0u n\u1ec1n", +"Custom...": "T\u00f9y ch\u1ecdn...", +"Custom color": "M\u00e0u t\u00f9y ch\u1ecdn", +"No color": "Kh\u00f4ng m\u00e0u", +"Table of Contents": "M\u1ee5c l\u1ee5c", +"Show blocks": "Hi\u1ec3n th\u1ecb kh\u1ed1i", +"Show invisible characters": "Hi\u1ec3n th\u1ecb c\u00e1c k\u00fd t\u1ef1 \u1ea9n", +"Words: {0}": "T\u1eeb: {0}", +"{0} words": "{0} t\u1eeb", +"File": "T\u1eadp tin", +"Edit": "S\u1eeda", +"Insert": "Th\u00eam", +"View": "Xem", +"Format": "\u0110\u1ecbnh d\u1ea1ng", +"Table": "B\u1ea3ng", +"Tools": "C\u00f4ng c\u1ee5", +"Powered by {0}": "\u0110\u01b0\u1ee3c cung c\u1ea5p b\u1edfi {0}", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Khu v\u1ef1c so\u1ea1n th\u1ea3o. Nh\u1ea5n ALT-F9 \u0111\u1ec3 hi\u1ec7n menu, ALT-F10 \u0111\u1ec3 hi\u1ec7n thanh c\u00f4ng c\u1ee5. C\u1ea7n tr\u1ee3 gi\u00fap nh\u1ea5n ALT-0" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh.js deleted file mode 100644 index 42f7abbc96..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh.js +++ /dev/null @@ -1 +0,0 @@ -tinyMCE.addI18n({"zh-cn":{common:{"more_colors":"\u66f4\u591a\u989c\u8272","invalid_data":"\u9519\u8bef\uff1a\u6807\u8bb0\u4e3a\u7ea2\u8272\u7684\u90e8\u5206\u6709\u8bef\u3002","popup_blocked":"\u62b1\u6b49\uff0c\u60a8\u7981\u7528\u4e86\u5f39\u51fa\u7a97\u53e3\u529f\u80fd\u3002\u4e3a\u4e86\u4f7f\u7528\u8be5\u5de5\u5177\u7684\u5168\u90e8\u529f\u80fd\uff0c\u60a8\u9700\u8981\u5141\u8bb8\u5f39\u51fa\u7a97\u53e3\u3002","clipboard_no_support":"\u60a8\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\uff0c\u4f7f\u7528\u5feb\u6377\u952e\u4ee3\u66ff\u3002","clipboard_msg":"\u5728Mozilla\u548cFirefox\u4e2d\u4e0d\u80fd\u4f7f\u7528\u590d\u5236/\u7c98\u8d34/\u526a\u5207\u3002\u60a8\u8981\u67e5\u770b\u8be5\u95ee\u9898\u66f4\u591a\u7684\u4fe1\u606f\u5417\uff1f","not_set":"-- \u672a\u8bbe\u7f6e --","class_name":"\u7c7b\u522b",browse:"\u6d4f\u89c8",close:"\u5173\u95ed",cancel:"\u53d6\u6d88",update:"\u66f4\u65b0",insert:"\u63d2\u5165",apply:"\u5e94\u7528","edit_confirm":"\u8be5\u6587\u672c\u57df\u662f\u5426\u9700\u8981\u4f7f\u7528\u6240\u89c1\u5373\u6240\u5f97\u6a21\u5f0f\uff1f","invalid_data_number":"{#field} \u5fc5\u987b\u4e3a\u6570\u5b57","invalid_data_min":"{#field} \u5fc5\u987b\u4e3a\u5927\u4e8e {#min} \u7684\u6570\u5b57","invalid_data_size":"{#field} \u5fc5\u987b\u4e3a\u6570\u5b57\u6216\u767e\u5206\u6570",value:"(value)"},contextmenu:{full:"\u4e24\u7aef\u5bf9\u9f50",right:"\u53f3\u5bf9\u9f50",center:"\u5c45\u4e2d",left:"\u5de6\u5bf9\u9f50",align:"\u5bf9\u9f50"},insertdatetime:{"day_short":"\u5468\u65e5,\u5468\u4e00,\u5468\u4e8c,\u5468\u4e09,\u5468\u56db,\u5468\u4e94,\u5468\u516d,\u5468\u65e5","day_long":"\u661f\u671f\u65e5,\u661f\u671f\u4e00,\u661f\u671f\u4e8c,\u661f\u671f\u4e09,\u661f\u671f\u56db,\u661f\u671f\u4e94,\u661f\u671f\u516d,\u661f\u671f\u65e5","months_short":"1\u6708,2\u6708,3\u6708,4\u6708,5\u6708,6\u6708,7\u6708,8\u6708,9\u6708,10\u6708,11\u6708,12\u6708","months_long":"\u4e00\u6708,\u4e8c\u6708,\u4e09\u6708,\u56db\u6708,\u4e94\u6708,\u516d\u6708,\u4e03\u6708,\u516b\u6708,\u4e5d\u6708,\u5341\u6708,\u5341\u4e00\u6708,\u5341\u4e8c\u6708","inserttime_desc":"\u63d2\u5165\u65f6\u95f4","insertdate_desc":"\u63d2\u5165\u65e5\u671f","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"\u6253\u5370"},preview:{"preview_desc":"\u9884\u89c8"},directionality:{"rtl_desc":"\u6587\u5b57\u65b9\u5411\u4e3a\u4ece\u53f3\u5230\u5de6","ltr_desc":"\u6587\u5b57\u65b9\u5411\u4e3a\u4ece\u5de6\u5230\u53f3"},layer:{content:"\u65b0\u5efa\u5c42...","absolute_desc":"\u5207\u6362\u5230\u7edd\u5bf9\u4f4d\u7f6e","backward_desc":"\u7f6e\u540e","forward_desc":"\u7f6e\u524d","insertlayer_desc":"\u63d2\u5165\u65b0\u5c42"},save:{"save_desc":"\u4fdd\u5b58","cancel_desc":"\u53d6\u6d88\u66f4\u6539"},nonbreaking:{"nonbreaking_desc":"\u63d2\u5165\u4e0d\u95f4\u65ad\u7a7a\u683c\u7b26"},iespell:{download:"\u62fc\u5199\u68c0\u67e5\u672a\u5b89\u88c5\uff0c\u662f\u5426\u9a6c\u4e0a\u5b89\u88c5\uff1f","iespell_desc":"\u62fc\u5199\u68c0\u67e5"},advhr:{"delta_height":"\u9ad8\u5ea6","delta_width":"\u5bbd\u5ea6","advhr_desc":"\u6c34\u5e73\u7ebf"},emotions:{"emotions_desc":"\u8868\u60c5","delta_height":"","delta_width":""},searchreplace:{"replace_desc":"\u67e5\u627e/\u66ff\u6362","search_desc":"\u67e5\u627e","delta_width":"","delta_height":""},advimage:{"image_desc":"\u63d2\u5165/\u7f16\u8f91 \u56fe\u7247","delta_width":"","delta_height":""},advlink:{"link_desc":"\u63d2\u5165/\u7f16\u8f91 \u8d85\u94fe\u63a5","delta_height":"","delta_width":""},xhtmlxtras:{"attribs_desc":"\u63d2\u5165/\u7f16\u8f91\u5c5e\u6027","ins_desc":"\u63d2\u5165","del_desc":"\u5220\u9664","acronym_desc":"\u9996\u5b57\u6bcd\u7f29\u5199","abbr_desc":"\u7f29\u5199","cite_desc":"\u5f15\u7528","attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":""},style:{desc:"\u7f16\u8f91CSS\u6837\u5f0f","delta_height":"","delta_width":""},paste:{"plaintext_mode":"\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u6a21\u5f0f\u7c98\u8d34\uff0c\u518d\u6b21\u70b9\u51fb\u8fd4\u56de\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002","plaintext_mode_sticky":"\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u6a21\u5f0f\u7c98\u8d34\u3002\u518d\u6b21\u70b9\u51fb\u8fd4\u56de\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\uff0c\u5728\u60a8\u7c98\u8d34\u5185\u5bb9\u540e\u5c06\u8fd4\u56de\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002","selectall_desc":"\u5168\u9009","paste_word_desc":"\u4eceWord\u7c98\u8d34","paste_text_desc":"\u4ee5\u7eaf\u6587\u672c\u7c98\u8d34"},"paste_dlg":{"word_title":"\u4f7f\u7528CTRL V\u7c98\u8d34\u6587\u672c\u5230\u7a97\u53e3\u4e2d\u3002","text_linebreaks":"\u4fdd\u7559\u65ad\u884c","text_title":"\u4f7f\u7528CTRL V\u7c98\u8d34\u6587\u672c\u5230\u7a97\u53e3\u4e2d\u3002"},table:{cell:"\u5355\u5143\u683c",col:"\u5217",row:"\u884c",del:"\u5220\u9664\u8868\u683c","copy_row_desc":"\u590d\u5236\u884c","cut_row_desc":"\u526a\u5207\u884c","paste_row_after_desc":"\u5728\u4e0b\u65b9\u7c98\u8d34\u884c","paste_row_before_desc":"\u5728\u4e0a\u65b9\u7c98\u8d34\u884c","props_desc":"\u8868\u683c\u5c5e\u6027","cell_desc":"\u5355\u5143\u683c\u5c5e\u6027","row_desc":"\u884c\u5c5e\u6027","merge_cells_desc":"\u5408\u5e76\u5355\u5143\u683c","split_cells_desc":"\u5206\u5272\u5355\u5143\u683c","delete_col_desc":"\u5220\u9664\u5217","col_after_desc":"\u5728\u53f3\u4fa7\u63d2\u5165\u5217","col_before_desc":"\u5728\u5de6\u4fa7\u63d2\u5165\u5217","delete_row_desc":"\u5220\u9664\u884c","row_after_desc":"\u5728\u4e0b\u65b9\u63d2\u5165\u884c","row_before_desc":"\u5728\u4e0a\u65b9\u63d2\u5165\u884c",desc:"\u63d2\u5165\u65b0\u8868\u683c","merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":""},autosave:{"warning_message":"\u5982\u679c\u6062\u590d\u4fdd\u5b58\u7684\u5185\u5bb9\uff0c\u60a8\u5f53\u524d\u7f16\u8f91\u7684\u6240\u6709\u7684\u5185\u5bb9\u5c06\u4e22\u5931\u3002nn\u60a8\u786e\u5b9a\u8981\u6062\u590d\u4fdd\u5b58\u7684\u5185\u5bb9\u5417\uff1f","restore_content":"\u6062\u590d\u81ea\u52a8\u4fdd\u5b58\u7684\u5185\u5bb9\u3002","unload_msg":"\u5982\u679c\u9000\u51fa\u8be5\u9875\uff0c\u60a8\u6240\u505a\u7684\u66f4\u6539\u5c06\u4e22\u5931\u3002"},fullscreen:{desc:"\u5207\u6362\u5168\u5c4f\u6a21\u5f0f"},media:{edit:"\u7f16\u8f91\u5d4c\u5165\u5f0f\u5a92\u4f53",desc:"\u63d2\u5165/\u7f16\u8f91 \u5d4c\u5165\u5f0f\u5a92\u4f53","delta_height":"","delta_width":""},fullpage:{desc:"\u6587\u4ef6\u5c5e\u6027","delta_width":"\u5bbd\u5ea6","delta_height":"\u9ad8\u5ea6"},template:{desc:"\u63d2\u5165\u9884\u8bbe\u7684\u6a21\u677f\u5185\u5bb9"},visualchars:{desc:"\u663e\u793a/\u9690\u85cf \u975e\u53ef\u89c1\u5b57\u7b26"},spellchecker:{desc:"\u62fc\u5199\u68c0\u67e5",menu:"\u62fc\u5199\u68c0\u67e5\u8bbe\u7f6e","ignore_word":"\u5ffd\u7565","ignore_words":"\u5168\u90e8\u5ffd\u7565",langs:"\u8bed\u8a00",wait:"\u8bf7\u7a0d\u5019...",sug:"\u5efa\u8bae","no_sug":"\u65e0\u5efa\u8bae","no_mpell":"\u65e0\u62fc\u5199\u9519\u8bef","learn_word":"\u5b66\u4e60\u8bcd\u7ec4"},pagebreak:{desc:"\u63d2\u5165\u5206\u9875\u7b26"},advlist:{types:"\u6837\u5f0f",def:"\u9ed8\u8ba4","lower_alpha":"\u5c0f\u5199\u5b57\u6bcd","lower_greek":"\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd","lower_roman":"\u5c0f\u5199\u7f57\u9a6c\u6570\u5b57","upper_alpha":"\u5927\u5199\u5b57\u6bcd","upper_roman":"\u5927\u5199\u7f57\u9a6c\u6570\u5b57",circle:"\u5706\u5708",disc:"\u5706\u70b9",square:"\u65b9\u5757"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"\u5bcc\u6587\u672c\u57df"},wordcount:{words:"\u5b57\u6570:"}}}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_CN.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_CN.js new file mode 100644 index 0000000000..0f3cf923ef --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_CN.js @@ -0,0 +1,261 @@ +tinymce.addI18n('zh_CN',{ +"Redo": "\u91cd\u590d", +"Undo": "\u64a4\u6d88", +"Cut": "\u526a\u5207", +"Copy": "\u590d\u5236", +"Paste": "\u7c98\u8d34", +"Select all": "\u5168\u9009", +"New document": "\u65b0\u6587\u6863", +"Ok": "\u786e\u5b9a", +"Cancel": "\u53d6\u6d88", +"Visual aids": "\u7f51\u683c\u7ebf", +"Bold": "\u7c97\u4f53", +"Italic": "\u659c\u4f53", +"Underline": "\u4e0b\u5212\u7ebf", +"Strikethrough": "\u5220\u9664\u7ebf", +"Superscript": "\u4e0a\u6807", +"Subscript": "\u4e0b\u6807", +"Clear formatting": "\u6e05\u9664\u683c\u5f0f", +"Align left": "\u5de6\u5bf9\u9f50", +"Align center": "\u5c45\u4e2d", +"Align right": "\u53f3\u5bf9\u9f50", +"Justify": "\u4e24\u7aef\u5bf9\u9f50", +"Bullet list": "\u9879\u76ee\u7b26\u53f7", +"Numbered list": "\u7f16\u53f7\u5217\u8868", +"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb", +"Increase indent": "\u589e\u52a0\u7f29\u8fdb", +"Close": "\u5173\u95ed", +"Formats": "\u683c\u5f0f", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002", +"Headers": "\u6807\u9898", +"Header 1": "\u6807\u98981", +"Header 2": "\u6807\u98982", +"Header 3": "\u6807\u98983", +"Header 4": "\u6807\u98984", +"Header 5": "\u6807\u98985", +"Header 6": "\u6807\u98986", +"Headings": "\u6807\u9898", +"Heading 1": "\u6807\u98981", +"Heading 2": "\u6807\u98982", +"Heading 3": "\u6807\u98983", +"Heading 4": "\u6807\u98984", +"Heading 5": "\u6807\u98985", +"Heading 6": "\u6807\u98986", +"Preformatted": "\u9884\u683c\u5f0f\u5316", +"Div": "Div\u533a\u5757", +"Pre": "\u9884\u683c\u5f0f\u6587\u672c", +"Code": "\u4ee3\u7801", +"Paragraph": "\u6bb5\u843d", +"Blockquote": "\u5f15\u7528", +"Inline": "\u6587\u672c", +"Blocks": "\u533a\u5757", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002", +"Font Family": "\u5b57\u4f53", +"Font Sizes": "\u5b57\u53f7", +"Class": "Class", +"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf", +"OR": "\u6216", +"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64", +"Upload": "\u4e0a\u4f20", +"Block": "\u5757", +"Align": "\u5bf9\u9f50", +"Default": "\u9ed8\u8ba4", +"Circle": "\u7a7a\u5fc3\u5706", +"Disc": "\u5b9e\u5fc3\u5706", +"Square": "\u65b9\u5757", +"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd", +"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd", +"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd", +"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd", +"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd", +"Anchor": "\u951a\u70b9", +"Name": "\u540d\u79f0", +"Id": "\u6807\u8bc6\u7b26", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002", +"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f", +"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f", +"Special character": "\u7279\u6b8a\u7b26\u53f7", +"Source code": "\u6e90\u4ee3\u7801", +"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b", +"Language": "\u8bed\u8a00", +"Code sample": "\u4ee3\u7801\u793a\u4f8b", +"Color": "\u989c\u8272", +"R": "R", +"G": "G", +"B": "B", +"Left to right": "\u4ece\u5de6\u5230\u53f3", +"Right to left": "\u4ece\u53f3\u5230\u5de6", +"Emoticons": "\u8868\u60c5", +"Document properties": "\u6587\u6863\u5c5e\u6027", +"Title": "\u6807\u9898", +"Keywords": "\u5173\u952e\u8bcd", +"Description": "\u63cf\u8ff0", +"Robots": "\u673a\u5668\u4eba", +"Author": "\u4f5c\u8005", +"Encoding": "\u7f16\u7801", +"Fullscreen": "\u5168\u5c4f", +"Action": "\u64cd\u4f5c", +"Shortcut": "\u5feb\u6377\u952e", +"Help": "\u5e2e\u52a9", +"Address": "\u5730\u5740", +"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f", +"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f", +"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84", +"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355", +"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", +"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", +"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", +"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):", +"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a", +"Learn more...": "\u4e86\u89e3\u66f4\u591a...", +"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}", +"Plugins": "\u63d2\u4ef6", +"Handy Shortcuts": "\u5feb\u6377\u952e", +"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf", +"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247", +"Image description": "\u56fe\u7247\u63cf\u8ff0", +"Source": "\u5730\u5740", +"Dimensions": "\u5927\u5c0f", +"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4", +"General": "\u666e\u901a", +"Advanced": "\u9ad8\u7ea7", +"Style": "\u6837\u5f0f", +"Vertical space": "\u5782\u76f4\u8fb9\u8ddd", +"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd", +"Border": "\u8fb9\u6846", +"Insert image": "\u63d2\u5165\u56fe\u7247", +"Image": "\u56fe\u7247", +"Image list": "\u56fe\u7247\u5217\u8868", +"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c", +"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c", +"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c", +"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c", +"Edit image": "\u7f16\u8f91\u56fe\u7247", +"Image options": "\u56fe\u7247\u9009\u9879", +"Zoom in": "\u653e\u5927", +"Zoom out": "\u7f29\u5c0f", +"Crop": "\u88c1\u526a", +"Resize": "\u8c03\u6574\u5927\u5c0f", +"Orientation": "\u65b9\u5411", +"Brightness": "\u4eae\u5ea6", +"Sharpen": "\u9510\u5316", +"Contrast": "\u5bf9\u6bd4\u5ea6", +"Color levels": "\u989c\u8272\u5c42\u6b21", +"Gamma": "\u4f3d\u9a6c\u503c", +"Invert": "\u53cd\u8f6c", +"Apply": "\u5e94\u7528", +"Back": "\u540e\u9000", +"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4", +"Date\/time": "\u65e5\u671f\/\u65f6\u95f4", +"Insert link": "\u63d2\u5165\u94fe\u63a5", +"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5", +"Text to display": "\u663e\u793a\u6587\u5b57", +"Url": "\u5730\u5740", +"Target": "\u6253\u5f00\u65b9\u5f0f", +"None": "\u65e0", +"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00", +"Remove link": "\u5220\u9664\u94fe\u63a5", +"Anchors": "\u951a\u70b9", +"Link": "\u94fe\u63a5", +"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f", +"Link list": "\u94fe\u63a5\u5217\u8868", +"Insert video": "\u63d2\u5165\u89c6\u9891", +"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891", +"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53", +"Alternative source": "\u955c\u50cf", +"Poster": "\u5c01\u9762", +"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:", +"Embed": "\u5185\u5d4c", +"Media": "\u5a92\u4f53", +"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c", +"Page break": "\u5206\u9875\u7b26", +"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c", +"Preview": "\u9884\u89c8", +"Print": "\u6253\u5370", +"Save": "\u4fdd\u5b58", +"Find": "\u67e5\u627e", +"Replace with": "\u66ff\u6362\u4e3a", +"Replace": "\u66ff\u6362", +"Replace all": "\u5168\u90e8\u66ff\u6362", +"Prev": "\u4e0a\u4e00\u4e2a", +"Next": "\u4e0b\u4e00\u4e2a", +"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362", +"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.", +"Match case": "\u533a\u5206\u5927\u5c0f\u5199", +"Whole words": "\u5168\u5b57\u5339\u914d", +"Spellcheck": "\u62fc\u5199\u68c0\u67e5", +"Ignore": "\u5ffd\u7565", +"Ignore all": "\u5168\u90e8\u5ffd\u7565", +"Finish": "\u5b8c\u6210", +"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178", +"Insert table": "\u63d2\u5165\u8868\u683c", +"Table properties": "\u8868\u683c\u5c5e\u6027", +"Delete table": "\u5220\u9664\u8868\u683c", +"Cell": "\u5355\u5143\u683c", +"Row": "\u884c", +"Column": "\u5217", +"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027", +"Merge cells": "\u5408\u5e76\u5355\u5143\u683c", +"Split cell": "\u62c6\u5206\u5355\u5143\u683c", +"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165", +"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165", +"Delete row": "\u5220\u9664\u884c", +"Row properties": "\u884c\u5c5e\u6027", +"Cut row": "\u526a\u5207\u884c", +"Copy row": "\u590d\u5236\u884c", +"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9", +"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9", +"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165", +"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165", +"Delete column": "\u5220\u9664\u5217", +"Cols": "\u5217", +"Rows": "\u884c", +"Width": "\u5bbd", +"Height": "\u9ad8", +"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd", +"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd", +"Caption": "\u6807\u9898", +"Left": "\u5de6\u5bf9\u9f50", +"Center": "\u5c45\u4e2d", +"Right": "\u53f3\u5bf9\u9f50", +"Cell type": "\u5355\u5143\u683c\u7c7b\u578b", +"Scope": "\u8303\u56f4", +"Alignment": "\u5bf9\u9f50\u65b9\u5f0f", +"H Align": "\u6c34\u5e73\u5bf9\u9f50", +"V Align": "\u5782\u76f4\u5bf9\u9f50", +"Top": "\u9876\u90e8\u5bf9\u9f50", +"Middle": "\u5782\u76f4\u5c45\u4e2d", +"Bottom": "\u5e95\u90e8\u5bf9\u9f50", +"Header cell": "\u8868\u5934\u5355\u5143\u683c", +"Row group": "\u884c\u7ec4", +"Column group": "\u5217\u7ec4", +"Row type": "\u884c\u7c7b\u578b", +"Header": "\u8868\u5934", +"Body": "\u8868\u4f53", +"Footer": "\u8868\u5c3e", +"Border color": "\u8fb9\u6846\u989c\u8272", +"Insert template": "\u63d2\u5165\u6a21\u677f", +"Templates": "\u6a21\u677f", +"Template": "\u6a21\u677f", +"Text color": "\u6587\u5b57\u989c\u8272", +"Background color": "\u80cc\u666f\u8272", +"Custom...": "\u81ea\u5b9a\u4e49...", +"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272", +"No color": "\u65e0", +"Table of Contents": "\u5185\u5bb9\u5217\u8868", +"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846", +"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26", +"Words: {0}": "\u5b57\u6570\uff1a{0}", +"{0} words": "{0} \u5b57", +"File": "\u6587\u4ef6", +"Edit": "\u7f16\u8f91", +"Insert": "\u63d2\u5165", +"View": "\u89c6\u56fe", +"Format": "\u683c\u5f0f", +"Table": "\u8868\u683c", +"Tools": "\u5de5\u5177", +"Powered by {0}": "\u7531{0}\u9a71\u52a8", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_TW.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_TW.js new file mode 100644 index 0000000000..cc5f157949 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/zh_TW.js @@ -0,0 +1,261 @@ +tinymce.addI18n('zh_TW',{ +"Redo": "\u53d6\u6d88\u5fa9\u539f", +"Undo": "\u5fa9\u539f", +"Cut": "\u526a\u4e0b", +"Copy": "\u8907\u88fd", +"Paste": "\u8cbc\u4e0a", +"Select all": "\u5168\u9078", +"New document": "\u65b0\u6587\u4ef6", +"Ok": "\u78ba\u5b9a", +"Cancel": "\u53d6\u6d88", +"Visual aids": "\u5c0f\u5e6b\u624b", +"Bold": "\u7c97\u9ad4", +"Italic": "\u659c\u9ad4", +"Underline": "\u5e95\u7dda", +"Strikethrough": "\u522a\u9664\u7dda", +"Superscript": "\u4e0a\u6a19", +"Subscript": "\u4e0b\u6a19", +"Clear formatting": "\u6e05\u9664\u683c\u5f0f", +"Align left": "\u7f6e\u5de6\u5c0d\u9f4a", +"Align center": "\u7f6e\u4e2d\u5c0d\u9f4a", +"Align right": "\u7f6e\u53f3\u5c0d\u9f4a", +"Justify": "\u5de6\u53f3\u5c0d\u9f4a", +"Bullet list": "\u9805\u76ee\u6e05\u55ae", +"Numbered list": "\u6578\u5b57\u6e05\u55ae", +"Decrease indent": "\u6e1b\u5c11\u7e2e\u6392", +"Increase indent": "\u589e\u52a0\u7e2e\u6392", +"Close": "\u95dc\u9589", +"Formats": "\u683c\u5f0f", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u60a8\u7684\u700f\u89bd\u5668\u4e0d\u652f\u63f4\u5b58\u53d6\u526a\u8cbc\u7c3f\uff0c\u53ef\u4ee5\u4f7f\u7528\u5feb\u901f\u9375 Ctrl + X\/C\/V \u4ee3\u66ff\u526a\u4e0b\u3001\u8907\u88fd\u8207\u8cbc\u4e0a\u3002", +"Headers": "\u6a19\u984c", +"Header 1": "\u6a19\u984c 1", +"Header 2": "\u6a19\u984c 2", +"Header 3": "\u6a19\u984c 3", +"Header 4": "\u6a19\u984c 4", +"Header 5": "\u6a19\u984c 5", +"Header 6": "\u6a19\u984c 6", +"Headings": "\u6a19\u984c", +"Heading 1": "\u6a19\u984c 1", +"Heading 2": "\u6a19\u984c 2", +"Heading 3": "\u6a19\u984c 3", +"Heading 4": "\u6a19\u984c 4", +"Heading 5": "\u6a19\u984c 5", +"Heading 6": "\u6a19\u984c 6", +"Preformatted": "\u9810\u5148\u6392\u7248", +"Div": "Div", +"Pre": "Pre", +"Code": "\u7a0b\u5f0f\u78bc", +"Paragraph": "\u6bb5\u843d", +"Blockquote": "\u5f15\u7528", +"Inline": "Inline", +"Blocks": "\u5340\u584a", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u76ee\u524d\u5c07\u4ee5\u7d14\u6587\u5b57\u7684\u6a21\u5f0f\u8cbc\u4e0a\uff0c\u60a8\u53ef\u4ee5\u518d\u9ede\u9078\u4e00\u6b21\u53d6\u6d88\u3002", +"Font Family": "\u5b57\u9ad4", +"Font Sizes": "\u5b57\u578b\u5927\u5c0f", +"Class": "\u985e\u5225", +"Browse for an image": "\u5f9e\u5716\u7247\u4e2d\u700f\u89bd", +"OR": "\u6216", +"Drop an image here": "\u62d6\u66f3\u5716\u7247\u81f3\u6b64", +"Upload": "\u4e0a\u50b3", +"Block": "\u5340\u584a", +"Align": "\u5c0d\u9f4a", +"Default": "\u9810\u8a2d", +"Circle": "\u7a7a\u5fc3\u5713", +"Disc": "\u5be6\u5fc3\u5713", +"Square": "\u6b63\u65b9\u5f62", +"Lower Alpha": "\u5c0f\u5beb\u82f1\u6587\u5b57\u6bcd", +"Lower Greek": "\u5e0c\u81d8\u5b57\u6bcd", +"Lower Roman": "\u5c0f\u5beb\u7f85\u99ac\u6578\u5b57", +"Upper Alpha": "\u5927\u5beb\u82f1\u6587\u5b57\u6bcd", +"Upper Roman": "\u5927\u5beb\u7f85\u99ac\u6578\u5b57", +"Anchor": "\u52a0\u5165\u9328\u9ede", +"Name": "\u540d\u7a31", +"Id": "Id", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id\u61c9\u4ee5\u5b57\u6bcd\u958b\u982d\uff0c\u5f8c\u9762\u63a5\u8457\u5b57\u6bcd\uff0c\u6578\u5b57\uff0c\u7834\u6298\u865f\uff0c\u9ede\u6578\uff0c\u5192\u865f\u6216\u4e0b\u5283\u7dda\u3002", +"You have unsaved changes are you sure you want to navigate away?": "\u7de8\u8f2f\u5c1a\u672a\u88ab\u5132\u5b58\uff0c\u4f60\u78ba\u5b9a\u8981\u96e2\u958b\uff1f", +"Restore last draft": "\u8f09\u5165\u4e0a\u4e00\u6b21\u7de8\u8f2f\u7684\u8349\u7a3f", +"Special character": "\u7279\u6b8a\u5b57\u5143", +"Source code": "\u539f\u59cb\u78bc", +"Insert\/Edit code sample": "\u63d2\u5165\/\u7de8\u8f2f \u7a0b\u5f0f\u78bc\u7bc4\u4f8b", +"Language": "\u8a9e\u8a00", +"Code sample": "\u7a0b\u5f0f\u78bc\u7bc4\u4f8b", +"Color": "\u984f\u8272", +"R": "\u7d05", +"G": "\u7da0", +"B": "\u85cd", +"Left to right": "\u5f9e\u5de6\u5230\u53f3", +"Right to left": "\u5f9e\u53f3\u5230\u5de6", +"Emoticons": "\u8868\u60c5", +"Document properties": "\u6587\u4ef6\u7684\u5c6c\u6027", +"Title": "\u6a19\u984c", +"Keywords": "\u95dc\u9375\u5b57", +"Description": "\u63cf\u8ff0", +"Robots": "\u6a5f\u5668\u4eba", +"Author": "\u4f5c\u8005", +"Encoding": "\u7de8\u78bc", +"Fullscreen": "\u5168\u87a2\u5e55", +"Action": "\u52d5\u4f5c", +"Shortcut": "\u5feb\u901f\u9375", +"Help": "\u5e6b\u52a9", +"Address": "\u5730\u5740", +"Focus to menubar": "\u8df3\u81f3\u9078\u55ae\u5217", +"Focus to toolbar": "\u8df3\u81f3\u5de5\u5177\u5217", +"Focus to element path": "\u8df3\u81f3HTML\u5143\u7d20\u5217", +"Focus to contextual toolbar": "\u8df3\u81f3\u5feb\u6377\u9078\u55ae", +"Insert link (if link plugin activated)": "\u65b0\u589e\u6377\u5f91 (\u6377\u5f91\u5916\u639b\u555f\u7528\u6642)", +"Save (if save plugin activated)": "\u5132\u5b58 (\u5132\u5b58\u5916\u639b\u555f\u7528\u6642)", +"Find (if searchreplace plugin activated)": "\u5c0b\u627e (\u5c0b\u627e\u53d6\u4ee3\u5916\u639b\u555f\u7528\u6642)", +"Plugins installed ({0}):": "({0}) \u500b\u5916\u639b\u5df2\u5b89\u88dd\uff1a", +"Premium plugins:": "\u52a0\u503c\u5916\u639b\uff1a", +"Learn more...": "\u4e86\u89e3\u66f4\u591a...", +"You are using {0}": "\u60a8\u6b63\u5728\u4f7f\u7528 {0}", +"Plugins": "\u5916\u639b", +"Handy Shortcuts": "\u5feb\u901f\u9375", +"Horizontal line": "\u6c34\u5e73\u7dda", +"Insert\/edit image": "\u63d2\u5165\/\u7de8\u8f2f \u5716\u7247", +"Image description": "\u5716\u7247\u63cf\u8ff0", +"Source": "\u5716\u7247\u7db2\u5740", +"Dimensions": "\u5c3a\u5bf8", +"Constrain proportions": "\u7b49\u6bd4\u4f8b\u7e2e\u653e", +"General": "\u4e00\u822c", +"Advanced": "\u9032\u968e", +"Style": "\u6a23\u5f0f", +"Vertical space": "\u9ad8\u5ea6", +"Horizontal space": "\u5bec\u5ea6", +"Border": "\u908a\u6846", +"Insert image": "\u63d2\u5165\u5716\u7247", +"Image": "\u5716\u7247", +"Image list": "\u5716\u7247\u6e05\u55ae", +"Rotate counterclockwise": "\u9006\u6642\u91dd\u65cb\u8f49", +"Rotate clockwise": "\u9806\u6642\u91dd\u65cb\u8f49", +"Flip vertically": "\u5782\u76f4\u7ffb\u8f49", +"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f49", +"Edit image": "\u7de8\u8f2f\u5716\u7247", +"Image options": "\u5716\u7247\u9078\u9805", +"Zoom in": "\u653e\u5927", +"Zoom out": "\u7e2e\u5c0f", +"Crop": "\u88c1\u526a", +"Resize": "\u8abf\u6574\u5927\u5c0f", +"Orientation": "\u65b9\u5411", +"Brightness": "\u4eae\u5ea6", +"Sharpen": "\u92b3\u5316", +"Contrast": "\u5c0d\u6bd4", +"Color levels": "\u984f\u8272\u5c64\u6b21", +"Gamma": "\u4f3d\u99ac\u503c", +"Invert": "\u53cd\u8f49", +"Apply": "\u61c9\u7528", +"Back": "\u5f8c\u9000", +"Insert date\/time": "\u63d2\u5165 \u65e5\u671f\/\u6642\u9593", +"Date\/time": "\u65e5\u671f\/\u6642\u9593", +"Insert link": "\u63d2\u5165\u9023\u7d50", +"Insert\/edit link": "\u63d2\u5165\/\u7de8\u8f2f\u9023\u7d50", +"Text to display": "\u986f\u793a\u6587\u5b57", +"Url": "\u7db2\u5740", +"Target": "\u958b\u555f\u65b9\u5f0f", +"None": "\u7121", +"New window": "\u53e6\u958b\u8996\u7a97", +"Remove link": "\u79fb\u9664\u9023\u7d50", +"Anchors": "\u52a0\u5165\u9328\u9ede", +"Link": "\u9023\u7d50", +"Paste or type a link": "\u8cbc\u4e0a\u6216\u8f38\u5165\u9023\u7d50", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u70ba\u96fb\u5b50\u90f5\u4ef6\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7db4\u55ce\uff1f", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u5c6c\u65bc\u5916\u90e8\u93c8\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7db4\u55ce\uff1f", +"Link list": "\u9023\u7d50\u6e05\u55ae", +"Insert video": "\u63d2\u5165\u5f71\u97f3", +"Insert\/edit video": "\u63d2\u4ef6\/\u7de8\u8f2f \u5f71\u97f3", +"Insert\/edit media": "\u63d2\u5165\/\u7de8\u8f2f \u5a92\u9ad4", +"Alternative source": "\u66ff\u4ee3\u5f71\u97f3", +"Poster": "\u9810\u89bd\u5716\u7247", +"Paste your embed code below:": "\u8acb\u5c07\u60a8\u7684\u5d4c\u5165\u5f0f\u7a0b\u5f0f\u78bc\u8cbc\u5728\u4e0b\u9762:", +"Embed": "\u5d4c\u5165\u78bc", +"Media": "\u5a92\u9ad4", +"Nonbreaking space": "\u4e0d\u5206\u884c\u7684\u7a7a\u683c", +"Page break": "\u5206\u9801", +"Paste as text": "\u4ee5\u7d14\u6587\u5b57\u8cbc\u4e0a", +"Preview": "\u9810\u89bd", +"Print": "\u5217\u5370", +"Save": "\u5132\u5b58", +"Find": "\u641c\u5c0b", +"Replace with": "\u66f4\u63db", +"Replace": "\u66ff\u63db", +"Replace all": "\u66ff\u63db\u5168\u90e8", +"Prev": "\u4e0a\u4e00\u500b", +"Next": "\u4e0b\u4e00\u500b", +"Find and replace": "\u5c0b\u627e\u53ca\u53d6\u4ee3", +"Could not find the specified string.": "\u7121\u6cd5\u67e5\u8a62\u5230\u6b64\u7279\u5b9a\u5b57\u4e32", +"Match case": "\u76f8\u5339\u914d\u6848\u4ef6", +"Whole words": "\u6574\u500b\u55ae\u5b57", +"Spellcheck": "\u62fc\u5b57\u6aa2\u67e5", +"Ignore": "\u5ffd\u7565", +"Ignore all": "\u5ffd\u7565\u6240\u6709", +"Finish": "\u5b8c\u6210", +"Add to Dictionary": "\u52a0\u5165\u5b57\u5178\u4e2d", +"Insert table": "\u63d2\u5165\u8868\u683c", +"Table properties": "\u8868\u683c\u5c6c\u6027", +"Delete table": "\u522a\u9664\u8868\u683c", +"Cell": "\u5132\u5b58\u683c", +"Row": "\u5217", +"Column": "\u884c", +"Cell properties": "\u5132\u5b58\u683c\u5c6c\u6027", +"Merge cells": "\u5408\u4f75\u5132\u5b58\u683c", +"Split cell": "\u5206\u5272\u5132\u5b58\u683c", +"Insert row before": "\u63d2\u5165\u5217\u5728...\u4e4b\u524d", +"Insert row after": "\u63d2\u5165\u5217\u5728...\u4e4b\u5f8c", +"Delete row": "\u522a\u9664\u5217", +"Row properties": "\u5217\u5c6c\u6027", +"Cut row": "\u526a\u4e0b\u5217", +"Copy row": "\u8907\u88fd\u5217", +"Paste row before": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u524d", +"Paste row after": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u5f8c", +"Insert column before": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u524d", +"Insert column after": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u5f8c", +"Delete column": "\u522a\u9664\u884c", +"Cols": "\u6b04\u4f4d\u6bb5", +"Rows": "\u5217", +"Width": "\u5bec\u5ea6", +"Height": "\u9ad8\u5ea6", +"Cell spacing": "\u5132\u5b58\u683c\u5f97\u9593\u8ddd", +"Cell padding": "\u5132\u5b58\u683c\u7684\u908a\u8ddd", +"Caption": "\u8868\u683c\u6a19\u984c", +"Left": "\u5de6\u908a", +"Center": "\u4e2d\u9593", +"Right": "\u53f3\u908a", +"Cell type": "\u5132\u5b58\u683c\u7684\u985e\u578b", +"Scope": "\u7bc4\u570d", +"Alignment": "\u5c0d\u9f4a", +"H Align": "\u6c34\u5e73\u4f4d\u7f6e", +"V Align": "\u5782\u76f4\u4f4d\u7f6e", +"Top": "\u7f6e\u9802", +"Middle": "\u7f6e\u4e2d", +"Bottom": "\u7f6e\u5e95", +"Header cell": "\u6a19\u982d\u5132\u5b58\u683c", +"Row group": "\u5217\u7fa4\u7d44", +"Column group": "\u6b04\u4f4d\u7fa4\u7d44", +"Row type": "\u884c\u7684\u985e\u578b", +"Header": "\u6a19\u982d", +"Body": "\u4e3b\u9ad4", +"Footer": "\u9801\u5c3e", +"Border color": "\u908a\u6846\u984f\u8272", +"Insert template": "\u63d2\u5165\u6a23\u7248", +"Templates": "\u6a23\u7248", +"Template": "\u6a23\u677f", +"Text color": "\u6587\u5b57\u984f\u8272", +"Background color": "\u80cc\u666f\u984f\u8272", +"Custom...": "\u81ea\u8a02", +"Custom color": "\u81ea\u8a02\u984f\u8272", +"No color": "No color", +"Table of Contents": "\u76ee\u9304", +"Show blocks": "\u986f\u793a\u5340\u584a\u8cc7\u8a0a", +"Show invisible characters": "\u986f\u793a\u96b1\u85cf\u5b57\u5143", +"Words: {0}": "\u5b57\u6578\uff1a{0}", +"{0} words": "{0} \u5b57\u5143", +"File": "\u6a94\u6848", +"Edit": "\u7de8\u8f2f", +"Insert": "\u63d2\u5165", +"View": "\u6aa2\u8996", +"Format": "\u683c\u5f0f", +"Table": "\u8868\u683c", +"Tools": "\u5de5\u5177", +"Powered by {0}": "\u7531 {0} \u63d0\u4f9b", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u8c50\u5bcc\u7684\u6587\u672c\u5340\u57df\u3002\u6309ALT-F9\u524d\u5f80\u4e3b\u9078\u55ae\u3002\u6309ALT-F10\u547c\u53eb\u5de5\u5177\u6b04\u3002\u6309ALT-0\u5c0b\u6c42\u5e6b\u52a9" +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 4b3afbad18..2566f56244 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -14,7 +14,7 @@ "@babel/core": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.4.tgz", - "integrity": "sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==", + "integrity": "sha1-br2f4Akl9sPhd7tyahiLX1eAiP8=", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", @@ -739,7 +739,7 @@ "@babel/preset-env": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.3.tgz", - "integrity": "sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ==", + "integrity": "sha1-nhvwWi4taHA20kxA5GOdxGzvInE=", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -865,7 +865,7 @@ "@gulp-sourcemaps/identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "integrity": "sha1-Hm/l2AJ7HyhdwNMXYvVmvM1z1ak=", "dev": true, "requires": { "acorn": "^5.0.3", @@ -884,7 +884,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -902,7 +902,7 @@ "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "integrity": "sha1-Olgr21OATGum0UZXnEblITDPSjs=", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.3", @@ -912,13 +912,13 @@ "@nodelib/fs.stat": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "integrity": "sha1-NNxfTKu8cg9OYPdadH5+zWwXW9M=", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "integrity": "sha1-ARuSAqcKY2bkNspcBlhEUoqwSXY=", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.3", @@ -928,7 +928,7 @@ "@sindresorhus/is": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "integrity": "sha1-mgb08TfuhNffBGDB/bETX/psUP0=", "dev": true, "optional": true }, @@ -940,13 +940,13 @@ "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "integrity": "sha1-KGLz9Yqaf3w+eNefEw3U1xwlwqc=", "dev": true }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "integrity": "sha1-qlmhxuP7xCHgfM0xqUTDDrpSFXU=", "dev": true, "requires": { "@types/events": "*", @@ -957,7 +957,7 @@ "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "integrity": "sha1-PcoOPzOyAPx9ETnAzZbBJoyt/Z0=", "dev": true }, "@types/node": { @@ -969,13 +969,13 @@ "@types/q": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", + "integrity": "sha1-aQoUdbhPKohP0HzXl8APXzE1bqg=", "dev": true }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", "dev": true, "requires": { "mime-types": "~2.1.24", @@ -985,7 +985,7 @@ "accord": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", - "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", + "integrity": "sha1-t0HBdtAENcWSnUZt/oz2vukzseQ=", "dev": true, "requires": { "convert-source-map": "^1.5.0", @@ -1032,7 +1032,7 @@ "ace-builds": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.2.tgz", - "integrity": "sha512-M1JtZctO2Zg+1qeGUFZXtYKsyaRptqQtqpVzlj80I0NzGW9MF3um0DBuizIvQlrPYUlTdm+wcOPZpZoerkxQdA==" + "integrity": "sha1-avwuQ6e17/3ETYQHQ2EShSVo6A0=" }, "acorn": { "version": "7.1.0", @@ -1099,19 +1099,19 @@ "dev": true }, "angular": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz", - "integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw==" + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.9.tgz", + "integrity": "sha1-5SYW6HAcF3JMPCOM/k+URv1XC8Q=" }, "angular-animate": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.5.tgz", - "integrity": "sha512-kU/fHIGf2a4a3bH7E1tzALTHk+QfoUSCK9fEcMFisd6ZWvNDwPzXWAilItqOC3EDiAXPmGHaNc9/aXiD9xrAxQ==" + "integrity": "sha1-H/xsKpze4ieiunnMbNj3HsRNtdw=" }, "angular-aria": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.5.tgz", - "integrity": "sha512-X2dGRw+PK7hrV7/X1Ns4e5P3KC/OBFi1l7z//D/v7zbZObsAx48qBoX7unsck+s4+mnO+ikNNkHG5N49VfAyRw==" + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.9.tgz", + "integrity": "sha1-kMYYlf+9h26VkVIisyp70AcK9+M=" }, "angular-chart.js": { "version": "1.1.1", @@ -1136,12 +1136,12 @@ "angular-cookies": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-cookies/-/angular-cookies-1.7.5.tgz", - "integrity": "sha512-/8xvvSl/Z9Vwu8ChRm+OQE3vmli8Icwl8uTYkHqD7j7cknJP9kNaf7SgsENlsLVtOqLE/I7TCFYrSx3bmSeNQA==" + "integrity": "sha1-HFqzwFzcQ/F3e+lQbmRYfLNUNjQ=" }, "angular-dynamic-locale": { "version": "0.1.37", "resolved": "https://registry.npmjs.org/angular-dynamic-locale/-/angular-dynamic-locale-0.1.37.tgz", - "integrity": "sha512-m5Kyk8W8/mOZSqRxuByOwHBjv8labLBAgvl0Z3iQx2xT/tWCqb94imKUPwumudszdPDjxeopwyucQvm8Sw7ogw==", + "integrity": "sha1-fon70uxFvdaryJ82zaiJODjkk1Q=", "requires": { "@types/angular": "^1.6.25" } @@ -1149,7 +1149,7 @@ "angular-i18n": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-i18n/-/angular-i18n-1.7.5.tgz", - "integrity": "sha512-52+Jpt8HRJV2bqSbSU6fWkwOvGzj/DxbNpKXxnTuCS9heuJrlm77BS/lhrF4BA8+Uudnh7npr5/yRELobP+8Yw==" + "integrity": "sha1-Lie2Thl3qMa2sFHFHQF1xtTcglI=" }, "angular-local-storage": { "version": "0.7.1", @@ -1159,32 +1159,32 @@ "angular-messages": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.5.tgz", - "integrity": "sha512-YDpJpFLyrIgZjE/sIAjgww1y6r3QqXBJbNDI0QjftD37vHXLkwvAOo3A4bxPw8BikyGLcJrFrgf6hRAzntJIWA==" + "integrity": "sha1-fC/XgTFaQ6GYOLEX2gFCqYhFThQ=" }, "angular-mocks": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.5.tgz", - "integrity": "sha512-I+Ue2Bkx6R9W5178DYrNvzjIdGh4wKKoCWsgz8dc7ysH4mA70Q3M9v5xRF0RUu7r+2CZj+nDeUecvh2paxcYvg==" + "integrity": "sha1-yLq6WgbtYLk0aXAmtJIWliavOEs=" }, "angular-route": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.7.5.tgz", - "integrity": "sha512-7KfyEVVOWTI+jTY/j5rUNCIHGRyeCOx7YqZI/Ci3IbDK7GIsy6xH+hS5ai0Xi0sLjzDZ0PUDO4gBn+K0dVtlOg==" + "integrity": "sha1-NKNkjEB6FKAw0HXPSFMY4zuiPw4=" }, "angular-sanitize": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.5.tgz", - "integrity": "sha512-wjKCJOIwrkEvfD0keTnKGi6We13gtoCAQIHcdoqyoo3gwvcgNfYymVQIS3+iCGVcjfWz0jHuS3KgB4ysRWsTTA==" + "integrity": "sha1-ddSeFQccqccFgedtIJQPJjcuJNI=" }, "angular-touch": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.7.5.tgz", - "integrity": "sha512-XNAZNG0RA1mtdwBJheViCF1H/7wOygp4MLIfs5y1K+rne6AeaYKZcV6EJs9fvgfLKLO6ecm1+3J8hoCkdhhxQw==" + "integrity": "sha1-7SYyKmhfApmyPLauqYNMEZQk2kY=" }, "angular-ui-sortable": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/angular-ui-sortable/-/angular-ui-sortable-0.19.0.tgz", - "integrity": "sha512-u/uc981Nzg4XN1bMU9qKleMTSt7F1XjMWnyGw6gxPLIeQeLZm8jWNy7tj8y2r2HmvzXFbQVq2z6rObznFKAekQ==", + "integrity": "sha1-SsQ5H8TU3lcRDbS10xp8GY0xT9A=", "requires": { "angular": ">=1.2.x", "jquery": ">=3.1.x", @@ -1247,7 +1247,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -1389,7 +1389,7 @@ "arch": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "integrity": "sha1-j1wnMao1owkpIhuwZA7tZRdeyE4=", "dev": true, "optional": true }, @@ -1501,7 +1501,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -1509,7 +1509,7 @@ "array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "integrity": "sha1-eqdwc/7FZd2rJJP1+IGF9ASp0zY=", "dev": true, "requires": { "is-number": "^4.0.0" @@ -1518,7 +1518,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -1532,7 +1532,7 @@ "array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "integrity": "sha1-5MBTVkU/VvU1EqfR1hI/LFTAqIo=", "dev": true, "requires": { "default-compare": "^1.0.0", @@ -1543,7 +1543,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -1551,7 +1551,7 @@ "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "integrity": "sha1-t5hCCtvrHego2ErNii4j0+/oXo0=", "dev": true }, "array-uniq": { @@ -1582,7 +1582,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -1603,13 +1603,13 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", "dev": true }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", "dev": true, "requires": { "lodash": "^4.17.14" @@ -1626,7 +1626,7 @@ "async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "integrity": "sha1-XhWqcplipLB0FPUoqIzfGOCykKI=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -1638,13 +1638,13 @@ "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", "dev": true }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=", "dev": true }, "async-settle": { @@ -1665,13 +1665,13 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", "dev": true }, "autoprefixer": { "version": "9.6.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.5.tgz", - "integrity": "sha512-rGd50YV8LgwFQ2WQp4XzOTG69u1qQsXn0amww7tjqV5jJuNazgFKYEVItEBngyyvVITKOg20zr2V+9VsrXJQ2g==", + "integrity": "sha1-mPSv5+k8zPMjKHUV1CYBlhl3Xl4=", "dev": true, "requires": { "browserslist": "^4.7.0", @@ -1706,7 +1706,7 @@ "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "integrity": "sha1-8A9Qe9qjw+P/bn5emNkKesq5b38=", "dev": true, "requires": { "object.assign": "^4.1.0" @@ -1805,7 +1805,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", "dev": true }, "base64id": { @@ -1841,7 +1841,7 @@ "bin-build": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz", - "integrity": "sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA==", + "integrity": "sha1-xXgKJaip+WbYJEIX5sH1CCoUOGE=", "dev": true, "optional": true, "requires": { @@ -1855,7 +1855,7 @@ "bin-check": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "integrity": "sha1-/ElZcL3Ii7HVo1/BfmXEoUn8Skk=", "dev": true, "optional": true, "requires": { @@ -1866,7 +1866,7 @@ "bin-version": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz", - "integrity": "sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ==", + "integrity": "sha1-WwnrKAdSsb0o8MnbP5by9DtsCDk=", "dev": true, "optional": true, "requires": { @@ -1877,7 +1877,7 @@ "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", "dev": true, "optional": true, "requires": { @@ -1893,7 +1893,7 @@ "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", "dev": true, "optional": true, "requires": { @@ -1903,7 +1903,7 @@ "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", "dev": true, "optional": true, "requires": { @@ -1916,7 +1916,7 @@ "bin-version-check": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz", - "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", + "integrity": "sha1-fYGcYklpkfgNiT5uAqMDI2Fgj3E=", "dev": true, "optional": true, "requires": { @@ -1928,7 +1928,7 @@ "bin-wrapper": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz", - "integrity": "sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q==", + "integrity": "sha1-mTSPLPhQMePvfvzn5TAK6q6WBgU=", "dev": true, "optional": true, "requires": { @@ -1943,7 +1943,7 @@ "download": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz", - "integrity": "sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==", + "integrity": "sha1-kFmqnXC1A+52oTKJe+beyOVYcjM=", "dev": true, "optional": true, "requires": { @@ -1973,14 +1973,14 @@ "file-type": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", - "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==", + "integrity": "sha1-JE87fvZBu+DMoZbHJ25LMyOZ9ow=", "dev": true, "optional": true }, "got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "integrity": "sha1-HSP2Q5Dpf3dsrFLluTbl9RTS6Tc=", "dev": true, "optional": true, "requires": { @@ -2015,7 +2015,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "optional": true, "requires": { @@ -2034,14 +2034,14 @@ "p-cancelable": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "integrity": "sha1-NfNj1n1SCByNlYXje8zrfgu8sqA=", "dev": true, "optional": true }, "p-event": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", - "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", + "integrity": "sha1-WWJ57xaassPgyuiMHPuwgHmZPvY=", "dev": true, "optional": true, "requires": { @@ -2051,7 +2051,7 @@ "p-timeout": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=", "dev": true, "requires": { "p-finally": "^1.0.0" @@ -2060,7 +2060,7 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", "dev": true, "optional": true }, @@ -2086,7 +2086,7 @@ "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", "dev": true }, "bl": { @@ -2134,7 +2134,7 @@ "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "integrity": "sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=", "dev": true }, "bluebird": { @@ -2146,7 +2146,7 @@ "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", "dev": true, "requires": { "bytes": "3.1.0", @@ -2164,7 +2164,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -2179,7 +2179,7 @@ "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", "dev": true } } @@ -2193,7 +2193,7 @@ "bootstrap": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", - "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" + "integrity": "sha1-w6NH1Bniia0R9AM+PEEyuHwIHXI=" }, "bootstrap-social": { "version": "5.1.1", @@ -2301,7 +2301,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", "dev": true }, "bufferstreams": { @@ -2316,7 +2316,7 @@ "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", "dev": true }, "cache-base": { @@ -2362,7 +2362,7 @@ "normalize-url": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "integrity": "sha1-g1qdoVUfom9w6SMpBpojqmV01+Y=", "dev": true, "optional": true, "requires": { @@ -2449,7 +2449,7 @@ "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2473,7 +2473,7 @@ "caw": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", - "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "integrity": "sha1-bDygcfwZRyCIPC3F2psHS/x+npU=", "dev": true, "requires": { "get-proxy": "^2.0.0", @@ -2495,7 +2495,7 @@ "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2506,7 +2506,7 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", "dev": true }, "chart.js": { @@ -2530,7 +2530,7 @@ "chartjs-color-string": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "integrity": "sha1-HfCWYhwOcHIKZPQTXqFx0FFAL3E=", "requires": { "color-name": "^1.0.0" } @@ -2538,7 +2538,7 @@ "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -2588,7 +2588,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", "dev": true } } @@ -2619,7 +2619,7 @@ "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "integrity": "sha1-LUEe92uFabbQyEBo2r6FsKpeXBc=", "dev": true, "requires": { "source-map": "~0.6.0" @@ -2628,7 +2628,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -2636,7 +2636,7 @@ "cli-color": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", + "integrity": "sha1-fRBzj0hSaCT4/n2lGFfLD1cv4B8=", "dev": true, "requires": { "ansi-regex": "^2.1.1", @@ -2665,7 +2665,7 @@ "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "integrity": "sha1-g22v1mzw/qXXHOXVsL9ulYAJES0=", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -2714,7 +2714,7 @@ "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "integrity": "sha1-EgoAywU7+2OiIucJ+Wg+ouEdjOw=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -2757,7 +2757,7 @@ "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "integrity": "sha1-Q/bCEVG07yv1cYfbDXPeIp4+fsM=", "dev": true, "requires": { "@types/q": "^1.5.1", @@ -2795,7 +2795,7 @@ "color": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "integrity": "sha1-aBSOf4XUGtdknF+oyBBvCY0inhA=", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -2827,12 +2827,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", "dev": true, "requires": { "color-name": "^1.0.0", @@ -2854,13 +2854,13 @@ "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "integrity": "sha1-xQSRR51MG9rtLJztMs98fcI2D3g=", "dev": true }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "integrity": "sha1-4BKJUNCCuGohaFgHlqCqXWxo2MU=", "dev": true, "requires": { "color": "3.0.x", @@ -2870,7 +2870,7 @@ "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "integrity": "sha1-2SC0Mo1TSjrIKV1o971LpsQnvpo=", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -2897,7 +2897,7 @@ "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -2921,7 +2921,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", "dev": true }, "component-inherit": { @@ -2939,7 +2939,7 @@ "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3000,7 +3000,7 @@ "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", "dev": true, "requires": { "ini": "^1.3.4", @@ -3010,7 +3010,7 @@ "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", "dev": true, "requires": { "debug": "2.6.9", @@ -3022,7 +3022,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -3046,7 +3046,7 @@ "consolidate": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "integrity": "sha1-IasEMjXHGgfUXZqtmFk7DbpWurc=", "dev": true, "requires": { "bluebird": "^3.1.1" @@ -3055,7 +3055,7 @@ "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -3091,7 +3091,7 @@ "copy-props": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "integrity": "sha1-k7scrfr9MdpbuKnUtB9HHsOnLf4=", "dev": true, "requires": { "each-props": "^1.3.0", @@ -3151,7 +3151,7 @@ "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "integrity": "sha1-BA9yaAnFked6F8CjYmykW08Wixo=", "dev": true, "requires": { "import-fresh": "^2.0.0", @@ -3163,7 +3163,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -3176,7 +3176,7 @@ "css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -3188,7 +3188,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -3202,7 +3202,7 @@ "css-declaration-sorter": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "integrity": "sha1-wZiUD2OnbX42wecQGLABchBUyyI=", "dev": true, "requires": { "postcss": "^7.0.1", @@ -3224,7 +3224,7 @@ "css-select-base-adapter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "integrity": "sha1-Oy/0lyzDYquIVhUHqVQIoUMhNdc=", "dev": true }, "css-tree": { @@ -3258,7 +3258,7 @@ "cssnano": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "integrity": "sha1-CsQfCxPRPUZUh+ERt3jULaYxuLI=", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -3270,7 +3270,7 @@ "cssnano-preset-default": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "integrity": "sha1-UexmLM/KD4izltzZZ5zbkxvhf3Y=", "dev": true, "requires": { "css-declaration-sorter": "^4.0.1", @@ -3320,7 +3320,7 @@ "cssnano-util-raw-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "integrity": "sha1-sm1f1fcqEd/np4RvtMZyYPlr8oI=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -3329,7 +3329,7 @@ "cssnano-util-same-parent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "integrity": "sha1-V0CC+yhZ0ttDOFWDXZqEVuoYu/M=", "dev": true }, "csso": { @@ -3378,7 +3378,7 @@ "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", "dev": true, "requires": { "es5-ext": "^0.10.50", @@ -3397,7 +3397,7 @@ "date-format": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "integrity": "sha1-MdW16iEc9f12TNOLr50DPffhJc8=", "dev": true }, "dateformat": { @@ -3409,7 +3409,7 @@ "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", "dev": true, "requires": { "ms": "^2.1.1" @@ -3418,7 +3418,7 @@ "debug-fabulous": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "integrity": "sha1-r4oIYyRlIk70F0qfBjCMPCoevI4=", "dev": true, "requires": { "debug": "3.X", @@ -3429,7 +3429,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "dev": true, "requires": { "ms": "^2.1.1" @@ -3474,7 +3474,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "requires": { "pify": "^3.0.0" @@ -3502,7 +3502,7 @@ "decompress-tar": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", - "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "integrity": "sha1-cYy9P8sWIJcW5womuE57pFkuWvE=", "dev": true, "requires": { "file-type": "^5.2.0", @@ -3521,7 +3521,7 @@ "decompress-tarbz2": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", - "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "integrity": "sha1-MIKluIDqQEOBY0nzeLVsUWvho5s=", "dev": true, "requires": { "decompress-tar": "^4.1.0", @@ -3534,7 +3534,7 @@ "file-type": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "integrity": "sha1-5QzXXTVv/tTjBtxPW89Sp5kDqRk=", "dev": true } } @@ -3542,7 +3542,7 @@ "decompress-targz": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", - "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "integrity": "sha1-wJvDXE0R894J8tLaU+neI+fOHu4=", "dev": true, "requires": { "decompress-tar": "^4.1.1", @@ -3603,7 +3603,7 @@ "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "integrity": "sha1-y2ETGESthNhHiPto/QFoHKd4Gi8=", "dev": true, "requires": { "kind-of": "^5.0.2" @@ -3612,7 +3612,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -3626,7 +3626,7 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3682,7 +3682,7 @@ "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" }, "depd": { "version": "1.1.2", @@ -3711,7 +3711,7 @@ "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "integrity": "sha1-yrasM99wydmnJ0kK5DrJladpsio=", "dev": true, "requires": { "colorspace": "1.1.x", @@ -3722,12 +3722,12 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=" }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "integrity": "sha1-Vtv3PZkqSpO6FYT0U0Bj/S5BcX8=", "dev": true, "requires": { "path-type": "^4.0.0" @@ -3736,7 +3736,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "integrity": "sha1-hO0BwKe6OAr+CdkKjBgNzZ0DBDs=", "dev": true } } @@ -3744,7 +3744,7 @@ "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3783,13 +3783,13 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", "dev": true }, "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", "dev": true, "requires": { "domelementtype": "1" @@ -3798,7 +3798,7 @@ "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "integrity": "sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=", "dev": true, "requires": { "dom-serializer": "0", @@ -3817,7 +3817,7 @@ "download": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", - "integrity": "sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==", + "integrity": "sha1-rNalQuTNC7Qspwz8mMnkOwcDlxQ=", "dev": true, "optional": true, "requires": { @@ -3844,7 +3844,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "optional": true, "requires": { @@ -3877,7 +3877,7 @@ "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -3921,7 +3921,7 @@ "each-props": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "integrity": "sha1-6kWkFNFt1c+kGbGoFyDVygaJIzM=", "dev": true, "requires": { "is-plain-object": "^2.0.1", @@ -3980,7 +3980,7 @@ "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", "dev": true, "requires": { "once": "^1.4.0" @@ -4000,7 +4000,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4073,7 +4073,7 @@ "engine.io-parser": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "integrity": "sha1-dXq5cPvy37Mse3SwMyFtVznveaY=", "dev": true, "requires": { "after": "0.8.2", @@ -4092,7 +4092,7 @@ "entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "integrity": "sha1-aNYITKsbB5dnVA2A5Wo5tCPkq/Q=", "dev": true }, "env-variable": { @@ -4114,7 +4114,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -4174,7 +4174,7 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=", "dev": true }, "es6-symbol": { @@ -4190,7 +4190,7 @@ "es6-weak-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "integrity": "sha1-ttofFswswNm+Q+a9v8Xn383zHVM=", "dev": true, "requires": { "d": "1", @@ -4344,7 +4344,7 @@ "eslint-scope": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "integrity": "sha1-6HyIh8c+jR7ITxylkWRcNYv8j7k=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4354,7 +4354,7 @@ "eslint-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "integrity": "sha1-dP7HxU0Hdrb2fgJRBAtYBlZOmB8=", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -4363,7 +4363,7 @@ "eslint-visitor-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "integrity": "sha1-4qgs6oT/JGrW+1f5veW0ZiFFnsI=", "dev": true }, "espree": { @@ -4380,7 +4380,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", "dev": true }, "esquery": { @@ -4422,13 +4422,13 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", "dev": true }, "event-emitter": { @@ -4444,7 +4444,7 @@ "eventemitter3": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "integrity": "sha1-1lF2FjiH7lnzhtZMgmELaWpKdOs=", "dev": true }, "exec-buffer": { @@ -4501,7 +4501,7 @@ "executable": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "integrity": "sha1-QVMr/zYdPlevTXY7cFgtsY9dEzw=", "dev": true, "optional": true, "requires": { @@ -4526,7 +4526,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -4570,7 +4570,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "dev": true, "requires": { "is-number": "^2.1.0", @@ -4627,7 +4627,7 @@ "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", "dev": true, "requires": { "mime-db": "^1.28.0" @@ -4636,7 +4636,7 @@ "ext-name": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", "dev": true, "requires": { "ext-list": "^2.0.0", @@ -4646,7 +4646,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", "dev": true }, "extend-shallow": { @@ -4673,7 +4673,7 @@ "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "integrity": "sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=", "dev": true, "requires": { "chardet": "^0.7.0", @@ -4761,7 +4761,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -4802,7 +4802,7 @@ "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "integrity": "sha1-28GRVPVYaQFQojlToK29A1vkX8c=", "dev": true, "requires": { "ansi-gray": "^0.1.1", @@ -4935,7 +4935,7 @@ "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "integrity": "sha1-yg9u+m3T1WEzP7FFFQZcL6/fQ5w=", "dev": true, "requires": { "flat-cache": "^2.0.1" @@ -4962,7 +4962,7 @@ "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", - "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", + "integrity": "sha1-iPr0lfsbR6v9YSMAACoWIoxnfuk=", "dev": true, "requires": { "filename-reserved-regex": "^2.0.0", @@ -4996,7 +4996,7 @@ "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", "dev": true, "requires": { "debug": "2.6.9", @@ -5011,7 +5011,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -5070,7 +5070,7 @@ "fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "integrity": "sha1-0AvszxqitHXRbUI7Aji3E6LEo3s=", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -5124,13 +5124,13 @@ "flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "integrity": "sha1-595vEnnd2cqarIpZcdYYYGs6q0E=", "dev": true }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "integrity": "sha1-XSltbwS9pEpGMKMBQTvbwuwIXsA=", "dev": true, "requires": { "flatted": "^2.0.0", @@ -5141,18 +5141,18 @@ "flatpickr": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.2.tgz", - "integrity": "sha512-jDy4QYGpmiy7+Qk8QvKJ4spjDdxcx9cxMydmq1x427HkKWBw0qizLYeYM2F6tMcvvqGjU5VpJS55j4LnsaBblA==" + "integrity": "sha1-R8itRyoJbl+350uAmwcDU1OD8g0=" }, "flatted": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "integrity": "sha1-aeV8qo8OrLwoHS4stFjUb9tEngg=", "dev": true }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5240,7 +5240,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -5347,7 +5347,7 @@ "fs-readfile-promise": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-3.0.1.tgz", - "integrity": "sha512-LsSxMeaJdYH27XrW7Dmq0Gx63mioULCRel63B5VeELYLavi1wF5s0XfsIdKDFdCL9hsfQ2qBvXJszQtQJ9h17A==", + "integrity": "sha1-0NMHt/au38kgwx+m5XEu+qKXyVg=", "dev": true, "requires": { "graceful-fs": "^4.1.11" @@ -5400,12 +5400,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5420,17 +5422,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5547,7 +5552,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5559,6 +5565,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5573,6 +5580,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5580,12 +5588,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5604,6 +5614,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5684,7 +5695,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5696,6 +5708,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5817,6 +5830,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5903,13 +5917,13 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", "dev": true }, "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", - "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", + "integrity": "sha1-NJ8rTZHUTE1NTpy6KtkBQ/rF75M=", "dev": true, "requires": { "npm-conf": "^1.1.0" @@ -5945,7 +5959,7 @@ "gifsicle": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gifsicle/-/gifsicle-4.0.1.tgz", - "integrity": "sha512-A/kiCLfDdV+ERV/UB+2O41mifd+RxH8jlRG8DMxZO84Bma/Fw0htqZ+hY2iaalLRNyUu7tYZQslqUBJxBggxbg==", + "integrity": "sha1-MOHmHj7kiE73AmQbLpihXCEnsuI=", "dev": true, "optional": true, "requires": { @@ -5958,7 +5972,7 @@ "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", "dev": true, "optional": true, "requires": { @@ -5974,7 +5988,7 @@ "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", "dev": true, "optional": true, "requires": { @@ -5984,7 +5998,7 @@ "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", "dev": true, "optional": true, "requires": { @@ -6107,7 +6121,7 @@ "glob-watcher": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "integrity": "sha1-iKir8cTRMeuTkomUvEpZPC5d1iY=", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -6157,7 +6171,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", "dev": true }, "globby": { @@ -6193,7 +6207,7 @@ "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "integrity": "sha1-LX3XAr7aIus7/634gGltpthGMT8=", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -6210,7 +6224,7 @@ "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "integrity": "sha1-BUUP2ECU5rvqVvRRpDqcKJFmOFo=", "dev": true, "optional": true, "requires": { @@ -6251,7 +6265,7 @@ "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "integrity": "sha1-VDZRBw/Q9qsKBlDGo+b/WnywnKo=", "dev": true, "requires": { "glob-watcher": "^5.0.3", @@ -6284,7 +6298,7 @@ "gulp-babel": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", - "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", + "integrity": "sha1-4NqW9PLsSojdOjAw9HbjirISbYc=", "dev": true, "requires": { "plugin-error": "^1.0.1", @@ -6304,7 +6318,7 @@ "gulp-clean-css": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz", - "integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==", + "integrity": "sha1-kV7CWNxtPmpQBD9hAGbVwurE9U4=", "dev": true, "requires": { "clean-css": "4.2.1", @@ -6342,7 +6356,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -6353,7 +6367,7 @@ "gulp-cli": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", - "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", + "integrity": "sha1-VTMSbut/5BWn4+hKKX0zTVz3Drw=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -6472,7 +6486,7 @@ "gulp-eslint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-6.0.0.tgz", - "integrity": "sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig==", + "integrity": "sha1-fUArtF+KZ2UrhoJ3ARgSBXNwqDI=", "dev": true, "requires": { "eslint": "^6.0.0", @@ -6483,7 +6497,7 @@ "gulp-imagemin": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-6.1.1.tgz", - "integrity": "sha512-fqaSR8bMc5lhqa6HzRPuJaDY6lY7rcCipe6WtqQ5+hNYTCSPYjXic+1gvFG1+8X879gjVJmIxwmqIbfjuMqTpQ==", + "integrity": "sha1-jm/ezQKZMPLObhKbJmAf0sJhPW4=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -6502,7 +6516,7 @@ "gulp-less": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", - "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", + "integrity": "sha1-NIwzpd3nogfFdxsdgmHRrBAhzu0=", "dev": true, "requires": { "accord": "^0.29.0", @@ -6581,7 +6595,7 @@ "gulp-notify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", - "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==", + "integrity": "sha1-KugiUAnfiB7vWb5d1aLxM3OHdk4=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -6633,7 +6647,7 @@ "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "integrity": "sha1-+XYZXPPzR9DV9SSDVp/oAxzM6Ks=", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0", @@ -6643,7 +6657,7 @@ "lodash.templatesettings": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "integrity": "sha1-5IExDwSdPPbUfpEq0JMTsVTw+zM=", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0" @@ -6667,7 +6681,7 @@ "gulp-postcss": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", - "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", + "integrity": "sha1-jTdyzU0nvKVeyMtMjlduO95NxVA=", "dev": true, "requires": { "fancy-log": "^1.3.2", @@ -6680,7 +6694,7 @@ "gulp-rename": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", - "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", + "integrity": "sha1-3hxxjnxAla6GH3KW708ySGSCQL0=", "dev": true }, "gulp-sort": { @@ -6695,7 +6709,7 @@ "gulp-sourcemaps": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "integrity": "sha1-o/AC2HNG0sDzrsNq9+uHPyPeiuY=", "dev": true, "requires": { "@gulp-sourcemaps/identity-map": "1.X", @@ -6720,7 +6734,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -6781,7 +6795,7 @@ "gulp-watch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", - "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", + "integrity": "sha1-g9N4dS9b+0baAj5zwX7R2nBmIV0=", "dev": true, "requires": { "ansi-colors": "1.1.0", @@ -6882,7 +6896,7 @@ "gulp-wrap": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", - "integrity": "sha512-f17zkGObA+hE/FThlg55gfA0nsXbdmHK1WqzjjB2Ytq1TuhLR7JiCBJ3K4AlMzCyoFaCjfowos+VkToUNE0WTQ==", + "integrity": "sha1-6QFMm7hkOrMQ6TjURpuFaFUaVS8=", "dev": true, "requires": { "consolidate": "^0.15.1", @@ -6926,7 +6940,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -6992,7 +7006,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", "dev": true, "requires": { "ajv": "^6.5.5", @@ -7002,7 +7016,7 @@ "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -7058,7 +7072,7 @@ "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", "dev": true }, "has-symbols": { @@ -7070,7 +7084,7 @@ "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", "dev": true, "requires": { "has-symbol-support-x": "^1.4.1" @@ -7121,13 +7135,13 @@ "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "integrity": "sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=", "dev": true }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", "dev": true, "requires": { "parse-passwd": "^1.0.0" @@ -7154,7 +7168,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", "dev": true }, "htmlparser2": { @@ -7174,7 +7188,7 @@ "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", "dev": true }, "isarray": { @@ -7212,14 +7226,14 @@ "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "integrity": "sha1-ObDhat2bYFvwqe89nar0hDtMrNI=", "dev": true, "optional": true }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", "dev": true, "requires": { "depd": "~1.1.2", @@ -7240,7 +7254,7 @@ "http-proxy": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "integrity": "sha1-2+VfY+daNH2389mZdPJpKjFKajo=", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -7262,7 +7276,7 @@ "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -7271,13 +7285,13 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", "dev": true }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", "dev": true }, "image-size": { @@ -7312,7 +7326,7 @@ "imagemin-gifsicle": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-6.0.1.tgz", - "integrity": "sha512-kuu47c6iKDQ6R9J10xCwL0lgs0+sMz3LRHqRcJ2CRBWdcNmo3T5hUaM8hSZfksptZXJLGKk8heSAvwtSdB1Fng==", + "integrity": "sha1-arrU6VVm1S5aEEq6HCS087SFgbM=", "dev": true, "optional": true, "requires": { @@ -7324,7 +7338,7 @@ "imagemin-jpegtran": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-6.0.0.tgz", - "integrity": "sha512-Ih+NgThzqYfEWv9t58EItncaaXIHR0u9RuhKa8CtVBlMBvY0dCIxgQJQCfwImA4AV1PMfmUKlkyIHJjb7V4z1g==", + "integrity": "sha1-yNO8+27JxWHCCphxQoVL5w2QsE8=", "dev": true, "optional": true, "requires": { @@ -7336,7 +7350,7 @@ "imagemin-optipng": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz", - "integrity": "sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ==", + "integrity": "sha1-IiXILDXlwpt/qY1Pns7hFhpo6Ig=", "dev": true, "optional": true, "requires": { @@ -7387,7 +7401,7 @@ "import-lazy": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", - "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "integrity": "sha1-iRJ5ICyKIoD9vWZ029jaGh38Z8w=", "dev": true, "optional": true }, @@ -7437,7 +7451,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", "dev": true }, "ini": { @@ -7522,7 +7536,7 @@ "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "integrity": "sha1-1QYaYiS+WOgIOYX1AU2EQ1lXYpY=", "dev": true }, "into-stream": { @@ -7539,7 +7553,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -7554,13 +7568,13 @@ "irregular-plurals": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", - "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "integrity": "sha1-OdQPBbAPZW0Lf6RxIw3TtxSvKHI=", "dev": true }, "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "integrity": "sha1-Yc/23TxBk9uUo9YlggcrROVkXXk=", "dev": true }, "is-absolute": { @@ -7739,7 +7753,7 @@ "is-gif": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz", - "integrity": "sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw==", + "integrity": "sha1-xL5gsmowHWlbuDOyDZtdZsbPg7E=", "dev": true, "optional": true, "requires": { @@ -7749,7 +7763,7 @@ "file-type": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", - "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "integrity": "sha1-KWHQnkZ1ufuaPua2npzSP0P9GJA=", "dev": true, "optional": true } @@ -7833,7 +7847,7 @@ "is-png": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz", - "integrity": "sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==", + "integrity": "sha1-7oy8npsFBCXO3utKb7dKZJsKSo0=", "dev": true, "optional": true }, @@ -7882,7 +7896,7 @@ "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "integrity": "sha1-13hIi9CkZmo76KFIK58rqv7eqLQ=", "dev": true }, "is-stream": { @@ -7894,7 +7908,7 @@ "is-svg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "integrity": "sha1-kyHb0pwhLlypnE+peUxxS8r6L3U=", "dev": true, "requires": { "html-comment-regex": "^1.1.0" @@ -7957,7 +7971,7 @@ "isbinaryfile": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "integrity": "sha1-XW3vPt6/boyoyunDAYOoBLX4voA=", "dev": true, "requires": { "buffer-alloc": "^1.2.0" @@ -7984,7 +7998,7 @@ "isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", "dev": true, "requires": { "has-to-string-tag-x": "^1.2.0", @@ -7994,13 +8008,13 @@ "jasmine-core": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "integrity": "sha1-Eywj5kWvlthci8oTyHWLGEKfweQ=", "dev": true }, "jpegtran-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-4.0.0.tgz", - "integrity": "sha512-2cRl1ism+wJUoYAYFt6O/rLBfpXNWG2dUWbgcEkTt5WGMnqI46eEro8T4C5zGROxKRqyKpCBSdHPvt5UYCtxaQ==", + "integrity": "sha1-0ArtgJ+6eqbzCBflnu5N3xmPjxA=", "dev": true, "optional": true, "requires": { @@ -8012,7 +8026,7 @@ "jquery": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", - "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" + "integrity": "sha1-cU8fjZ3eS9+lV2S6N+8hRjDYDvI=" }, "jquery-ui-dist": { "version": "1.12.1", @@ -8027,19 +8041,19 @@ "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "integrity": "sha1-xs7ljrNVA3LfjeuF+tXOZs4B1Z0=", "dev": true }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", "dev": true }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "integrity": "sha1-r/FRswv9+o5J4F2iLnQV6d+jeEc=", "dev": true, "requires": { "argparse": "^1.0.7", @@ -8055,7 +8069,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", "dev": true }, "json-buffer": { @@ -8068,7 +8082,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", "dev": true }, "json-schema": { @@ -8098,7 +8112,7 @@ "json5": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "integrity": "sha1-gbbLBOm6SW8ccAXQe0NoomOPkLY=", "dev": true, "requires": { "minimist": "^1.2.0" @@ -8137,7 +8151,7 @@ "junk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", - "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "integrity": "sha1-MUmQmNkCt+mMXZucgPQ0V6iKv6E=", "dev": true }, "just-debounce": { @@ -8149,7 +8163,7 @@ "karma": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", - "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "integrity": "sha1-bZqqsDejETbcB0ACYg7hHowuMqs=", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -8183,7 +8197,7 @@ "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -8193,13 +8207,13 @@ "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "integrity": "sha1-I8DfFPaogHf1+YbA0WfsA8PVU3w=", "dev": true }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -8224,7 +8238,7 @@ "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -8240,7 +8254,7 @@ "glob-parent": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "integrity": "sha1-X0wdHnSNMM1zrSlEs1d6gbCB6MI=", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8249,7 +8263,7 @@ "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -8267,19 +8281,19 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", "dev": true }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "integrity": "sha1-vXuRE1/GsBzePpuuM9ZZtj2IV+U=", "dev": true }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", "dev": true }, "readdirp": { @@ -8294,13 +8308,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", "dev": true, "requires": { "is-number": "^7.0.0" @@ -8311,7 +8325,7 @@ "karma-jasmine": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", + "integrity": "sha1-JuPjHy+vJy3YDrsOGJiRTMOhl2M=", "dev": true, "requires": { "jasmine-core": "^3.3" @@ -8320,7 +8334,7 @@ "karma-junit-reporter": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz", - "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==", + "integrity": "sha1-007vfwsv0GTgiWlU6IUakM8UyPM=", "dev": true, "requires": { "path-is-absolute": "^1.0.0", @@ -8355,7 +8369,7 @@ "keyv": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "integrity": "sha1-RJI7o55osSp87H32wyaMAx8u83M=", "dev": true, "optional": true, "requires": { @@ -8389,7 +8403,7 @@ "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "integrity": "sha1-73x4TzbJ+24W3TFQ0VJneysCKKY=", "dev": true, "requires": { "colornames": "^1.1.1" @@ -8478,7 +8492,7 @@ "less": { "version": "3.10.3", "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz", - "integrity": "sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow==", + "integrity": "sha1-QXoJddXu7MUs/0vPo8CdNXgeZ5I=", "dev": true, "requires": { "clone": "^2.1.2", @@ -8501,7 +8515,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -8559,7 +8573,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=", "dev": true }, "lodash._basecopy": { @@ -8675,7 +8689,7 @@ "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=", "dev": true }, "lodash.partialright": { @@ -8732,7 +8746,7 @@ "log4js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", - "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "integrity": "sha1-5UNiXpfZ5vPm58n8GW3WqyyuMLU=", "dev": true, "requires": { "date-format": "^2.0.0", @@ -8782,7 +8796,7 @@ "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -8821,7 +8835,7 @@ "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -8887,7 +8901,7 @@ "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "integrity": "sha1-tkIB8FHScbHtwQoE0a6bdLuOXA4=", "dev": true }, "matchdep": { @@ -8905,13 +8919,13 @@ "math-random": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "integrity": "sha1-XdaUPJOFSCZwFtTjTwV1gwgMUUw=", "dev": true }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "integrity": "sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=", "dev": true }, "media-typer": { @@ -8923,7 +8937,7 @@ "memoizee": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "integrity": "sha1-B6APIEaZ+alcLZ53IYJxx81hDVc=", "dev": true, "requires": { "d": "1", @@ -8967,13 +8981,13 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", "dev": true }, "merge2": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "integrity": "sha1-WzZu6DsvFYLEj4fkfPGpNSEDyoE=", "dev": true }, "micromatch": { @@ -9000,7 +9014,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", "dev": true, "optional": true }, @@ -9028,13 +9042,13 @@ "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "integrity": "sha1-SSNTiHju9CBjy4o+OweYeBSHqxs=", "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9049,7 +9063,7 @@ "minimize": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/minimize/-/minimize-2.2.0.tgz", - "integrity": "sha512-IxR2XMbw9pXCxApkdD9BTcH2U4XlXhbeySUrv71rmMS9XDA8BVXEsIuFu24LtwCfBgfbL7Fuh8/ZzkO5DaTLlQ==", + "integrity": "sha1-ixZ28wBR2FmNdDZGvRJpCwdNpMM=", "dev": true, "requires": { "argh": "^0.1.4", @@ -9064,7 +9078,7 @@ "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", "dev": true, "requires": { "for-in": "^1.0.2", @@ -9074,7 +9088,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -9107,7 +9121,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", "dev": true }, "multipipe": { @@ -9122,7 +9136,7 @@ "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "integrity": "sha1-rLAwDrTeI6fd7sAU4+lgRLNHIzE=", "dev": true }, "mute-stream": { @@ -9141,7 +9155,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9166,7 +9180,7 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", "dev": true }, "next-tick": { @@ -9183,13 +9197,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, "node-notifier": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "integrity": "sha1-y3La+UyTkECY4oucWQ/YZuRkvVA=", "dev": true, "requires": { "growly": "^1.3.0", @@ -9211,7 +9225,7 @@ "node.extend": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", - "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", + "integrity": "sha1-tEBFJUlKzJl0DzcDxJa31Rgsxsw=", "dev": true, "requires": { "has": "^1.0.3", @@ -9221,7 +9235,7 @@ "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -9248,27 +9262,27 @@ "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=", "dev": true }, "nouislider": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.0.2.tgz", - "integrity": "sha512-N4AQStV4frh+XcLUwMI/hZpBP6tRboDE/4LZ7gzfxMVXFi/2J9URphnm40Ff4KEyrAVGSGaWApvljoMzTNWBlA==" + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.1.1.tgz", + "integrity": "sha1-3vgSsqqqLM+eekHdAUSiXatWc+U=" }, "now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "integrity": "sha1-jlechoV2SnzALLaAOA6U9DzLH3w=", "dev": true, "requires": { "once": "^1.3.2" } }, "npm": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.12.0.tgz", - "integrity": "sha512-juj5VkB3/k+PWbJUnXD7A/8oc8zLusDnK/sV9PybSalsbOVOTIp5vSE0rz5rQ7BsmUgQS47f/L2GYQnWXaKgnQ==", + "version": "6.13.6", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.6.tgz", + "integrity": "sha1-ht+DBaTYJp0JNOyQeSDnqwec9dk=", "requires": { "JSONStream": "^1.3.5", "abbrev": "~1.1.1", @@ -9276,12 +9290,12 @@ "ansistyles": "~0.1.3", "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.3", + "bin-links": "^1.1.6", "bluebird": "^3.5.5", "byte-size": "^5.0.1", "cacache": "^12.0.3", "call-limit": "^1.1.1", - "chownr": "^1.1.2", + "chownr": "^1.1.3", "ci-info": "^2.0.0", "cli-columns": "^3.1.2", "cli-table3": "^0.5.1", @@ -9297,9 +9311,9 @@ "find-npm-prefix": "^1.0.2", "fs-vacuum": "~1.2.10", "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.2.1", + "gentle-fs": "^2.3.0", "glob": "^7.1.4", - "graceful-fs": "^4.2.2", + "graceful-fs": "^4.2.3", "has-unicode": "~2.0.1", "hosted-git-info": "^2.8.5", "iferr": "^1.0.2", @@ -9312,7 +9326,7 @@ "is-cidr": "^3.0.0", "json-parse-better-errors": "^1.0.2", "lazy-property": "~1.0.0", - "libcipm": "^4.0.4", + "libcipm": "^4.0.7", "libnpm": "^3.0.1", "libnpmaccess": "^3.0.2", "libnpmhook": "^5.0.3", @@ -9346,25 +9360,25 @@ "npm-install-checks": "^3.0.2", "npm-lifecycle": "^3.1.4", "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.4", + "npm-packlist": "^1.4.7", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", + "npm-registry-fetch": "^4.0.2", "npm-user-validate": "~1.0.0", "npmlog": "~4.1.2", "once": "~1.4.0", "opener": "^1.5.1", "osenv": "^0.1.5", - "pacote": "^9.5.8", + "pacote": "^9.5.12", "path-is-inside": "~1.0.2", "promise-inflight": "~1.0.1", "qrcode-terminal": "^0.12.0", "query-string": "^6.8.2", "qw": "~1.0.1", "read": "~1.0.7", - "read-cmd-shim": "^1.0.4", + "read-cmd-shim": "^1.0.5", "read-installed": "~4.0.3", - "read-package-json": "^2.1.0", + "read-package-json": "^2.1.1", "read-package-tree": "^5.3.1", "readable-stream": "^3.4.0", "readdir-scoped-modules": "^1.1.0", @@ -9379,7 +9393,7 @@ "sorted-union-stream": "~2.1.3", "ssri": "^6.0.1", "stringify-package": "^1.0.1", - "tar": "^4.4.12", + "tar": "^4.4.13", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "uid-number": "0.0.6", @@ -9387,7 +9401,7 @@ "unique-filename": "^1.1.1", "unpipe": "~1.0.0", "update-notifier": "^2.5.0", - "uuid": "^3.3.2", + "uuid": "^3.3.3", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "~3.0.0", "which": "^1.3.1", @@ -9535,13 +9549,14 @@ } }, "bin-links": { - "version": "1.1.3", + "version": "1.1.6", "bundled": true, "requires": { "bluebird": "^3.5.3", "cmd-shim": "^3.0.0", - "gentle-fs": "^2.0.1", + "gentle-fs": "^2.3.0", "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", "write-file-atomic": "^2.3.0" } }, @@ -9633,7 +9648,7 @@ } }, "chownr": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true }, "ci-info": { @@ -10194,7 +10209,7 @@ }, "dependencies": { "minipass": { - "version": "2.8.6", + "version": "2.9.0", "bundled": true, "requires": { "safe-buffer": "^5.1.2", @@ -10290,11 +10305,12 @@ "bundled": true }, "gentle-fs": { - "version": "2.2.1", + "version": "2.3.0", "bundled": true, "requires": { "aproba": "^1.1.2", "chownr": "^1.1.2", + "cmd-shim": "^3.0.3", "fs-vacuum": "^1.2.10", "graceful-fs": "^4.1.11", "iferr": "^0.1.5", @@ -10376,7 +10392,7 @@ } }, "graceful-fs": { - "version": "4.2.2", + "version": "4.2.3", "bundled": true }, "har-schema": { @@ -10436,7 +10452,7 @@ } }, "https-proxy-agent": { - "version": "2.2.2", + "version": "2.2.4", "bundled": true, "requires": { "agent-base": "^4.3.0", @@ -10462,7 +10478,7 @@ "bundled": true }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "requires": { "minimatch": "^3.0.4" @@ -10676,7 +10692,7 @@ } }, "libcipm": { - "version": "4.0.4", + "version": "4.0.7", "bundled": true, "requires": { "bin-links": "^1.1.2", @@ -10945,14 +10961,14 @@ } }, "make-fetch-happen": { - "version": "5.0.0", + "version": "5.0.2", "bundled": true, "requires": { "agentkeepalive": "^3.4.1", "cacache": "^12.0.0", "http-cache-semantics": "^3.8.1", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^2.2.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "node-fetch-npm": "^2.0.2", @@ -10998,27 +11014,23 @@ "version": "0.0.8", "bundled": true }, - "minipass": { - "version": "2.3.3", + "minizlib": { + "version": "1.3.3", "bundled": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "minipass": "^2.9.0" }, "dependencies": { - "yallist": { - "version": "3.0.2", - "bundled": true + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } } } }, - "minizlib": { - "version": "1.2.2", - "bundled": true, - "requires": { - "minipass": "^2.2.1" - } - }, "mississippi": { "version": "3.0.0", "bundled": true, @@ -11143,8 +11155,11 @@ } }, "npm-bundled": { - "version": "1.0.6", - "bundled": true + "version": "1.1.1", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } }, "npm-cache-filename": { "version": "1.0.2", @@ -11175,6 +11190,10 @@ "version": "1.2.1", "bundled": true }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true + }, "npm-package-arg": { "version": "6.1.1", "bundled": true, @@ -11186,7 +11205,7 @@ } }, "npm-packlist": { - "version": "1.4.4", + "version": "1.4.7", "bundled": true, "requires": { "ignore-walk": "^3.0.1", @@ -11212,7 +11231,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.0", + "version": "4.0.2", "bundled": true, "requires": { "JSONStream": "^1.3.4", @@ -11220,7 +11239,14 @@ "figgy-pudding": "^3.4.1", "lru-cache": "^5.1.1", "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0" + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } } }, "npm-run-path": { @@ -11337,7 +11363,7 @@ } }, "pacote": { - "version": "9.5.8", + "version": "9.5.12", "bundled": true, "requires": { "bluebird": "^3.5.3", @@ -11354,6 +11380,7 @@ "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", "npm-package-arg": "^6.1.0", "npm-packlist": "^1.1.12", "npm-pick-manifest": "^3.0.0", @@ -11372,7 +11399,7 @@ }, "dependencies": { "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "requires": { "safe-buffer": "^5.1.2", @@ -11572,7 +11599,7 @@ } }, "read-cmd-shim": { - "version": "1.0.4", + "version": "1.0.5", "bundled": true, "requires": { "graceful-fs": "^4.1.2" @@ -11592,14 +11619,14 @@ } }, "read-package-json": { - "version": "2.1.0", + "version": "2.1.1", "bundled": true, "requires": { "glob": "^7.1.1", "graceful-fs": "^4.1.2", "json-parse-better-errors": "^1.0.1", "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "npm-normalize-package-bin": "^1.0.0" } }, "read-package-tree": { @@ -11752,24 +11779,20 @@ "version": "3.0.2", "bundled": true }, - "slash": { - "version": "1.0.0", - "bundled": true - }, "slide": { "version": "1.1.6", "bundled": true }, "smart-buffer": { - "version": "4.0.2", + "version": "4.1.0", "bundled": true }, "socks": { - "version": "2.3.2", + "version": "2.3.3", "bundled": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" + "ip": "1.1.5", + "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { @@ -11984,7 +12007,7 @@ } }, "tar": { - "version": "4.4.12", + "version": "4.4.13", "bundled": true, "requires": { "chownr": "^1.1.1", @@ -11997,7 +12020,7 @@ }, "dependencies": { "minipass": { - "version": "2.8.6", + "version": "2.9.0", "bundled": true, "requires": { "safe-buffer": "^5.1.2", @@ -12159,7 +12182,7 @@ } }, "uuid": { - "version": "3.3.2", + "version": "3.3.3", "bundled": true }, "validate-npm-package-license": { @@ -12320,7 +12343,7 @@ "npm-conf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "dev": true, "requires": { "config-chain": "^1.1.11", @@ -12347,7 +12370,7 @@ "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", "dev": true, "requires": { "boolbase": "~1.0.0" @@ -12368,7 +12391,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", "dev": true }, "object-assign": { @@ -12423,7 +12446,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", "dev": true }, "object-visit": { @@ -12438,7 +12461,7 @@ "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -12599,7 +12622,7 @@ "optipng-bin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/optipng-bin/-/optipng-bin-6.0.0.tgz", - "integrity": "sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg==", + "integrity": "sha1-N2Eg+nnV5x7uL1JBdu/dOl6r0xY=", "dev": true, "optional": true, "requires": { @@ -12652,7 +12675,7 @@ "os-filter-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", + "integrity": "sha1-HAti1fOiRCdJotE55t3e5ugdjRY=", "dev": true, "optional": true, "requires": { @@ -12677,7 +12700,7 @@ "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "integrity": "sha1-ueEjgAvOu3rBOkeb4ZW1B7mNMPo=", "dev": true, "optional": true }, @@ -12717,7 +12740,7 @@ "p-pipe": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.0.0.tgz", - "integrity": "sha512-gwwdRFmaxsT3IU+Tl3vYKVRdjfhg8Bbdjw7B+E0y6F7Yz6l+eaQLn0BRmGMXIhcPDONPtOkMoNwx1etZh4zPJA==", + "integrity": "sha1-qx+4fAuN15s7sDqKI2gPydBU4TI=", "dev": true }, "p-reduce": { @@ -12739,7 +12762,7 @@ "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", "dev": true, "requires": { "callsites": "^3.0.0" @@ -12748,7 +12771,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", "dev": true } } @@ -12806,7 +12829,7 @@ "parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "integrity": "sha1-4rXb7eAOf6m8NjYH9TMn6LBzGJs=", "dev": true }, "parse-passwd": { @@ -12836,7 +12859,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", "dev": true }, "pascalcase": { @@ -12875,7 +12898,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", "dev": true }, "path-root": { @@ -12985,7 +13008,7 @@ "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -12997,7 +13020,7 @@ "plur": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", - "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "integrity": "sha1-YCZ5Z4ZqjYEVBP5Y8vqrojdUals=", "dev": true, "requires": { "irregular-plurals": "^2.0.0" @@ -13052,7 +13075,7 @@ "postcss-colormin": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "integrity": "sha1-rgYLzpPteUrHEmTwgTLVUJVr04E=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13065,7 +13088,7 @@ "postcss-convert-values": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "integrity": "sha1-yjgT7U2g+BL51DcDWE5Enr4Ymn8=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13075,7 +13098,7 @@ "postcss-discard-comments": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "integrity": "sha1-H7q9LCRr/2qq15l7KwkY9NevQDM=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13084,7 +13107,7 @@ "postcss-discard-duplicates": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "integrity": "sha1-P+EzzTyCKC5VD8myORdqkge3hOs=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13093,7 +13116,7 @@ "postcss-discard-empty": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "integrity": "sha1-yMlR6fc+2UKAGUWERKAq2Qu592U=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13102,7 +13125,7 @@ "postcss-discard-overridden": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "integrity": "sha1-ZSrvipZybwKfXj4AFG7npOdV/1c=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13111,7 +13134,7 @@ "postcss-load-config": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "integrity": "sha1-yE1pK3u3tB3c7ZTuYuirMbQXsAM=", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -13121,7 +13144,7 @@ "postcss-merge-longhand": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "integrity": "sha1-YvSaE+Sg7gTnuY9CuxYGLKJUniQ=", "dev": true, "requires": { "css-color-names": "0.0.4", @@ -13133,7 +13156,7 @@ "postcss-merge-rules": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "integrity": "sha1-NivqT/Wh+Y5AdacTxsslrv75plA=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13160,7 +13183,7 @@ "postcss-minify-font-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "integrity": "sha1-zUw0TM5HQ0P6xdgiBqssvLiv1aY=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13170,7 +13193,7 @@ "postcss-minify-gradients": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "integrity": "sha1-k7KcL/UJnFNe7NpWxKpuZlpmNHE=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13182,7 +13205,7 @@ "postcss-minify-params": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "integrity": "sha1-a5zvAwwR41Jh+V9hjJADbWgNuHQ=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13196,7 +13219,7 @@ "postcss-minify-selectors": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "integrity": "sha1-4uXrQL/uUA0M2SQ1APX46kJi+9g=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13221,7 +13244,7 @@ "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "integrity": "sha1-izWt067oOhNrBHHg1ZvlilAoXdQ=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13230,7 +13253,7 @@ "postcss-normalize-display-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "integrity": "sha1-Db4EpM6QY9RmftK+R2u4MMglk1o=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13241,7 +13264,7 @@ "postcss-normalize-positions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "integrity": "sha1-BfdX+E8mBDc3g2ipH4ky1LECkX8=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13253,7 +13276,7 @@ "postcss-normalize-repeat-style": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "integrity": "sha1-xOu8KJ85kaAo1EdRy90RkYsXkQw=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13265,7 +13288,7 @@ "postcss-normalize-string": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "integrity": "sha1-zUTECrB6DHo23F6Zqs4eyk7CaQw=", "dev": true, "requires": { "has": "^1.0.0", @@ -13276,7 +13299,7 @@ "postcss-normalize-timing-functions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "integrity": "sha1-jgCcoqOUnNr4rSPmtquZy159KNk=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13287,7 +13310,7 @@ "postcss-normalize-unicode": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "integrity": "sha1-hBvUj9zzAZrUuqdJOj02O1KuHPs=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13298,7 +13321,7 @@ "postcss-normalize-url": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "integrity": "sha1-EOQ3+GvHx+WPe5ZS7YeNqqlfquE=", "dev": true, "requires": { "is-absolute-url": "^2.0.0", @@ -13310,7 +13333,7 @@ "postcss-normalize-whitespace": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "integrity": "sha1-vx1AcP5Pzqh9E0joJdjMDF+qfYI=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13320,7 +13343,7 @@ "postcss-ordered-values": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "integrity": "sha1-DPdcgg7H1cTSgBiVWeC1ceusDu4=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13331,7 +13354,7 @@ "postcss-reduce-initial": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "integrity": "sha1-f9QuvqXpyBRgljniwuhK4nC6SN8=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13343,7 +13366,7 @@ "postcss-reduce-transforms": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "integrity": "sha1-F++kBerMbge+NBSlyi0QdGgdTik=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13366,7 +13389,7 @@ "postcss-svgo": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "integrity": "sha1-F7mXvHEbMzurFDqu07jT1uPTglg=", "dev": true, "requires": { "is-svg": "^3.0.0", @@ -13378,7 +13401,7 @@ "postcss-unique-selectors": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "integrity": "sha1-lEaRHzKJv9ZMbWgPBzwDsfnuS6w=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13414,7 +13437,7 @@ "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", - "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", + "integrity": "sha1-8oSeJ9t5+01s/iR2T8QTTxZZifI=", "dev": true }, "pretty-hrtime": { @@ -13426,19 +13449,19 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", "dev": true }, "promise": { @@ -13479,7 +13502,7 @@ "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -13489,7 +13512,7 @@ "pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -13500,7 +13523,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", "dev": true }, "q": { @@ -13518,13 +13541,13 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", "dev": true }, "query-string": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "integrity": "sha1-p4wBK3HBfgXy4/ojGd0zBoLvs8s=", "dev": true, "optional": true, "requires": { @@ -13545,7 +13568,7 @@ "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "integrity": "sha1-t3bvxZN1mE42xTey9RofCv8Noe0=", "dev": true, "requires": { "is-number": "^4.0.0", @@ -13556,7 +13579,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -13564,13 +13587,13 @@ "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", "dev": true }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", "dev": true, "requires": { "bytes": "3.1.0", @@ -13615,7 +13638,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -13684,7 +13707,7 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", "dev": true }, "regenerate-unicode-properties": { @@ -13727,7 +13750,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", "dev": true }, "regexpu-core": { @@ -13747,7 +13770,7 @@ "regjsgen": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "integrity": "sha1-SPC/Gl6iBRlpKcDZeYtC0e2YRDw=", "dev": true }, "regjsparser": { @@ -13770,7 +13793,7 @@ "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "integrity": "sha1-wr8eN3Ug0yT2I4kuM8EMrCwlK1M=", "dev": true, "requires": { "is-buffer": "^1.1.5", @@ -13797,7 +13820,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", "dev": true }, "repeat-string": { @@ -13967,13 +13990,13 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "integrity": "sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY=", "dev": true }, "rfdc": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "integrity": "sha1-unLME2egzNnPgahws7WL060H+MI=", "dev": true }, "rgb-regex": { @@ -14000,7 +14023,7 @@ "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "integrity": "sha1-stEE/g2Psnz54KHNqCYt04M8bKs=", "dev": true, "requires": { "glob": "^7.1.3" @@ -14018,7 +14041,7 @@ "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "integrity": "sha1-yd06fPn0ssS2JE4XOm7YZuYd1nk=", "dev": true }, "run-sequence": { @@ -14162,7 +14185,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", "dev": true }, "semver-greatest-satisfied-range": { @@ -14177,7 +14200,7 @@ "semver-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "integrity": "sha1-qTwsWERTmncCMzeRB7OMe0rJ0zg=", "dev": true, "optional": true }, @@ -14200,7 +14223,7 @@ "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -14223,7 +14246,7 @@ "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", "dev": true }, "shebang-command": { @@ -14244,7 +14267,7 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", "dev": true }, "signal-exit": { @@ -14256,7 +14279,7 @@ "signalr": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.0.tgz", - "integrity": "sha512-GPJHb3pcNk3IUui5/WG8lMuarEn+Vpc8wEvJ60w0KQ43W9FHnJcuNcF8dkZePr81eBslzicsRdyEunKNF7KjZQ==", + "integrity": "sha1-kq8AjmtSetSzbpT7s0DhNQh6YNI=", "requires": { "jquery": ">=1.6.4" } @@ -14273,7 +14296,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=", "dev": true } } @@ -14287,7 +14310,7 @@ "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "integrity": "sha1-ys12k0YaY3pXiNkqfdT7oGjoFjY=", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -14322,7 +14345,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -14428,7 +14451,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", "dev": true, "requires": { "debug": "~3.1.0", @@ -14442,7 +14465,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14465,7 +14488,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", "dev": true, "requires": { "backo2": "1.0.2", @@ -14493,7 +14516,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14593,13 +14616,13 @@ "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "integrity": "sha1-AI22XtzmxQ7sDF4ijhlFBh3QQ3w=", "dev": true }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "integrity": "sha1-+4PlBERSaPFUsHTiGMh8ADzTHfQ=", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -14609,7 +14632,7 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=", "dev": true }, "spdx-expression-parse": { @@ -14625,7 +14648,7 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "integrity": "sha1-NpS1gEVnpFjTyARYQqY1hjL2JlQ=", "dev": true }, "spectrum-colorpicker": { @@ -14693,7 +14716,7 @@ "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", "dev": true, "requires": { "asn1": "~0.2.3", @@ -14749,7 +14772,7 @@ "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "integrity": "sha1-rNrI2lnvK8HheiwMz2wyDRIOVV0=", "dev": true }, "stream-shift": { @@ -14761,7 +14784,7 @@ "streamroller": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", - "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "integrity": "sha1-gWfYSW7Z8Z8F7ksVjZYRMhuMrNk=", "dev": true, "requires": { "async": "^2.6.2", @@ -14774,7 +14797,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "dev": true, "requires": { "ms": "^2.1.1" @@ -14883,7 +14906,7 @@ "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", - "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "integrity": "sha1-SYdzYmT8NEzyD2w0rKnRPR1O1sU=", "dev": true, "requires": { "is-natural-number": "^4.0.1" @@ -14908,7 +14931,7 @@ "strip-json-comments": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "integrity": "sha1-hXE5dakfuHvxswXMp3OV5A0qZKc=", "dev": true }, "strip-outer": { @@ -14923,7 +14946,7 @@ "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "integrity": "sha1-Zxj8r00eB9ihMYaQiB6NlnJqcdU=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -14947,7 +14970,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -14987,7 +15010,7 @@ "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "integrity": "sha1-EpLRlQDOP4YFOwXw6Ofko7shB54=", "dev": true, "requires": { "ajv": "^6.10.2", @@ -14999,7 +15022,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", "dev": true }, "is-fullwidth-code-point": { @@ -15011,7 +15034,7 @@ "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -15022,7 +15045,7 @@ "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -15033,7 +15056,7 @@ "tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", "dev": true, "requires": { "bl": "^1.0.0", @@ -15096,7 +15119,7 @@ "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "integrity": "sha1-adycGxdEbueakr9biEu0uRJ1BvU=", "dev": true }, "text-table": { @@ -15120,7 +15143,7 @@ "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", "dev": true, "requires": { "readable-stream": "~2.3.6", @@ -15162,7 +15185,7 @@ "through2-concurrent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/through2-concurrent/-/through2-concurrent-2.0.0.tgz", - "integrity": "sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A==", + "integrity": "sha1-yd0sFGUE7Jli28hqUWi2PWYmafo=", "dev": true, "requires": { "through2": "^2.0.0" @@ -15171,7 +15194,7 @@ "through2-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "integrity": "sha1-cA54bfI2fCyIzYqlvkz5weeDElQ=", "dev": true, "requires": { "through2": "~2.0.0", @@ -15193,7 +15216,7 @@ "timers-ext": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "integrity": "sha1-b1ethXjgej+5+R2Th9ZWR1VeJcY=", "dev": true, "requires": { "es5-ext": "~0.10.46", @@ -15209,12 +15232,12 @@ "tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" }, "tinymce": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.2.tgz", - "integrity": "sha512-ZRoTGG4GAsOI73QPSNkabO7nkoYw9H6cglRB44W2mMkxSiqxYi8WJlgkUphk0fDqo6ZD6r3E+NSP4UHxF2lySg==" + "version": "4.9.7", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.7.tgz", + "integrity": "sha1-dM5U8Hz8AmilrF409hAERljJL0c=" }, "tmp": { "version": "0.0.33", @@ -15307,7 +15330,7 @@ "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", "dev": true }, "tough-cookie": { @@ -15374,7 +15397,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=", "dev": true }, "type-check": { @@ -15389,7 +15412,7 @@ "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15474,7 +15497,7 @@ "unbzip2-stream": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", - "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "integrity": "sha1-0VbSBeZw2NjDk+HALr1QZCKHP2o=", "dev": true, "requires": { "buffer": "^5.2.1", @@ -15490,12 +15513,12 @@ "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + "integrity": "sha1-BtzjSg5op7q8KbNluOdLiSUgOWE=" }, "undertaker": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "integrity": "sha1-cBZi/4zjWHFTJN/UkqTwNgVd/ks=", "dev": true, "requires": { "arr-flatten": "^1.0.1", @@ -15518,13 +15541,13 @@ "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=", "dev": true }, "unicode-match-property-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", @@ -15546,7 +15569,7 @@ "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -15570,7 +15593,7 @@ "unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "integrity": "sha1-xl0RDppK35psWUiygFPZqNBMvqw=", "dev": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", @@ -15580,7 +15603,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", "dev": true }, "unpipe": { @@ -15644,7 +15667,7 @@ "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", "dev": true }, "uri-js": { @@ -15681,13 +15704,13 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", "dev": true }, "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -15725,13 +15748,13 @@ "v8-compile-cache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "integrity": "sha1-4U3jezGm0ZT1aQ1n78Tn9vxqsw4=", "dev": true }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -15811,7 +15834,7 @@ "vinyl-fs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "integrity": "sha1-yFhJQF9nQo/qu71cXb3WT0fTG8c=", "dev": true, "requires": { "fs-mkdirp-stream": "^1.0.0", @@ -15970,7 +15993,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { "isexe": "^2.0.0" @@ -16013,7 +16036,7 @@ "write": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "integrity": "sha1-CADhRSO5I6OH5BUSPIZWFqrg9cM=", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -16033,7 +16056,7 @@ "xmlbuilder": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", - "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", + "integrity": "sha1-4u1nXgaDSgid37hNuW4sKwP3jBo=", "dev": true }, "xmlhttprequest-ssl": { @@ -16045,7 +16068,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", "dev": true }, "y18n": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 0f02aba5e2..c150af79de 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "ace-builds": "1.4.2", - "angular": "1.7.5", + "angular": "1.7.9", "angular-animate": "1.7.5", - "angular-aria": "1.7.5", + "angular-aria": "1.7.9", "angular-chart.js": "^1.1.1", "angular-cookies": "1.7.5", "angular-dynamic-locale": "0.1.37", @@ -39,10 +39,10 @@ "moment": "2.22.2", "ng-file-upload": "12.2.13", "nouislider": "14.1.1", - "npm": "6.12.0", + "npm": "6.13.6", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", - "tinymce": "4.9.2", + "tinymce": "4.9.7", "typeahead.js": "0.11.1", "underscore": "1.9.1" }, diff --git a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg index 6c0515906f..75bf0d52af 100644 Binary files a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg and b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg differ 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/buttons/umbtoggle.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js index 5d34ad2906..79cb99cf07 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js @@ -75,6 +75,8 @@ scope.displayLabelOff = ""; function onInit() { + scope.inputId = scope.inputId || "umb-toggle_" + String.CreateGuid(); + setLabelText(); // must wait until the current digest cycle is finished before we emit this event on init, // otherwise other property editors might not yet be ready to receive the event 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 cab71842b1..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 @@ -791,6 +793,7 @@ $scope.content.variants[i].expireDate = model.variants[i].expireDate; $scope.content.variants[i].releaseDateFormatted = model.variants[i].releaseDateFormatted; $scope.content.variants[i].expireDateFormatted = model.variants[i].expireDateFormatted; + $scope.content.variants[i].save = model.variants[i].save; } model.submitButtonState = "busy"; 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 78a2111fc5..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 @@ -130,6 +130,7 @@ view: "default", content: labels.doctypeChangeWarning, submitButtonLabelKey: "general_continue", + submitButtonStyle: "warning", closeButtonLabelKey: "general_cancel", submit: function () { openDocTypeEditor(documentType); @@ -327,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/content/umbvariantcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js index 5dc8903e65..6fec20b256 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js @@ -58,9 +58,9 @@ /** Called when the component has linked all elements, this is when the form controller is available */ function postLink() { //set the content to dirty if the header changes - unsubscribe.push($scope.$watch("contentHeaderForm.$dirty", - function(newValue, oldValue) { - if (newValue === true) { + unsubscribe.push($scope.$watch("vm.editor.content.name", + function (newValue, oldValue) { + if (newValue !== oldValue) { vm.editor.content.isDirty = true; } })); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js index b3948bd7c4..fe2a6aa40a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js @@ -17,8 +17,8 @@ scope.isNew = scope.content.state == "NotCreated"; localizationService.localizeMany([ - scope.isNew ? "placeholders_a11yCreateItem" : "placeholders_a11yEdit", - "placeholders_a11yName", + scope.isNew ? "visuallyHiddenTexts_createItem" : "visuallyHiddenTexts_edit", + "visuallyHiddenTexts_name", scope.isNew ? "general_new" : "general_edit"] ).then(function (data) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index 431a05778c..87053c083c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -233,8 +233,8 @@ Use this directive to construct a header inside the main editor window. editorState.current.id === "-1"; var localizeVars = [ - scope.isNew ? "placeholders_a11yCreateItem" : "placeholders_a11yEdit", - "placeholders_a11yName", + scope.isNew ? "visuallyHiddenTexts_createItem" : "visuallyHiddenTexts_edit", + "visuallyHiddenTexts_name", scope.isNew ? "general_new" : "general_edit" ]; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js index ff51b1ae90..9a9d6d4a76 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js @@ -46,6 +46,8 @@ vm.change = change; function onInit() { + vm.inputId = vm.inputId || "umb-check_" + String.CreateGuid(); + // If a labelKey is passed let's update the returned text if it's does not contain an opening square bracket [ if (vm.labelKey) { localizationService.localize(vm.labelKey).then(function (data) { @@ -69,6 +71,7 @@ templateUrl: 'views/components/forms/umb-checkbox.html', controller: UmbCheckboxController, controllerAs: 'vm', + transclude: true, bindings: { model: "=", inputId: "@", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js index f3ecac2a74..d79140f947 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js @@ -44,6 +44,8 @@ vm.change = change; function onInit() { + vm.inputId = vm.inputId || "umb-radio_" + String.CreateGuid(); + // If a labelKey is passed let's update the returned text if it's does not contain an opening square bracket [ if (vm.labelKey) { localizationService.localize(vm.labelKey).then(function (data) { @@ -67,6 +69,7 @@ templateUrl: 'views/components/forms/umb-radiobutton.html', controller: UmbRadiobuttonController, controllerAs: 'vm', + transclude: true, bindings: { model: "=", inputId: "@", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js index bc3993458e..fa1f4227a2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js @@ -213,7 +213,6 @@ Opens an overlay to show a custom YSOD.
var unsubscribe = []; function activate() { - setView(); setButtonText(); @@ -247,10 +246,20 @@ Opens an overlay to show a custom YSOD.
setOverlayIndent(); + focusOnOverlayHeading() }); } + // Ideally this would focus on the first natively focusable element in the overlay, but as the content can be dynamic, it is focusing on the heading. + function focusOnOverlayHeading() { + var heading = el.find(".umb-overlay__title"); + + if(heading) { + heading.focus(); + } + } + function setView() { if (scope.view) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js index 6f195dcc52..3e2e7e362e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js @@ -14,7 +14,8 @@ function treeSearchBox(localizationService, searchService, $q) { section: "@", datatypeKey: "@", hideSearchCallback: "=", - searchCallback: "=" + searchCallback: "=", + autoFocus: "=" }, restrict: "E", // restrict to an element replace: true, // replace the html element with the template 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 4fc22c4b74..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,20 +179,36 @@ 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(); })); + // sortable options for allowed child content types + scope.sortableOptions = { + axis: "y", + cancel: ".unsortable", + containment: "parent", + distance: 10, + opacity: 0.7, + tolerance: "pointer", + scroll: true, + zIndex: 6000, + update: function (e, ui) { + if(scope.onSort) { + scope.onSort(); + } + } + }; + // clean up scope.$on('$destroy', function(){ // unbind watchers @@ -209,7 +230,8 @@ Use this directive to render a ui component for selecting child items to a paren parentIcon: "=", parentId: "=", onRemove: "=", - onAdd: "=" + onAdd: "=", + onSort: "=" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcodesnippet.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcodesnippet.directive.js new file mode 100644 index 0000000000..f0dad31ee2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcodesnippet.directive.js @@ -0,0 +1,119 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbCodeSnippet +@restrict E +@scope + +@description + +

    Markup example

    +
    +	
    + + + {{code}} + + +
    +
    + +

    Controller example

    +
    +	(function () {
    +		"use strict";
    +
    +		function Controller() {
    +
    +            var vm = this;
    +
    +        }
    +
    +		angular.module("umbraco").controller("My.Controller", Controller);
    +
    +	})();
    +
    + +@param {string=} language Language of the code snippet, e.g csharp, html, css. +**/ + + +(function () { + 'use strict'; + + var umbCodeSnippet = { + templateUrl: 'views/components/umb-code-snippet.html', + controller: UmbCodeSnippetController, + controllerAs: 'vm', + transclude: true, + bindings: { + language: '<' + } + }; + + function UmbCodeSnippetController($timeout) { + + const vm = this; + + vm.page = {}; + + vm.$onInit = onInit; + vm.copySuccess = copySuccess; + vm.copyError = copyError; + + function onInit() { + vm.guid = String.CreateGuid(); + + if (vm.language) + { + switch (vm.language.toLowerCase()) { + case "csharp": + case "c#": + vm.language = "C#"; + break; + case "html": + vm.language = "HTML"; + break; + case "css": + vm.language = "CSS"; + break; + case "javascript": + vm.language = "JavaScript"; + break; + } + } + + } + + // copy to clip board success + function copySuccess() { + if (vm.page.copyCodeButtonState !== "success") { + $timeout(function () { + vm.page.copyCodeButtonState = "success"; + }); + $timeout(function () { + resetClipboardButtonState(); + }, 1000); + } + } + + // copy to clip board error + function copyError() { + if (vm.page.copyCodeButtonState !== "error") { + $timeout(function () { + vm.page.copyCodeButtonState = "error"; + }); + $timeout(function () { + resetClipboardButtonState(); + }, 1000); + } + } + + function resetClipboardButtonState() { + vm.page.copyCodeButtonState = "init"; + } + } + + angular.module('umbraco.directives').component('umbCodeSnippet', umbCodeSnippet); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js index 1b011d2e19..1c4bf4d583 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js @@ -312,14 +312,7 @@ Use this directive to generate a thumbnail grid of media items. scope.onDetailsHover(item, $event, hover); } }; - - scope.clickEdit = function(item, $event) { - if (scope.onClickEdit) { - scope.onClickEdit({"item": item}) - $event.stopPropagation(); - } - }; - + var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) { if (angular.isArray(newValue)) { activate(); @@ -341,8 +334,8 @@ Use this directive to generate a thumbnail grid of media items. onDetailsHover: "=", onClick: '=', onClickName: "=", - onClickEdit: "&?", - allowOnClickEdit: "@?", + allowOpenFolder: "=", + allowOpenFile: "=", filterBy: "=", itemMaxWidth: "@", itemMaxHeight: "@", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js index 1142863bd0..66e03a7302 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js @@ -58,7 +58,9 @@ entityResource.getPagedChildren(miniListView.node.id, scope.entityType, miniListView.pagination) .then(function (data) { - + if (scope.onItemsLoaded) { + scope.onItemsLoaded({items: data.items}); + } // update children miniListView.children = data.items; _.each(miniListView.children, function(c) { @@ -208,6 +210,7 @@ startNodeId: "=", onSelect: "&", onClose: "&", + onItemsLoaded: "&", entityTypeFilter: "=" }, link: link diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js deleted file mode 100644 index 7914dfc3f0..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Konami Code directive for AngularJS - * @version v0.0.1 - * @license MIT License, https://www.opensource.org/licenses/MIT - */ - -angular.module('umbraco.directives') - .directive('konamiCode', ['$document', function ($document) { - var konamiKeysDefault = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]; - - return { - restrict: 'A', - link: function (scope, element, attr) { - - if (!attr.konamiCode) { - throw ('Konami directive must receive an expression as value.'); - } - - // Let user define a custom code. - var konamiKeys = attr.konamiKeys || konamiKeysDefault; - var keyIndex = 0; - - /** - * Fired when konami code is type. - */ - function activated() { - if ('konamiOnce' in attr) { - stopListening(); - } - // Execute expression. - scope.$eval(attr.konamiCode); - } - - /** - * Handle keydown events. - */ - function keydown(e) { - if (e.keyCode === konamiKeys[keyIndex++]) { - if (keyIndex === konamiKeys.length) { - keyIndex = 0; - activated(); - } - } else { - keyIndex = 0; - } - } - - /** - * Stop to listen typing. - */ - function stopListening() { - $document.off('keydown', keydown); - } - - // Start listening to key typing. - $document.on('keydown', keydown); - - // Stop listening when scope is destroyed. - scope.$on('$destroy', stopListening); - } - }; - }]); 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/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index 3874ff9bf6..ea7f3a6d4c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -84,7 +84,7 @@ angular.module('umbraco.mocks'). "buttons_save": "Save", "buttons_saveAndPublish": "Save and publish", "buttons_saveToPublish": "Save and send for approval", - "buttons_showPage": "Preview", + "buttons_saveAndPreview": "Save and preview", "buttons_showPageDisabled": "Preview is disabled because there's no template assigned", "buttons_styleChoose": "Choose style", "buttons_styleShow": "Show styles", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index 64accc18c1..97bebef062 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -351,6 +351,16 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca return umbRequestHelper.resourcePromise( $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateDefaultTemplate", { id: id })), 'Failed to create default template for content type with id ' + id); + }, + + hasContentNodes: function (id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentTypeApiBaseUrl", + "HasContentNodes", + [{ id: id }])), + 'Failed to retrieve indication for whether content type with id ' + id + ' has associated content nodes'); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/emailmarketing.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/emailmarketing.resource.js new file mode 100644 index 0000000000..4ac56ad13b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/emailmarketing.resource.js @@ -0,0 +1,34 @@ +/** + * @ngdoc service + * @name umbraco.resources.emailMarketingResource + * @description Used to add a backoffice user to Umbraco's email marketing system, if user opts in + * + * + **/ +function emailMarketingResource($http, umbRequestHelper) { + + // LOCAL + // http://localhost:7071/api/EmailProxy + + // LIVE + // https://emailcollector.umbraco.io/api/EmailProxy + + const emailApiUrl = 'https://emailcollector.umbraco.io/api/EmailProxy'; + + //the factory object returned + return { + + postAddUserToEmailMarketing: (user) => { + return umbRequestHelper.resourcePromise( + $http.post(emailApiUrl, + { + name: user.name, + email: user.email, + usergroup: user.userGroups // [ "admin", "sensitiveData" ] + }), + 'Failed to add user to email marketing list'); + } + }; +} + +angular.module('umbraco.resources').factory('emailMarketingResource', emailMarketingResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 9cf1181cfa..61d646afc0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -127,6 +127,25 @@ function entityResource($q, $http, umbRequestHelper) { 'Failed to retrieve url for id:' + id); }, + getUrlByUdi: function (udi, culture) { + + if (!udi) { + return ""; + } + + if (!culture) { + culture = ""; + } + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "GetUrl", + [{ udi: udi }, {culture: culture }])), + 'Failed to retrieve url for UDI:' + udi); + }, + /** * @ngdoc method * @name umbraco.resources.entityResource#getById @@ -166,18 +185,22 @@ function entityResource($q, $http, umbRequestHelper) { }, - getUrlAndAnchors: function (id) { + getUrlAndAnchors: function (id, culture) { if (id === -1 || id === "-1") { return null; } + if (!culture) { + culture = ""; + } + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetUrlAndAnchors", - [{ id: id }])), + [{ id: id }, {culture: culture }])), 'Failed to retrieve url and anchors data for id ' + id); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js new file mode 100644 index 0000000000..a937cd2675 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js @@ -0,0 +1,36 @@ +/** + * @ngdoc service + * @name umbraco.resources.imageUrlGeneratorResource + * @function + * + * @description + * Used by the various controllers to get an image URL formatted correctly for the current image URL generator + */ +(function () { + 'use strict'; + + function imageUrlGeneratorResource($http, umbRequestHelper) { + + function getCropUrl(mediaPath, width, height, imageCropMode, animationProcessMode) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "imageUrlGeneratorApiBaseUrl", + "GetCropUrl", + { mediaPath, width, height, imageCropMode, animationProcessMode })), + 'Failed to get crop URL'); + } + + + var resource = { + getCropUrl: getCropUrl + }; + + return resource; + + } + + angular.module('umbraco.resources').factory('imageUrlGeneratorResource', imageUrlGeneratorResource); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js index bcdaddd22f..84c0f60e59 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js @@ -164,69 +164,6 @@ function logResource($q, $http, umbRequestHelper) { 'Failed to retrieve log data for id'); }, - /** - * @ngdoc method - * @name umbraco.resources.logResource#getEntityLog - * @methodOf umbraco.resources.logResource - * - * @description - * [OBSOLETE] use getPagedEntityLog instead
    - * Gets the log history for a give entity id - * - * ##usage - *
    -         * logResource.getEntityLog(1234)
    -         *    .then(function(log) {
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {Int} id id of entity to return log history - * @returns {Promise} resourcePromise object containing the log. - * - */ - getEntityLog: function(id) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetEntityLog", - [{ id: id }])), - 'Failed to retrieve user data for id ' + id); - }, - - /** - * @ngdoc method - * @name umbraco.resources.logResource#getUserLog - * @methodOf umbraco.resources.logResource - * - * @description - * [OBSOLETE] use getPagedUserLog instead
    - * Gets the current user's log history for a given type of log entry - * - * ##usage - *
    -         * logResource.getUserLog("save", new Date())
    -         *    .then(function(log) {
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {String} type logtype to query for - * @param {DateTime} since query the log back to this date, by defalt 7 days ago - * @returns {Promise} resourcePromise object containing the log. - * - */ - getUserLog: function(type, since) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetCurrentUserLog", - [{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])), - 'Failed to retrieve log data for current user of type ' + type + ' since ' + since); - }, - /** * @ngdoc method * @name umbraco.resources.logResource#getLog diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/tour.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/tour.resource.js index 40baf0f389..485b0d299a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/tour.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/tour.resource.js @@ -20,10 +20,21 @@ "GetTours")), 'Failed to get tours'); } + + function getToursForDoctype(doctypeAlias) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "tourApiBaseUrl", + "GetToursForDoctype", + [{ doctypeAlias: doctypeAlias }])), + 'Failed to get tours'); + } var resource = { - getTours: getTours + getTours: getTours, + getToursForDoctype: getToursForDoctype }; return resource; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js index 0fd308ffd0..2b9e0c0fd5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js @@ -405,6 +405,43 @@ formattedSaveData), "Failed to save user"); } + + /** + * @ngdoc method + * @name umbraco.resources.usersResource#changePassword + * @methodOf umbraco.resources.usersResource + * + * @description + * Changes a user's password + * + * ##usage + *
    +          * usersResource.changePassword(changePasswordModel)
    +          *    .then(function() {
    +          *        // password changed
    +          *    });
    +          * 
    + * + * @param {Object} model object to save + * @returns {Promise} resourcePromise object containing the updated user. + * + */ + function changePassword(changePasswordModel) { + if (!changePasswordModel) { + throw "password model not specified"; + } + + //need to convert the password data into the correctly formatted save data - it is *not* the same and we don't want to over-post + var formattedPasswordData = umbDataFormatter.formatChangePasswordModel(changePasswordModel); + + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "userApiBaseUrl", + "PostChangePassword"), + formattedPasswordData), + "Failed to save user"); + } /** * @ngdoc method @@ -447,6 +484,7 @@ createUser: createUser, inviteUser: inviteUser, saveUser: saveUser, + changePassword: changePassword, deleteNonLoggedInUser: deleteNonLoggedInUser, clearAvatar: clearAvatar }; 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 8b922d7ec8..9c935086a0 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 @@ -169,6 +169,7 @@ When building a custom infinite editor view you can use the same components as a let editorsKeyboardShorcuts = []; var editors = []; var isEnabled = true; + var lastElementInFocus = null; // events for backdrop @@ -261,6 +262,12 @@ When building a custom infinite editor view you can use the same components as a */ unbindKeyboardShortcuts(); + // if this is the first editor layer, save the currently focused element + // so we can re-apply focus to it once all the editor layers are closed + if (editors.length === 0) { + lastElementInFocus = document.activeElement; + } + // set flag so we know when the editor is open in "infinite mode" editor.infiniteMode = true; @@ -301,6 +308,10 @@ When building a custom infinite editor view you can use the same components as a $timeout(function() { // rebind keyboard shortcuts for the new editor in focus rebindKeyboardShortcuts(); + + if (editors.length === 0 && lastElementInFocus) { + lastElementInFocus.focus(); + } }, 0); } @@ -367,7 +378,7 @@ When building a custom infinite editor view you can use the same components as a */ function contentPicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; editor.section = "content"; editor.treeAlias = "content"; open(editor); @@ -390,7 +401,7 @@ When building a custom infinite editor view you can use the same components as a */ function contentTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; editor.section = "settings"; editor.treeAlias = "documentTypes"; open(editor); @@ -413,7 +424,7 @@ When building a custom infinite editor view you can use the same components as a */ function mediaTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; editor.section = "settings"; editor.treeAlias = "mediaTypes"; open(editor); @@ -436,7 +447,7 @@ When building a custom infinite editor view you can use the same components as a */ function memberTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; editor.section = "settings"; editor.treeAlias = "memberTypes"; open(editor); @@ -457,7 +468,7 @@ When building a custom infinite editor view you can use the same components as a function copy(editor) { editor.view = "views/common/infiniteeditors/copy/copy.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -477,7 +488,7 @@ When building a custom infinite editor view you can use the same components as a function move(editor) { editor.view = "views/common/infiniteeditors/move/move.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -495,7 +506,7 @@ When building a custom infinite editor view you can use the same components as a function embed(editor) { editor.view = "views/common/infiniteeditors/embed/embed.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -514,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"; - editor.size = "small"; + if (!editor.size) editor.size = "medium"; open(editor); } @@ -534,7 +545,7 @@ When building a custom infinite editor view you can use the same components as a */ function linkPicker(editor) { editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -577,7 +588,7 @@ When building a custom infinite editor view you can use the same components as a */ function mediaPicker(editor) { editor.view = "views/common/infiniteeditors/mediapicker/mediapicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "medium"; editor.updatedMediaNodes = []; open(editor); } @@ -598,7 +609,7 @@ When building a custom infinite editor view you can use the same components as a */ function iconPicker(editor) { editor.view = "views/common/infiniteeditors/iconpicker/iconpicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -640,6 +651,23 @@ When building a custom infinite editor view you can use the same components as a editor.view = "views/mediatypes/edit.html"; open(editor); } + + /** + * @ngdoc method + * @name umbraco.services.editorService#memberTypeEditor + * @methodOf umbraco.services.editorService + * + * @description + * Opens the member type editor in infinite editing, the submit callback returns the saved member type + * @param {Object} editor rendering options + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + function memberTypeEditor(editor) { + editor.view = "views/membertypes/edit.html"; + open(editor); + } /** * @ngdoc method @@ -692,7 +720,7 @@ When building a custom infinite editor view you can use the same components as a */ function treePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -710,7 +738,7 @@ When building a custom infinite editor view you can use the same components as a */ function nodePermissions(editor) { editor.view = "views/common/infiniteeditors/nodepermissions/nodepermissions.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -728,7 +756,7 @@ When building a custom infinite editor view you can use the same components as a */ function insertCodeSnippet(editor) { editor.view = "views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -746,7 +774,7 @@ When building a custom infinite editor view you can use the same components as a */ function userGroupPicker(editor) { editor.view = "views/common/infiniteeditors/usergrouppicker/usergrouppicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -756,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 @@ -782,7 +810,7 @@ When building a custom infinite editor view you can use the same components as a */ function sectionPicker(editor) { editor.view = "views/common/infiniteeditors/sectionpicker/sectionpicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -800,7 +828,7 @@ When building a custom infinite editor view you can use the same components as a */ function insertField(editor) { editor.view = "views/common/infiniteeditors/insertfield/insertfield.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -818,7 +846,7 @@ When building a custom infinite editor view you can use the same components as a */ function templateSections(editor) { editor.view = "views/common/infiniteeditors/templatesections/templatesections.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -836,7 +864,7 @@ When building a custom infinite editor view you can use the same components as a */ function userPicker(editor) { editor.view = "views/common/infiniteeditors/userpicker/userpicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -858,7 +886,7 @@ When building a custom infinite editor view you can use the same components as a */ function itemPicker(editor) { editor.view = "views/common/infiniteeditors/itempicker/itempicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -876,7 +904,7 @@ When building a custom infinite editor view you can use the same components as a */ function macroPicker(editor) { editor.view = "views/common/infiniteeditors/macropicker/macropicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -896,7 +924,7 @@ When building a custom infinite editor view you can use the same components as a */ function memberGroupPicker(editor) { editor.view = "views/common/infiniteeditors/membergrouppicker/membergrouppicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; open(editor); } @@ -917,7 +945,7 @@ When building a custom infinite editor view you can use the same components as a */ function memberPicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; - editor.size = "small"; + if (!editor.size) editor.size = "small"; editor.section = "member"; editor.treeAlias = "member"; open(editor); @@ -1011,6 +1039,7 @@ When building a custom infinite editor view you can use the same components as a iconPicker: iconPicker, documentTypeEditor: documentTypeEditor, mediaTypeEditor: mediaTypeEditor, + memberTypeEditor: memberTypeEditor, queryBuilder: queryBuilder, treePicker: treePicker, nodePermissions: nodePermissions, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editorstate.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editorstate.service.js index 97a9ac5c4b..28daa3f245 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editorstate.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editorstate.service.js @@ -10,7 +10,7 @@ * * it is possible to modify this object, so should be used with care */ -angular.module('umbraco.services').factory("editorState", function ($rootScope) { +angular.module('umbraco.services').factory("editorState", function ($rootScope, eventsService) { var current = null; @@ -30,6 +30,7 @@ angular.module('umbraco.services').factory("editorState", function ($rootScope) */ set: function (entity) { current = entity; + eventsService.emit("editorState.changed", { entity: entity }); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/common/services/notifications.service.js b/src/Umbraco.Web.UI.Client/src/common/services/notifications.service.js index c123ac6cea..e5701b9de0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/notifications.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/notifications.service.js @@ -148,7 +148,7 @@ angular.module('umbraco.services') break; case 1: //info - this.success(args.header, args.message); + this.info(args.header, args.message); break; case 2: //error @@ -297,4 +297,4 @@ angular.module('umbraco.services') }; return service; -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index b0941bd5ad..d2b91a3707 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -15,25 +15,85 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var fallbackStyles = [{ title: "Page header", block: "h2" }, { title: "Section header", block: "h3" }, { title: "Paragraph header", block: "h4" }, { title: "Normal", block: "p" }, { title: "Quote", block: "blockquote" }, { title: "Code", block: "code" }]; // these languages are available for localization var availableLanguages = [ + 'ar', + 'ar_SA', + 'hy', + 'az', + 'eu', + 'be', + 'bn_BD', + 'bs', + 'bg_BG', + 'ca', + 'zh_CN', + 'zh_TW', + 'hr', + 'cs', 'da', - 'de', - 'en', - 'en_us', + 'dv', + 'nl', + 'en_CA', + 'en_GB', + 'et', + 'fo', 'fi', - 'fr', - 'he', + 'fr_FR', + 'gd', + 'gl', + 'ka_GE', + 'de', + 'de_AT', + 'el', + 'he_IL', + 'hi_IN', + 'hu_HU', + 'is_IS', + 'id', 'it', 'ja', - 'nl', - 'no', + 'kab', + 'kk', + 'km_KH', + 'ko_KR', + 'ku', + 'ku_IQ', + 'lv', + 'lt', + 'lb', + 'ml', + 'ml_IN', + 'mn_MN', + 'nb_NO', + 'fa', + 'fa_IR', 'pl', - 'pt', + 'pt_BR', + 'pt_PT', + 'ro', 'ru', - 'sv', - 'zh' + 'sr', + 'si_LK', + 'sk', + 'sl_SI', + 'es', + 'es_MX', + 'sv_SE', + 'tg', + 'ta', + 'ta_IN', + 'tt', + 'th_TH', + 'tr', + 'tr_TR', + 'ug', + 'uk', + 'uk_UA', + 'vi', + 'vi_VN', + 'cy' ]; //define fallback language - var defaultLanguage = 'en_us'; + var defaultLanguage = 'en_US'; /** * Returns a promise of an object containing the stylesheets and styleFormats collections @@ -109,7 +169,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //wheras tinymce is in the format of ru, de, en, en_us, etc. var localeId = $locale.id.replace('-', '_'); //try matching the language using full locale format - var languageMatch = _.find(availableLanguages, function (o) { return o === localeId; }); + var languageMatch = _.find(availableLanguages, function (o) { return o.toLowerCase() === localeId; }); //if no matches, try matching using only the language if (languageMatch === undefined) { var localeParts = localeId.split('_'); @@ -248,6 +308,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var src = imgUrl + "?width=" + newSize.width + "&height=" + newSize.height; editor.dom.setAttrib(imageDomElement, 'data-mce-src', src); } + + editor.execCommand("mceAutoResize", false, null, null); } } @@ -488,7 +550,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s * @methodOf umbraco.services.tinyMceService * * @description - * Creates the umbrco insert embedded media tinymce plugin + * Creates the umbraco insert embedded media tinymce plugin * * @param {Object} editor the TinyMCE editor instance */ @@ -575,7 +637,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s * @methodOf umbraco.services.tinyMceService * * @description - * Creates the umbrco insert media tinymce plugin + * Creates the umbraco insert media tinymce plugin * * @param {Object} editor the TinyMCE editor instance */ @@ -705,7 +767,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s * @methodOf umbraco.services.tinyMceService * * @description - * Creates the insert umbrco macro tinymce plugin + * Creates the insert umbraco macro tinymce plugin * * @param {Object} editor the TinyMCE editor instance */ diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js index e102da5d34..8fcab445b3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js @@ -134,31 +134,39 @@ var groupedTours = []; tours.forEach(function (item) { - var groupExists = false; - var newGroup = { - "group": "", - "tours": [] - }; + if (item.contentType === null || item.contentType === '') { + var groupExists = false; + var newGroup = { + "group": "", + "tours": [] + }; - groupedTours.forEach(function(group){ - // extend existing group if it is already added - if(group.group === item.group) { - if(item.groupOrder) { - group.groupOrder = item.groupOrder + groupedTours.forEach(function (group) { + // extend existing group if it is already added + if (group.group === item.group) { + if (item.groupOrder) { + group.groupOrder = item.groupOrder; + } + groupExists = true; + + if(item.hidden === false){ + group.tours.push(item); } - groupExists = true; - group.tours.push(item) - } - }); + } + }); - // push new group to array if it doesn't exist - if(!groupExists) { - newGroup.group = item.group; - if(item.groupOrder) { - newGroup.groupOrder = item.groupOrder + // push new group to array if it doesn't exist + if (!groupExists) { + newGroup.group = item.group; + if (item.groupOrder) { + newGroup.groupOrder = item.groupOrder; + } + + if(item.hidden === false){ + newGroup.tours.push(item); + groupedTours.push(newGroup); + } } - newGroup.tours.push(item); - groupedTours.push(newGroup); } }); @@ -188,6 +196,24 @@ return deferred.promise; } + /** + * @ngdoc method + * @name umbraco.services.tourService#getToursForDoctype + * @methodOf umbraco.services.tourService + * + * @description + * Returns a promise of the tours found by documenttype alias. + * @param {Object} doctypeAlias The doctype alias for which the tours which should be returned + * @returns {Array} An array of tour objects for the doctype + */ + function getToursForDoctype(doctypeAlias) { + var deferred = $q.defer(); + tourResource.getToursForDoctype(doctypeAlias).then(function (tours) { + deferred.resolve(tours); + }); + return deferred.promise; + } + /////////// /** @@ -269,7 +295,8 @@ completeTour: completeTour, getCurrentTour: getCurrentTour, getGroupedTours: getGroupedTours, - getTourByAlias: getTourByAlias + getTourByAlias: getTourByAlias, + getToursForDoctype : getToursForDoctype }; return service; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index a3017548a4..906cf58e12 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -152,8 +152,7 @@ formatUserPostData: function (displayModel) { //create the save model from the display model - var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message', 'changePassword'); - saveModel.changePassword = this.formatChangePasswordModel(saveModel.changePassword); + var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message'); //make sure the userGroups are just a string array var currGroups = saveModel.userGroups; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 7723c8f4bb..afd7b606e7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -1,5 +1,5 @@ angular.module('umbraco.services') - .factory('userService', function ($rootScope, eventsService, $q, $location, requestRetryQueue, authResource, $timeout, angularHelper) { + .factory('userService', function ($rootScope, eventsService, $q, $location, requestRetryQueue, authResource, emailMarketingResource, $timeout, angularHelper) { var currentUser = null; var lastUserId = null; @@ -262,6 +262,11 @@ angular.module('umbraco.services') /** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */ setUserTimeout: function (newTimeout) { setUserTimeoutInternal(newTimeout); + }, + + /** Calls out to a Remote Azure Function to deal with email marketing service */ + addUserToEmailMarketing: (user) => { + return emailMarketingResource.postAddUserToEmailMarketing(user); } }; diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 7d199c5c4f..d5c5166d21 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -1,6 +1,6 @@ /** Executed when the application starts, binds to events and set global state */ -app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', 'appState', 'assetsService', 'eventsService', '$cookies', 'tourService', - function ($rootScope, $route, $location, urlHelper, navigationService, appState, assetsService, eventsService, $cookies, tourService) { +app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', 'appState', 'assetsService', 'eventsService', '$cookies', 'tourService', 'localStorageService', + function ($rootScope, $route, $location, urlHelper, navigationService, appState, assetsService, eventsService, $cookies, tourService, localStorageService) { //This sets the default jquery ajax headers to include our csrf token, we // need to user the beforeSend method because our token changes per user/login so @@ -23,11 +23,35 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', appReady(data); tourService.registerAllTours().then(function () { - // Auto start intro tour + + // Start intro tour tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) { // start intro tour if it hasn't been completed or disabled if (introTour && introTour.disabled !== true && introTour.completed !== true) { tourService.startTour(introTour); + localStorageService.set("introTourShown", true); + } + else { + + const introTourShown = localStorageService.get("introTourShown"); + if(!introTourShown){ + // Go & show email marketing tour (ONLY when intro tour is completed or been dismissed) + tourService.getTourByAlias("umbEmailMarketing").then(function (emailMarketingTour) { + // Only show the email marketing tour one time - dismissing it or saying no will make sure it never appears again + // Unless invoked from tourService JS Client code explicitly. + // Accepted mails = Completed and Declicned mails = Disabled + if (emailMarketingTour && emailMarketingTour.disabled !== true && emailMarketingTour.completed !== true) { + + // Only show the email tour once per logged in session + // The localstorage key is removed on logout or user session timeout + const emailMarketingTourShown = localStorageService.get("emailMarketingTourShown"); + if(!emailMarketingTourShown){ + tourService.startTour(emailMarketingTour); + localStorageService.set("emailMarketingTourShown", true); + } + } + }); + } } }); }); diff --git a/src/Umbraco.Web.UI.Client/src/less/application/umb-outline.less b/src/Umbraco.Web.UI.Client/src/less/application/umb-outline.less index 1a04dd10c8..939366d5ac 100644 --- a/src/Umbraco.Web.UI.Client/src/less/application/umb-outline.less +++ b/src/Umbraco.Web.UI.Client/src/less/application/umb-outline.less @@ -1,3 +1,7 @@ +*:focus { + outline-color: @ui-outline; +} + .umb-outline { &:focus { outline:none; @@ -10,7 +14,28 @@ left: 0; right: 0; border-radius: 3px; - box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid; + box-shadow: 0 0 2px 0px @ui-outline, inset 0 0 2px 2px @ui-outline; } } + + &.umb-outline--surrounding { + &:focus { + .tabbing-active &::after { + top: -6px; + bottom: -6px; + left: -6px; + right: -6px; + border-radius: 9px; + } + } + } + + &.umb-outline--thin { + &:focus { + .tabbing-active &::after { + box-shadow: 0 0 2px @ui-outline, inset 0 0 2px 1px @ui-outline; + } + } + } + } diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index f6490fc79b..0921f46aac 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -132,6 +132,7 @@ @import "components/umb-content-grid.less"; @import "components/umb-contextmenu.less"; @import "components/umb-layout-selector.less"; +@import "components/umb-mini-search.less"; @import "components/tooltip/umb-tooltip.less"; @import "components/tooltip/umb-tooltip-list.less"; @import "components/overlays/umb-overlay-backdrop.less"; @@ -140,6 +141,7 @@ @import "components/umb-empty-state.less"; @import "components/umb-property-editor.less"; @import "components/umb-property-actions.less"; +@import "components/umb-code-snippet.less"; @import "components/umb-color-swatches.less"; @import "components/check-circle.less"; @import "components/umb-file-icon.less"; @@ -192,6 +194,8 @@ @import "components/contextdialogs/umb-dialog-datatype-delete.less"; +@import "components/umbemailmarketing.less"; + // Utilities @import "utilities/layout/_display.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index 85532f4231..2b50b60ae8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -23,8 +23,7 @@ border-radius: 3px; // Hover/focus state - &:hover, - &:focus { + &:hover { background: @btnBackgroundHighlight; color: @gray-4; background-position: 0 -15px; @@ -35,11 +34,6 @@ .transition(background-position .1s linear); } - // Focus state for keyboard and accessibility - &:focus { - .tab-focus(); - } - // Active state &.active, &:active { @@ -54,7 +48,7 @@ &:disabled:hover { cursor: default; border-color: @btnBorder; - .opacity(65); + .opacity(80); .box-shadow(none); } @@ -219,7 +213,7 @@ input[type="button"] { } // Made for Umbraco, 2019, used for buttons that has to stand back. .btn-white { - .buttonBackground(@btnWhiteBackground, @btnWhiteBackgroundHighlight, @btnWhiteType, @btnWhiteTypeHover); + .buttonBackground(@btnWhiteBackground, @btnWhiteBackgroundHighlight, @btnWhiteType, @btnWhiteTypeHover, @gray-10, @gray-7); } // Inverse appears as dark gray .btn-inverse { @@ -230,8 +224,7 @@ input[type="button"] { .buttonBackground(@btnNeutralBackground, @btnNeutralBackgroundHighlight); color: @gray-5; // Hover/focus state - &:hover, - &:focus { + &:hover { color: @gray-5; } @@ -261,18 +254,18 @@ input[type="button"] { .btn-outline { border: 1px solid; border-color: @gray-7; - background: @white; + background: transparent; color: @blueExtraDark; padding: 5px 13px; - transition: all .2s linear; + transition: border-color .12s linear, color .12s linear; + font-weight: 600; } -.btn-outline:hover, -.btn-outline:focus, -.btn-outline:active { +.btn-outline:hover { border-color: @ui-light-type-hover; color: @ui-light-type-hover; - background: @white; + background: transparent; + transition: border-color .12s linear, color .12s linear; } // Cross-browser Jank @@ -309,14 +302,12 @@ input[type="submit"].btn { color: @linkColor; .border-radius(0); } -.btn-link:hover, -.btn-link:focus { +.btn-link:hover { color: @linkColorHover; text-decoration: underline; background-color: transparent; } -.btn-link[disabled]:hover, -.btn-link[disabled]:focus { +.btn-link[disabled]:hover { color: @gray-4; text-decoration: none; } @@ -324,8 +315,7 @@ input[type="submit"].btn { // Make a reverse type of a button link .btn-link-reverse{ text-decoration:underline; - &:hover, - &:focus{ + &:hover { text-decoration:none; } } @@ -362,7 +352,7 @@ input[type="submit"].btn { outline: 0; -webkit-appearance: none; - &:hover, &:focus { + &:hover { color: @ui-icon-hover; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-drawer.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-drawer.less index 064ad67438..5c77a15ec7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-drawer.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-drawer.less @@ -86,6 +86,7 @@ } .umb-help-badge__title { + display: block; font-size: 15px; font-weight: bold; color: @black; @@ -160,6 +161,9 @@ border-radius: 0; border-bottom: 1px solid @gray-9; padding: 10px; + background: transparent; + width:100%; + border: 0 none; } .umb-help-list-item:last-child { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less index 7d91783e32..4e3741905f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less @@ -19,13 +19,13 @@ box-sizing: border-box; color: @ui-option-type; width: 100%; + outline-offset: -3px; } .umb-language-picker__expand { font-size: 14px; } -.umb-language-picker__toggle:focus, .umb-language-picker__toggle:hover { background: @ui-option-hover; color:@ui-option-type-hover; @@ -54,10 +54,10 @@ font-size: 14px; width: 100%; text-align: left; + outline-offset: -3px; } -.umb-language-picker__dropdown-item:hover, -.umb-language-picker__dropdown-item:focus { +.umb-language-picker__dropdown-item:hover { background: @ui-option-hover; text-decoration: none; color:@ui-option-type-hover; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less index 70e4f3d372..2f9430ef41 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less @@ -16,6 +16,10 @@ box-shadow: 0 10px 20px rgba(0,0,0,.12),0 6px 6px rgba(0,0,0,.14); } +.umb-search__label{ + margin: 0; +} + /* Search field */ @@ -107,4 +111,4 @@ .umb-search-result__description { color: @gray-5; font-size: 13px; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less index 33a723a3f7..bf2f030cea 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less @@ -112,3 +112,24 @@ .umb-tour-is-visible .umb-backdrop { z-index: @zindexTourBackdrop; } + +.umb-tour__popover .underline{ + font-size: 13px; + background: transparent; + border: none; + padding: 0; +} + +.umb-tour__popover--promotion { + width: 800px; + min-height: 400px; + padding: 40px; + border-radius: @baseBorderRadius * 2; + .umb-tour-step__close { + top: 40px; + right: 40px; + } + a { + text-decoration: underline; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less index 7fc965a8fa..4127c2201c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less @@ -8,21 +8,6 @@ position: relative; } -.umb-button__button:focus { - outline: none; - .tabbing-active &:after { - content: ''; - position: absolute; - z-index: 10000; - top: 0; - bottom: 0; - left: 0; - right: 0; - border-radius: 3px; - box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid; - } -} - .umb-button__content { opacity: 1; transition: opacity 0.25s ease; 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/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index 85fcc249f9..bc84b0d35e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -164,6 +164,7 @@ a.umb-editor-header__close-split-view:hover { /* variant switcher */ .umb-variant-switcher__toggle { + position: relative; display: flex; align-items: center; padding: 0 10px; @@ -173,6 +174,8 @@ a.umb-editor-header__close-split-view:hover { text-decoration: none !important; font-size: 13px; color: @ui-action-discreet-type; + background: transparent; + border: none; max-width: 50%; white-space: nowrap; @@ -185,7 +188,7 @@ a.umb-editor-header__close-split-view:hover { } } -a.umb-variant-switcher__toggle { +button.umb-variant-switcher__toggle { transition: color 0.2s ease-in-out; &:hover { //background-color: @gray-10; @@ -242,8 +245,7 @@ a.umb-variant-switcher__toggle { border-left: 4px solid @ui-active; } -.umb-variant-switcher__item:hover, -.umb-variant-switcher__item:focus { +.umb-variant-switcher__item:hover { outline: none; } @@ -267,7 +269,7 @@ a.umb-variant-switcher__toggle { align-items: center; justify-content: center; margin-left: 5px; - top: -6px; + top: -3px; width: 14px; height: 14px; border-radius: 7px; @@ -285,8 +287,10 @@ a.umb-variant-switcher__toggle { flex: 1; cursor: pointer; padding-top: 6px !important; - padding-bottom: 6px !important; - border-left: 2px solid transparent; + padding-bottom: 6px !important; + background-color: transparent; + border: none; + border-left: 2px solid transparent; } .umb-variant-switcher__name { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 1217441f4e..4ebfa94b6f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -24,8 +24,9 @@ .umb-editor-sub-header.--state-selection { padding-left: 10px; padding-right: 10px; - background-color: @pinkLight; - border-color: @pinkLight; + background-color: @ui-selected-border; + border-color: @ui-selected-border; + color: @white; border-radius: 3px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less index df01477880..4a483ce3f0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less @@ -4,16 +4,16 @@ width: auto; margin-top:1px; - .umb-tree-item__label { - user-select: none; - } - &:hover .umb-tree-item__arrow { visibility: visible; cursor: pointer } } +.umb-tree-item__label { + user-select: none; +} + .umb-tree-item__arrow { position: relative; margin-left: -16px; @@ -92,18 +92,6 @@ color: @blue; } - .umb-options { - - &:hover i { - opacity: .7; - } - - i { - background: @ui-active-type; - transition: opacity 120ms ease; - } - } - a, .umb-tree-icon, .umb-tree-item__arrow { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less index 0a0fb29eed..839e61c5f9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less @@ -99,6 +99,7 @@ body.touch .umb-tree { .umb-tree-item__inner { border: 2px solid transparent; + overflow: visible; } .umb-tree-header { @@ -176,9 +177,25 @@ body.touch .umb-tree { cursor: pointer; border-radius: @baseBorderRadius; - &:hover { - background: @btnBackgroundHighlight; + i { + height: 5px !important; + width: 5px !important; + border-radius: 20px; + display: inline-block; + margin: 0 2px 0 0; + background: @ui-active-type; + + &:last-child { + margin: 0; + } } + &:hover { + background: rgba(255, 255, 255, .5); + i { + background: @ui-active-type-hover; + } + } + // NOTE - We're having to repeat ourselves here due to an .sr-only class appearing in umbraco/lib/font-awesome/css/font-awesome.min.css &.sr-only--hoverable:hover, &.sr-only--focusable:focus { @@ -193,19 +210,6 @@ body.touch .umb-tree { border-radius: 3px; } - i { - height: 5px !important; - width: 5px !important; - border-radius: 20px; - background: @black; - display: inline-block; - margin: 0 2px 0 0; - - &:last-child { - margin: 0; - } - } - .hide-options & { display: none !important; } @@ -252,7 +256,7 @@ body.touch .umb-tree { } &.current > .umb-tree-item__inner > .umb-tree-item__annotation { - background-color: @pinkLight; + background-color: @ui-active; } } @@ -289,9 +293,8 @@ body.touch .umb-tree { } .no-access { - .umb-tree-icon, - .root-link, - .umb-tree-item__label { + > .umb-tree-item__inner .umb-tree-icon, + > .umb-tree-item__inner .umb-tree-item__label { color: @gray-7; cursor: not-allowed; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less index 0afcfdd1f9..de678f9798 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less @@ -4,6 +4,7 @@ margin-left: 0; display: flex; flex-wrap: wrap; + user-select: none; } .umb-breadcrumbs__ancestor { @@ -12,10 +13,23 @@ } .umb-breadcrumbs__action { + position: relative; background: transparent; border: 0 none; - padding: 0; - margin-top: -4px; + border-radius: 3px; + padding: 0 4px; + color: @ui-option-type; + + &.--current { + font-weight: bold; + pointer-events: none; + } + + &:hover { + color: @ui-option-type-hover; + background-color: @white; + } + } .umb-breadcrumbs__ancestor-link, @@ -26,6 +40,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + padding: 0 4px; } .umb-breadcrumbs__ancestor-link { @@ -39,13 +54,13 @@ .umb-breadcrumbs__separator { position: relative; top: 1px; - margin-left: 5px; - margin-right: 5px; + margin: 0 1px; + margin-top: -3px; color: @gray-7; } input.umb-breadcrumbs__add-ancestor { - height: 25px; - margin: 0 0 0 3px; + height: 24px; + margin: -2px 0 -2px 3px; width: 100px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less index 021fc8cc9b..f82e47bf88 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less @@ -2,21 +2,30 @@ border: 2px solid @white; width: 25px; height: 25px; - border: 1px solid @gray-7; + border: 1px solid @ui-action-discreet-border; border-radius: 3px; box-sizing: border-box; display: flex; justify-content: center; align-items: center; - color: @gray-7; + color: @ui-selected-type; cursor: pointer; font-size: 15px; + &:hover { + border-color:@ui-action-discreet-border-hover; + color: @ui-selected-type-hover; + } } .umb-checkmark--checked { - background: @ui-active; - border-color: @ui-active; + background: @ui-selected-border; + border-color: @ui-selected-border; color: @white; + &:hover { + background: @ui-selected-border-hover; + border-color: @ui-selected-border-hover; + color: @white; + } } .umb-checkmark--xs { @@ -45,4 +54,4 @@ width: 50px; height: 50px; font-size: 20px; -} \ No newline at end of file +} 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 b6cdc0e8d9..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,20 +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; + margin-left: 30px; + + .umb-child-selector__child.ui-sortable-handle { + cursor: move; + } } .umb-child-selector__child-description { @@ -62,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-code-snippet.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-code-snippet.less new file mode 100644 index 0000000000..b372841910 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-code-snippet.less @@ -0,0 +1,43 @@ +.umb-code-snippet { + + .umb-code-snippet__header { + box-sizing: content-box; + background-color: @gray-10; + display: flex; + flex-direction: row; + font-size: .8rem; + border: 1px solid @gray-8; + border-radius: 3px 3px 0 0; + border-bottom: 0; + margin-top: 16px; + min-height: 30px; + + .language { + display: flex; + align-items: center; + justify-content: flex-start; + flex-grow: 1; + padding: 2px 10px; + } + + button { + background-color: transparent; + border: none; + border-left: 1px solid @gray-8; + border-radius: 0; + color: #000; + + &:hover { + background-color: @grayLighter; + } + } + } + + .umb-code-snippet__content { + pre { + border-radius: 0 0 3px 3px; + overflow: auto; + white-space: nowrap; + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index f27e1e4ec8..622dcb8b0a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -29,15 +29,15 @@ border-radius: 5px; box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20); pointer-events: none; + transition: opacity 100ms; } - } } .umb-content-grid__item:hover { &::before { - opacity: .33; + opacity: .2; } } .umb-content-grid__item.-selected:hover { @@ -46,6 +46,7 @@ } } + .umb-content-grid__icon-container { height: 75px; display: flex; @@ -66,8 +67,10 @@ } .umb-content-grid__item-name { + position: relative; + padding: 5px; + margin: -5px -5px 15px -5px; font-weight: bold; - margin-bottom: 15px; line-height: 1.4em; display: inline-flex; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less index c26c89a478..2cca776614 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less @@ -4,6 +4,7 @@ &__action, > a { + position: relative; background: transparent; text-align: center; cursor: pointer; @@ -18,26 +19,20 @@ align-items: center; justify-content: center; height: calc(~'@{editorHeaderHeight}'- ~'1px'); // need to offset the 1px border-bottom on .umb-editor-header - avoids overflowing top of the container - position: relative; color: @ui-active-type; - &:focus, &:hover { color: @ui-active-type-hover !important; text-decoration: none; } - &:focus { - outline: none; - } - - &::after { + &::before { content: ""; + position: absolute; height: 0px; left: 8px; right: 8px; background-color: @ui-light-active-border; - position: absolute; bottom: 0; border-radius: 3px 3px 0 0; opacity: 0; @@ -47,14 +42,13 @@ &.is-active { color: @ui-light-active-type; - &::after { + &::before { opacity: 1; height: 4px; } } } - &__action:focus, &__action:active, & > a:active { .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); @@ -111,7 +105,6 @@ &__anchor_dropdown { // inherits from .dropdown-menu margin: 0; - overflow: hidden; // center align horizontal left: 50%; @@ -122,7 +115,7 @@ li { &.is-active a { - border-left-color: @ui-selected-border; + border-left-color: @ui-active-border; } a { @@ -192,4 +185,4 @@ &::after { background-color: @red; } -} \ No newline at end of file +} 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 76a4df0056..a52f81b92a 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 @@ -1,17 +1,26 @@ -@checkboxWidth: 16px; -@checkboxHeight: 16px; +@checkboxWidth: 18px; +@checkboxHeight: 18px; +label.umb-form-check--checkbox{ + margin:3px 0; +} .umb-form-check { display: flex; - flex-wrap: wrap; - align-items: center; position: relative; - padding: 0 0 0 26px !important; + padding-left: 0px; margin: 0; min-height: 22px; - line-height: 22px; cursor: pointer !important; + .umb-form-check__symbol { + margin-top: 4px; + margin-right: 10px; + } + .umb-form-check__info { + margin-left:20px; + } + + &.-small-text{ font-size: 13px; } @@ -22,7 +31,6 @@ &__text { position: relative; - top: 1px; user-select: none; } @@ -90,10 +98,10 @@ &__state { display: flex; height: 18px; - margin-top: 2px; position: absolute; + margin-top: 2px; top: 0; - left: 0; + left: -1px; } &__check { @@ -101,6 +109,7 @@ position: relative; background: @white; border: 1px solid @inputBorder; + border-radius: @baseBorderRadius; width: @checkboxWidth; height: @checkboxHeight; @@ -160,5 +169,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-grid-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid-selector.less index e25349f555..1ae476d584 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid-selector.less @@ -41,12 +41,17 @@ margin-top: 10px; } +button.umb-grid-selector__item { + width: 169px; + height: 194px; +} + .umb-grid-selector__item-icon { - font-size: 50px; - color: @gray-8; - display: block; - line-height: 50px; - margin-bottom: 15px; + font-size: 50px; + color: @gray-8; + display: block; + line-height: 50px; + margin-bottom: 15px; } .umb-grid-selector__item-label { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 479074fee9..26d61412ae 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -162,6 +162,8 @@ } .umb-grid .umb-row .umb-cell-placeholder { + display: block; + width: 100%; min-height: 88px; border-width: 1px; border-style: dashed; @@ -226,6 +228,7 @@ .umb-grid .cell-tools-add.-bar { display: block; + width: calc(100% - 20px); text-align: center; padding: 5px; border: 1px dashed @ui-action-discreet-border; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less index e8a62f739d..98b2b1d72d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less @@ -15,7 +15,9 @@ overflow: hidden; } -.umb-iconpicker-item a { +.umb-iconpicker-item button { + background: transparent; + border: 0 none; display: flex; justify-content: center; align-items: center; @@ -26,8 +28,8 @@ border-radius: 3px; } -.umb-iconpicker-item a:hover, -.umb-iconpicker-item a:focus { +.umb-iconpicker-item button:hover, +.umb-iconpicker-item button:focus { background: @gray-10; outline: none; } @@ -39,7 +41,7 @@ box-sizing: border-box; } -.umb-iconpicker-item a:active { +.umb-iconpicker-item button:active { background: @gray-10; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less index 9ebd6d6e5d..c0ac89622d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less @@ -6,7 +6,8 @@ .umb-layout-selector__active-layout { background: transparent; box-sizing: border-box; - border: 1px solid @inputBorder; + border: 1px solid @ui-action-discreet-border; + color: @ui-action-discreet-type; cursor: pointer; height: 30px; width: 30px; @@ -17,7 +18,8 @@ } .umb-layout-selector__active-layout:hover { - border-color: @inputBorderFocus; + border-color: @ui-action-discreet-border-hover; + color: @ui-action-discreet-type-hover; } .umb-layout-selector__dropdown { @@ -31,6 +33,7 @@ flex-direction: column; transform: translate(-50%,0); left: 50%; + border-radius: 3px; } .umb-layout-selector__dropdown-item { @@ -46,11 +49,11 @@ } .umb-layout-selector__dropdown-item:hover { - border: 1px solid @gray-8; + border: 1px solid @ui-action-discreet-border; } .umb-layout-selector__dropdown-item.-active { - border: 1px solid @blue; + border: 1px solid @ui-action-discreet-border-hover; } .umb-layout-selector__dropdown-item-icon, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 4feadc272c..5d6b7ad962 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -40,25 +40,41 @@ } } -.umb-media-grid__item.-selectable { +.umb-media-grid__item.-selectable, +.umb-media-grid__item.-folder {// If folders isnt selectable, they opens if clicked, therefor... cursor: pointer; - - .tabbing-active &:focus { - outline: 2px solid @inputBorderTabFocus; - } } .umb-media-grid__item.-file { background-color: @white; } +.umb-media-grid__item.-folder { + + &.-selectable { + .media-grid-item-edit:hover .umb-media-grid__item-name, + .media-grid-item-edit:focus .umb-media-grid__item-name { + text-decoration: underline; + } + } + + &.-unselectable { + &:hover, &:focus { + .umb-media-grid__item-name { + text-decoration: underline; + } + } + } +} + + .umb-media-grid__item.-selected { color:@ui-selected-type; .umb-media-grid__item-overlay { color: @ui-selected-type; } } -.umb-media-grid__item.-selected, +.umb-media-grid__item.-selected, .umb-media-grid__item.-selectable:hover { &::before { content: ""; @@ -139,10 +155,10 @@ background: fade(@white, 92%); transition: opacity 150ms; - &:hover { + &.-can-open:hover { text-decoration: underline; } - + .tabbing-active &:focus { opacity: 1; } @@ -190,7 +206,7 @@ align-items: center; color: @black; transition: opacity 150ms; - + &:hover { color: @ui-action-discreet-type-hover; } 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 new file mode 100644 index 0000000000..ec598c17eb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less @@ -0,0 +1,51 @@ +.umb-mini-search { + position: relative; + display: block; + + .icon { + position: absolute; + width: 30px; + height: 30px; + display: flex; + justify-content: center; + align-items: center; + margin: 1px; + padding: 0; + pointer-events: none; + color: @ui-action-discreet-type; + transition: color .1s linear; + } + + input { + width: 0px; + padding-left: 24px; + margin-bottom: 0px; + 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 { + .icon { + color: @ui-action-discreet-type-hover; + } + input { + color: @ui-action-discreet-border-hover; + border-color: @ui-action-discreet-border-hover; + } + } + + input:focus, &:focus-within input { + 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 { + width: 190px; + padding-left:30px; + } + +} 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 699496f5d3..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 @@ -41,11 +41,8 @@ } .umb-nested-content__item.ui-sortable-placeholder { - background: @gray-10; - border: 1px solid @gray-9; + margin-top: 1px; visibility: visible !important; - height: 55px; - margin-top: -1px; } .umb-nested-content__item--single > .umb-nested-content__content { @@ -138,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-progress-circle.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-progress-circle.less index 03816637a7..d8fb3b4d8f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-progress-circle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-progress-circle.less @@ -5,6 +5,7 @@ .umb-progress-circle__view-box { position: absolute; transform: rotate(-90deg); + right: 0; } // circle highlight on progressbar 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/components/umb-sub-views.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-sub-views.less index d3ce368356..cc6be8fa37 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-sub-views.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-sub-views.less @@ -8,7 +8,6 @@ padding-left: 0; padding-right: 0; &:focus { - outline: none; text-decoration: none; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 94c0318fca..202c488bb4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -34,8 +34,12 @@ text-decoration: none; padding: 0; margin-left: 1px; + body:not(.tabbing-active) & { + outline: 0; + } } + input.umb-table__input { margin: 0 auto; } @@ -47,6 +51,8 @@ input.umb-table__input { .umb-table-head { font-size: 14px; font-weight: bold; + + color: @ui-disabled-type; } .umb-table-head__link { @@ -68,10 +74,12 @@ input.umb-table__input { .umb-table-head__link.sortable { cursor: pointer; + color: @ui-action-discreet-type; &:hover { - text-decoration: none; - color: @black; + color: @ui-action-discreet-type-hover; } + + outline-offset: 1px; } .umb-table-thead__icon { @@ -129,6 +137,9 @@ input.umb-table__input { &::before { opacity:.66; } + .umb-table-body__checkicon { + color: @ui-selected-border; + } } } @@ -141,21 +152,19 @@ input.umb-table__input { } .umb-table-body__link { + position: relative; color: @ui-option-type; font-size: 14px; font-weight: bold; text-decoration: none; - &:hover, &:focus { + &:hover { color: @ui-option-type-hover; text-decoration: underline; - outline: none; } } -.umb-table-body__icon, -.umb-table-body__icon[class^="icon-"], -.umb-table-body__icon[class*=" icon-"] { +.umb-table-body__icon { margin: 0 auto; font-size: 20px; line-height: 20px; @@ -164,13 +173,11 @@ input.umb-table__input { text-decoration: none; } -.umb-table-body__checkicon, -.umb-table-body__checkicon[class^="icon-"], -.umb-table-body__checkicon[class*=" icon-"] { +.umb-table-body__checkicon { display: none; font-size: 18px; line-height: 20px; - color: @green; + color: @ui-selected-border; } .umb-table-body .umb-table__name { @@ -179,7 +186,8 @@ input.umb-table__input { font-weight: bold; a { color: @ui-option-type; - &:hover, &:focus { + outline-offset: 1px; + &:hover { color: @ui-option-type-hover; text-decoration: underline; } @@ -249,8 +257,8 @@ input.umb-table__input { flex-flow: row nowrap; flex: 1 1 5%; position: relative; - margin: auto 14px; - padding: 6px 2px; + margin: auto 0; + padding: 6px 16px; text-align: left; overflow:hidden; } @@ -268,8 +276,8 @@ input.umb-table__input { .umb-table-cell:first-of-type:not(.not-fixed) { flex: 0 0 25px; - margin: 0 0 0 15px; - padding: 15px 0; + margin: 0; + padding: 15px 0 15px 15px; } .umb-table-cell--auto-width { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umbemailmarketing.less b/src/Umbraco.Web.UI.Client/src/less/components/umbemailmarketing.less new file mode 100644 index 0000000000..f4b3183045 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umbemailmarketing.less @@ -0,0 +1,44 @@ +.umb-email-marketing { + + h2 { + font-weight: 800; + max-width: 26ex; + margin-top: 20px; + } + + .layout { + display: flex; + align-items: center; + align-content: stretch; + + .primary { + flex-basis: 50%; + padding-right: 40px; + padding-top: 20px; + padding-bottom: 20px; + .notice { + color: @gray-5; + font-style: italic; + a { + color: @gray-5; + &:hover { + color: @ui-action-type-hover; + } + } + } + } + + .secondary { + flex-basis: 50%; + svg { + height: 200px; + width: 100%; + margin-top: -60px; + } + } + } + + .cta { + text-align: right; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-group-picker-list.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-group-picker-list.less index dff78ce627..0a06120b11 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-group-picker-list.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-group-picker-list.less @@ -7,11 +7,17 @@ display: flex; margin-bottom: 5px; padding: 10px; + position: relative; } -.umb-user-group-picker-list-item:active, -.umb-user-group-picker-list-item:focus { - text-decoration: none; +.umb-user-group-picker__action{ + background: transparent; + border: 0 none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } .umb-user-group-picker-list-item:hover { @@ -35,4 +41,4 @@ .umb-user-group-picker-list-item__permission { font-size: 13px; color: @gray-4; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 72abb3ba00..ffc0626981 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -252,7 +252,7 @@ input[type="color"], outline: 0; .tabbing-active & { - outline: 2px solid @inputBorderTabFocus; + outline: 2px solid @ui-outline; } } } @@ -297,11 +297,10 @@ select[size] { } // Focus for select, file, radio, and checkbox -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - .tab-focus(); +input[type="file"], +input[type="radio"], +input[type="checkbox"] { + .umb-outline(); } diff --git a/src/Umbraco.Web.UI.Client/src/less/gridview.less b/src/Umbraco.Web.UI.Client/src/less/gridview.less index 238feead90..11ba7b2795 100644 --- a/src/Umbraco.Web.UI.Client/src/less/gridview.less +++ b/src/Umbraco.Web.UI.Client/src/less/gridview.less @@ -101,6 +101,9 @@ position:relative; } + .usky-grid .grid-layout { + max-width: 600px; +} // ROW // ------------------------- @@ -517,6 +520,25 @@ position:relative; } + .usky-grid .uSky-templates .layout { + margin-top: 5px; + margin-bottom: 20px; + float: left; +} + + +.usky-grid .uSky-templates .columns { + margin-top: 5px; + margin-bottom: 25px; + float: left; +} + + +.usky-grid .uSky-templates .columns .preview-cell p { + font-size: 6px; + line-height: 8px; + text-align: center; +} /**************************************************************************************************/ diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index 3ead4d6905..8d3117febe 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -185,40 +185,38 @@ iframe, .content-column-body { // Inline code // 1: Revert border radius to match look and feel of 7.4+ -code{ - .border-radius(@baseBorderRadius); // 1 +code { + .border-radius(@baseBorderRadius); // 1 } // Blocks of code // 1: Wrapping code is unreadable on small devices. pre { - display: block; - padding: (@baseLineHeight - 1) / 2; - margin: 0 0 @baseLineHeight / 2; - font-family: @sansFontFamily; - //font-size: @baseFontSize - 1; // 14px to 13px - color: @gray-2; - line-height: @baseLineHeight; - white-space: pre-wrap; // 1 - overflow-x: auto; // 1 - background-color: @gray-10; - border: 1px solid @gray-8; - .border-radius(@baseBorderRadius); + display: block; + padding: (@baseLineHeight - 1) / 2; + margin: 0 0 @baseLineHeight / 2; + font-family: @sansFontFamily; + color: @gray-2; + line-height: @baseLineHeight; + white-space: pre-wrap; // 1 + overflow-x: auto; // 1 + background-color: @brownGrayLight; + border: 1px solid @gray-8; + .border-radius(@baseBorderRadius); + // Make prettyprint styles more spaced out for readability + &.prettyprint { + margin-bottom: @baseLineHeight; + } - // Make prettyprint styles more spaced out for readability - &.prettyprint { - margin-bottom: @baseLineHeight; - } - - // Account for some code outputs that place code tags in pre tags - code { - padding: 0; - white-space: pre; // 1 - word-wrap: normal; // 1 - background-color: transparent; - border: 0; - } + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + white-space: pre; // 1 + word-wrap: normal; // 1 + background-color: transparent; + border: 0; + } } /* Styling for content/media sort order dialog */ diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less index e964ed3c6f..4e24161e59 100644 --- a/src/Umbraco.Web.UI.Client/src/less/installer.less +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -3,6 +3,7 @@ @import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "colors.less"; @import "mixins.less"; +@import "application/umb-outline.less"; @import "buttons.less"; @import "forms.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/listview.less b/src/Umbraco.Web.UI.Client/src/less/listview.less index 975dbdbd4a..fe8af6dbc4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/listview.less +++ b/src/Umbraco.Web.UI.Client/src/less/listview.less @@ -1,6 +1,10 @@ // Listview // ------------------------- +.umb-listview { + min-height: 100px; +} + .umb-listview table { border: 1px solid @gray-8; } @@ -43,6 +47,15 @@ /* add padding */ .left-addon input[type="text"] { padding-left: 30px !important; padding-right: 6px; } .right-addon input[type="text"] { padding-right: 30px; padding-left: 6px !important; } + + &__label-icon{ + width: 30px; + height: 30px; + position: absolute; + top: -1px; + left:0; + margin:0 + } } .umb-listview table form { @@ -136,7 +149,36 @@ /* TEMP */ .umb-minilistview { - .umb-table-row.not-allowed { opacity: 0.6; cursor: not-allowed; } + .umb-table-row.not-allowed { + opacity: 0.6; + cursor: not-allowed; + } + + div.umb-mini-list-view__breadcrumb { + margin-bottom: 10px; + } + + div.no-display { + display: none + } + + div.umb-table-cell-padding { + padding-top: 8px; + padding-bottom: 8px; + } + + div.umb-table-cell .form-search { + width: 100%; + margin-right: 0; + + input { + width: 100%; + } + + .icon-search { + font-size: 14px; + } + } } .umb-listview .table-striped tbody td { diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 0d646d11c6..b34f313435 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -117,6 +117,12 @@ h5.-black { } .umb-control-group { position: relative; + + &.umb-control-group__listview { + // position: relative messes up the listview status messages (e.g. "no search results") + position: unset; + } + &::after { content: ''; display:block; diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index 21b9c5c550..f5c499cbfc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -30,7 +30,6 @@ outline: thin dotted @gray-3; // Webkit outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; } // Center-align a block level element @@ -419,7 +418,7 @@ // -------------------------------------------------- // Limit width of specific property editors .umb-property-editor--limit-width { - max-width: 800px; + max-width: @propertyEditorLimitedWidth; } // Horizontal dividers @@ -435,7 +434,7 @@ // Button backgrounds // ------------------ -.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor) { +.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor, @disabledColor: @sand-1, @disabledTextColor: @white) { color: @textColor; border-color: @startColor @startColor darken(@startColor, 15%); @@ -449,14 +448,14 @@ } // in these cases the gradient won't cover the background, so we override - &:hover, &:focus, &:active, &.active { + &:hover { color: @textColorHover; background-color: @hoverColor; } &.disabled, &[disabled] { - color: @white; - background-color: @sand-1; + background-color: @disabledColor; + color: @disabledTextColor; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index 925f845c4c..4ce907d06f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -88,9 +88,13 @@ background: @white; } -.umb-dialog .umb-btn-toolbar .umb-control-group{ - border: none; - padding: none; +.umb-dialog .abstract{ + margin-bottom:20px; +} + +.umb-dialog .umb-btn-toolbar .umb-control-group { + border: none; + padding: none; } .umb-dialog-body{ diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index 5b97464e31..c347404619 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -233,11 +233,14 @@ } .dropdown-menu > li > a { + position: relative; padding: 8px 20px; color: @ui-option-type; + text-decoration: none; } .dropdown-menu > li > button { + position: relative; background: transparent; border: 0; padding: 8px 20px; @@ -253,11 +256,9 @@ } .dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, .dropdown-menu > li > button:hover, -.dropdown-menu > li > button:focus, .dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { +.dropdown-submenu:hover > button { color: @ui-option-type-hover; background: @ui-option-hover; } @@ -300,8 +301,7 @@ // Active:hover/:focus dropdown links // ------------------------- -.nav > .dropdown.active > a:hover, -.nav > .dropdown.active > a:focus { +.nav > .dropdown.active > a:hover { cursor: pointer; } @@ -309,24 +309,21 @@ // ------------------------- .nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover, -.nav > li.dropdown.open.active > a:focus { +.nav > li.dropdown.open.active > a:hover { /*color: @white;*/ background-color: @gray-8; border-color: @gray-8; } .nav li.dropdown.open .caret, .nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret, -.nav li.dropdown.open a:focus .caret { +.nav li.dropdown.open a:hover .caret { border-top-color: @white; border-bottom-color: @white; .opacity(100); } // Dropdowns in stacked tabs -.tabs-stacked .open > a:hover, -.tabs-stacked .open > a:focus { +.tabs-stacked .open > a:hover { border-color: @gray-8; } @@ -377,15 +374,13 @@ } .tabs-below > .nav-tabs > li > a { .border-radius(0 0 4px 4px); - &:hover, - &:focus { + &:hover { border-bottom-color: transparent; border-top-color: @gray-8; } } .tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover, -.tabs-below > .nav-tabs > .active > a:focus { +.tabs-below > .nav-tabs > .active > a:hover { border-color: transparent @gray-8 @gray-8 @gray-8; } @@ -414,13 +409,11 @@ margin-right: -1px; .border-radius(4px 0 0 4px); } -.tabs-left > .nav-tabs > li > a:hover, -.tabs-left > .nav-tabs > li > a:focus { +.tabs-left > .nav-tabs > li > a:hover { border-color: @gray-10 @gray-8 @gray-10 @gray-10; } .tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover, -.tabs-left > .nav-tabs .active > a:focus { +.tabs-left > .nav-tabs .active > a:hover { border-color: @gray-8 transparent @gray-8 @gray-8; *border-right-color: @white; } @@ -435,13 +428,11 @@ margin-left: -1px; .border-radius(0 4px 4px 0); } -.tabs-right > .nav-tabs > li > a:hover, -.tabs-right > .nav-tabs > li > a:focus { +.tabs-right > .nav-tabs > li > a:hover { border-color: @gray-10 @gray-10 @gray-10 @gray-8; } .tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover, -.tabs-right > .nav-tabs .active > a:focus { +.tabs-right > .nav-tabs .active > a:hover { border-color: @gray-8 @gray-8 @gray-8 transparent; *border-left-color: @white; } @@ -456,8 +447,7 @@ color: @gray-8; } // Nuke hover/focus effects -.nav > .disabled > a:hover, -.nav > .disabled > a:focus { +.nav > .disabled > a:hover { text-decoration: none; background-color: transparent; cursor: default; diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index bad0ab9715..40c70f5331 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -341,6 +341,7 @@ .umb-panel-header-icon { cursor: pointer; margin-right: 5px; + margin-top: -6px; height: 50px; display: flex; justify-content: center; 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 5a71635c4d..764b73c593 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -249,26 +249,11 @@ transition: all 150ms ease-in-out; - &:focus, + &:active, &:hover { color: @ui-action-discreet-type-hover; border-color: @ui-action-discreet-type-hover; } - - &:focus { - outline: none; - .tabbing-active &:after { - content: ''; - position: absolute; - z-index: 10000; - top: -6px; - bottom: -6px; - left: -6px; - right: -6px; - border-radius: 3px; - box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid; - } - } } .umb-mediapicker .label { @@ -687,7 +672,8 @@ } .imagecropper .umb-sortable-thumbnails li .crop-name, - .imagecropper .umb-sortable-thumbnails li .crop-size { + .imagecropper .umb-sortable-thumbnails li .crop-size, + .imagecropper .umb-sortable-thumbnails li .crop-annotation { display: block; text-align: left; font-size: 13px; @@ -699,17 +685,28 @@ margin: 10px 0 5px; } - .imagecropper .umb-sortable-thumbnails li .crop-size { + .imagecropper .umb-sortable-thumbnails li .crop-size, + .imagecropper .umb-sortable-thumbnails li .crop-annotation { font-size: 10px; font-style: italic; margin: 0 0 5px; } + .imagecropper .umb-sortable-thumbnails li .crop-annotation { + color: @gray-6; + } + .btn-crop-delete { display: block; text-align: left; } + .imagecropper .cropList-container { + h5 { + margin-left: 10px; + margin-top: 0; + } + } // // folder-browser // -------------------------------------------------- @@ -725,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 40921c5b76..d4b5cfc998 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -31,7 +31,7 @@ ul.sections { height: 4px; bottom: 0; transform: translateY(4px); - background-color: @ui-active; + background-color: @pinkLight; position: absolute; border-radius: 3px 3px 0 0; opacity: 0; @@ -41,13 +41,13 @@ ul.sections { &:focus .section__name { .tabbing-active & { - border: 1px solid; - border-color: @gray-9; + border: 1px solid @gray-9; } } } .section__name { + border: 1px solid transparent; border-radius: 3px; margin-top: 1px; padding: 3px 10px 4px 10px; @@ -55,8 +55,8 @@ ul.sections { transition: opacity .1s linear, box-shadow .1s; } - &.current a { - color: @ui-active; + &.current > a { + color: @pinkLight; &::after { opacity: 1; @@ -75,9 +75,16 @@ ul.sections { opacity: 0.6; transition: opacity .1s linear; } - + + &.current { + i { + opacity: 1; + background: @pinkLight; + } + } + &:hover i { - opacity:1; + opacity: 1; } } @@ -104,7 +111,7 @@ ul.sections-tray { li { &.current a { - color: @ui-active; + color: @ui-active-border; opacity: 1; &::after { @@ -124,7 +131,7 @@ ul.sections-tray { content: ""; width: 4px; height: 100%; - background-color: @ui-active; + background-color: @ui-active-border; position: absolute; border-radius: 0 3px 3px 0; opacity: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 6071c4a5ef..accd09fdd9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -75,6 +75,8 @@ @gray-9: #E9E9EB; @gray-10: #F3F3F5; @gray-11: #F6F6F7; +@gray-12: #F9F9FA; +@gray-13: #FBFBFD; @sand-1: #DED4CF;// added 2019 @sand-2: #EBDED6;// added 2019 @@ -108,6 +110,7 @@ //@blueLight: #4f89de; @blue: #2E8AEA; @blueMid: #2152A3;// updated 2019 +@blueMidLight: #6ab4f0; @blueDark: #3544b1;// updated 2019 @blueExtraDark: #1b264f;// added 2019 @blueLight: #ADD8E6; @@ -115,6 +118,7 @@ //@orange: #f79c37;// updated 2019 @pink: #D93F4C;// #C3325F;// update 2019 @pinkLight: #f5c1bc;// added 2019 +@pinkExtraLight: #fee4e1;// added 2020 @pinkRedLight: #ff8a89;// added 2019 @brown: #9d8057;// added 2019 @brownLight: #e4e0dd;// added 2019 @@ -133,14 +137,18 @@ @ui-option-type: @blueExtraDark; @ui-option-type-hover: @blueMid; @ui-option: @white; -@ui-option-hover: @sand-7; +@ui-option-hover: @gray-12; @ui-option-disabled-type: @gray-6; @ui-option-disabled-type-hover: @gray-5; -@ui-option-disabled-hover: @sand-7; +@ui-option-disabled-hover: @gray-12; + +@ui-disabled-type: @gray-6; +@ui-disabled-border: @gray-6; //@ui-active: #346ab3; -@ui-active: @pinkLight; +@ui-active: @pinkExtraLight; +@ui-active-border: @pinkLight; @ui-active-blur: @brownLight; @ui-active-type: @blueExtraDark; @ui-active-type-hover: @blueMid; @@ -149,32 +157,34 @@ @ui-selected-hover: ligthen(@sand-5, 10); @ui-selected-type: @blueExtraDark; @ui-selected-type-hover: @blueMid; -@ui-selected-border: @pinkLight; -@ui-selected-border-hover: darken(@pinkLight, 10); +@ui-selected-border: @blueDark; +@ui-selected-border-hover: darken(@blueDark, 10); @ui-light-border: @pinkLight; @ui-light-type: @gray-4; @ui-light-type-hover: @blueMid; @ui-light-active-border: @pinkLight; -@ui-light-active-type: @blueExtraDark; -@ui-light-active-type-hover: @blueMid; +@ui-light-active-type: @blueMid; +@ui-light-active-type-hover: @blueMidLight; @ui-action: @white; -@ui-action-hover: @sand-7; +@ui-action-hover: @gray-12; @ui-action-type: @blueExtraDark; @ui-action-type-hover: @blueMid; @ui-action-border: @blueExtraDark; @ui-action-border-hover: @blueMid; @ui-action-discreet: @white; -@ui-action-discreet-hover: @sand-7; +@ui-action-discreet-hover: @gray-12; @ui-action-discreet-type: @blueExtraDark; @ui-action-discreet-type-hover: @blueMid; @ui-action-discreet-border: @gray-7; @ui-action-discreet-border-hover: @blueMid; +@ui-outline: @blueMidLight; + @type-white: @white; @type-black: @blueNight; @@ -238,6 +248,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; @@ -255,7 +266,7 @@ // Buttons // ------------------------- @btnBackground: @gray-9; -@btnBackgroundHighlight: @gray-9; +@btnBackgroundHighlight: @gray-10; @btnBorder: @gray-9; @btnPrimaryBackground: @ui-btn-positive; @@ -293,7 +304,7 @@ @inputBackground: @white; @inputBorder: @gray-8; @inputBorderFocus: @gray-7; -@inputBorderTabFocus: @blueExtraDark; +@inputBorderTabFocus: @ui-outline; @inputBorderRadius: 0; @inputDisabledBackground: @gray-10; @formActionsBackground: @gray-9; @@ -448,7 +459,7 @@ @successBorder: transparent; @infoText: @white; -@infoBackground: @turquoise-d1; +@infoBackground: @blueDark; @infoBorder: transparent; @alertBorderRadius: 0; diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index 93870f8a56..883907d1dc 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -67,13 +67,18 @@ function MainController($scope, $location, appState, treeService, notificationsS }; var evts = []; - + //when a user logs out or timesout evts.push(eventsService.on("app.notAuthenticated", function (evt, data) { $scope.authenticated = null; $scope.user = null; const isTimedOut = data && data.isTimedOut ? true : false; $scope.showLoginScreen(isTimedOut); + + // Remove the localstorage items for tours shown + // Means that when next logged in they can be re-shown if not already dismissed etc + localStorageService.remove("emailMarketingTourShown"); + localStorageService.remove("introTourShown"); })); evts.push(eventsService.on("app.userRefresh", function(evt) { 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/drawers/help/help.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.controller.js index 3323f2bfb3..268bfb3a8c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function HelpDrawerController($scope, $routeParams, $timeout, dashboardResource, localizationService, userService, eventsService, helpService, appState, tourService, $filter) { + function HelpDrawerController($scope, $routeParams, $timeout, dashboardResource, localizationService, userService, eventsService, helpService, appState, tourService, $filter, editorState) { var vm = this; var evts = []; @@ -18,6 +18,10 @@ vm.startTour = startTour; vm.getTourGroupCompletedPercentage = getTourGroupCompletedPercentage; vm.showTourButton = showTourButton; + + vm.showDocTypeTour = false; + vm.docTypeTours = []; + vm.nodeName = ''; function startTour(tour) { tourService.startTour(tour); @@ -58,9 +62,16 @@ handleSectionChange(); })); + evts.push(eventsService.on("editorState.changed", + function (e, args) { + setDocTypeTour(args.entity); + })); + findHelp(vm.section, vm.tree, vm.userType, vm.userLang); }); + + setDocTypeTour(editorState.getCurrent()); // check if a tour is running - if it is open the matching group var currentTour = tourService.getCurrentTour(); @@ -84,7 +95,7 @@ setSectionName(); findHelp(vm.section, vm.tree, vm.userType, vm.userLang); - + setDocTypeTour(); } }); } @@ -168,6 +179,26 @@ }); } + function setDocTypeTour(node) { + vm.showDocTypeTour = false; + vm.docTypeTours = []; + vm.nodeName = ''; + + if (vm.section === 'content' && vm.tree === 'content') { + + if (node) { + tourService.getToursForDoctype(node.contentTypeAlias).then(function (data) { + if (data && data.length > 0) { + vm.docTypeTours = data; + var currentVariant = _.find(node.variants, (x) => x.active); + vm.nodeName = currentVariant.name; + vm.showDocTypeTour = true; + } + }); + } + } + } + evts.push(eventsService.on("appState.tour.complete", function (event, tour) { tourService.getGroupedTours().then(function(groupedTours) { vm.tours = groupedTours; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html index 4ae3121098..aa6126e73e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html @@ -8,26 +8,56 @@ - -
    + +
    +
    Need help editing current item '{{vm.nodeName}}' ?
    -
    Tours
    +
    -
    + +
    +
    +
    +
    + {{ tour.name }} +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    + +
    + Tours +
    + + - -
    -
    -
    {{dashboard.label}}
    -
    -
    -
    + +
    +
    +
    {{dashboard.label}}
    +
    +
    +
    +
    -
    - -
    -
    Articles
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html index 7430d45ce6..8496aab80c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html @@ -39,10 +39,10 @@ autocomplete="off" maxlength="255" /> - + {{vm.currentVariant.language.name}} @@ -50,10 +50,10 @@ - +
    Open in split view
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index c46efb7b74..e1bc01a7a1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -12,13 +12,17 @@
    -
    - -
    - -
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html index dda8fa70f4..d743907d07 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html @@ -4,7 +4,7 @@ hotkey="{{::vm.hotkey}}" hotkey-when-hidden="true" ng-class="{'is-active': vm.item.active, '-has-error': vm.item.hasError}" - class="umb-sub-views-nav-item__action"> + class="umb-sub-views-nav-item__action umb-outline umb-outline--thin"> {{ vm.item.name }}
    {{vm.item.badge.count}}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html index 82b21e4c3b..ba5adf199a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html @@ -1,22 +1,28 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html index 9ee50bcae1..a34d548ea6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html @@ -1,16 +1,20 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html b/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html index b5531f477a..cbd1ac3f30 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html @@ -1,4 +1,4 @@ 
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index c2f9ceebc4..ca57679f51 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -1,6 +1,6 @@
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html index fb8ae6b22f..1d8829b4ca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html @@ -11,7 +11,7 @@ - {{node.name}} + {{node.name}} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html index 6bb2d120c8..217489f14c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html @@ -5,9 +5,9 @@ ng-model="term" class="umb-search-field search-query -full-width-input" placeholder="{{searchPlaceholderText}}" - focus-when="{{showSearch}}"> + umb-auto-focus="{{autoFocus ? 'true' : 'false'}}">

    Search {{searchFromName}}

    -
    \ No newline at end of file +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-checkmark.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-checkmark.html index 89201a144f..faf4dca742 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-checkmark.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-checkmark.html @@ -1 +1 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-child-selector.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-child-selector.html index 686937e8c9..1d88c0eb96 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-child-selector.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-child-selector.html @@ -14,23 +14,25 @@
    -
    +
    - +
    - {{ selectedChild.name }} + {{selectedChild.name}}
    - +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-code-snippet.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-code-snippet.html new file mode 100644 index 0000000000..199d7dec56 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-code-snippet.html @@ -0,0 +1,23 @@ +
    +
    + {{vm.language}} + + + + +
    +
    +
    +            
    +        
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm-action.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm-action.html index f4e4dff3af..b45a3bb843 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm-action.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm-action.html @@ -6,11 +6,11 @@ '-left': direction === 'left'}" on-outside-click="clickCancel()"> - + - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html index 93fa590f68..0276ae2a98 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html @@ -1,14 +1,14 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html index c6c841f8b1..1fa917a07f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html @@ -1,6 +1,6 @@
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html index e14315f9f4..d66e7f7f90 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html @@ -1,18 +1,18 @@
    -
    - +

    {{ miniListView.node.name }}

    - + Back / @@ -30,13 +30,12 @@
    - -
    - -
      - - + +
    {{ child.name }}
    @@ -76,8 +76,8 @@
    - - + No items have been added + Sorry, we can not find what you are looking for.
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html new file mode 100644 index 0000000000..d4e75908bd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html @@ -0,0 +1,15 @@ + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js new file mode 100644 index 0000000000..d7aee744e4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js @@ -0,0 +1,50 @@ +(function () { + 'use strict'; + + angular + .module('umbraco') + .component('umbMiniSearch', { + templateUrl: 'views/components/umb-mini-search/umb-mini-search.html', + controller: UmbMiniSearchController, + controllerAs: 'vm', + bindings: { + model: "=", + onStartTyping: "&?", + onSearch: "&?", + onBlur: "&?" + } + }); + + function UmbMiniSearchController($scope) { + + var vm = this; + + var searchDelay = _.debounce(function () { + $scope.$apply(function () { + if (vm.onSearch) { + vm.onSearch(); + } + }); + }, 500); + + vm.onKeyDown = function (ev) { + //13: enter + switch (ev.keyCode) { + case 13: + if (vm.onSearch) { + vm.onSearch(); + } + break; + } + }; + + vm.onChange = function () { + if (vm.onStartTyping) { + vm.onStartTyping(); + } + searchDelay(); + }; + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html index 475e7c14fe..719a53e361 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html @@ -1,3 +1,3 @@ -
    + -
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index df817c1657..3b6d82f73c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -37,7 +37,7 @@
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html index 13cc5ab34f..9b4e60a413 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html @@ -44,9 +44,10 @@
    -
    - Remove file + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html index ee9034fac6..8368369ce8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html @@ -1,7 +1,7 @@
    -
    @@ -46,7 +46,7 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-group-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-group-preview.html index 33c861c3d0..20718cf804 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-group-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-group-preview.html @@ -39,8 +39,8 @@
    - Edit - Remove + +
    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/assigndomain.html b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html index 42876cc27a..6238d11a69 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html @@ -2,7 +2,7 @@ -
    +
    @@ -24,11 +24,11 @@
    Domains
    - +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/imagepicker.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/imagepicker.html index 5321e4845d..d797501d91 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/imagepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/imagepicker.html @@ -15,7 +15,7 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.html index 87ccf08d62..5d93c59f5e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.html @@ -27,7 +27,7 @@
  • -
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.html index bb5e5fe782..f569105083 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.html @@ -9,12 +9,12 @@
    - +
    - Remove +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js index 828763bc1c..f9c8ae8b0e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js @@ -19,15 +19,16 @@ angular.module('umbraco') }; } - if($scope.model.value.id && $scope.model.value.type !== "member"){ - entityResource.getById($scope.model.value.id, entityType()).then(function(item){ + if($scope.model.value.id && $scope.model.value.type !== "member"){ + entityResource.getById($scope.model.value.id, entityType()).then(function(item){ populate(item); - }); + }); + } + else { + $timeout(function () { + treeSourceChanged(); + }, 100); } - - $timeout(function () { - treeSourceChanged(); - }, 100); function entityType() { var ent = "Document"; diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js index ef781c6014..a87377c84b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js @@ -7,7 +7,6 @@ function TreeSourceTypePickerController($scope, contentTypeResource, mediaTypeRe var allItemTypes = null; var currentItemType = null; - var initialLoad = true; function init() { vm.loading = true; @@ -86,13 +85,12 @@ function TreeSourceTypePickerController($scope, contentTypeResource, mediaTypeRe } eventsService.on("treeSourceChanged", function (e, args) { - currentItemType = args.value; // reset the model value if we changed node type (but not on the initial load) - if (!initialLoad) { + if (!!currentItemType && currentItemType !== args.value) { vm.itemTypes = []; updateModel(); } - initialLoad = false; + currentItemType = args.value; init(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index ab9b078433..ba22ca9d80 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -31,7 +31,7 @@ ... -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 0f19bf3b1a..24affc6ac1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -85,13 +85,27 @@ function dateTimePickerController($scope, angularHelper, dateHelper, validationM }; $scope.datePickerChange = function(date) { - setDate(date); + const momentDate = moment(date); + setDate(momentDate); setDatePickerVal(); }; - $scope.inputChanged = function() { - setDate($scope.model.datetimePickerValue); - setDatePickerVal(); + $scope.inputChanged = function () { + if ($scope.model.datetimePickerValue === "" && $scope.hasDatetimePickerValue) { + // $scope.hasDatetimePickerValue indicates that we had a value before the input was changed, + // but now the input is empty. + $scope.clearDate(); + } else if ($scope.model.datetimePickerValue) { + var momentDate = moment($scope.model.datetimePickerValue, $scope.model.config.format, true); + if (!momentDate || !momentDate.isValid()) { + momentDate = moment(new Date($scope.model.datetimePickerValue)); + } + if (momentDate && momentDate.isValid()) { + setDate(momentDate); + } + setDatePickerVal(); + flatPickr.setDate($scope.model.value, false); + } } //here we declare a special method which will be called whenever the value has changed from the server @@ -103,15 +117,14 @@ function dateTimePickerController($scope, angularHelper, dateHelper, validationM var newDate = moment(newVal); if (newDate.isAfter(minDate)) { - setDate(newVal); + setDate(newDate); } else { $scope.clearDate(); } } }; - function setDate(date) { - const momentDate = moment(date); + function setDate(momentDate) { angularHelper.safeApply($scope, function() { // when a date is changed, update the model if (momentDate && momentDate.isValid()) { @@ -123,12 +136,11 @@ function dateTimePickerController($scope, angularHelper, dateHelper, validationM $scope.hasDatetimePickerValue = false; $scope.model.datetimePickerValue = null; } - updateModelValue(date); + updateModelValue(momentDate); }); } - function updateModelValue(date) { - const momentDate = moment(date); + function updateModelValue(momentDate) { if ($scope.hasDatetimePickerValue) { if ($scope.model.config.pickTime) { //check if we are supposed to offset the time diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js deleted file mode 100644 index 5e0cc40db9..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js +++ /dev/null @@ -1,29 +0,0 @@ -angular.module("umbraco") - -//this controller is obsolete and should not be used anymore -//it proxies everything to the system media list view which has overtaken -//all the work this property editor used to perform -.controller("Umbraco.PropertyEditors.FolderBrowserController", - function ($rootScope, $scope, contentTypeResource) { - //get the system media listview - contentTypeResource.getPropertyTypeScaffold(-96) - .then(function(dt) { - - $scope.fakeProperty = { - alias: "contents", - config: dt.config, - description: "", - editor: dt.editor, - hideLabel: true, - id: 1, - label: "Contents:", - validation: { - mandatory: false, - pattern: null - }, - value: "", - view: dt.view - }; - - }); -}); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html deleted file mode 100644 index 8cf5229750..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html +++ /dev/null @@ -1,3 +0,0 @@ -
    - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowdeleteconfirm.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowdeleteconfirm.html index 2bf1f00b0e..02b92f44f7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowdeleteconfirm.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowdeleteconfirm.html @@ -1,53 +1,45 @@
    + + + + +

    Warning!

    - +

    + You are deleting the row configuration {{model.dialogData.rowName}} +

    - - - +

    + + Modifying a row configuration name will result in loss of + data for any existing content that is based on this configuration. + +

    +

    + Are you sure? +

    +
    +
    +
    -

    Warning!

    - -

    - You are deleting the row configuration '{{model.dialogData.rowName}}' -

    - -

    - Modifying a row configuration name will result in loss of - data for any existing content that is based on this configuration. -

    - -

    - Are you sure? -

    - - - - -
    -
    -
    - - - - + + - - + - - - + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 3db1221f5e..5adf121a56 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -402,6 +402,15 @@ angular.module("umbraco") eventsService.emit("grid.rowAdded", { scope: $scope, element: $element, row: row }); + // TODO: find a nicer way to do this without relying on setTimeout + setTimeout(function () { + var newRowEl = $element.find("[data-rowid='" + row.$uniqueId + "']"); + + if(newRowEl !== null) { + newRowEl.focus(); + } + }, 0); + }; $scope.removeRow = function (section, $index) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index e889067321..bde2b9148e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -95,7 +95,7 @@
    - + -
    +
    +
    - +
    - +
    -
    +
    @@ -249,14 +249,16 @@
    - - - - + + + Add new role + +
    @@ -264,10 +266,11 @@

    -
    + ng-click="addRow(section, layout)" + type="button">
    @@ -285,7 +288,7 @@ {{layout.label || layout.name}} -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html index 92d1a9ef26..34a59cabf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html @@ -1,9 +1,15 @@
    -
    +
    -

    -

    +

    + + Grid Layouts + +

    +

    + Layouts are the overall work area for the grid editor, usually you only need one or two different layouts +

      + class="preview-rows layout">
      {{template.name}}
      - - +
    -
    -
    +
    -

    -

    +

    + Row Configurations +

    +

    + Rows are predefined cells arranged horizontally +

      + class="preview-rows columns">
      -

      {{area.maxItems}}

      +

      {{area.maxItems}}

      @@ -74,17 +84,18 @@
      {{layout.label || layout.name}}
      - - - - +
    -
    @@ -93,7 +104,7 @@
    -
    +
    @@ -108,22 +119,21 @@ ng-model="model.value.config">
  • - + - - +
    • -
    • - - - - - +
    • +
    @@ -137,22 +147,22 @@ ng-model="model.value.styles">
  • - + - - +
    • -
    • - - - - +
    • +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index b5ae731c94..e3576426a3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -13,6 +13,7 @@ angular.module('umbraco') $scope.clear = clear; $scope.reset = reset; $scope.close = close; + $scope.isCustomCrop = isCustomCrop; $scope.focalPointChanged = focalPointChanged; //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; @@ -201,6 +202,10 @@ angular.module('umbraco') $scope.imageCropperForm.$setDirty(); }; + function isCustomCrop(crop) { + return !!crop.coordinates; + } + var unsubscribe = $scope.$on("formSubmitting", function () { $scope.currentCrop = null; $scope.currentPoint = null; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index a0faefe690..84c696ab2e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -41,7 +41,8 @@ on-value-changed="focalPointChanged(left, top)" on-image-loaded="imageLoaded(isCroppable, hasDimensions)"> - Remove file + +
    @@ -59,6 +60,7 @@
    {{value.alias}} {{value.width}}px x {{value.height}}px + User defined 
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html index 6c7a9c7f06..07d5215793 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html @@ -52,7 +52,9 @@ + on-click-name="vm.goToItem" + allow-open-folder="true" + allow-open-file="true"> 1) { - $scope.createAllowedButtonMultiWithBlueprints = true; - } - }); - - $scope.contentId = id; $scope.isTrashed = editorState.current ? editorState.current.trashed : id === "-20" || id === "-21"; @@ -779,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/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 304f3f3fbc..ee1847b430 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -14,18 +14,18 @@
    -
    - @@ -46,11 +46,11 @@
    - - +
  • - - +
  • @@ -126,21 +126,10 @@ - -
    - - -
    -
    + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/orderDirection.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/orderDirection.prevalues.html index 83a905ccf7..7ec0895936 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/orderDirection.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/orderDirection.prevalues.html @@ -1,10 +1,16 @@ 
    - - +
      +
    • + +
    • +
    • + +
    • +
    - Required + Required
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js index f05b1e31d8..bb87f0463d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js @@ -27,6 +27,20 @@ function MarkdownEditorController($scope, $element, assetsService, editorService editorService.mediaPicker(mediaPicker); } + function openLinkPicker(callback) { + var linkPicker = { + hideTarget: true, + submit: function(model) { + callback(model.target.url, model.target.name); + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.linkPicker(linkPicker); + } + assetsService .load([ "lib/markdown/markdown.converter.js", @@ -53,6 +67,12 @@ function MarkdownEditorController($scope, $element, assetsService, editorService return true; // tell the editor that we'll take care of getting the image url }); + //subscribe to the link dialog clicks + editor2.hooks.set("insertLinkDialog", function (callback) { + openLinkPicker(callback); + return true; // tell the editor that we'll take care of getting the link url + }); + editor2.hooks.set("onPreviewRefresh", function () { // We must manually update the model as there is no way to hook into the markdown editor events without exstensive edits to the library. if ($scope.model.value !== $("textarea", $element).val()) { 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 c9d4caf312..340e6865ef 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,43 @@ 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 => { + // 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. + var found = medias.find(m => 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 +94,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 +105,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 +170,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(); } }; @@ -205,12 +198,12 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl multiPicker: multiPicker, onlyImages: onlyImages, disableFolderSelect: disableFolderSelect, - allowMediaEdit: true, + submit: function (model) { 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 +273,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 1f9bd4e3c0..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/multiurlpicker/multiurlpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js index 8381a53644..172f9b2249 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js @@ -19,7 +19,10 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en var currentForm = angularHelper.getCurrentForm($scope); $scope.sortableOptions = { + axis: "y", + containment: "parent", distance: 10, + opacity: 0.7, tolerance: "pointer", scroll: true, zIndex: 6000, @@ -79,6 +82,7 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en currentTarget: target, dataTypeKey: $scope.model.dataTypeKey, ignoreUserStartNodes : ($scope.model.config && $scope.model.config.ignoreUserStartNodes) ? $scope.model.config.ignoreUserStartNodes : "0", + hideAnchor: $scope.model.config && $scope.model.config.hideAnchor ? true : false, submit: function (model) { if (model.target.url || model.target.anchor) { // if an anchor exists, check that it is appropriately prefixed @@ -140,6 +144,16 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en if ($scope.model.validation && $scope.model.validation.mandatory && !$scope.model.config.minNumber) { $scope.model.config.minNumber = 1; } + + _.each($scope.model.value, function (item){ + // we must reload the "document" link URLs to match the current editor culture + if (item.udi && item.udi.indexOf("/document/") > 0) { + item.url = null; + entityResource.getUrlByUdi(item.udi).then(function (data) { + item.url = data; + }); + } + }); } init(); 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 d625917afb..a25d5e798e 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 @@ -41,7 +41,7 @@ if (vm.maxItems === 0) vm.maxItems = 1000; - vm.singleMode = vm.minItems === 1 && vm.maxItems === 1; + vm.singleMode = vm.minItems === 1 && vm.maxItems === 1 && model.config.contentTypes.length === 1;; vm.showIcons = Object.toBoolean(model.config.showIcons); vm.wideMode = Object.toBoolean(model.config.hideLabel); vm.hasContentTypes = model.config.contentTypes.length > 0; @@ -131,10 +131,11 @@ setCurrentNode(newNode); setDirty(); + validate(); }; vm.openNodeTypePicker = function ($event) { - if (vm.nodes.length >= vm.maxItems) { + if (vm.overlayMenu || vm.nodes.length >= vm.maxItems) { return; } @@ -220,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; } @@ -234,12 +236,22 @@ } }; + vm.canDeleteNode = function (idx) { + return (vm.nodes.length > vm.minItems) + ? true + : model.config.contentTypes.length > 1; + } + function deleteNode(idx) { vm.nodes.splice(idx, 1); setDirty(); updateModel(); + validate(); }; vm.requestDeleteNode = function (idx) { + if (!vm.canDeleteNode(idx)) { + return; + } if (model.config.confirmDeletes === true) { localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) { const overlay = { @@ -265,6 +277,9 @@ }; vm.getName = function (idx) { + if (!model.value || !model.value.length) { + return ""; + } var name = ""; @@ -314,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"; } @@ -468,11 +487,13 @@ } } - // Auto-fill with elementTypes, but only if we have one type to choose from, and if this property is empty. - if (vm.singleMode === true && vm.nodes.length === 0 && model.config.minItems > 0) { + // 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 @@ -480,8 +501,14 @@ setCurrentNode(vm.nodes[0]); } + validate(); + vm.inited = true; + if (modelWasChanged) { + updateModel(); + } + updatePropertyActionStates(); checkAbilityToPasteContent(); } @@ -564,8 +591,8 @@ } function updatePropertyActionStates() { - copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0; - removeAllEntriesAction.isDisabled = !model.value || model.value.length === 0; + copyAllEntriesAction.isDisabled = !model.value || !model.value.length; + removeAllEntriesAction.isDisabled = copyAllEntriesAction.isDisabled; } @@ -585,25 +612,28 @@ updateModel(); }); + var validate = function () { + if (vm.nodes.length < vm.minItems) { + $scope.nestedContentForm.minCount.$setValidity("minCount", false); + } + else { + $scope.nestedContentForm.minCount.$setValidity("minCount", true); + } + + if (vm.nodes.length > vm.maxItems) { + $scope.nestedContentForm.maxCount.$setValidity("maxCount", false); + } + else { + $scope.nestedContentForm.maxCount.$setValidity("maxCount", true); + } + } + var watcher = $scope.$watch( function () { return vm.nodes.length; }, function () { - //Validate! - if (vm.nodes.length < vm.minItems) { - $scope.nestedContentForm.minCount.$setValidity("minCount", false); - } - else { - $scope.nestedContentForm.minCount.$setValidity("minCount", true); - } - - if (vm.nodes.length > vm.maxItems) { - $scope.nestedContentForm.maxCount.$setValidity("maxCount", false); - } - else { - $scope.nestedContentForm.maxCount.$setValidity("maxCount", true); - } + validate(); } ); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js index 6e807ffaa4..d4380d6c64 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js @@ -134,6 +134,9 @@ event: $event, submit: function (model) { config.ncAlias = model.selectedItem.alias; + if (model.selectedItem.tabs.length === 1) { + config.ncTabAlias = model.selectedItem.tabs[0]; + } overlayService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html index c6860140a5..f62894e043 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html @@ -56,11 +56,13 @@

    Group:
    - Select the group whose properties should be displayed. If left blank, the first group on the element type will be used. + Select the group whose properties should be displayed. If left blank, the first group on the element type will be used.

    Template:
    - Enter an angular expression to evaluate against each item for its name. Use {{$index}} to display the item index + Enter an angular expression to evaluate against each item for its name. Use + {{$index}} + to display the item index

    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html index d24d3796f3..da6e466b50 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html @@ -1,14 +1,14 @@ 
    - + - +
    -
    +
    @@ -17,7 +17,7 @@ {{vm.labels.copy_icon_title}} -
    - 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/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 8e3e91551f..0d012de068 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -21,7 +21,8 @@ //create the initial model for change password vm.changePasswordModel = { config: {}, - isChanging: false + isChanging: false, + value: {} }; vm.goToPage = goToPage; @@ -37,7 +38,8 @@ vm.changeAvatar = changeAvatar; vm.clearAvatar = clearAvatar; vm.save = save; - + + vm.changePassword = changePassword; vm.toggleChangePassword = toggleChangePassword; function init() { @@ -111,9 +113,28 @@ } function toggleChangePassword() { - vm.changePasswordModel.isChanging = !vm.changePasswordModel.isChanging; - //reset it - vm.user.changePassword = null; + //reset it + vm.user.changePassword = null; + + localizationService.localizeMany(["general_cancel", "general_confirm", "general_changePassword"]) + .then(function (data) { + const overlay = { + view: "changepassword", + title: data[2], + changePassword: vm.user.changePassword, + config: vm.changePasswordModel.config, + closeButtonLabel: data[0], + submitButtonLabel: data[1], + submitButtonStyle: 'success', + close: () => overlayService.close(), + submit: model => { + overlayService.close(); + vm.changePasswordModel.value = model.changePassword; + changePassword(); + } + }; + overlayService.open(overlay); + }); } function save() { @@ -138,11 +159,7 @@ //restore vm.user.navigation = currentNav; setUserDisplayState(); - formatDatesToLocal(vm.user); - - vm.changePasswordModel.isChanging = false; - //the user has a password if they are not states: Invited, NoCredentials - vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4; + formatDatesToLocal(vm.user); vm.page.saveButtonState = "success"; @@ -158,6 +175,36 @@ } } + /** + * + */ + function changePassword() { + //anytime a user is changing another user's password, we are in effect resetting it so we need to set that flag here + if (vm.changePasswordModel.value) { + //NOTE: the check for allowManuallyChangingPassword is due to this legacy user membership provider setting, if that is true, then the current user + //can change their own password without entering their current one (this is a legacy setting since that is a security issue but we need to maintain compat). + //if allowManuallyChangingPassword=false, then we are using default settings and the user will need to enter their old password to change their own password. + vm.changePasswordModel.value.reset = (!vm.changePasswordModel.value.oldPassword && !vm.user.isCurrentUser) || vm.changePasswordModel.config.allowManuallyChangingPassword; + } + + // since we don't send the entire user model, the id is required + vm.changePasswordModel.value.id = vm.user.id; + + usersResource.changePassword(vm.changePasswordModel.value) + .then(() => { + vm.changePasswordModel.isChanging = false; + vm.changePasswordModel.value = {}; + + //the user has a password if they are not states: Invited, NoCredentials + vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4; + }, err => { + contentEditingHelper.handleSaveError({ + err: err, + showNotifications: true + }); + }); + } + /** * Used to emit the save event and await any async operations being performed by editor extensions * @param {any} savedUser diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js index b21859f5c4..984866dca1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js @@ -15,6 +15,8 @@ vm.selectUserGroup = selectUserGroup; vm.deleteUserGroups = deleteUserGroups; + vm.filter = null; + var currentUser = null; function onInit() { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html index 4d252a3ae0..46923dd27d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html @@ -8,7 +8,7 @@ @@ -18,18 +18,10 @@ - + + + + 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 b35a29d2de..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"> - + @@ -196,23 +192,22 @@ @@ -259,7 +254,7 @@
    -
    - - - - - - - - - -


    Password reset to value: {{model.user.resetPasswordValue}}

    diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js index 024d4539bc..f3c28fdb9d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js @@ -112,6 +112,7 @@ vm.selectAll = selectAll; vm.areAllSelected = areAllSelected; vm.searchUsers = searchUsers; + vm.onBlurSearch = onBlurSearch; vm.getFilterName = getFilterName; vm.setUserStatesFilter = setUserStatesFilter; vm.setUserGroupFilter = setUserGroupFilter; @@ -150,10 +151,12 @@ function initViewOptions() { // Start with default view options. + vm.usersOptions.filter = ""; vm.usersOptions.orderBy = "Name"; vm.usersOptions.orderDirection = "Ascending"; // Update from querystring if available. + initViewOptionFromQueryString("filter"); initViewOptionFromQueryString("orderBy"); initViewOptionFromQueryString("orderDirection"); initViewOptionFromQueryString("pageNumber"); @@ -451,6 +454,7 @@ var search = _.debounce(function () { $scope.$apply(function () { + vm.usersOptions.pageNumber = 1; getUsers(); }); }, 500); @@ -459,6 +463,10 @@ search(); } + function onBlurSearch() { + updateLocation("filter", vm.usersOptions.filter); + } + function getFilterName(array) { var name = vm.labels.all; var found = false; @@ -512,7 +520,7 @@ } updateLocation("userStates", vm.usersOptions.userStates.join(",")); - getUsers(); + changePageNumber(1); } function setUserGroupFilter(userGroup) { @@ -529,7 +537,7 @@ } updateLocation("userGroups", vm.usersOptions.userGroups.join(",")); - getUsers(); + changePageNumber(1); } function setOrderByFilter(value, direction) { @@ -547,6 +555,7 @@ } function updateLocation(key, value) { + $location.search("filter", vm.usersOptions.filter);// update filter, but first when something else requests a url update. $location.search(key, value); } @@ -657,7 +666,8 @@ function usersOptionsAsQueryString() { var qs = "?orderBy=" + vm.usersOptions.orderBy + "&orderDirection=" + vm.usersOptions.orderDirection + - "&pageNumber=" + vm.usersOptions.pageNumber; + "&pageNumber=" + vm.usersOptions.pageNumber + + "&filter=" + vm.usersOptions.filter; qs += addUsersOptionsFilterCollectionToQueryString("userStates", vm.usersOptions.userStates); qs += addUsersOptionsFilterCollectionToQueryString("userGroups", vm.usersOptions.userGroups); diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html index afaaf865c8..bb53413060 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html @@ -11,7 +11,7 @@ @@ -27,18 +27,10 @@ - + + + + @@ -180,7 +172,7 @@
    -
    +
    {{ user.userDisplayState.name }} @@ -233,7 +225,7 @@
    + class="umb-table-row umb-user-table-row umb-outline umb-outline--surrounding">
    + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json new file mode 100644 index 0000000000..b145249bb5 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:36804", + "sslPort": 44354 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Umbraco.Web.UI.BackOffice": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs new file mode 100644 index 0000000000..8e4da28917 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Website.AspNetCore; + + +namespace Umbraco.Web.UI.BackOffice +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddUmbracoWebsite(); + services.AddUmbracoBackOffice(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseUmbracoWebsite(); + app.UseUmbracoBackOffice(); + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); + }); + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj new file mode 100644 index 0000000000..69d223bcc6 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + Umbraco.Web.UI.BackOffice + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.Development.json b/src/Umbraco.Web.UI.NetCore/appsettings.Development.json new file mode 100644 index 0000000000..8983e0fc1c --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json new file mode 100644 index 0000000000..d9d9a9bff6 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Umbraco.Web.UI.NetCore/wwwroot/favicon.ico b/src/Umbraco.Web.UI.NetCore/wwwroot/favicon.ico new file mode 100644 index 0000000000..c0749ddf7f Binary files /dev/null and b/src/Umbraco.Web.UI.NetCore/wwwroot/favicon.ico differ diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index d1de4de358..1087cce531 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -86,7 +86,6 @@ - @@ -98,14 +97,14 @@ - 1.0.0-beta2-19324-01 + 1.0.0 runtime; build; native; contentfiles; analyzers; buildtransitive all - + - 3.3.0 + 3.4.0 runtime; build; native; contentfiles; analyzers all @@ -113,22 +112,22 @@ - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Abstractions - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + {29aa69d9-b597-4395-8d42-43b1263c240a} Umbraco.Core - - Umbraco.Examine - {07FBC26B-2927-4A22-8D96-D644C667FECC} + + {0fad7d2a-d7dd-45b1-91fd-488bb6cdacea} + Umbraco.Examine.Lucene {52ac0ba8-a60e-4e36-897b-e8b97a54ed1c} Umbraco.ModelsBuilder.Embedded + + {f6de8da0-07cc-4ef2-8a59-2bc81dbb3830} + Umbraco.PublishedCache.NuCache + {651e1350-91b6-44b7-bd60-7207006d7003} Umbraco.Web @@ -138,13 +137,6 @@ Properties\SolutionInfo.cs - - noNodes.aspx - ASPXCodeBehind - - - noNodes.aspx - True @@ -170,11 +162,10 @@ - + - ClientDependency.config @@ -351,10 +342,6 @@ 9000 / http://localhost:9000/ - http://localhost:8600/ - 8500 - / - http://localhost:8500 False False diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Breadcrumb.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Breadcrumb.cshtml index 263d4ae66e..94519f53da 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Breadcrumb.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Breadcrumb.cshtml @@ -1,4 +1,5 @@ -@using Umbraco.Web +@using Umbraco.Core +@using Umbraco.Web @inherits Umbraco.Web.Macros.PartialViewMacroPage @* @@ -17,7 +18,7 @@ @* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@ @foreach (var item in selection.OrderBy(x => x.Level)) { -
  • @item.Name /
  • +
  • @item.Name /
  • } @* Display the current page as the last item in the list *@ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index 7034404ca3..5f75f8d792 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -1,11 +1,12 @@ @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web +@using Umbraco.Web.Composing @using Umbraco.Web.Controllers @inherits Umbraco.Web.Macros.PartialViewMacroPage @{ - var profileModel = Members.GetCurrentMemberProfileModel(); + var profileModel = Current.MembershipHelper.GetCurrentMemberProfileModel(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); @@ -19,11 +20,11 @@ @*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ @Html.RenderJsHere() -@if (Members.IsLoggedIn() && profileModel != null) +@if (Current.MembershipHelper.IsLoggedIn() && profileModel != null) { if (success) { - @* This message will show if RedirectOnSucces is set to false (default) *@ + @* This message will show if profileModel.RedirectUrl is not defined (default) *@

    Profile updated

    } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml index 5aa09a3cb3..9462b3b638 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml @@ -1,6 +1,8 @@ @using Umbraco.Core.Models.PublishedContent @using Umbraco.Web @using Umbraco.Core +@using Umbraco.Core.Models +@using Umbraco.Web.Composing @inherits Umbraco.Web.Macros.PartialViewMacroPage @* @@ -24,18 +26,18 @@
    @foreach (var mediaId in mediaIds.Split(',')) { - var media = Umbraco.Media(mediaId); + var media = Current.PublishedContentQuery.Media(mediaId); @* a single image *@ if (media.IsDocumentType("Image")) { - @Render(media); + @Render(media) } @* a folder with images under it *@ - foreach (var image in media.Children()) + foreach (var image in media.Children(Current.VariationContextAccessor)) { - @Render(image); + @Render(image) } }
    @@ -44,8 +46,8 @@ @helper Render(IPublishedContent item) { } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListAncestorsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListAncestorsFromCurrentPage.cshtml index b654935558..070f67ff23 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListAncestorsFromCurrentPage.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListAncestorsFromCurrentPage.cshtml @@ -1,3 +1,4 @@ +@using Umbraco.Core @using Umbraco.Web @inherits Umbraco.Web.Macros.PartialViewMacroPage @@ -17,7 +18,7 @@ @* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@ @foreach (var item in selection.OrderBy(x => x.Level)) { -
  • @item.Name »
  • +
  • @item.Name »
  • } @* Display the current page as the last item in the list *@ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml index 46c8de695c..ec41d45c0c 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml @@ -1,4 +1,5 @@ @using Umbraco.Web +@using Umbraco.Web.Composing @inherits Umbraco.Web.Macros.PartialViewMacroPage @* @@ -18,7 +19,7 @@ @if (startNodeId != null) { @* Get the starting page *@ - var startNode = Umbraco.Content(startNodeId); + var startNode = Current.PublishedContentQuery.Content(startNodeId); var selection = startNode.Children.Where(x => x.IsVisible()).ToArray(); if (selection.Length > 0) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml index 51fdbadb00..386bc824df 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml @@ -1,3 +1,5 @@ +@using Umbraco.Web +@using Umbraco.Web.Composing @inherits Umbraco.Web.Macros.PartialViewMacroPage @* @@ -17,7 +19,7 @@ @if (mediaId != null) { @* Get the media item associated with the id passed in *@ - var media = Umbraco.Media(mediaId); + var media = Current.PublishedContentQuery.Media(mediaId); var selection = media.Children.ToArray(); if (selection.Length > 0) @@ -26,7 +28,7 @@ @foreach (var item in selection) {
  • - @item.Name + @item.Name
  • } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 8eadbb342f..78b06151af 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -1,11 +1,12 @@ @using System.Web.Mvc.Html @using Umbraco.Web +@using Umbraco.Web.Composing @using Umbraco.Web.Models @using Umbraco.Web.Controllers @inherits Umbraco.Web.Macros.PartialViewMacroPage @{ - var loginStatusModel = Members.GetCurrentLoginStatus(); + var loginStatusModel = Current.MembershipHelper.GetCurrentLoginStatus(); var logoutModel = new PostRedirectModel(); @* diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml index 1c01eeb855..15427f4b3c 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml @@ -1,3 +1,4 @@ +@using Umbraco.Core @using Umbraco.Web @inherits Umbraco.Web.Macros.PartialViewMacroPage @@ -15,7 +16,7 @@ @foreach (var item in selection) {
  • - @item.Name + @item.Name
  • } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index 17ed95ea31..81389f4a3d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -1,6 +1,7 @@ @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web +@using Umbraco.Web.Composing @using Umbraco.Web.Controllers @inherits Umbraco.Web.Macros.PartialViewMacroPage @@ -12,7 +13,7 @@ var registerModel = Members.CreateRegistrationModel("Custom Member"); *@ - var registerModel = Members.CreateRegistrationModel(); + var registerModel = Current.MembershipHelper.CreateRegistrationModel(); @* Configurable here: @@ -45,7 +46,7 @@ @if (success) { - @* This message will show if RedirectOnSucces is set to false (default) *@ + @* This message will show if registerModel.RedirectUrl is not defined (default) *@

    Registration succeeded.

    } else diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml index 567ed5d07d..a4127a9636 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml @@ -1,3 +1,4 @@ +@using Umbraco.Core @using Umbraco.Core.Models.PublishedContent @using Umbraco.Web @inherits Umbraco.Web.Macros.PartialViewMacroPage @@ -32,7 +33,7 @@ @foreach (var item in selection) {
  • - @item.Name + @item.Name @* Run the traverse helper again for any child pages *@ @Traverse(item) diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index f59e70e174..14ef376a2c 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -1,17 +1,8 @@ -@using System.Collections -@using System.Net.Http -@using System.Web.Mvc.Html -@using Umbraco.Core +@using Umbraco.Core @using ClientDependency.Core @using ClientDependency.Core.Mvc -@using Microsoft.Owin.Security -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq -@using Umbraco.Core.Composing -@using Umbraco.Core.IO +@using Umbraco.Web.Composing @using Umbraco.Web -@using Umbraco.Web.Editors -@using Umbraco.Core.Configuration @inherits System.Web.Mvc.WebViewPage @{ Layout = null; @@ -26,14 +17,14 @@ - + Umbraco @Html.RenderCssHere( - new BasicPath("Umbraco", Current.IOHelper.ResolveUrl(Current.Configs.Global().UmbracoPath))) + new BasicPath("Umbraco", Current.IOHelper.ResolveUrl(Model.GlobalSettings.UmbracoPath))) @*Because we're lazy loading angular js, the embedded cloak style will not be loaded initially, but we need it*@ -
  • @@ -45,7 +45,9 @@
    - + + + @@ -53,9 +55,8 @@ 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/default/StartupDashboardIntro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html index 0478e6ba3c..17a4dcdb65 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html @@ -40,8 +40,12 @@
    -

    Welcome to The Friendly CMS

    -

    Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.

    +

    + Welcome to The Friendly CMS +

    +

    + Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible. +

    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html index 3a93abbf7a..6fb0eae349 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html @@ -2,20 +2,25 @@
    - -

    Umbraco Forms

    + +

    + Umbraco Forms +

    -

    Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!

    +

    + Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! +

    @@ -23,7 +28,7 @@
    -

    Installing...

    +

    Installing...

    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/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index cd936bcdf7..97f8b6bd79 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -26,17 +26,16 @@ function ExamineManagementController($scope, $http, $q, $timeout, $location, umb function showSearchResultDialog(values) { if (vm.searchResults) { - localizationService.localize("examineManagement_fieldValues").then(function (value) { - - vm.searchResults.overlay = { + editorService.open({ title: value, searchResultValues: values, + size: "medium", view: "views/dashboard/settings/examinemanagementresults.html", close: function () { - vm.searchResults.overlay = null; + editorService.close(); } - }; + }); }); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index 632127e38c..35962be39f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -408,10 +408,4 @@
    - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html index bb8a29fca5..26fa0cb72f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html @@ -1,18 +1,41 @@
    - - - - - - - - - - - - - - -
    FieldValue
    {{key}}{{val}}
    - + + + + + + + + + + + + + + + + + + + +
    FieldValue
    {{key}}{{val}}
    +
    +
    +
    + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html index 4c041a573e..a73b5eccd7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html @@ -5,10 +5,10 @@
    -

    - The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. - You can add your own health checks, have a look at the documentation for more information about custom health checks. -

    + +

    The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. + You can add your own health checks, have a look at the documentation for more information about custom health checks.

    +
    @@ -85,9 +85,12 @@
    {{ vm.selectedGroup.name }}
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.controller.js similarity index 80% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.controller.js index c3baf7246e..5270892fa5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.controller.js @@ -1,4 +1,4 @@ -function nuCacheController($scope, $http, umbRequestHelper, localizationService, overlayService) { +function publishedSnapshotCacheController($scope, $http, umbRequestHelper, localizationService, overlayService) { var vm = this; @@ -35,7 +35,7 @@ if (vm.working) return; vm.working = true; umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("nuCacheStatusBaseUrl", "Collect")), + $http.get(umbRequestHelper.getApiUrl("publishedSnapshotCacheStatusBaseUrl", "Collect")), 'Failed to verify the cache.') .then(function (result) { vm.working = false; @@ -47,7 +47,7 @@ if (vm.working) return; vm.working = true; umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("nuCacheStatusBaseUrl", "GetStatus")), + $http.get(umbRequestHelper.getApiUrl("publishedSnapshotCacheStatusBaseUrl", "GetStatus")), 'Failed to verify the cache.') .then(function (result) { vm.working = false; @@ -83,7 +83,7 @@ vm.working = true; umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("nuCacheStatusBaseUrl", "ReloadCache")), + $http.post(umbRequestHelper.getApiUrl("publishedSnapshotCacheStatusBaseUrl", "ReloadCache")), 'Failed to trigger a cache reload') .then(function (result) { vm.working = false; @@ -94,7 +94,7 @@ vm.working = true; umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("nuCacheStatusBaseUrl", "RebuildDbCache")), + $http.post(umbRequestHelper.getApiUrl("publishedSnapshotCacheStatusBaseUrl", "RebuildDbCache")), 'Failed to rebuild the cache.') .then(function (result) { vm.working = false; @@ -109,4 +109,4 @@ init(); } -angular.module("umbraco").controller("Umbraco.Dashboard.NuCacheController", nuCacheController); +angular.module("umbraco").controller("Umbraco.Dashboard.PublishedSnapshotCacheController", publishedSnapshotCacheController); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.html similarity index 99% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.html index 0b14e9c3ff..e1f95ad62d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.html @@ -1,4 +1,4 @@ -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js index 27c3e57ddf..ca4d5e1450 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js @@ -7,8 +7,6 @@ $http.get(umbRequestHelper.getApiUrl('publishedStatusBaseUrl', 'GetPublishedStatusUrl')), 'Failed to get published status url') .then(function (result) { - - //result = 'views/dashboard/developer/nucache.html' vm.includeUrl = result; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js index d4aff871a2..be8ddba592 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js @@ -6,7 +6,7 @@ * @description * The controller for the info view of the datatype editor */ -function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsService, $timeout) { +function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsService, $timeout, editorService) { var vm = this; var evts = []; @@ -17,6 +17,9 @@ function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsSe vm.view = {}; vm.view.loading = true; + vm.openDocumentType = openDocumentType; + vm.openMediaType = openMediaType; + vm.openMemberType = openMemberType; /** Loads in the data type references one time */ function loadRelations() { @@ -31,6 +34,57 @@ function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsSe } } + function openDocumentType(id, event) { + open(id, event, "documentType"); + } + + function openMediaType(id, event) { + open(id, event, "mediaType"); + } + + function openMemberType(id, event) { + open(id, event, "memberType"); + } + + function open(id, event, type) { + // targeting a new tab/window? + if (event.ctrlKey || + event.shiftKey || + event.metaKey || // apple + (event.button && event.button === 1) // middle click, >IE9 + everyone else + ) { + // yes, let the link open itself + return; + } + event.stopPropagation(); + event.preventDefault(); + + const editor = { + id: id, + submit: function (model) { + editorService.close(); + vm.view.loading = true; + referencesLoaded = false; + loadRelations(); + }, + close: function () { + editorService.close(); + } + }; + + switch (type) { + case "documentType": + editorService.documentTypeEditor(editor); + break; + case "mediaType": + editorService.mediaTypeEditor(editor); + break; + case "memberType": + editorService.memberTypeEditor(editor); + break; + } + } + // load data type references when the references tab is activated evts.push(eventsService.on("app.tabChange", function (event, args) { $timeout(function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html index 16b2d4b263..0af1634b29 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html @@ -43,7 +43,7 @@
    {{::reference.name}}
    {{::reference.alias}}
    {{::reference.properties | umbCmsJoinArray:', ':'name'}}
    - +
    @@ -73,7 +73,7 @@
    {{::reference.name}}
    {{::reference.alias}}
    {{::reference.properties | umbCmsJoinArray:', ':'name'}}
    - +
    @@ -104,7 +104,7 @@
    {{::reference.name}}
    {{::reference.alias}}
    {{::reference.properties | umbCmsJoinArray:', ':'name'}}
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js index a3b422e4f3..dd591090f9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js @@ -9,7 +9,7 @@ (function() { 'use strict'; - function PermissionsController($scope, contentTypeResource, iconHelper, contentTypeHelper, localizationService, overlayService) { + function PermissionsController($scope, $timeout, contentTypeResource, iconHelper, contentTypeHelper, localizationService, overlayService) { /* ----------- SCOPE VARIABLES ----------- */ @@ -23,8 +23,10 @@ vm.addChild = addChild; vm.removeChild = removeChild; + vm.sortChildren = sortChildren; vm.toggleAllowAsRoot = toggleAllowAsRoot; vm.toggleAllowCultureVariants = toggleAllowCultureVariants; + vm.canToggleIsElement = false; vm.toggleIsElement = toggleIsElement; /* ---------- INIT ---------- */ @@ -48,9 +50,16 @@ if($scope.model.id === 0) { contentTypeHelper.insertChildNodePlaceholder(vm.contentTypes, $scope.model.name, $scope.model.icon, $scope.model.id); } - }); + // Can only switch to an element type if there are no content nodes already created from the type. + if ($scope.model.id > 0 && !$scope.model.isElement ) { + contentTypeResource.hasContentNodes($scope.model.id).then(function (result) { + vm.canToggleIsElement = !result; + }); + } else { + vm.canToggleIsElement = true; + } } function addChild($event) { @@ -84,6 +93,13 @@ $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); } + function sortChildren() { + // we need to wait until the next digest cycle for vm.selectedChildren to be updated + $timeout(function () { + $scope.model.allowedContentTypes = _.pluck(vm.selectedChildren, "id"); + }); + } + // note: "safe toggling" here ie handling cases where the value is undefined, etc function toggleAllowAsRoot() { diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index e890921ad4..e29956a48e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -33,7 +33,8 @@ parent-icon="model.icon" parent-id="model.id" on-add="vm.addChild" - on-remove="vm.removeChild"> + on-remove="vm.removeChild" + on-sort="vm.sortChildren"> @@ -61,11 +62,13 @@
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/logviewer/search.controller.js b/src/Umbraco.Web.UI.Client/src/views/logviewer/search.controller.js index 70cfd0e190..d03f292717 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logviewer/search.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/logviewer/search.controller.js @@ -262,11 +262,7 @@ submitButtonLabel: "Save Search", disableSubmitButton: true, view: "logviewersearch", - query: { - filterExpression: vm.logOptions.filterExpression, - startDate: vm.logOptions.startDate, - endDate: vm.logOptions.endDate - }, + query: vm.logOptions.filterExpression, submit: function (model) { //Resource call with two params (name & query) //API that opens the JSON and adds it to the bottom diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html index d34cf1810d..7706734327 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html @@ -11,7 +11,7 @@ - + + { 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/mediatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html index c059710ebb..fb0c4c3a52 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html @@ -9,7 +9,7 @@
    + on-cancel="cancel" confirm-button-style="danger">
    diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.html index 814ef7282d..153a0a4505 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.html @@ -9,6 +9,7 @@ + on-remove="vm.removeChild" + on-sort="vm.sortChildren"> diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js index ab54a453b5..b2e515e187 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js @@ -13,8 +13,13 @@ var evts = []; var vm = this; + var infiniteMode = $scope.model && $scope.model.infiniteMode; + var memberTypeId = infiniteMode ? $scope.model.id : $routeParams.id; + var create = infiniteMode ? $scope.model.create : $routeParams.create; vm.save = save; + vm.close = close; + vm.editorfor = "visuallyHiddenTexts_newMember"; vm.header = {}; vm.header.editorfor = "content_membergroup"; @@ -25,6 +30,7 @@ vm.page.loading = false; vm.page.saveButtonState = "init"; vm.labels = {}; + vm.saveButtonKey = infiniteMode ? "buttons_saveAndClose" : "buttons_save"; var labelKeys = [ "general_design", @@ -86,7 +92,7 @@ vm.page.defaultButton = { hotKey: "ctrl+s", hotKeyWhenHidden: true, - labelKey: "buttons_save", + labelKey: vm.saveButtonKey, letter: "S", type: "submit", handler: function () { vm.save(); } @@ -94,7 +100,7 @@ vm.page.subButtons = [{ hotKey: "ctrl+g", hotKeyWhenHidden: true, - labelKey: "buttons_saveAndGenerateModels", + labelKey: infiniteMode ? "buttons_generateModelsAndClose" : "buttons_saveAndGenerateModels", letter: "G", handler: function () { @@ -147,12 +153,12 @@ } }); - if ($routeParams.create) { + if (create) { vm.page.loading = true; //we are creating so get an empty data type item - memberTypeResource.getScaffold($routeParams.id) + memberTypeResource.getScaffold(memberTypeId) .then(function (dt) { init(dt); @@ -163,10 +169,12 @@ vm.page.loading = true; - memberTypeResource.getById($routeParams.id).then(function (dt) { + memberTypeResource.getById(memberTypeId).then(function (dt) { init(dt); - syncTreeNode(vm.contentType, dt.path, true); + if(!infiniteMode) { + syncTreeNode(vm.contentType, dt.path, true); + } vm.page.loading = false; }); @@ -219,10 +227,16 @@ } }).then(function (data) { //success - syncTreeNode(vm.contentType, data.path); + if(!infiniteMode) { + syncTreeNode(vm.contentType, data.path); + } vm.page.saveButtonState = "success"; + if(infiniteMode && $scope.model.submit) { + $scope.model.submit(); + } + deferred.resolve(data); }, function (err) { //error @@ -307,6 +321,12 @@ }); } + + function close() { + if(infiniteMode && $scope.model.close) { + $scope.model.close(); + } + } evts.push(eventsService.on("editors.groupsBuilder.changed", function(name, args) { angularHelper.getCurrentForm($scope).$setDirty(); diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.html index 07824bc7ec..c4c521c857 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.html @@ -40,6 +40,14 @@ + + + + label-key="{{vm.saveButtonKey}}">
    {{ installedPackage.name }}
    - {{ installedPackage.version }} | {{ installedPackage.url }}| {{ installedPackage.author }} + {{ installedPackage.version }} | {{ installedPackage.url }} | {{ installedPackage.author }}
    - - - -
    - - - - - -
    - -
    - -
    -
    - - - - - - -
    -
    -
    - - - - -
    - - - - -
    -

    - Password reset requested -

    -

    - Your username to login to the Umbraco back-office is: %0% -

    -

    - - - - - - -
    - - Click this link to reset your password - -
    -

    -

    If you cannot click on the link, copy and paste this URL into your browser window:

    - - - - -
    - - %1% - -
    -

    -
    -
    -


    -
    -
    - - - ]]> - - - Dashboard - Sections - Content - - - Choose page above... - %0% has been copied to %1% - Select where the document %0% should be copied to below - %0% has been moved to %1% - Select where the document %0% should be moved to below - has been selected as the root of your new content, click 'ok' below. - No node selected yet, please select a node in the list above before clicking 'ok' - The current node is not allowed under the chosen node because of its type - The current node cannot be moved to one of its subpages - The current node cannot exist at the root - The action isn't allowed since you have insufficient permissions on 1 or more child documents. - Relate copied items to original - - - %0%]]> - Notification settings saved for - - The following languages have been modified %0% - - - - - - - - - - - -
    - - - - - -
    - -
    - -
    -
    - - - - - - -
    -
    -
    - - - - -
    - - - - -
    -

    - Hi %0%, -

    -

    - This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' -

    - - - - - - -
    - -
    - EDIT
    -
    -

    -

    Update summary:

    - %6% -

    -

    - Have a nice day!

    - Cheers from the Umbraco robot -

    -
    -
    -


    -
    -
    - - - ]]>
    - The following languages have been modified:

    - %0% - ]]>
    - [%0%] Notification about %1% performed on %2% - Notifications - - - Actions - Created - Create package - - button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. - ]]> - This will delete the package - Drop to upload - Include all child nodes - or click here to choose package file - Upload package - Install a local package by selecting it from your machine. Only install packages from sources you know and trust - Upload another package - Cancel and upload another package - I accept - terms of use - - Path to file - Absolute path to file (ie: /bin/umbraco.bin) - Installed - Installed packages - Install local - Finish - This package has no configuration view - No packages have been created yet - You don’t have any packages installed - 'Packages' icon in the top right of your screen]]> - Package Actions - Author URL - Package Content - Package Files - Icon URL - Install package - License - License URL - Package Properties - Search for packages - Results for - We couldn’t find anything for - Please try searching for another package or browse through the categories - Popular - New releases - has - karma points - Information - Owner - Contributors - Created - Current version - .NET version - Downloads - Likes - Compatibility - This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be guaranteed for versions reported below 100% - External sources - Author - Documentation - Package meta data - Package name - Package doesn't contain any items -
    - You can safely remove this from the system by clicking "uninstall package" below.]]>
    - Package options - Package readme - Package repository - Confirm package uninstall - Package was uninstalled - The package was successfully uninstalled - Uninstall package - - Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, - so uninstall with caution. If in doubt, contact the package author.]]> - Package version - Package already installed - This package cannot be installed, it requires a minimum Umbraco version of - Uninstalling... - Downloading... - Importing... - Installing... - Restarting, please wait... - All done, your browser will now refresh, please wait... - Please click 'Finish' to complete installation and reload the page. - Uploading package... - - - Paste with full formatting (Not recommended) - The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web. - Paste as raw text without any formatting at all - Paste, but remove formatting (Recommended) - - - Group based protection - If you want to grant access to all members of specific member groups - You need to create a member group before you can use group based authentication - Error Page - Used when people are logged on, but do not have access - %0%]]> - %0% is now protected]]> - %0%]]> - Login Page - Choose the page that contains the login form - Remove protection... - %0%?]]> - Select the pages that contain login form and error messages - %0%]]> - %0%]]> - Specific members protection - If you wish to grant access to specific members - - - - - - - - - Include unpublished subpages - Publishing in progress - please wait... - %0% out of %1% pages have been published... - %0% has been published - %0% and subpages have been published - Publish %0% and all its subpages - Publish to publish %0% and thereby making its content publicly available.

    - You can publish this page and all its subpages by checking Include unpublished subpages below. - ]]>
    - - - You have not configured any approved colours - - - You can only select items of type(s): %0% - You have picked a content item currently deleted or in the recycle bin - You have picked content items currently deleted or in the recycle bin - - - Deleted item - You have picked a media item currently deleted or in the recycle bin - You have picked media items currently deleted or in the recycle bin - Trashed - - - enter external link - choose internal page - Caption - Link - Open in new window - enter the display caption - Enter the link - - - Reset crop - Save crop - Add new crop - Done - Undo edits - - - Select a version to compare with the current version - Current version - Red text will not be shown in the selected version. , green means added]]> - Document has been rolled back - This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view - Rollback to - Select version - View - - - Edit script file - - - Concierge - Content - Courier - Developer - Forms - Help - Umbraco Configuration Wizard - Media - Members - Newsletters - Packages - Settings - Statistics - Translation - Users - - - The best Umbraco video tutorials - - - Default template - To import a document type, find the ".udt" file on your computer by clicking the "Browse" button and click "Import" (you'll be asked for confirmation on the next screen) - New Tab Title - Node type - Type - Stylesheet - Script - Tab - Tab Title - Tabs - Master Content Type enabled - This Content Type uses - as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself - No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. - Create matching template - Add icon - - - Sort order - Creation date - Sorting complete. - Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items - - - - Validation - Validation errors must be fixed before the item can be saved - Failed - Saved - Insufficient user permissions, could not complete the operation - Cancelled - Operation was cancelled by a 3rd party add-in - Publishing was cancelled by a 3rd party add-in - Property type already exists - Property type created - DataType: %1%]]> - Propertytype deleted - Document Type saved - Tab created - Tab deleted - Tab with id: %0% deleted - Stylesheet not saved - Stylesheet saved - Stylesheet saved without any errors - Datatype saved - Dictionary item saved - Publishing failed because the parent page isn't published - Content published - and visible on the website - Content saved - Remember to publish to make changes visible - Sent For Approval - Changes have been sent for approval - Media saved - Member group saved - Media saved without any errors - Member saved - Stylesheet Property Saved - Stylesheet saved - Template saved - Error saving user (check log) - User Saved - User type saved - User group saved - File not saved - file could not be saved. Please check file permissions - File saved - File saved without any errors - Language saved - Media Type saved - Member Type saved - Member Group saved - Template not saved - Please make sure that you do not have 2 templates with the same alias - Template saved - Template saved without any errors! - Content unpublished - Partial view saved - Partial view saved without any errors! - Partial view not saved - An error occurred saving the file. - Permissions saved for - Deleted %0% user groups - %0% was deleted - Enabled %0% users - Disabled %0% users - %0% is now enabled - %0% is now disabled - User groups have been set - Unlocked %0% users - %0% is now unlocked - Member was exported to file - An error occurred while exporting the member - User %0% was deleted - Invite user - Invitation has been re-sent to %0% - Document type was exported to file - An error occurred while exporting the document type - - - Add style - Edit style - Rich text editor styles - Define the styles that should be available in the rich text editor for this stylesheet - Edit stylesheet - Edit stylesheet property - The name displayed in the editor style selector - Preview - How the text will look like in the rich text editor. - Selector - Uses CSS syntax, e.g. "h1" or ".redHeader" - Styles - The CSS that should be applied in the rich text editor, e.g. "color:red;" - Code - Editor - - - Failed to delete template with ID %0% - Edit template - Sections - Insert content area - Insert content area placeholder - Insert - Choose what to insert into your template - Dictionary item - A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. - Macro - - A Macro is a configurable component which is great for - reusable parts of your design, where you need the option to provide parameters, - such as galleries, forms and lists. - - Value - Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. - Partial view - - A partial view is a separate template file which can be rendered inside another - template, it's great for reusing markup or for separating complex templates into separate files. - - Master template - No master - Render child template - @RenderBody() placeholder. - ]]> - Define a named section - @section { ... }. This can be rendered in a - specific area of the parent of this template, by using @RenderSection. - ]]> - Render a named section - @RenderSection(name)
    placeholder. - This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. - ]]> - Section Name - Section is mandatory - - If mandatory, the child template must contain a @section definition, otherwise an error is shown. - - Query builder - items returned, in - copy to clipboard - I want - all content - content of type "%0%" - from - my website - where - and - is - is not - before - before (including selected date) - after - after (including selected date) - equals - does not equal - contains - does not contain - greater than - greater than or equal to - less than - less than or equal to - Id - Name - Created Date - Last Updated Date - order by - ascending - descending - Template - - - Image - Macro - Choose type of content - Choose a layout - Add a row - Add content - Drop content - Settings applied - This content is not allowed here - This content is allowed here - Click to embed - Click to insert image - Image caption... - Write here... - Grid Layouts - Layouts are the overall work area for the grid editor, usually you only need one or two different layouts - Add Grid Layout - Adjust the layout by setting column widths and adding additional sections - Row configurations - Rows are predefined cells arranged horizontally - Add row configuration - Adjust the row by setting cell widths and adding additional cells - Columns - Total combined number of columns in the grid layout - Settings - Configure what settings editors can change - Styles - Configure what styling editors can change - Allow all editors - Allow all row configurations - Maximum items - Leave blank or set to 0 for unlimited - Set as default - Choose extra - Choose default - are added - - - Compositions - Group - You have not added any groups - Add group - Inherited from - Add property - Required label - Enable list view - Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree - Allowed Templates - Choose which templates editors are allowed to use on content of this type - Allow as root - Allow editors to create content of this type in the root of the content tree. - Allowed child node types - Allow content of the specified types to be created underneath content of this type. - Choose child node - Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. - This content type is used in a composition, and therefore cannot be composed itself. - There are no content types available to use as a composition. - Removing a composition will delete all the associated property data. Once you save the document type there's no way back. - Create new - Use existing - Editor settings - Configuration - Yes, delete - was moved underneath - was copied underneath - Select the folder to move - Select the folder to copy - to in the tree structure below - All Document types - All Documents - All media items - using this document type will be deleted permanently, please confirm you want to delete these as well. - using this media type will be deleted permanently, please confirm you want to delete these as well. - using this member type will be deleted permanently, please confirm you want to delete these as well - and all documents using this type - and all media items using this type - and all members using this type - Member can edit - Allow this property value to be edited by the member on their profile page - Is sensitive data - Hide this property value from content editors that don't have access to view sensitive information - Show on member profile - Allow this property value to be displayed on the member profile page - tab has no sort order - Where is this composition used? - This composition is currently used in the composition of the following content types: - Allow varying by culture - Allow editors to create content of this type in different languages. - Allow varying by culture - Element type - Is an Element type - An Element type is meant to be used for instance in Nested Content, and not in the tree. - This is not applicable for an Element type - You have made changes to this property. Are you sure you want to discard them? - - - Add language - Mandatory language - Properties on this language have to be filled out before the node can be published. - Default language - An Umbraco site can only have one default language set. - Switching default language may result in default content missing. - Falls back to - No fall back language - To allow multi-lingual content to fall back to another language if not present in the requested language, select it here. - Fall back language - none - - - - Add parameter - Edit parameter - Enter macro name - Parameters - Define the parameters that should be available when using this macro. - Select partial view macro file - - - Building models - this can take a bit of time, don't worry - Models generated - Models could not be generated - Models generation has failed, see exception in U log - - - Add fallback field - Fallback field - Add default value - Default value - Fallback field - Default value - Casing - Encoding - Choose field - Convert line breaks - Yes, convert line breaks - Replaces line breaks with 'br' html tag - Custom Fields - Date only - Format and encoding - Format as date - Format the value as a date, or a date with time, according to the active culture - HTML encode - Will replace special characters by their HTML equivalent. - Will be inserted after the field value - Will be inserted before the field value - Lowercase - Modify output - None - Output sample - Insert after field - Insert before field - Recursive - Yes, make it recursive - Separator - Standard Fields - Uppercase - URL encode - Will format special characters in URLs - Will only be used when the field values above are empty - This field will only be used if the primary field is empty - Date and time - - - Translation details - Download XML DTD - Fields - Include subpages - - No translator users found. Please create a translator user before you start sending content to translation - The page '%0%' has been send to translation - Send the page '%0%' to translation - Total words - Translate to - Translation completed. - You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages. - Translation failed, the XML file might be corrupt - Translation options - Translator - Upload translation XML - - - Content - Content Templates - Media - Cache Browser - Recycle Bin - Created packages - Data Types - Dictionary - Installed packages - Install skin - Install starter kit - Languages - Install local package - Macros - Media Types - Members - Member Groups - Member Roles - Member Types - Document Types - Relation Types - Packages - Packages - Partial Views - Partial View Macro Files - Install from repository - Install Runway - Runway modules - Scripting Files - Scripts - Stylesheets - Templates - Log Viewer - Users - Settings - Templating - Third Party - - - New update ready - %0% is ready, click here for download - No connection to server - Error checking for update. Please review trace-stack for further information - - - Access - Based on the assigned groups and start nodes, the user has access to the following nodes - Assign access - Administrator - Category field - User created - Change Your Password - Change photo - New password - hasn't been locked out - The password hasn't been changed - Confirm new password - You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button - Content Channel - Create another user - Create new users to give them access to Umbraco. When a new user is created a password will be generated that you can share with the user. - Description field - Disable User - Document Type - Editor - Excerpt field - Failed login attempts - Go to user profile - Add groups to assign access and permissions - Invite another user - Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours. - Language - Set the language you will see in menus and dialogs - Last lockout date - Last login - Password last changed - Username - Media start node - Limit the media library to a specific start node - Media start nodes - Limit the media library to specific start nodes - Sections - Disable Umbraco Access - has not logged in yet - Old password - Password - Reset password - Your password has been changed! - Please confirm the new password - Enter your new password - Your new password cannot be blank! - Current password - Invalid current password - There was a difference between the new password and the confirmed password. Please try again! - The confirmed password doesn't match the new password! - Replace child node permissions - You are currently modifying permissions for the pages: - Select pages to modify their permissions - Remove photo - Default permissions - Granular permissions - Set permissions for specific nodes - Profile - Search all children - Add sections to give users access - Select user groups - No start node selected - No start nodes selected - Content start node - Limit the content tree to a specific start node - Content start nodes - Limit the content tree to specific start nodes - User last updated - has been created - The new user has successfully been created. To log in to Umbraco use the password below. - User management - Name - User permissions - User group - has been invited - An invitation has been sent to the new user with details about how to log in to Umbraco. - Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar. - Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it. - Uploading a photo of yourself will make it easy for other users to recognize you. Click the circle above to upload your photo. - Writer - Change - Your profile - Your recent history - Session expires in - Invite user - Create user - Send invite - Back to users - Umbraco: Invitation - - - - - - - - - - - -
    - - - - - -
    - -
    - -
    -
    - - - - - - -
    -
    -
    - - - - -
    - - - - -
    -

    - Hi %0%, -

    -

    - You have been invited by %1% to the Umbraco Back Office. -

    -

    - Message from %1%: -
    - %2% -

    - - - - - - -
    - - - - - - -
    - - Click this link to accept the invite - -
    -
    -

    If you cannot click on the link, copy and paste this URL into your browser window:

    - - - - -
    - - %3% - -
    -

    -
    -
    -


    -
    -
    - - ]]>
    - Invite - Resending invitation... - Delete User - Are you sure you wish to delete this user account? - All - Active - Disabled - Locked out - Invited - Inactive - Name (A-Z) - Name (Z-A) - Newest - Oldest - Last login - - - Validation - No validation - Validate as an email address - Validate as a number - Validate as a URL - ...or enter a custom validation - Field is mandatory - Enter a custom validation error message (optional) - Enter a regular expression - Enter a custom validation error message (optional) - You need to add at least - You can only have - items - items selected - Invalid date - Not a number - Invalid email - Custom validation - %1% more.]]> - %1% too many.]]> - - - - Value is set to the recommended value: '%0%'. - Value was set to '%1%' for XPath '%2%' in configuration file '%3%'. - Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'. - Found unexpected value '%0%' for '%2%' in configuration file '%3%'. - - Custom errors are set to '%0%'. - Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live. - Custom errors successfully set to '%0%'. - MacroErrors are set to '%0%'. - MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'. - MacroErrors are now set to '%0%'. - - Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'. - Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%). - Try Skip IIS Custom Errors successfully set to '%0%'. - - File does not exist: '%0%'. - '%0%' in config file '%1%'.]]> - There was an error, check log for full error: %0%. - Database - The database schema is correct for this version of Umbraco - %0% problems were detected with your database schema (Check the log for details) - Some errors were detected while validating the database schema against the current version of Umbraco. - Your website's certificate is valid. - Certificate validation error: '%0%' - Your website's SSL certificate has expired. - Your website's SSL certificate is expiring in %0% days. - Error pinging the URL %0% - '%1%' - You are currently %0% viewing the site using the HTTPS scheme. - The appSetting 'Umbraco.Core.UseHttps' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'. - The appSetting 'Umbraco.Core.UseHttps' is set to '%0%' in your web.config file, your cookies are %1% marked as secure. - Could not update the 'Umbraco.Core.UseHttps' setting in your web.config file. Error: %0% - - Enable HTTPS - Sets umbracoSSL setting to true in the appSettings of the web.config file. - The appSetting 'Umbraco.Core.UseHttps' is now set to 'true' in your web.config file, your cookies will be marked as secure. - Fix - Cannot fix a check with a value comparison type of 'ShouldNotEqual'. - Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value. - Value to fix check not provided. - Debug compilation mode is disabled. - Debug compilation mode is currently enabled. It is recommended to disable this setting before go live. - Debug compilation mode successfully disabled. - Trace mode is disabled. - Trace mode is currently enabled. It is recommended to disable this setting before go live. - Trace mode successfully disabled. - All folders have the correct permissions set. - - %0%.]]> - %0%. If they aren't being written to no action need be taken.]]> - All files have the correct permissions set. - - %0%.]]> - %0%. If they aren't being written to no action need be taken.]]> - X-Frame-Options used to control whether a site can be IFRAMEd by another was found.]]> - X-Frame-Options used to control whether a site can be IFRAMEd by another was not found.]]> - Set Header in Config - Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMEd by other websites. - A setting to create a header preventing IFRAMEing of the site by other websites has been added to your web.config file. - Could not update web.config file. Error: %0% - X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was found.]]> - X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was not found.]]> - Adds a value to the httpProtocol/customHeaders section of web.config to protect against MIME sniffing vulnerabilities. - A setting to create a header protecting against MIME sniffing vulnerabilities has been added to your web.config file. - Strict-Transport-Security, also known as the HSTS-header, was found.]]> - Strict-Transport-Security was not found.]]> - Adds the header 'Strict-Transport-Security' with the value 'max-age=10886400' to the httpProtocol/customHeaders section of web.config. Use this fix only if you will have your domains running with https for the next 18 weeks (minimum). - The HSTS header has been added to your web.config file. - X-XSS-Protection was found.]]> - X-XSS-Protection was not found.]]> - Adds the header 'X-XSS-Protection' with the value '1; mode=block' to the httpProtocol/customHeaders section of web.config. - The X-XSS-Protection header has been added to your web.config file. - - %0%.]]> - No headers revealing information about the website technology were found. - In the Web.config file, system.net/mailsettings could not be found. - In the Web.config file system.net/mailsettings section, the host is not configured. - SMTP settings are configured correctly and the service is operating as expected. - The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct. - %0%.]]> - %0%.]]> -

    Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

    %2%]]>
    - Umbraco Health Check Status: %0% - - - Disable URL tracker - Enable URL tracker - Original URL - Redirected To - Redirect Url Management - The following URLs redirect to this content item: - No redirects have been made - When a published page gets renamed or moved a redirect will automatically be made to the new page. - Are you sure you want to remove the redirect from '%0%' to '%1%'? - Redirect URL removed. - Error removing redirect URL. - This will remove the redirect - Are you sure you want to disable the URL tracker? - URL tracker has now been disabled. - Error disabling the URL tracker, more information can be found in your log file. - URL tracker has now been enabled. - Error enabling the URL tracker, more information can be found in your log file. - - - No Dictionary items to choose from - - - %0% characters left.]]> - %1% too many.]]> - - - Trashed content with Id: {0} related to original parent content with Id: {1} - Trashed media with Id: {0} related to original parent media item with Id: {1} - Cannot automatically restore this item - There is no location where this item can be automatically restored. You can move the item manually using the tree below. - was restored under - - - Direction - Parent to child - Bidirectional - Parent - Child - Count - Relations - Created - Comment - Name - No relations for this relation type. - Relation Type - Relations - - - Getting Started - Redirect URL Management - Content - Welcome - Examine Management - Published Status - Models Builder - Health Check - Profiling - Getting Started - Install Umbraco Forms - - - Go back - Active layout: - Jump to - group - passed - warning - failed - suggestion - Check passed - Check failed - Open backoffice search - Open/Close backoffice help - Open/Close your profile options - Open context menu for - Current language - Switch language to - Create new folder - Partial View - Partial View Macro - Member - Data type - - - References - This Data Type has no references. - Used in Document Types - No references to Document Types. - Used in Media Types - No references to Media Types. - Used in Member Types - No references to Member Types. - Used by - Used in Documents - Used in Members - Used in Media - - - Log Levels - Saved Searches - Total Items - Timestamp - Level - Machine - Message - Exception - Properties - Search With Google - Search this message with Google - Search With Bing - Search this message with Bing - Search Our Umbraco - Search this message on Our Umbraco forums and docs - Search Our Umbraco with Google - Search Our Umbraco forums using Google - Search Umbraco Source - Search within Umbraco source code on Github - Search Umbraco Issues - Search Umbraco Issues on Github - Delete this search - Find Logs with Request ID - Find Logs with Namespace - Find Logs with Machine Name - Open - - - Copy %0% - %0% from %1% - Remove all items - - - Open Property Actions - - - Wait - Refresh status - Memory Cache - - - - Reload - Database Cache - - Rebuilding can be expensive. - Use it when reloading is not enough, and you think that the database cache has not been - properly generated—which would indicate some critical Umbraco issue. - ]]> - - Rebuild - Internals - - not need to use it. - ]]> - - Collect - Published Cache Status - Caches - - - Performance profiling - - - Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. -

    -

    - If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. -

    -

    - If you want the profiler to be activated by default for all page renderings, you can use the toggle below. - It will set a cookie in your browser, which then activates the profiler automatically. - In other words, the profiler will only be active by default in your browser - not everyone else's. -

    - ]]> -
    - Activate the profiler by default - Friendly reminder - - - You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. -

    - ]]> -
    - - - Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. -

    -

    - Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. -

    - ]]> -
    - - - 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

    - ]]> -
    - To get you started - - - Start here - This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section - Find out more - - in the Documentation section of Our Umbraco - ]]> - - - Community Forum - ]]> - - - tutorial videos (some are free, some require a subscription) - ]]> - - - productivity boosting tools and commercial support - ]]> - - - training and certification opportunities - ]]> - - - + + + + The Umbraco community + https://our.umbraco.com/documentation/Extending-Umbraco/Language-Files + + + Culture and Hostnames + Audit Trail + Browse Node + Change Document Type + Copy + Create + Export + Create Package + Create group + Delete + Disable + Empty recycle bin + Enable + Export Document Type + Import Document Type + Import Package + Edit in Canvas + Exit + Move + Notifications + Public access + Publish + Unpublish + Reload + Republish entire site + Rename + Restore + Set permissions for the page %0% + Choose where to copy + Choose where to move + to in the tree structure below + Choose where to copy the selected item(s) + Choose where to move the selected item(s) + was moved to + was copied to + was deleted + Permissions + Rollback + Send To Publish + Send To Translation + Set group + Sort + Translate + Update + Set permissions + Unlock + Create Content Template + Resend Invitation + + + Content + Administration + Structure + Other + + + Allow access to assign culture and hostnames + Allow access to view a node's history log + Allow access to view a node + Allow access to change document type for a node + Allow access to copy a node + Allow access to create nodes + Allow access to delete nodes + Allow access to move a node + Allow access to set and change public access for a node + Allow access to publish a node + Allow access to unpublish a node + Allow access to change permissions for a node + Allow access to roll back a node to a previous state + Allow access to send a node for approval before publishing + Allow access to send a node for translation + Allow access to change the sort order for nodes + Allow access to translate a node + Allow access to save a node + Allow access to create a Content Template + + + Content + Info + + + Permission denied. + Add new Domain + remove + Invalid node. + One or more domains have an invalid format. + Domain has already been assigned. + Language + Domain + New domain '%0%' has been created + Domain '%0%' is deleted + Domain '%0%' has already been assigned + Domain '%0%' has been updated + Edit Current Domains + + + Inherit + Culture + + or inherit culture from parent nodes. Will also apply
    + to the current node, unless a domain below applies too.]]> +
    + Domains + + + Clear selection + Select + Do something else + Bold + Cancel Paragraph Indent + Insert form field + Insert graphic headline + Edit Html + Indent Paragraph + Italic + Center + Justify Left + Justify Right + Insert Link + Insert local link (anchor) + Bullet List + Numeric List + Insert macro + Insert picture + Publish and close + Publish with descendants + Edit relations + Return to list + Save + Save and close + Save and publish + Save and schedule + Save and send for approval + Save list view + Schedule + Save and preview + Preview is disabled because there's no template assigned + Choose style + Show styles + Insert table + Save and generate models + Undo + Redo + Delete tag + Cancel + Confirm + More publishing options + + + Viewing for + Content deleted + Content unpublished + Content saved and Published + Content saved and published for languages: %0% + Content saved + Content saved for languages: %0% + Content moved + Content copied + Content rolled back + Content sent for publishing + Content sent for publishing for languages: %0% + Sort child items performed by user + Copy + Publish + Publish + Move + Save + Save + Delete + Unpublish + Rollback + Send To Publish + Send To Publish + Sort + History (all variants) + + + To change the document type for the selected content, first select from the list of valid types for this location. + Then confirm and/or amend the mapping of properties from the current type to the new, and click Save. + The content has been re-published. + Current Property + Current type + The document type cannot be changed, as there are no alternatives valid for this location. An alternative will be valid if it is allowed under the parent of the selected content item and that all existing child content items are allowed to be created under it. + Document Type Changed + Map Properties + Map to Property + New Template + New Type + none + Content + Select New Document Type + The document type of the selected content has been successfully changed to [new type] and the following properties mapped: + to + Could not complete property mapping as one or more properties have more than one mapping defined. + Only alternate types valid for the current location are displayed. + + + Failed to create a folder under parent with ID %0% + Failed to create a folder under parent with name %0% + The folder name cannot contain illegal characters. + Failed to delete item: %0% + + + Is Published + About this page + Alias + (how would you describe the picture over the phone) + Alternative Links + Click to edit this item + Created by + Original author + Updated by + Created + Date/time this document was created + Document Type + Editing + Remove at + This item has been changed after publication + This item is not published + Last published + There are no items to show + There are no items to show in the list. + No content has been added + No members have been added + Media Type + Link to media item(s) + Member Group + Role + Member Type + No changes have been made + No date chosen + Page title + This media item has no link + Properties + This document is published but is not visible because the parent '%0%' is unpublished + This culture is published but is not visible because it is unpublished on parent '%0%' + This document is published but is not in the cache + Could not get the url + This document is published but its url would collide with content %0% + This document is published but its url cannot be routed + Publish + Published + Published (pending changes) + Publication Status + Publish with descendants to publish %0% and all content items underneath and thereby making their content publicly available.]]> + Publish with descendants to publish the selected languages and the same languages of content items underneath and thereby making their content publicly available.]]> + Publish at + Unpublish at + Clear Date + Set date + Sortorder is updated + To sort the nodes, simply drag the nodes or click one of the column headers. You can select multiple nodes by holding the "shift" or "control" key while selecting + Statistics + Title (optional) + Alternative text (optional) + Type + Unpublish + Unpublished + Last edited + Date/time this document was edited + Remove file(s) + Click here to remove the image from the media item + Click here to remove the file from the media item + Link to document + Member of group(s) + Not a member of group(s) + Child items + Target + This translates to the following time on the server: + What does this mean?]]> + Are you sure you want to delete this item? + Property %0% uses editor %1% which is not supported by Nested Content. + Are you sure you want to delete all items? + No content types are configured for this property. + Add element type + Select element type + Select the group whose properties should be displayed. If left blank, the first group on the element type will be used. + Enter an angular expression to evaluate against each item for its name. Use + to display the item index + Add another text box + Remove this text box + Content root + Include drafts: also publish unpublished content items. + This value is hidden. If you need access to view this value please contact your website administrator. + This value is hidden. + What languages would you like to publish? All languages with content are saved! + What languages would you like to publish? + What languages would you like to save? + All languages with content are saved on creation! + What languages would you like to send for approval? + What languages would you like to schedule? + Select the languages to unpublish. Unpublishing a mandatory language will unpublish all languages. + Published Languages + Unpublished Languages + Unmodified Languages + These languages haven't been created + Ready to Publish? + Ready to Save? + Send for approval + Select the date and time to publish and/or unpublish the content item. + Create new + Paste from clipboard + This item is in the Recycle Bin + + + Create a new Content Template from '%0%' + Blank + Select a Content Template + Content Template created + A Content Template was created from '%0%' + Another Content Template with the same name already exists + A Content Template is predefined content that an editor can select to use as the basis for creating new content + + + Click to upload + or click here to choose files + You can drag files here to upload + Cannot upload this file, it does not have an approved file type + Max file size is + Media root + Failed to move media + Failed to copy media + Failed to create a folder under parent id %0% + Failed to rename the folder with id %0% + Drag and drop your file(s) into the area + + + Create a new member + All Members + Member groups have no additional properties for editing. + + + Where do you want to create the new %0% + Create an item under + Select the document type you want to make a content template for + Enter a folder name + Choose a type and a title + Document Types within the Settings section, by editing the Allowed child node types under Permissions.]]> + Document Types within the Settings section.]]> + The selected page in the content tree doesn't allow for any pages to be created below it. + Edit permissions for this document type + Create a new document type + Document Types within the Settings section, by changing the Allow as root option under Permissions.]]> + Media Types Types within the Settings section, by editing the Allowed child node types under Permissions.]]> + The selected media in the tree doesn't allow for any other media to be created below it. + Edit permissions for this media type + Document Type without a template + New folder + New data type + New JavaScript file + New empty partial view + New partial view macro + New partial view from snippet + New partial view macro from snippet + New partial view macro (without macro) + New style sheet file + New Rich Text Editor style sheet file + + + Browse your website + - Hide + If Umbraco isn't opening, you might need to allow popups from this site + has opened in a new window + Restart + Visit + Welcome + + + Stay + Discard changes + You have unsaved changes + Are you sure you want to navigate away from this page? - you have unsaved changes + Publishing will make the selected items visible on the site. + Unpublishing will remove the selected items and all their descendants from the site. + Unpublishing will remove this page and all its descendants from the site. + You have unsaved changes. Making changes to the Document Type will discard the changes. + + + Done + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + + + Link title + Link + Anchor / querystring + Name + Manage hostnames + Close this window + Are you sure you want to delete + Are you sure you want to disable + Are you sure? + Are you sure? + Cut + Edit Dictionary Item + Edit Language + Edit selected media + Insert local link + Insert character + Insert graphic headline + Insert picture + Insert link + Click to add a Macro + Insert table + This will delete the language + Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt + Last Edited + Link + Internal link: + When using local links, insert "#" in front of link + Open in new window? + Macro Settings + This macro does not contain any properties you can edit + Paste + Edit permissions for + Set permissions for + Set permissions for %0% for user group %1% + Select the users groups you want to set permissions for + The items in the recycle bin are now being deleted. Please do not close this window while this operation takes place + The recycle bin is now empty + When items are deleted from the recycle bin, they will be gone forever + regexlib.com's webservice is currently experiencing some problems, which we have no control over. We are very sorry for this inconvenience.]]> + Search for a regular expression to add validation to a form field. Example: 'email, 'zip-code' 'url' + Remove Macro + Required Field + Site is reindexed + The website cache has been refreshed. All publish content is now up to date. While all unpublished content is still unpublished + The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished. + Number of columns + Number of rows + Click on the image to see full size + Pick item + View Cache Item + Relate to original + Include descendants + The friendliest community + Link to page + Opens the linked document in a new window or tab + Link to media + Select content start node + Select media + Select media type + Select icon + Select item + Select link + Select macro + Select content + Select content type + Select media start node + Select member + Select member group + Select member type + Select node + Select sections + Select users + No icons were found + There are no parameters for this macro + There are no macros available to insert + External login providers + Exception Details + Stacktrace + Inner Exception + Link your + Un-link your + account + Select editor + Select snippet + This will delete the node and all its languages. If you only want to delete one language, you should unpublish the node in that language instead. + + + There are no dictionary items. + + + %0%' below + ]]> + Culture Name + + Dictionary overview + + + Configured Searchers + Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher) + Field values + Health status + The health status of the index and if it can be read + Indexers + Index info + Lists the properties of the index + Manage Examine's indexes + Allows you to view the details of each index and provides some tools for managing the indexes + Rebuild index + + Depending on how much content there is in your site this could take a while.
    + It is not recommended to rebuild an index during times of high website traffic or when editors are editing content. + ]]> +
    + Searchers + Search the index and view the results + Tools + Tools to manage the index + fields + The index cannot be read and will need to be rebuilt + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + This index cannot be rebuilt because it has no assigned + IIndexPopulator + + + Enter your username + Enter your password + Confirm your password + Name the %0%... + Enter a name... + Enter an email... + Enter a username... + Label... + Enter a description... + Type to search... + Type to filter... + Type to add tags (press enter after each tag)... + Enter your email + Enter a message... + Your username is usually your email + #value or ?key=value + Enter alias... + Generating alias... + Create item + Create + Edit + Name + + + Create custom list view + Remove custom list view + A content type, media type or member type with this alias already exists + + + Renamed + Enter a new folder name here + %0% was renamed to %1% + + + Add prevalue + Database datatype + Property editor GUID + Property editor + Buttons + Enable advanced settings for + Enable context menu + Maximum default size of inserted images + Related stylesheets + Show label + Width and height + All property types & property data + using this data type will be deleted permanently, please confirm you want to delete these as well + Yes, delete + and all property types & property data using this data type + Select the folder to move + to in the tree structure below + was moved underneath + + + Your data has been saved, but before you can publish this page there are some errors you need to fix first: + The current membership provider does not support changing password (EnablePasswordRetrieval need to be true) + %0% already exists + There were errors: + There were errors: + The password should be a minimum of %0% characters long and contain at least %1% non-alpha numeric character(s) + %0% must be an integer + The %0% field in the %1% tab is mandatory + %0% is a mandatory field + %0% at %1% is not in a correct format + %0% is not in a correct format + + + Received an error from the server + The specified file type has been disallowed by the administrator + NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough. + Please fill both alias and name on the new property type! + There is a problem with read/write access to a specific file or folder + Error loading Partial View script (file: %0%) + Please enter a title + Please choose a type + You're about to make the picture larger than the original size. Are you sure that you want to proceed? + Startnode deleted, please contact your administrator + Please mark content before changing style + No active styles available + Please place cursor at the left of the two cells you wish to merge + You cannot split a cell that hasn't been merged. + This property is invalid + + + About + Action + Actions + Add + Alias + All + Are you sure? + Back + Back to overview + Border + by + Cancel + Cell margin + Choose + Clear + Close + Close Window + Comment + Confirm + Constrain + Constrain proportions + Content + Continue + Copy + Create + Database + Date + Default + Delete + Deleted + Deleting... + Design + Dictionary + Dimensions + Down + Download + Edit + Edited + Elements + Email + Error + Field + Find + First + Focal point + General + Groups + Group + Height + Help + Hide + History + Icon + Id + Import + Search only this folder + Info + Inner margin + Insert + Install + Invalid + Justify + Label + Language + Last + Layout + Links + Loading + Locked + Login + Log off + Logout + Macro + Mandatory + Message + Move + Name + New + Next + No + of + Off + OK + Open + Options + On + or + Order by + Password + Path + One moment please... + Previous + Properties + Rebuild + Email to receive form data + Recycle Bin + Your recycle bin is empty + Reload + Remaining + Remove + Rename + Renew + Required + Retrieve + Retry + Permissions + Scheduled Publishing + Search + Sorry, we can not find what you are looking for. + No items have been added + Server + Settings + Show + Show page on Send + Size + Sort + Status + Submit + Success + Type + Type to search... + under + Up + Update + Upgrade + Upload + Url + User + Username + Value + View + Welcome... + Width + Yes + Folder + Search results + Reorder + I am done reordering + Preview + Change password + to + List view + Saving... + current + Embed + selected + Other + Articles + Videos + Clear + Installing + + + Blue + + + Add group + Add property + Add editor + Add template + Add child node + Add child + Edit data type + Navigate sections + Shortcuts + show shortcuts + Toggle list view + Toggle allow as root + Comment/Uncomment lines + Remove line + Copy Lines Up + Copy Lines Down + Move Lines Up + Move Lines Down + General + Editor + Toggle allow culture variants + + + Background colour + Bold + Text colour + Font + Text + + + Page + + + The installer cannot connect to the database. + Could not save the web.config file. Please modify the connection string manually. + Your database has been found and is identified as + Database configuration + + install button to install the Umbraco %0% database + ]]> + + Next to proceed.]]> + Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

    +

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    +

    + Click the retry button when + done.
    + More information on editing web.config here.

    ]]>
    + + Please contact your ISP if necessary. + If you're installing on a local machine or server you might need information from your system administrator.]]> + + Press the upgrade button to upgrade your database to Umbraco %0%

    +

    + Don't worry - no content will be deleted and everything will continue working afterwards! +

    + ]]>
    + Press Next to + proceed. ]]> + next to continue the configuration wizard]]> + The Default users' password needs to be changed!]]> + The Default user has been disabled or has no access to Umbraco!

    No further actions needs to be taken. Click Next to proceed.]]> + The Default user's password has been successfully changed since the installation!

    No further actions needs to be taken. Click Next to proceed.]]> + The password is changed! + Get a great start, watch our introduction videos + By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this Umbraco distribution consists of two different licenses, the open source MIT license for the framework and the Umbraco freeware license that covers the UI. + Not installed yet. + Affected files and folders + More information on setting up permissions for Umbraco here + You need to grant ASP.NET modify permissions to the following files/folders + Your permission settings are almost perfect!

    + You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
    + How to Resolve + Click here to read the text version + video tutorial on setting up folder permissions for Umbraco or read the text version.]]> + Your permission settings might be an issue! +

    + You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
    + Your permission settings are not ready for Umbraco! +

    + In order to run Umbraco, you'll need to update your permission settings.]]>
    + Your permission settings are perfect!

    + You are ready to run Umbraco and install packages!]]>
    + Resolving folder issue + Follow this link for more information on problems with ASP.NET and creating folders + Setting up folder permissions + + I want to start from scratch + learn how) + You can still choose to install Runway later on. Please go to the Developer section and choose Packages. + ]]> + You've just set up a clean Umbraco platform. What do you want to do next? + Runway is installed + + This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules + ]]> + Only recommended for experienced users + I want to start with a simple website + + "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, + but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, + Runway offers an easy foundation based on best practices to get you started faster than ever. + If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. +

    + + Included with Runway: Home page, Getting Started page, Installing Modules page.
    + Optional Modules: Top Navigation, Sitemap, Contact, Gallery. +
    + ]]>
    + What is Runway + Step 1/5 Accept license + Step 2/5: Database configuration + Step 3/5: Validating File Permissions + Step 4/5: Check Umbraco security + Step 5/5: Umbraco is ready to get you started + Thank you for choosing Umbraco + Browse your new site +You installed Runway, so why not see how your new website looks.]]> + Further help and information +Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> + Umbraco %0% is installed and ready for use + /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> + started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, +you can find plenty of resources on our getting started pages.]]>
    + Launch Umbraco +To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]> + Connection to database failed. + Umbraco Version 3 + Umbraco Version 4 + Watch + Umbraco %0% for a fresh install or upgrading from version 3.0. +

    + Press "next" to start the wizard.]]>
    + + + Culture Code + Culture Name + + + You've been idle and logout will automatically occur in + Renew now to save your work + + + Happy super Sunday + Happy manic Monday + Happy tubular Tuesday + Happy wonderful Wednesday + Happy thunderous Thursday + Happy funky Friday + Happy Caturday + Log in below + Sign in with + Session timed out + © 2001 - %0%
    Umbraco.com

    ]]>
    + Forgotten password? + An email will be sent to the address specified with a link to reset your password + An email with password reset instructions will be sent to the specified address if it matched our records + Show password + Hide password + Return to login form + Please provide a new password + Your Password has been updated + The link you have clicked on is invalid or has expired + Umbraco: Reset Password + + + + + + + + + + + +
    + + + + + +
    + +
    + +
    +
    + + + + + + +
    +
    +
    + + + + +
    + + + + +
    +

    + Password reset requested +

    +

    + Your username to login to the Umbraco back-office is: %0% +

    +

    + + + + + + +
    + + Click this link to reset your password + +
    +

    +

    If you cannot click on the link, copy and paste this URL into your browser window:

    + + + + +
    + + %1% + +
    +

    +
    +
    +


    +
    +
    + + + ]]>
    + + + Dashboard + Sections + Content + + + Choose page above... + %0% has been copied to %1% + Select where the document %0% should be copied to below + %0% has been moved to %1% + Select where the document %0% should be moved to below + has been selected as the root of your new content, click 'ok' below. + No node selected yet, please select a node in the list above before clicking 'ok' + The current node is not allowed under the chosen node because of its type + The current node cannot be moved to one of its subpages + The current node cannot exist at the root + The action isn't allowed since you have insufficient permissions on 1 or more child documents. + Relate copied items to original + + + %0%]]> + Notification settings saved for + + The following languages have been modified %0% + + + + + + + + + + + +
    + + + + + +
    + +
    + +
    +
    + + + + + + +
    +
    +
    + + + + +
    + + + + +
    +

    + Hi %0%, +

    +

    + This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' +

    + + + + + + +
    + +
    + EDIT
    +
    +

    +

    Update summary:

    + %6% +

    +

    + Have a nice day!

    + Cheers from the Umbraco robot +

    +
    +
    +


    +
    +
    + + + ]]>
    + The following languages have been modified:

    + %0% + ]]>
    + [%0%] Notification about %1% performed on %2% + Notifications + + + Actions + Created + Create package + + button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. + ]]> + This will delete the package + Drop to upload + Include all child nodes + or click here to choose package file + Upload package + Install a local package by selecting it from your machine. Only install packages from sources you know and trust + Upload another package + Cancel and upload another package + I accept + terms of use + + Path to file + Absolute path to file (ie: /bin/umbraco.bin) + Installed + Installed packages + Install local + Finish + This package has no configuration view + No packages have been created yet + You don’t have any packages installed + 'Packages' icon in the top right of your screen]]> + Package Actions + Author URL + Package Content + Package Files + Icon URL + Install package + License + License URL + Package Properties + Search for packages + Results for + We couldn’t find anything for + Please try searching for another package or browse through the categories + Popular + New releases + has + karma points + Information + Owner + Contributors + Created + Current version + .NET version + Downloads + Likes + Compatibility + This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be guaranteed for versions reported below 100% + External sources + Author + Documentation + Package meta data + Package name + Package doesn't contain any items +
    + You can safely remove this from the system by clicking "uninstall package" below.]]>
    + Package options + Package readme + Package repository + Confirm package uninstall + Package was uninstalled + The package was successfully uninstalled + Uninstall package + + Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, + so uninstall with caution. If in doubt, contact the package author.]]> + Package version + Package already installed + This package cannot be installed, it requires a minimum Umbraco version of + Uninstalling... + Downloading... + Importing... + Installing... + Restarting, please wait... + All done, your browser will now refresh, please wait... + Please click 'Finish' to complete installation and reload the page. + Uploading package... + + + Paste with full formatting (Not recommended) + The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web. + Paste as raw text without any formatting at all + Paste, but remove formatting (Recommended) + + + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication + Error Page + Used when people are logged on, but do not have access + %0%]]> + %0% is now protected]]> + %0%]]> + Login Page + Choose the page that contains the login form + Remove protection... + %0%?]]> + Select the pages that contain login form and error messages + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members + + + + + + + + + Include unpublished subpages + Publishing in progress - please wait... + %0% out of %1% pages have been published... + %0% has been published + %0% and subpages have been published + Publish %0% and all its subpages + Publish to publish %0% and thereby making its content publicly available.

    + You can publish this page and all its subpages by checking Include unpublished subpages below. + ]]>
    + + + You have not configured any approved colours + + + You can only select items of type(s): %0% + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + + + Deleted item + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Trashed + + + enter external link + choose internal page + Caption + Link + Open in new window + enter the display caption + Enter the link + + + Reset crop + Save crop + Add new crop + Done + Undo edits + User defined + + + Select a version to compare with the current version + Current version + Red text will not be shown in the selected version. , green means added]]> + Document has been rolled back + This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view + Rollback to + Select version + View + + + Edit script file + + + Concierge + Content + Courier + Developer + Forms + Help + Umbraco Configuration Wizard + Media + Members + Newsletters + Packages + Settings + Statistics + Translation + Users + + + Tours + The best Umbraco video tutorials + Visit our.umbraco.com + Visit umbraco.tv + + + Default template + To import a document type, find the ".udt" file on your computer by clicking the "Browse" button and click "Import" (you'll be asked for confirmation on the next screen) + New Tab Title + Node type + Type + Stylesheet + Script + Tab + Tab Title + Tabs + Master Content Type enabled + This Content Type uses + as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself + No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. + Create matching template + Add icon + + + Sort order + Creation date + Sorting complete. + Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items + + + + Validation + Validation errors must be fixed before the item can be saved + Failed + Saved + Insufficient user permissions, could not complete the operation + Cancelled + Operation was cancelled by a 3rd party add-in + Publishing was cancelled by a 3rd party add-in + Property type already exists + Property type created + DataType: %1%]]> + Propertytype deleted + Document Type saved + Tab created + Tab deleted + Tab with id: %0% deleted + Stylesheet not saved + Stylesheet saved + Stylesheet saved without any errors + Datatype saved + Dictionary item saved + Publishing failed because the parent page isn't published + Content published + and visible on the website + Content saved + Remember to publish to make changes visible + Sent For Approval + Changes have been sent for approval + Media saved + Member group saved + Media saved without any errors + Member saved + Stylesheet Property Saved + Stylesheet saved + Template saved + Error saving user (check log) + User Saved + User type saved + User group saved + Cultures and hostnames saved + Error saving cultures and hostnames + File not saved + file could not be saved. Please check file permissions + File saved + File saved without any errors + Language saved + Media Type saved + Member Type saved + Member Group saved + Template not saved + Please make sure that you do not have 2 templates with the same alias + Template saved + Template saved without any errors! + Content unpublished + Partial view saved + Partial view saved without any errors! + Partial view not saved + An error occurred saving the file. + Permissions saved for + Deleted %0% user groups + %0% was deleted + Enabled %0% users + Disabled %0% users + %0% is now enabled + %0% is now disabled + User groups have been set + Unlocked %0% users + %0% is now unlocked + Member was exported to file + An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% + Document type was exported to file + An error occurred while exporting the document type + + + Add style + Edit style + Rich text editor styles + Define the styles that should be available in the rich text editor for this stylesheet + Edit stylesheet + Edit stylesheet property + The name displayed in the editor style selector + Preview + How the text will look like in the rich text editor. + Selector + Uses CSS syntax, e.g. "h1" or ".redHeader" + Styles + The CSS that should be applied in the rich text editor, e.g. "color:red;" + Code + Editor + + + Failed to delete template with ID %0% + Edit template + Sections + Insert content area + Insert content area placeholder + Insert + Choose what to insert into your template + Dictionary item + A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. + Macro + + A Macro is a configurable component which is great for + reusable parts of your design, where you need the option to provide parameters, + such as galleries, forms and lists. + + Value + Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. + Partial view + + A partial view is a separate template file which can be rendered inside another + template, it's great for reusing markup or for separating complex templates into separate files. + + Master template + No master + Render child template + @RenderBody() placeholder. + ]]> + Define a named section + @section { ... }. This can be rendered in a + specific area of the parent of this template, by using @RenderSection. + ]]> + Render a named section + @RenderSection(name) placeholder. + This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. + ]]> + Section Name + Section is mandatory + + If mandatory, the child template must contain a @section definition, otherwise an error is shown. + + Query builder + items returned, in + copy to clipboard + I want + all content + content of type "%0%" + from + my website + where + and + is + is not + before + before (including selected date) + after + after (including selected date) + equals + does not equal + contains + does not contain + greater than + greater than or equal to + less than + less than or equal to + Id + Name + Created Date + Last Updated Date + order by + ascending + descending + Template + + + Image + Macro + Choose type of content + Choose a layout + Add a row + Add content + Drop content + Settings applied + This content is not allowed here + This content is allowed here + Click to embed + Click to insert image + Image caption... + Write here... + Grid Layouts + Layouts are the overall work area for the grid editor, usually you only need one or two different layouts + Add Grid Layout + Adjust the layout by setting column widths and adding additional sections + Row configurations + Rows are predefined cells arranged horizontally + Add row configuration + Adjust the row by setting cell widths and adding additional cells + Columns + Total combined number of columns in the grid layout + Settings + Configure what settings editors can change + Styles + Configure what styling editors can change + Allow all editors + Allow all row configurations + Maximum items + Leave blank or set to 0 for unlimited + Set as default + Choose extra + Choose default + are added + Warning + You are deleting the row configuration + + Deleting a row configuration name will result in loss of data for any existing content that is based on this configuration. + + + + Compositions + Group + You have not added any groups + Add group + Inherited from + Add property + Required label + Enable list view + Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree + Allowed Templates + Choose which templates editors are allowed to use on content of this type + Allow as root + Allow editors to create content of this type in the root of the content tree. + Allowed child node types + Allow content of the specified types to be created underneath content of this type. + Choose child node + Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. + This content type is used in a composition, and therefore cannot be composed itself. + There are no content types available to use as a composition. + Removing a composition will delete all the associated property data. Once you save the document type there's no way back. + Create new + Use existing + Editor settings + Configuration + Yes, delete + was moved underneath + was copied underneath + Select the folder to move + Select the folder to copy + to in the tree structure below + All Document types + All Documents + All media items + using this document type will be deleted permanently, please confirm you want to delete these as well. + using this media type will be deleted permanently, please confirm you want to delete these as well. + using this member type will be deleted permanently, please confirm you want to delete these as well + and all documents using this type + and all media items using this type + and all members using this type + Member can edit + Allow this property value to be edited by the member on their profile page + Is sensitive data + Hide this property value from content editors that don't have access to view sensitive information + Show on member profile + Allow this property value to be displayed on the member profile page + tab has no sort order + Where is this composition used? + This composition is currently used in the composition of the following content types: + Allow varying by culture + Allow editors to create content of this type in different languages. + Allow varying by culture + Element type + Is an Element type + An Element type is meant to be used for instance in Nested Content, and not in the tree. + A document type cannot be changed to an Element type once it has been used to create one or more content items. + This is not applicable for an Element type + You have made changes to this property. Are you sure you want to discard them? + + + Add language + Mandatory language + Properties on this language have to be filled out before the node can be published. + Default language + An Umbraco site can only have one default language set. + Switching default language may result in default content missing. + Falls back to + No fall back language + To allow multi-lingual content to fall back to another language if not present in the requested language, select it here. + Fall back language + none + + + + Add parameter + Edit parameter + Enter macro name + Parameters + Define the parameters that should be available when using this macro. + Select partial view macro file + + + Building models + this can take a bit of time, don't worry + Models generated + Models could not be generated + Models generation has failed, see exception in U log + + + Add fallback field + Fallback field + Add default value + Default value + Fallback field + Default value + Casing + Encoding + Choose field + Convert line breaks + Yes, convert line breaks + Replaces line breaks with 'br' html tag + Custom Fields + Date only + Format and encoding + Format as date + Format the value as a date, or a date with time, according to the active culture + HTML encode + Will replace special characters by their HTML equivalent. + Will be inserted after the field value + Will be inserted before the field value + Lowercase + Modify output + None + Output sample + Insert after field + Insert before field + Recursive + Yes, make it recursive + Separator + Standard Fields + Uppercase + URL encode + Will format special characters in URLs + Will only be used when the field values above are empty + This field will only be used if the primary field is empty + Date and time + + + Translation details + Download XML DTD + Fields + Include subpages + + No translator users found. Please create a translator user before you start sending content to translation + The page '%0%' has been send to translation + Send the page '%0%' to translation + Total words + Translate to + Translation completed. + You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages. + Translation failed, the XML file might be corrupt + Translation options + Translator + Upload translation XML + + + Content + Content Templates + Media + Cache Browser + Recycle Bin + Created packages + Data Types + Dictionary + Installed packages + Install skin + Install starter kit + Languages + Install local package + Macros + Media Types + Members + Member Groups + Member Roles + Member Types + Document Types + Relation Types + Packages + Packages + Partial Views + Partial View Macro Files + Install from repository + Install Runway + Runway modules + Scripting Files + Scripts + Stylesheets + Templates + Log Viewer + Users + Settings + Templating + Third Party + + + New update ready + %0% is ready, click here for download + No connection to server + Error checking for update. Please review trace-stack for further information + + + Access + Based on the assigned groups and start nodes, the user has access to the following nodes + Assign access + Administrator + Category field + User created + Change Your Password + Change photo + New password + hasn't been locked out + The password hasn't been changed + Confirm new password + You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button + Content Channel + Create another user + Create new users to give them access to Umbraco. When a new user is created a password will be generated that you can share with the user. + Description field + Disable User + Document Type + Editor + Excerpt field + Failed login attempts + Go to user profile + Add groups to assign access and permissions + Invite another user + Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours. + Language + Set the language you will see in menus and dialogs + Last lockout date + Last login + Password last changed + Username + Media start node + Limit the media library to a specific start node + Media start nodes + Limit the media library to specific start nodes + Sections + Disable Umbraco Access + has not logged in yet + Old password + Password + Reset password + Your password has been changed! + Password changed + Please confirm the new password + Enter your new password + Your new password cannot be blank! + Current password + Invalid current password + There was a difference between the new password and the confirmed password. Please try again! + The confirmed password doesn't match the new password! + Replace child node permissions + You are currently modifying permissions for the pages: + Select pages to modify their permissions + Remove photo + Default permissions + Granular permissions + Set permissions for specific nodes + Profile + Search all children + Add sections to give users access + Select user groups + No start node selected + No start nodes selected + Content start node + Limit the content tree to a specific start node + Content start nodes + Limit the content tree to specific start nodes + User last updated + has been created + The new user has successfully been created. To log in to Umbraco use the password below. + User management + Name + User permissions + User group + has been invited + An invitation has been sent to the new user with details about how to log in to Umbraco. + Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar. + Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it. + Uploading a photo of yourself will make it easy for other users to recognize you. Click the circle above to upload your photo. + Writer + Change + Your profile + Your recent history + Session expires in + Invite user + Create user + Send invite + Back to users + Umbraco: Invitation + + + + + + + + + + + +
    + + + + + +
    + +
    + +
    +
    + + + + + + +
    +
    +
    + + + + +
    + + + + +
    +

    + Hi %0%, +

    +

    + You have been invited by %1% to the Umbraco Back Office. +

    +

    + Message from %1%: +
    + %2% +

    + + + + + + +
    + + + + + + +
    + + Click this link to accept the invite + +
    +
    +

    If you cannot click on the link, copy and paste this URL into your browser window:

    + + + + +
    + + %3% + +
    +

    +
    +
    +


    +
    +
    + + ]]>
    + Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? + All + Active + Disabled + Locked out + Invited + Inactive + Name (A-Z) + Name (Z-A) + Newest + Oldest + Last login + No user groups have been added + + + Validation + No validation + Validate as an email address + Validate as a number + Validate as a URL + ...or enter a custom validation + Field is mandatory + Enter a custom validation error message (optional) + Enter a regular expression + Enter a custom validation error message (optional) + You need to add at least + You can only have + items + items selected + Invalid date + Not a number + Invalid email + Custom validation + %1% more.]]> + %1% too many.]]> + + + + Value is set to the recommended value: '%0%'. + Value was set to '%1%' for XPath '%2%' in configuration file '%3%'. + Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'. + Found unexpected value '%0%' for '%2%' in configuration file '%3%'. + + Custom errors are set to '%0%'. + Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live. + Custom errors successfully set to '%0%'. + MacroErrors are set to '%0%'. + MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'. + MacroErrors are now set to '%0%'. + + Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'. + Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%). + Try Skip IIS Custom Errors successfully set to '%0%'. + + File does not exist: '%0%'. + '%0%' in config file '%1%'.]]> + There was an error, check log for full error: %0%. + Database - The database schema is correct for this version of Umbraco + %0% problems were detected with your database schema (Check the log for details) + Some errors were detected while validating the database schema against the current version of Umbraco. + Your website's certificate is valid. + Certificate validation error: '%0%' + Your website's SSL certificate has expired. + Your website's SSL certificate is expiring in %0% days. + Error pinging the URL %0% - '%1%' + You are currently %0% viewing the site using the HTTPS scheme. + The appSetting 'Umbraco.Core.UseHttps' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'. + The appSetting 'Umbraco.Core.UseHttps' is set to '%0%' in your web.config file, your cookies are %1% marked as secure. + Could not update the 'Umbraco.Core.UseHttps' setting in your web.config file. Error: %0% + + Enable HTTPS + Sets umbracoSSL setting to true in the appSettings of the web.config file. + The appSetting 'Umbraco.Core.UseHttps' is now set to 'true' in your web.config file, your cookies will be marked as secure. + Fix + Cannot fix a check with a value comparison type of 'ShouldNotEqual'. + Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value. + Value to fix check not provided. + Debug compilation mode is disabled. + Debug compilation mode is currently enabled. It is recommended to disable this setting before go live. + Debug compilation mode successfully disabled. + Trace mode is disabled. + Trace mode is currently enabled. It is recommended to disable this setting before go live. + Trace mode successfully disabled. + All folders have the correct permissions set. + + %0%.]]> + %0%. If they aren't being written to no action need be taken.]]> + All files have the correct permissions set. + + %0%.]]> + %0%. If they aren't being written to no action need be taken.]]> + X-Frame-Options used to control whether a site can be IFRAMEd by another was found.]]> + X-Frame-Options used to control whether a site can be IFRAMEd by another was not found.]]> + Set Header in Config + Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMEd by other websites. + A setting to create a header preventing IFRAMEing of the site by other websites has been added to your web.config file. + Could not update web.config file. Error: %0% + X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was found.]]> + X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was not found.]]> + Adds a value to the httpProtocol/customHeaders section of web.config to protect against MIME sniffing vulnerabilities. + A setting to create a header protecting against MIME sniffing vulnerabilities has been added to your web.config file. + Strict-Transport-Security, also known as the HSTS-header, was found.]]> + Strict-Transport-Security was not found.]]> + Adds the header 'Strict-Transport-Security' with the value 'max-age=10886400' to the httpProtocol/customHeaders section of web.config. Use this fix only if you will have your domains running with https for the next 18 weeks (minimum). + The HSTS header has been added to your web.config file. + X-XSS-Protection was found.]]> + X-XSS-Protection was not found.]]> + Adds the header 'X-XSS-Protection' with the value '1; mode=block' to the httpProtocol/customHeaders section of web.config. + The X-XSS-Protection header has been added to your web.config file. + + %0%.]]> + No headers revealing information about the website technology were found. + In the Web.config file, system.net/mailsettings could not be found. + In the Web.config file system.net/mailsettings section, the host is not configured. + SMTP settings are configured correctly and the service is operating as expected. + The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct. + %0%.]]> + %0%.]]> +

    Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

    %2%]]>
    + Umbraco Health Check Status: %0% + Check All Groups + Check group + + The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. + You can add your own health checks, have a look at the documentation for more information about custom health checks.

    + ]]> +
    + + + Disable URL tracker + Enable URL tracker + Original URL + Redirected To + Redirect Url Management + The following URLs redirect to this content item: + No redirects have been made + When a published page gets renamed or moved a redirect will automatically be made to the new page. + Are you sure you want to remove the redirect from '%0%' to '%1%'? + Redirect URL removed. + Error removing redirect URL. + This will remove the redirect + Are you sure you want to disable the URL tracker? + URL tracker has now been disabled. + Error disabling the URL tracker, more information can be found in your log file. + URL tracker has now been enabled. + Error enabling the URL tracker, more information can be found in your log file. + + + No Dictionary items to choose from + + + %0% characters left.]]> + %1% too many.]]> + + + Trashed content with Id: {0} related to original parent content with Id: {1} + Trashed media with Id: {0} related to original parent media item with Id: {1} + Cannot automatically restore this item + There is no location where this item can be automatically restored. You can move the item manually using the tree below. + was restored under + + + Direction + Parent to child + Bidirectional + Parent + Child + Count + Relations + Created + Comment + Name + No relations for this relation type. + Relation Type + Relations + + + Getting Started + Redirect URL Management + Content + Welcome + Examine Management + Published Status + Models Builder + Health Check + Profiling + Getting Started + Install Umbraco Forms + + + Go back + Active layout: + Jump to + group + passed + warning + failed + suggestion + Check passed + Check failed + Open backoffice search + Open/Close backoffice help + Open/Close your profile options + Open context menu for + Current language + Switch language to + Create new folder + Partial View + Partial View Macro + Member + Data type + Search the redirect dashboard + Search the user group section + Search the users section + Create item + Create + Edit + Name + + + References + This Data Type has no references. + Used in Document Types + No references to Document Types. + Used in Media Types + No references to Media Types. + Used in Member Types + No references to Member Types. + Used by + Used in Documents + Used in Members + Used in Media + + + Log Levels + Saved Searches + Total Items + Timestamp + Level + Machine + Message + Exception + Properties + Search With Google + Search this message with Google + Search With Bing + Search this message with Bing + Search Our Umbraco + Search this message on Our Umbraco forums and docs + Search Our Umbraco with Google + Search Our Umbraco forums using Google + Search Umbraco Source + Search within Umbraco source code on Github + Search Umbraco Issues + Search Umbraco Issues on Github + Delete this search + Find Logs with Request ID + Find Logs with Namespace + Find Logs with Machine Name + Open + + + Copy %0% + %0% from %1% + Remove all items + + + Open Property Actions + + + Wait + Refresh status + Memory Cache + + + + Reload + Database Cache + + Rebuilding can be expensive. + Use it when reloading is not enough, and you think that the database cache has not been + properly generated—which would indicate some critical Umbraco issue. + ]]> + + Rebuild + Internals + + not need to use it. + ]]> + + Collect + Published Cache Status + Caches + + + Performance profiling + + + Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +

    +

    + If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +

    +

    + If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +

    + ]]> +
    + Activate the profiler by default + Friendly reminder + + + You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +

    + ]]> +
    + + + Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +

    +

    + Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +

    + ]]> +
    + + + 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

    + ]]> +
    + To get you started + + + Start here + This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section + Find out more + + in the Documentation section of Our Umbraco + ]]> + + + Community Forum + ]]> + + + tutorial videos (some are free, some require a subscription) + ]]> + + + productivity boosting tools and commercial support + ]]> + + + training and certification opportunities + ]]> + + + + Welcome to The Friendly CMS + Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible. + + + Umbraco Forms + Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + +
    diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index db84d945b0..482973f5e7 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -36,6 +36,8 @@ Choose where to copy Choose where to move to in the tree structure below + Choose where to copy the selected item(s) + Choose where to move the selected item(s) was moved to was copied to was deleted @@ -137,7 +139,7 @@ Send for approval Save list view Schedule - Preview + Save and preview Preview is disabled because there's no template assigned Choose style Show styles @@ -269,6 +271,8 @@ Last edited Date/time this document was edited Remove file(s) + Click here to remove the image from the media item + Click here to remove the file from the media item Link to document Member of group(s) Not a member of group(s) @@ -282,6 +286,9 @@ No content types are configured for this property. Add element type Select element type + Select the group whose properties should be displayed. If left blank, the first group on the element type will be used. + Enter an angular expression to evaluate against each item for its name. Use + to display the item index Add another text box Remove this text box Content root @@ -549,10 +556,6 @@ #value or ?key=value Enter alias... Generating alias... - Create item - Create - Edit - Name Create custom list view @@ -673,7 +676,7 @@ Icon Id Import - Include subfolders in search + Search only this folder Info Inner margin Insert @@ -735,6 +738,7 @@ Sort Status Submit + Success Type Type to search... under @@ -762,6 +766,11 @@ current Embed selected + Other + Articles + Videos + Clear + Installing Blue @@ -1307,11 +1316,12 @@ To manage your website, simply open the Umbraco back office and start adding con Enter the link - Reset crop + Reset crop Save crop Add new crop - Done - Undo edits + Done + Undo edits + User defined Select a version to compare with the current version @@ -1337,7 +1347,10 @@ To manage your website, simply open the Umbraco back office and start adding con Users + Tours The best Umbraco video tutorials + Visit our.umbraco.com + Visit umbraco.tv Default template @@ -1409,6 +1422,8 @@ To manage your website, simply open the Umbraco back office and start adding con User Saved User type saved User group saved + Cultures and hostnames saved + Error saving cultures and hostnames File not saved file could not be saved. Please check file permissions File saved @@ -1586,6 +1601,11 @@ To manage your website, simply open the Umbraco back office and start adding con Choose extra Choose default are added + Warning + You are deleting the row configuration + + Deleting a row configuration name will result in loss of data for any existing content that is based on this configuration. + Compositions @@ -1640,9 +1660,10 @@ To manage your website, simply open the Umbraco back office and start adding con Allow editors to create content of this type in different languages. Allow varying by culture Element type - Is an Element type - An Element type is meant to be used for instance in Nested Content, and not in the tree. - This is not applicable for an Element type + Is an element type + An element type is meant to be used for instance in Nested Content, and not in the tree. + A document type cannot be changed to an element type once it has been used to create one or more content items. + This is not applicable for an element type You have made changes to this property. Are you sure you want to discard them? @@ -1833,6 +1854,7 @@ To manage your website, simply open the Umbraco back office and start adding con Password Reset password Your password has been changed! + Password changed Please confirm the new password Enter your new password Your new password cannot be blank! @@ -1985,6 +2007,7 @@ To manage your website, simply open the Umbraco back office and start adding con Newest Oldest Last login + No user groups have been added Validation @@ -2112,6 +2135,14 @@ To manage your website, simply open the Umbraco back office and start adding con %0%.]]>

    Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

    %2%]]>
    Umbraco Health Check Status: %0% + Check All Groups + Check group + + The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. + You can add your own health checks, have a look at the documentation for more information about custom health checks.

    + ]]> +
    Disable URL tracker @@ -2197,6 +2228,13 @@ To manage your website, simply open the Umbraco back office and start adding con Partial View Macro Member Data type + Search the redirect dashboard + Search the user group section + Search the users section + Create item + Create + Edit + Name References @@ -2359,4 +2397,12 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> + + Welcome to The Friendly CMS + Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible. + + + Umbraco Forms + Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml index 371e6de7a2..c35c84ebdc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml @@ -124,7 +124,7 @@ Guardar y publicar Guardar y enviar para aprobación Guardar vista de lista - Previsualizar + Previsualizar La previsualización está deshabilitada porque no hay ninguna plantilla asignada Elegir estilo Mostrar estilos diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml index 18a5fe9c5a..b5d1c8feb2 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml @@ -124,7 +124,7 @@ Sauver et planifier Sauver et envoyer pour approbation Sauver la mise en page de la liste - Prévisualiser + Prévisualiser La prévisualisation est désactivée car aucun modèle n'a été assigné. Choisir un style Afficher les styles @@ -1180,7 +1180,7 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Type de propriété créé Type de données : %1%]]> Type de propriété supprimé - Type de documet sauvegardé + Type de document sauvegardé Onglet créé Onglet supprimé Onglet avec l'ID : %0% supprimé diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml index 9b816b4682..e100cb4301 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml @@ -68,7 +68,7 @@ שמור שמור ופרסם שמור ושלח לאישור - תצוגה מקדימה + תצוגה מקדימה בחר עיצוב הצג עיצוב הוספת טבלה diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml index d2a1d75ee7..4866fff843 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml @@ -69,7 +69,7 @@ Salva Salva e pubblica Salva e invia per approvazione - Anteprima + Anteprima Scegli lo stile Mostra gli stili diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml index dd68ed45e5..21559f915a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml @@ -85,7 +85,7 @@ 保存及び公開 保存して承認に送る リスト ビューの保存 - プレビュー + プレビュー テンプレートが指定されていないのでプレビューは無効になっています スタイルの選択 スタイルの表示 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml index 12f7c9ed50..a87f6f1410 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml @@ -67,7 +67,7 @@ 저장 저장 후 발행 저장 후 승인을 위해 전송 - 미리보기 + 미리보기 스타일 선택 스타일 보기 테이블 삽입 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index 78b0ebfb5a..731aea4a20 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -83,7 +83,7 @@ Lagre og publiser Lagre og planlegge Lagre og send til publisering - Forhåndsvis + Forhåndsvis Forhåndsvisning er deaktivert siden det ikke er angitt noen mal Velg formattering Vis stiler diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml index 90f06fe7a6..601626d896 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml @@ -90,7 +90,7 @@ Opslaan en publiceren Opslaan en verzenden voor goedkeuring Sla list view op - voorbeeld bekijken + voorbeeld bekijken Voorbeeld bekijken is uitgeschakeld omdat er geen template is geselecteerd Stijl kiezen Stijlen tonen diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml index de3e988118..fd806041c5 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml @@ -121,7 +121,7 @@ Zapisz i publikuj Zapisz i wyślij do zaakceptowania Zapisz widok listy - Podgląd + Podgląd Podgląd jest wyłączony, ponieważ żaden szablon nie został przydzielony Wybierz styl Pokaż style diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml index e7afd04acd..9fd4696c28 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml @@ -67,7 +67,7 @@ Salvar Salvar e publicar Salvar e mandar para aprovação - Prévia + Prévia Escolha estilo Mostrar estilos Inserir tabela diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml index c52e17e829..7a3e099262 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml @@ -155,7 +155,7 @@ Направить на публикацию Сохранить список Выбрать - Предварительный просмотр + Предварительный просмотр Предварительный просмотр запрещен, так как документу не сопоставлен шаблон Другие действия Выбрать стиль diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml index 31846e9e07..e7e7abe2cd 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml @@ -4,6 +4,9 @@ The Umbraco community https://our.umbraco.com/documentation/Extending-Umbraco/Language-Files + + Innehåll + Hantera domännamn Hantera versioner @@ -11,6 +14,7 @@ Ändra dokumenttyp Kopiera Skapa + Skapa grupp Skapa paket Skapa innehållsmall Standardvärde @@ -38,6 +42,12 @@ Avpublicera Uppdatera + + Innehåll + Administration + Struktur + Övrigt + Lägg till nytt domännamn Domännamn @@ -98,6 +108,7 @@ En innehållsmall är fördefinierat innehåll som en redaktör kan välja att använda som grund för att skapa nytt innehåll + Rensa urval Fetstil Minska indrag Infoga formulärfält @@ -121,8 +132,9 @@ Spara och publicera Spara och schemalägg Spara och skicka för godkännande + Schemaläggning Välj - Förhandsgranska + Förhandsgranska Förhandsgranskning är avstängt på grund av att det inte finns någon mall tilldelad Ångra Välj stil @@ -295,6 +307,12 @@ Webbplatsen har indexerats Cache för webbplatsen har uppdaterats. Allt publicerat innehåll är nu uppdaterat. Innehåll som inte har publicerats är fortfarande opublicerat. Webbplatsens cache kommer att uppdateras. Allt innehåll som är publicerat kommer att uppdateras. Innehåll som inte är publicerat kommer att förbli opublicerat. + Välj startnod för innehåll + Välj ikon + Välj startnod för media + Välj användargrupper + Välj sektioner + Välj användare Antal kolumner Antal rader Klicka på förhandsgranskningsbilden för att se bilden i full storlek @@ -425,6 +443,7 @@ Egenskaper E-postadress för formulärsdata Papperskorg + Din papperskorg är tom Återstående Ta bort Döp om @@ -568,6 +587,7 @@ Klicka för att ladda upp eller klicka här för att välja filer + Drag och släpp dina filer i denna yta Välj sida ovan... @@ -601,6 +621,7 @@ Paketalternativ Paket läsmig Paketvalv + Sök efter paket Bekräfta avinstallation Paketet har avinstallerats Paketet har avinstallerats utan problem @@ -621,6 +642,7 @@ Fyll i ditt lösenord Skriv för att söka... Fyll i ditt lösenord + Välj alias... Skriv för att lägga till taggar (och tryck enter efter varje tagg)... @@ -691,6 +713,7 @@ Media Medlemmar Nyhetsbrev + Paket Inställningar Statistik Översättning @@ -786,7 +809,7 @@ Sidmall - Image + Bild Macro Lägg till Choose layout @@ -866,6 +889,7 @@ Cacha webbläsare + Innehåll Papperskorg Skapade paket Datatyper @@ -902,24 +926,52 @@ Fel vid kontroll av uppdatering. Se trace-stack för mer information. + Åtkomst + Baserat på tilldelade grupper och startnod så har användaren åtkomst till följande noder + Tilldela åtkomst Administratör Kategorifält + Användare skapad Ändra lösenord - Du kan byta ditt lösenord för Umbraco Back Office genom att fylla i nedanstående formulär och klicka på knappen "Ändra lösenord". + Ändra bild Bekräfta det nya lösenordet + Du kan byta ditt lösenord för Umbraco Back Office genom att fylla i nedanstående formulär och klicka på knappen "Ändra lösenord". Innehållskanal + Skapa en till användare + Skapa nya användare för att ge dom åtkomst till Umbraco. När en ny användare skapas kommer ett lösenord genereras som du kan dela med användaren. Skapa användare + Ta bort användare User + Är du säker på att du vill ta bort användarens konto? Fält för beskrivning Avaktivera användare Dokumenttyp Redaktör Fält för utdrag + Misslyckade inloggningsförsök + Gå till användarens profil + Lägg till grupper för att tilldela åtkomst och rättigheter + Bjud in + Bjud in en till användare + Bjud in nya användare för att ge dom åtkomst till Umbraco. Ett e-postmeddelande kommer skikcas till användaren med information om hur man loggar in i Umbraco. Inbjudningar är giltiga i 72 timmar. Språk + Välj de språk som kommer visas i meny och dialoger + Senast utlåst + Senast inloggad + Lösenordet ändrades Login Startnod i mediabiblioteket + Begränsa media sectionen till en specifik startnod + Media startnoder + Begränsa media sectionen till specifika startnoder Sektioner Byt ditt lösenord + har inte blivit utlåst + har inte loggat in ännu Inaktivera tillgång till Umbraco + Lösenordet har inte ändrats + Ingen startnod vald + Inga startnoder valda + Gammalt lösenord Lösenord Ditt lösenord är nu ändrat! Vänligen bekräfta ditt nya lösenord @@ -933,17 +985,36 @@ Du redigerar nu rättigheterna för sidorna: Välj de sidor vars rättigheter du vill redigera Återställ lösenord + Ta bort bild + Standard rättigheter + Granulära rättigheter Sök igenom alla undernoder + Sätt rättigheter för specifika noder + Profil Sessionen går ut + Välj sektioner för användaråtkomst + Alla + Aktiv + Utlåst + Inbjuden + Inaktiv Namn (A-Z) Namn (Z-A) - Startnod i innehåll + Startnod för innehåll + Begränsa sidträdet till en specifik startnod + Startnoder för innehåll + Begränsa sidträdet till specifika startnoder + Användare ändrad Användarens namn + Användarhantering Användarrättigheter Användartyp Användartyper Skribent Din nuvarande historik Din profil + Nyast + Äldst + Senaste login diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml index 02069a53dc..f998080b06 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml @@ -85,7 +85,7 @@ Kaydet Kaydet ve Yayınla Kaydet ve Onay için gönder - Önizle + Önizle Önizleme kapalı, Atanmış şablon yok Stili seçin Stilleri Göster diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml index a8dd8a8bef..5210b46bcc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml @@ -90,7 +90,7 @@ 保存并发布 保存并提交审核 保存列表视图 - 预览 + 预览 因未设置模板无法预览 选择样式 显示样式 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml index bac817ad20..320c3f63d8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml @@ -87,7 +87,7 @@ 保存並發佈 保存並提交審核 保存清單檢視 - 預覽 + 預覽 因未設置範本無法預覽 選擇樣式 顯示樣式 diff --git a/src/Umbraco.Web.UI/Umbraco/js/main.controller.js b/src/Umbraco.Web.UI/Umbraco/js/main.controller.js index 93870f8a56..883907d1dc 100644 --- a/src/Umbraco.Web.UI/Umbraco/js/main.controller.js +++ b/src/Umbraco.Web.UI/Umbraco/js/main.controller.js @@ -67,13 +67,18 @@ function MainController($scope, $location, appState, treeService, notificationsS }; var evts = []; - + //when a user logs out or timesout evts.push(eventsService.on("app.notAuthenticated", function (evt, data) { $scope.authenticated = null; $scope.user = null; const isTimedOut = data && data.isTimedOut ? true : false; $scope.showLoginScreen(isTimedOut); + + // Remove the localstorage items for tours shown + // Means that when next logged in they can be re-shown if not already dismissed etc + localStorageService.remove("emailMarketingTourShown"); + localStorageService.remove("introTourShown"); })); evts.push(eventsService.on("app.userRefresh", function(evt) { diff --git a/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js b/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js index b585d22e9f..281be2d331 100644 --- a/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js +++ b/src/Umbraco.Web.UI/Umbraco/js/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) { @@ -510,6 +535,14 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if (!event) { return; } + closeTree(); + }; + + $scope.onOutsideClick = function() { + closeTree(); + }; + + function closeTree() { if (!appState.getGlobalState("touchDevice")) { treeActive = false; $timeout(function () { @@ -518,7 +551,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } }, 300); } - }; + } $scope.toggleLanguageSelector = function () { $scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen; 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.UI/Views/Partials/Grid/Editors/Media.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml index ea79ce41ad..0ed54ab958 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml @@ -2,20 +2,24 @@ @using Umbraco.Web.Templates @if (Model.value != null) -{ +{ var url = Model.value.image; if(Model.editor.config != null && Model.editor.config.size != null){ - url += "?width=" + Model.editor.config.size.width; - url += "&height=" + Model.editor.config.size.height; - - if(Model.value.focalPoint != null){ - url += "¢er=" + Model.value.focalPoint.top +"," + Model.value.focalPoint.left; - url += "&mode=crop"; - } + url = ImageCropperTemplateCoreExtensions.GetCropUrl(url, Umbraco.Web.Composing.Current.Factory.GetInstance(), + width: Model.editor.config.size.width, + height: Model.editor.config.size.height, + cropDataSet: Model.value.focalPoint == null ? null : new Umbraco.Core.PropertyEditors.ValueConverters.ImageCropperValue + { + FocalPoint = new Umbraco.Core.PropertyEditors.ValueConverters.ImageCropperValue.ImageCropperFocalPoint + { + Top = Model.value.focalPoint.top, + Left = Model.value.focalPoint.left + } + }); } var altText = Model.value.altText ?? Model.value.caption ?? string.Empty; - + @altText if (Model.value.caption != null) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml index 1001d10ec6..0e47b5bd78 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml @@ -5,7 +5,7 @@ @if (Model.editor.config.markup != null) { string markup = Model.editor.config.markup.ToString(); - markup = markup.Replace("#value#", Html.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode((string)Model.value.ToString())).ToString()); + markup = markup.Replace("#value#", Html.ReplaceLineBreaks(HttpUtility.HtmlEncode((string)Model.value.ToString())).ToString()); if (Model.editor.config.style != null) { diff --git a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json index e300e6562e..7b3f2a2184 100644 --- a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json +++ b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json @@ -1,4 +1,22 @@ [ + { + "name": "Email Marketing", + "alias": "umbEmailMarketing", + "group": "Email Marketing", + "groupOrder": 10, + "hidden": true, + "requiredSections": [ + "content" + ], + "steps": [ + { + "title": "Do you want to stay updated on everything Umbraco?", + "content": "

    Thank you for using Umbraco! Would you like to stay up-to-date with Umbraco product updates, security advisories, community news and special offers? Sign up for our newsletter and never miss out on the latest Umbraco news.

    By signing up, you agree that we can use your info according to our privacy policy.

    ", + "view": "emails", + "type": "promotion" + } + ] + }, { "name": "Introduction", "alias": "umbIntroIntroduction", diff --git a/src/Umbraco.Web.UI/config/imageprocessor/processing.config b/src/Umbraco.Web.UI/config/imageprocessor/processing.config index 34c9fd96c4..dddcddb0bd 100644 --- a/src/Umbraco.Web.UI/config/imageprocessor/processing.config +++ b/src/Umbraco.Web.UI/config/imageprocessor/processing.config @@ -1,38 +1,10 @@  - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -63,4 +35,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/serilog.Release.config b/src/Umbraco.Web.UI/config/serilog.Release.config index 4d9151b2fd..e3cf52b3c5 100644 --- a/src/Umbraco.Web.UI/config/serilog.Release.config +++ b/src/Umbraco.Web.UI/config/serilog.Release.config @@ -17,7 +17,7 @@ - + diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs deleted file mode 100644 index 51653d54ca..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.UI.Config.Splashes -{ - public partial class NoNodes : System.Web.UI.Page - { - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - var store = Current.UmbracoContext.Content; - if (store.HasContent()) - { - //if there is actually content, go to the root - Response.Redirect("~/"); - } - } - - } -} diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs deleted file mode 100644 index 97c97a3e14..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Config.Splashes { - - - public partial class NoNodes { - - /// - /// Form1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlForm Form1; - } -} diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml b/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml new file mode 100644 index 0000000000..4193e58873 --- /dev/null +++ b/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml @@ -0,0 +1,49 @@ +@inherits System.Web.Mvc.WebViewPage + + + + + + + + Umbraco: No Published Content + + + + + +
    +
    +
    + + +

    Welcome to your Umbraco installation

    +

    You're seeing this wonderful page because your website doesn't contain any published content yet.

    + + + +
    +
    +

    Easy start with Umbraco.tv

    +

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    + + Umbraco.tv → +
    + +
    +

    Be a part of the community

    +

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

    + + our.Umbraco → +
    +
    + +
    +
    + +
    + + + diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx deleted file mode 100644 index b0aa3d7837..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ /dev/null @@ -1,63 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Config.Splashes.NoNodes" CodeBehind="NoNodes.aspx.cs" %> -<%@ Import Namespace="Umbraco.Core" %> -<%@ Import Namespace="Umbraco.Core.Composing" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> -<%@ Import Namespace="Umbraco.Core.IO" %> - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -

    Welcome to your Umbraco installation

    -

    You're seeing this wonderful page because your website doesn't contain any published content yet.

    - - - - -
    -
    -

    Easy start with Umbraco.tv

    -

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    - - Umbraco.tv → -
    - -
    -

    Be a part of the community

    -

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

    - - our.Umbraco → -
    -
    - -
    -
    - -
    - - - - - diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 698c771db9..5814a82095 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -241,6 +241,14 @@ By default you can call any content Id in the url and show the content with that id, for example: http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting this setting to true stops that behavior + @disableRedirectUrlTracking + When the URL changes for content, redirects are automatically created for redirect handling within the + request pipeline. Setting this setting to true stops the automatic creation of redirects. Note that this + does not stop the request pipeline from handling any previously created redirects. + @urlProviderMode + By default Umbraco automatically figures out if internal URLs should be rendered as relative or absolute, + depending on the current request and the configured domains. By setting this setting to "Relative" or + "Absolute" you can force Umbraco to always render URLs as either relative or absolute. @umbracoApplicationUrl The url of the Umbraco application. By default, Umbraco will figure it out from the first request. Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config index 78fddac67e..c741f4ca5f 100644 --- a/src/Umbraco.Web.UI/web.Template.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.Debug.config @@ -1,7 +1,7 @@ - + + + + + + diff --git a/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..0fa911da00 --- /dev/null +++ b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.AspNetCore.Builder; +using SixLabors.ImageSharp.Web.DependencyInjection; + +namespace Umbraco.Web.Website.AspNetCore +{ + public static class UmbracoBackOfficeApplicationBuilderExtensions + { + public static IApplicationBuilder UseUmbracoWebsite(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + + // Important we handle image manipulations before the static files, otherwise the querystring is just ignored. + app.UseImageSharp(); + app.UseStaticFiles(); + + return app; + } + } +} diff --git a/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteServiceCollectionExtensions.cs b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteServiceCollectionExtensions.cs new file mode 100644 index 0000000000..5fb2825269 --- /dev/null +++ b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteServiceCollectionExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using SixLabors.ImageSharp.Web.DependencyInjection; + +namespace Umbraco.Web.Website.AspNetCore +{ + public static class UmbracoBackOfficeServiceCollectionExtensions + { + public static IServiceCollection AddUmbracoWebsite(this IServiceCollection services) + { + services.AddImageSharp(); + + + return services; + } + + } +} diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj new file mode 100644 index 0000000000..3a0e9e86e7 --- /dev/null +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + Library + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web/AppBuilderExtensions.cs b/src/Umbraco.Web/AppBuilderExtensions.cs index f740d57b79..e9833e4379 100644 --- a/src/Umbraco.Web/AppBuilderExtensions.cs +++ b/src/Umbraco.Web/AppBuilderExtensions.cs @@ -1,16 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using System.Web; using Microsoft.AspNet.SignalR; using Microsoft.Owin.Logging; using Owin; using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; +using Umbraco.Core.IO; using Umbraco.Web.Composing; -using Umbraco.Web.SignalR; +using Umbraco.Web.Logging; namespace Umbraco.Web { @@ -48,10 +43,10 @@ namespace Umbraco.Web /// Configures SignalR. ///
/// The app builder. - /// - public static IAppBuilder UseSignalR(this IAppBuilder app, IGlobalSettings globalSettings) + /// + public static IAppBuilder UseSignalR(this IAppBuilder app, IIOHelper ioHelper) { - var umbracoPath = globalSettings.GetUmbracoMvcArea(Current.IOHelper); + var umbracoPath = ioHelper.GetUmbracoMvcArea(); var signalrPath = HttpRuntime.AppDomainAppVirtualPath + umbracoPath + "/BackOffice/signalr"; return app.MapSignalR(signalrPath, new HubConfiguration { EnableDetailedErrors = true }); } diff --git a/src/Umbraco.Web/Net/AspNetBackOfficeInfo.cs b/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs similarity index 89% rename from src/Umbraco.Web/Net/AspNetBackOfficeInfo.cs rename to src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs index cabe56062c..1e60fedeea 100644 --- a/src/Umbraco.Web/Net/AspNetBackOfficeInfo.cs +++ b/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs @@ -11,15 +11,15 @@ namespace Umbraco.Web { private readonly IGlobalSettings _globalSettings; private readonly IIOHelper _ioHelper; - private readonly IUmbracoSettingsSection _settings; private readonly ILogger _logger; + private readonly IWebRoutingSettings _webRoutingSettings; - public AspNetBackOfficeInfo(IGlobalSettings globalSettings, IIOHelper ioHelper, IUmbracoSettingsSection settings, ILogger logger) + public AspNetBackOfficeInfo(IGlobalSettings globalSettings, IIOHelper ioHelper, ILogger logger, IWebRoutingSettings webRoutingSettings) { _globalSettings = globalSettings; _ioHelper = ioHelper; - _settings = settings; _logger = logger; + _webRoutingSettings = webRoutingSettings; } /// @@ -27,7 +27,7 @@ namespace Umbraco.Web private string GetAbsoluteUrlFromConfig() { - var url = _settings.WebRouting.UmbracoApplicationUrl; + var url = _webRoutingSettings.UmbracoApplicationUrl; if (url.IsNullOrWhiteSpace() == false) { var umbracoApplicationUrl = url.TrimEnd('/'); diff --git a/src/Umbraco.Web/AspNet/AspNetCookieManager.cs b/src/Umbraco.Web/AspNet/AspNetCookieManager.cs new file mode 100644 index 0000000000..d8fcefa0e1 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetCookieManager.cs @@ -0,0 +1,35 @@ +using System.Web; +using Umbraco.Core.Cookie; + +namespace Umbraco.Web +{ + public class AspNetCookieManager : ICookieManager + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCookieManager(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public void ExpireCookie(string cookieName) + { + _httpContextAccessor.HttpContext?.ExpireCookie(cookieName); + } + + public string GetCookieValue(string cookieName) + { + return _httpContextAccessor.HttpContext?.Request.GetCookieValue(cookieName); + } + + public void SetCookieValue(string cookieName, string value) + { + _httpContextAccessor.HttpContext?.Response.Cookies.Set(new HttpCookie(cookieName, value)); + } + + public bool HasCookie(string cookieName) + { + return !(GetCookieValue(cookieName) is null); + } + } +} diff --git a/src/Umbraco.Web/Hosting/AspNetHostingEnvironment.cs b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs similarity index 67% rename from src/Umbraco.Web/Hosting/AspNetHostingEnvironment.cs rename to src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs index 2da1c19948..aa07894aa9 100644 --- a/src/Umbraco.Web/Hosting/AspNetHostingEnvironment.cs +++ b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs @@ -1,15 +1,21 @@ using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Web; using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.IO; +using IRegisteredObject = Umbraco.Core.IRegisteredObject; namespace Umbraco.Web.Hosting { public class AspNetHostingEnvironment : IHostingEnvironment { + private readonly ConcurrentDictionary _registeredObjects = + new ConcurrentDictionary(); private readonly IHostingSettings _hostingSettings; private string _localTempPath; @@ -20,11 +26,9 @@ namespace Umbraco.Web.Hosting ApplicationId = HostingEnvironment.ApplicationID; ApplicationPhysicalPath = HostingEnvironment.ApplicationPhysicalPath; ApplicationVirtualPath = HostingEnvironment.ApplicationVirtualPath; - CurrentDomainId = AppDomain.CurrentDomain.Id; + IISVersion = HttpRuntime.IISVersion; } - public int CurrentDomainId { get; } - public string SiteName { get; } public string ApplicationId { get; } public string ApplicationPhysicalPath { get; } @@ -33,17 +37,32 @@ namespace Umbraco.Web.Hosting public bool IsDebugMode => HttpContext.Current?.IsDebuggingEnabled ?? _hostingSettings.DebugMode; /// public bool IsHosted => (HttpContext.Current != null || HostingEnvironment.IsHosted); + + public Version IISVersion { get; } + public string MapPath(string path) { return HostingEnvironment.MapPath(path); } public string ToAbsolute(string virtualPath, string root) => VirtualPathUtility.ToAbsolute(virtualPath, root); - public void LazyRestartApplication() + public void RegisterObject(IRegisteredObject registeredObject) { - HttpRuntime.UnloadAppDomain(); + var wrapped = new RegisteredObjectWrapper(registeredObject); + if (!_registeredObjects.TryAdd(registeredObject, wrapped)) + { + throw new InvalidOperationException("Could not register object"); + } + HostingEnvironment.RegisterObject(wrapped); } + public void UnregisterObject(IRegisteredObject registeredObject) + { + if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) + { + HostingEnvironment.UnregisterObject(wrapped); + } + } public string LocalTempPath { @@ -82,6 +101,21 @@ namespace Umbraco.Web.Hosting } } } + private class RegisteredObjectWrapper : System.Web.Hosting.IRegisteredObject + { + private readonly IRegisteredObject _inner; + public RegisteredObjectWrapper(IRegisteredObject inner) + { + _inner = inner; + } + + public void Stop(bool immediate) + { + _inner.Stop(immediate); + } + } } + + } diff --git a/src/Umbraco.Web/Net/AspNetHttpContextAccessor.cs b/src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs similarity index 60% rename from src/Umbraco.Web/Net/AspNetHttpContextAccessor.cs rename to src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs index babd8dfcfa..d222d18438 100644 --- a/src/Umbraco.Web/Net/AspNetHttpContextAccessor.cs +++ b/src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs @@ -5,11 +5,13 @@ namespace Umbraco.Web { internal class AspNetHttpContextAccessor : IHttpContextAccessor { - public HttpContext HttpContext + public HttpContextBase HttpContext { get { - return HttpContext.Current; + var httpContext = System.Web.HttpContext.Current; + + return httpContext is null ? null : new HttpContextWrapper(httpContext); } set { diff --git a/src/Umbraco.Web/Net/AspNetIpResolver.cs b/src/Umbraco.Web/AspNet/AspNetIpResolver.cs similarity index 74% rename from src/Umbraco.Web/Net/AspNetIpResolver.cs rename to src/Umbraco.Web/AspNet/AspNetIpResolver.cs index 8cec45ff30..7eaca54663 100644 --- a/src/Umbraco.Web/Net/AspNetIpResolver.cs +++ b/src/Umbraco.Web/AspNet/AspNetIpResolver.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web { public string GetCurrentRequestIpAddress() { - var httpContext = HttpContext.Current == null ? (HttpContextBase) null : new HttpContextWrapper(HttpContext.Current); + var httpContext = HttpContext.Current is null ? null : new HttpContextWrapper(HttpContext.Current); var ip = httpContext.GetCurrentRequestIpAddress(); if (ip.ToLowerInvariant().StartsWith("unknown")) ip = ""; return ip; diff --git a/src/Umbraco.Web/Net/AspNetPasswordHasher.cs b/src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs similarity index 100% rename from src/Umbraco.Web/Net/AspNetPasswordHasher.cs rename to src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs diff --git a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs new file mode 100644 index 0000000000..72bde75dc8 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs @@ -0,0 +1,43 @@ +using System; +using Umbraco.Core.Request; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.AspNet +{ + public class AspNetRequestAccessor : IRequestAccessor + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetRequestAccessor(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + + UmbracoModule.EndRequest += OnEndRequest; + UmbracoModule.RouteAttempt += OnRouteAttempt; + } + + + + public string GetRequestValue(string name) + { + return _httpContextAccessor.GetRequiredHttpContext().Request[name]; + } + + public string GetQueryStringValue(string name) + { + return _httpContextAccessor.GetRequiredHttpContext().Request.QueryString[name]; + } + + private void OnEndRequest(object sender, UmbracoRequestEventArgs args) + { + EndRequest?.Invoke(sender, args); + } + + private void OnRouteAttempt(object sender, RoutableAttemptEventArgs args) + { + RouteAttempt?.Invoke(sender, args); + } + public event EventHandler EndRequest; + public event EventHandler RouteAttempt; + } +} diff --git a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs new file mode 100644 index 0000000000..bf7b1c05c3 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs @@ -0,0 +1,26 @@ +using System.Web; +using Umbraco.Core.Session; +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetSessionManager: ISessionManager, ISessionIdResolver + { + + public AspNetSessionManager() + { + } + + public object GetSessionValue(string sessionName) + { + return HttpContext.Current.Session[sessionName]; + } + + public void SetSessionValue(string sessionName, object value) + { + HttpContext.Current.Session[sessionName] = value; + } + + public string SessionId => HttpContext.Current?.Session?.SessionID; + } +} 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/FrameworkMarchal.cs b/src/Umbraco.Web/AspNet/FrameworkMarchal.cs similarity index 100% rename from src/Umbraco.Web/FrameworkMarchal.cs rename to src/Umbraco.Web/AspNet/FrameworkMarchal.cs diff --git a/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs deleted file mode 100644 index 37e02a0149..0000000000 --- a/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.ComponentModel; -using Umbraco.Core.Cache; - -namespace Umbraco.Web.Cache -{ - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class UserGroupPermissionsCacheRefresher : CacheRefresherBase - { - public UserGroupPermissionsCacheRefresher(AppCaches appCaches) - : base(appCaches) - { } - - #region Define - - protected override UserGroupPermissionsCacheRefresher This => this; - - public static readonly Guid UniqueId = Guid.Parse("840AB9C5-5C0B-48DB-A77E-29FE4B80CD3A"); - - public override Guid RefresherUniqueId => UniqueId; - - public override string Name => "User Group Permissions Cache Refresher"; - - #endregion - - #region Refresher - - #endregion - } -} diff --git a/src/Umbraco.Web/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs deleted file mode 100644 index 1879e8b69b..0000000000 --- a/src/Umbraco.Web/Cache/WebCachingAppCache.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Web.Caching; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; - -namespace Umbraco.Web.Cache -{ - /// - /// Implements on top of a . - /// - /// The underlying cache is expected to be HttpRuntime.Cache. - internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache - { - // locker object that supports upgradeable read locking - // does not need to support recursion if we implement the cache correctly and ensure - // that methods cannot be reentrant, ie we do NOT create values while holding a lock. - private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - - private readonly System.Web.Caching.Cache _cache; - - /// - /// Initializes a new instance of the class. - /// - public WebCachingAppCache(System.Web.Caching.Cache cache, ITypeFinder typeFinder) : base(typeFinder) - { - _cache = cache; - } - - /// - public override object Get(string key, Func factory) - { - return Get(key, factory, null, dependentFiles: null); - } - - /// - public object Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - return GetImpl(key, factory, timeout, isSliding, dependency); - } - - /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - InsertImpl(key, factory, timeout, isSliding, dependency); - } - - #region Dictionary - - protected override IEnumerable GetDictionaryEntries() - { - const string prefix = CacheItemPrefix + "-"; - return _cache.Cast() - .Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix)); - } - - protected override void RemoveEntry(string key) - { - _cache.Remove(key); - } - - protected override object GetEntry(string key) - { - return _cache.Get(key); - } - - #endregion - - #region Lock - - protected override void EnterReadLock() - { - _locker.EnterReadLock(); - } - - protected override void EnterWriteLock() - { - _locker.EnterWriteLock();; - } - - protected override void ExitReadLock() - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - - protected override void ExitWriteLock() - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - - #endregion - - private object GetImpl(string key, Func factory, TimeSpan? timeout, bool isSliding = false, CacheDependency dependency = null) - { - key = GetCacheKey(key); - - // NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang, - // getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and - // nasty performance issues. - - // So.... we insert a Lazy in the cache while holding the global application lock, and then rely - // on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global - // application lock has been released. - - // NOTE - // The Lazy value creation may produce a null value. - // Must make sure (for backward compatibility) that we pretend they are not in the cache. - // So if we find an entry in the cache that already has its value created and is null, - // pretend it was not there. If value is not already created, wait... and return null, that's - // what prior code did. - - // NOTE - // The Lazy value creation may throw. - - // So... the null value _will_ be in the cache but never returned - - Lazy result; - - // Fast! - // Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other - // threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all - // reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of - // having to re-lock in case there's no value. Would need to benchmark to figure out whether - // it's worth it, though... - try - { - _locker.EnterReadLock(); - result = _cache.Get(key) as Lazy; // null if key not found - } - finally - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - var value = result == null ? null : SafeLazy.GetSafeLazyValue(result); - if (value != null) return value; - - using (var lck = new UpgradeableReadLock(_locker)) - { - result = _cache.Get(key) as Lazy; // null if key not found - - // cannot create value within the lock, so if result.IsValueCreated is false, just - // do nothing here - means that if creation throws, a race condition could cause - // more than one thread to reach the return statement below and throw - accepted. - - if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null - { - result = SafeLazy.GetSafeLazy(factory); - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - lck.UpgradeToWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(key, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - } - - // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache - // exceptions (but try again and again) and silently eat them - however at - // some point we have to report them - so need to re-throw here - - // this does not throw anymore - //return result.Value; - - value = result.Value; // will not throw (safe lazy) - if (value is SafeLazy.ExceptionHolder eh) eh.Exception.Throw(); // throw once! - return value; - } - - private void InsertImpl(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheDependency dependency = null) - { - // NOTE - here also we must insert a Lazy but we can evaluate it right now - // and make sure we don't store a null value. - - var result = SafeLazy.GetSafeLazy(getCacheItem); - var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache - if (value == null) return; // do not store null values (backward compat) - - cacheKey = GetCacheKey(cacheKey); - - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - try - { - _locker.EnterWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(cacheKey, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - finally - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - } - } -} diff --git a/src/Umbraco.Web/CacheHelperExtensions.cs b/src/Umbraco.Web/CacheHelperExtensions.cs index 615e6a3982..e27b0db1fc 100644 --- a/src/Umbraco.Web/CacheHelperExtensions.cs +++ b/src/Umbraco.Web/CacheHelperExtensions.cs @@ -3,6 +3,7 @@ using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; using Umbraco.Core.Cache; +using Umbraco.Core.Hosting; namespace Umbraco.Web { @@ -12,13 +13,11 @@ namespace Umbraco.Web /// public static class CacheHelperExtensions { - - public const string PartialViewCacheKey = "Umbraco.Web.PartialViewCacheKey"; - /// /// Outputs and caches a partial view in MVC /// /// + /// /// /// /// @@ -28,6 +27,7 @@ namespace Umbraco.Web /// public static IHtmlString CachedPartialView( this AppCaches appCaches, + IHostingEnvironment hostingEnvironment, HtmlHelper htmlHelper, string partialViewName, object model, @@ -36,25 +36,17 @@ namespace Umbraco.Web ViewDataDictionary viewData = null) { //disable cached partials in debug mode: http://issues.umbraco.org/issue/U4-5940 - if (htmlHelper.ViewContext.HttpContext.IsDebuggingEnabled) + if (hostingEnvironment.IsDebugMode) { // just return a normal partial view instead return htmlHelper.Partial(partialViewName, model, viewData); } return appCaches.RuntimeCache.GetCacheItem( - PartialViewCacheKey + cacheKey, + Core.CacheHelperExtensions.PartialViewCacheKey + cacheKey, () => htmlHelper.Partial(partialViewName, model, viewData), timeout: new TimeSpan(0, 0, 0, cachedSeconds)); } - /// - /// Clears the cache for partial views - /// - /// - public static void ClearPartialViewCache(this AppCaches appCaches) - { - appCaches.RuntimeCache.ClearByKey(PartialViewCacheKey); - } } } diff --git a/src/Umbraco.Core/Compose/AuditEventsComponent.cs b/src/Umbraco.Web/Compose/AuditEventsComponent.cs similarity index 99% rename from src/Umbraco.Core/Compose/AuditEventsComponent.cs rename to src/Umbraco.Web/Compose/AuditEventsComponent.cs index 21f3dd2037..fba6e754c8 100644 --- a/src/Umbraco.Core/Compose/AuditEventsComponent.cs +++ b/src/Umbraco.Web/Compose/AuditEventsComponent.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Net; +using Umbraco.Web.Security; namespace Umbraco.Core.Compose { diff --git a/src/Umbraco.Core/Compose/AuditEventsComposer.cs b/src/Umbraco.Web/Compose/AuditEventsComposer.cs similarity index 100% rename from src/Umbraco.Core/Compose/AuditEventsComposer.cs rename to src/Umbraco.Web/Compose/AuditEventsComposer.cs diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs deleted file mode 100644 index 7063032c76..0000000000 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Text; -using System.Threading.Tasks; -using System.Web.Compilation; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Composing -{ - /// - /// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites - /// - /// - /// This finder will also try to resolve dynamic assemblies created from App_Code - /// - internal class BuildManagerTypeFinder : TypeFinder, ITypeFinder - { - - public BuildManagerTypeFinder(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILogger logger, ITypeFinderConfig typeFinderConfig = null) : base(logger, typeFinderConfig) - { - if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper)); - if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); - if (logger == null) throw new ArgumentNullException(nameof(logger)); - - _allAssemblies = new Lazy>(() => - { - var isHosted = hostingEnvironment.IsHosted; - try - { - if (isHosted) - { - var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code"))); - //check if the folder exists and if there are any files in it with the supported file extensions - if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) - { - try - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - catch (FileNotFoundException ex) - { - //this will occur if it cannot load the assembly - logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); - } - } - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - // Not hosted, just use the default implementation - return new HashSet(base.AssembliesToScan); - }); - } - - private readonly Lazy> _allAssemblies; - - /// - /// Explicitly implement and return result from BuildManager - /// - /// - /// - Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false); - - /// - /// Explicitly implement and return result from BuildManager - /// - IEnumerable ITypeFinder.AssembliesToScan => _allAssemblies.Value; - - /// - /// TypeFinder config via appSettings - /// - internal class TypeFinderConfig : ITypeFinderConfig - { - private IEnumerable _assembliesAcceptingLoadExceptions; - public IEnumerable AssembliesAcceptingLoadExceptions - { - get - { - if (_assembliesAcceptingLoadExceptions != null) - return _assembliesAcceptingLoadExceptions; - - var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions]; - return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) - ? Array.Empty() - : s.Split(',').Select(x => x.Trim()).ToArray(); - } - } - } - } -} diff --git a/src/Umbraco.Web/Composing/CompositionExtensions/Installer.cs b/src/Umbraco.Web/Composing/CompositionExtensions/Installer.cs index 233db9bcb3..9a3e8d98f8 100644 --- a/src/Umbraco.Web/Composing/CompositionExtensions/Installer.cs +++ b/src/Umbraco.Web/Composing/CompositionExtensions/Installer.cs @@ -19,9 +19,10 @@ namespace Umbraco.Web.Composing.CompositionExtensions composition.Register(Lifetime.Scope); composition.Register(Lifetime.Scope); - composition.Register(Lifetime.Scope); - composition.Register(Lifetime.Scope); - composition.Register(Lifetime.Scope); + // TODO: Add these back once we have a compatible Starter kit + // composition.Register(Lifetime.Scope); + // composition.Register(Lifetime.Scope); + // composition.Register(Lifetime.Scope); composition.Register(Lifetime.Scope); diff --git a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs index 2d32517001..d5f3ba244b 100644 --- a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs +++ b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs @@ -1,6 +1,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Mapping; +using Umbraco.Web.Models.Identity; using Umbraco.Web.Models.Mapping; namespace Umbraco.Web.Composing.CompositionExtensions @@ -27,9 +28,11 @@ namespace Umbraco.Web.Composing.CompositionExtensions .Add() .Add() .Add() - .Add(); + .Add() + .Add();; composition.Register(); + composition.Register(); composition.Register(); return composition; diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 1a90f68898..eec3dcd1e3 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -26,11 +26,11 @@ using Umbraco.Web.HealthCheck; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using Umbraco.Web.Services; +using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using CoreCurrent = Umbraco.Core.Composing.Current; - namespace Umbraco.Web.Composing { // see notes in Umbraco.Core.Composing.Current. @@ -38,11 +38,32 @@ namespace Umbraco.Web.Composing { private static readonly object Locker = new object(); + private static IFactory _factory; + + /// + /// Gets or sets the factory. + /// + public static IFactory Factory + { + get + { + if (_factory == null) + throw new InvalidOperationException("No factory has been set."); + return _factory; + } + set + { + if (_factory != null) + throw new InvalidOperationException("A factory has already been set."); + _factory = value; + } + } + private static IUmbracoContextAccessor _umbracoContextAccessor; static Current() { - CoreCurrent.Resetted += (sender, args) => + Resetted += (sender, args) => { if (_umbracoContextAccessor != null) { @@ -53,17 +74,23 @@ namespace Umbraco.Web.Composing }; } - // for UNIT TESTS exclusively! - internal static void Reset() + /// + /// for UNIT TESTS exclusively! Resets . Indented for testing only, and not supported in production code. + /// + /// + /// For UNIT TESTS exclusively. + /// Resets everything that is 'current'. + /// + public static void Reset() { - CoreCurrent.Reset(); + _factory.DisposeIfDisposable(); + _factory = null; + + Resetted?.Invoke(null, EventArgs.Empty); } - /// - /// Gets the factory. - /// - public static IFactory Factory - => CoreCurrent.Factory; + internal static event EventHandler Resetted; + #region Temp & Special @@ -83,12 +110,15 @@ namespace Umbraco.Web.Composing #region Web Getters - public static UmbracoContext UmbracoContext + public static IUmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext; public static UmbracoHelper UmbracoHelper => Factory.GetInstance(); - + public static IUmbracoComponentRenderer UmbracoComponentRenderer + => Factory.GetInstance(); + public static ITagQuery TagQuery + => Factory.GetInstance(); public static DistributedCache DistributedCache => Factory.GetInstance(); @@ -168,70 +198,77 @@ namespace Umbraco.Web.Composing // proxy Core for convenience - public static UmbracoMapper Mapper => CoreCurrent.Mapper; + public static IMediaFileSystem MediaFileSystem => Factory.GetInstance(); - public static IRuntimeState RuntimeState => CoreCurrent.RuntimeState; + public static UmbracoMapper Mapper => Factory.GetInstance(); - public static TypeLoader TypeLoader => CoreCurrent.TypeLoader; + public static IRuntimeState RuntimeState => Factory.GetInstance(); - public static Configs Configs => CoreCurrent.Configs; + public static TypeLoader TypeLoader => Factory.GetInstance(); - public static UrlSegmentProviderCollection UrlSegmentProviders => CoreCurrent.UrlSegmentProviders; + public static Configs Configs => Factory.GetInstance(); - public static CacheRefresherCollection CacheRefreshers => CoreCurrent.CacheRefreshers; + public static UrlSegmentProviderCollection UrlSegmentProviders => Factory.GetInstance(); - public static DataEditorCollection DataEditors => CoreCurrent.DataEditors; + public static CacheRefresherCollection CacheRefreshers => Factory.GetInstance(); - public static DataValueReferenceFactoryCollection DataValueReferenceFactories => CoreCurrent.DataValueReferenceFactories; + public static DataEditorCollection DataEditors => Factory.GetInstance(); - public static PropertyEditorCollection PropertyEditors => CoreCurrent.PropertyEditors; + public static DataValueReferenceFactoryCollection DataValueReferenceFactories => Factory.GetInstance(); - public static ParameterEditorCollection ParameterEditors => CoreCurrent.ParameterEditors; + public static PropertyEditorCollection PropertyEditors => Factory.GetInstance(); - internal static ManifestValueValidatorCollection ManifestValidators => CoreCurrent.ManifestValidators; + public static ParameterEditorCollection ParameterEditors => Factory.GetInstance(); - internal static IPackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; + internal static ManifestValueValidatorCollection ManifestValidators => Factory.GetInstance(); - internal static PackageActionCollection PackageActions => CoreCurrent.PackageActions; + internal static IPackageActionRunner PackageActionRunner => Factory.GetInstance(); - internal static PropertyValueConverterCollection PropertyValueConverters => CoreCurrent.PropertyValueConverters; + internal static PackageActionCollection PackageActions => Factory.GetInstance(); - internal static IPublishedModelFactory PublishedModelFactory => CoreCurrent.PublishedModelFactory; + internal static PropertyValueConverterCollection PropertyValueConverters => Factory.GetInstance(); - public static IServerMessenger ServerMessenger => CoreCurrent.ServerMessenger; + internal static IPublishedModelFactory PublishedModelFactory => Factory.GetInstance(); - public static IServerRegistrar ServerRegistrar => CoreCurrent.ServerRegistrar; + public static IServerMessenger ServerMessenger => Factory.GetInstance(); - public static ICultureDictionaryFactory CultureDictionaryFactory => CoreCurrent.CultureDictionaryFactory; + public static IServerRegistrar ServerRegistrar => Factory.GetInstance(); - public static IShortStringHelper ShortStringHelper => CoreCurrent.ShortStringHelper; + public static ICultureDictionaryFactory CultureDictionaryFactory => Factory.GetInstance(); - public static ILogger Logger => CoreCurrent.Logger; + public static IShortStringHelper ShortStringHelper => Factory.GetInstance(); - public static IProfiler Profiler => CoreCurrent.Profiler; + public static ILogger Logger => Umbraco.Composing.Current.Logger; - public static IProfilingLogger ProfilingLogger => CoreCurrent.ProfilingLogger; + public static IProfiler Profiler => Factory.GetInstance(); - public static AppCaches AppCaches => CoreCurrent.AppCaches; + public static IProfilingLogger ProfilingLogger => Factory.GetInstance(); - public static ServiceContext Services => CoreCurrent.Services; + public static AppCaches AppCaches => Factory.GetInstance(); - public static IScopeProvider ScopeProvider => CoreCurrent.ScopeProvider; + public static ServiceContext Services => Factory.GetInstance(); - public static IFileSystems FileSystems => CoreCurrent.FileSystems; + public static IScopeProvider ScopeProvider => Factory.GetInstance(); - public static ISqlContext SqlContext=> CoreCurrent.SqlContext; + public static IFileSystems FileSystems => Factory.GetInstance(); - public static IPublishedContentTypeFactory PublishedContentTypeFactory => CoreCurrent.PublishedContentTypeFactory; + public static ISqlContext SqlContext=> Factory.GetInstance(); - public static IPublishedValueFallback PublishedValueFallback => CoreCurrent.PublishedValueFallback; + public static IPublishedContentTypeFactory PublishedContentTypeFactory => Factory.GetInstance(); - public static IVariationContextAccessor VariationContextAccessor => CoreCurrent.VariationContextAccessor; + public static IPublishedValueFallback PublishedValueFallback => Factory.GetInstance(); - public static IIOHelper IOHelper => CoreCurrent.IOHelper; - public static IHostingEnvironment HostingEnvironment => CoreCurrent.HostingEnvironment; + public static IVariationContextAccessor VariationContextAccessor => Factory.GetInstance(); + + public static IIOHelper IOHelper => Factory.GetInstance(); + public static IHostingEnvironment HostingEnvironment => Factory.GetInstance(); public static IIpResolver IpResolver => Factory.GetInstance(); 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 IUmbracoApplicationLifetime UmbracoApplicationLifetime => Factory.GetInstance(); + public static IPublishedContentQuery PublishedContentQuery => Factory.GetInstance(); #endregion } diff --git a/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs index 103cd32912..863e406067 100644 --- a/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs @@ -1,6 +1,7 @@ using System; using System.Web.Http; using LightInject; +using LightInject.Web; using Umbraco.Core.Composing.LightInject; namespace Umbraco.Web.Composing.LightInject @@ -23,6 +24,16 @@ namespace Umbraco.Web.Composing.LightInject public new static LightInjectContainer Create() => new LightInjectContainer(CreateServiceContainer()); + /// + /// Overridden to supply the .Net Framework based PerWebRequestScopeManagerProvider + /// + public override void EnablePerWebRequestScope() + { + if (!(Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) + throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); + smp.EnablePerWebRequestScope(new PerWebRequestScopeManagerProvider()); + } + /// public override void ConfigureForWeb() { diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index 27a56afc1e..c7c1da4ca9 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -104,13 +104,6 @@ namespace Umbraco.Web public static SectionCollectionBuilder Sections(this Composition composition) => composition.WithCollectionBuilder(); - /// - /// Gets the backoffice dashboards collection builder. - /// - /// The composition. - public static DashboardCollectionBuilder Dashboards(this Composition composition) - => composition.WithCollectionBuilder(); - /// /// Gets the backoffice OEmbed Providers collection builder. /// @@ -169,37 +162,6 @@ namespace Umbraco.Web composition.RegisterUnique(_ => finder); } - /// - /// Sets the published snapshot service. - /// - /// The type of the published snapshot service. - /// The composition. - public static void SetPublishedSnapshotService(this Composition composition) - where T : IPublishedSnapshotService - { - composition.RegisterUnique(); - } - - /// - /// Sets the published snapshot service. - /// - /// The composition. - /// A function creating a published snapshot service. - public static void SetPublishedSnapshotService(this Composition composition, Func factory) - { - composition.RegisterUnique(factory); - } - - /// - /// Sets the published snapshot service. - /// - /// The composition. - /// A published snapshot service. - public static void SetPublishedSnapshotService(this Composition composition, IPublishedSnapshotService service) - { - composition.RegisterUnique(_ => service); - } - /// /// Sets the site domain helper. /// diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 2ff80e2668..cd0489433c 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -6,18 +6,24 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Web.Security; namespace Umbraco.Web.Controllers { public class UmbLoginController : SurfaceController { + private readonly MembershipHelper _membershipHelper; + public UmbLoginController() { } - public UmbLoginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) - : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + public UmbLoginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, + MembershipHelper membershipHelper) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) { + _membershipHelper = membershipHelper; } [HttpPost] @@ -30,7 +36,7 @@ namespace Umbraco.Web.Controllers return CurrentUmbracoPage(); } - if (Members.Login(model.Username, model.Password) == false) + if (_membershipHelper.Login(model.Username, model.Password) == false) { //don't add a field level error, just model level ModelState.AddModelError("loginModel", "Invalid username or password"); @@ -46,7 +52,7 @@ namespace Umbraco.Web.Controllers // if it's not a local url we'll redirect to the root of the current site return Redirect(Url.IsLocalUrl(model.RedirectUrl) ? model.RedirectUrl - : CurrentPage.AncestorOrSelf(1).Url); + : CurrentPage.AncestorOrSelf(1).Url()); } //redirect to current page by default diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs index 8f572404fc..825dd1e904 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs @@ -7,19 +7,25 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Web.Security; namespace Umbraco.Web.Controllers { [MemberAuthorize] public class UmbLoginStatusController : SurfaceController { + private readonly MembershipHelper _membershipHelper; + public UmbLoginStatusController() { } - public UmbLoginStatusController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) - : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + public UmbLoginStatusController(IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, + IProfilingLogger profilingLogger, MembershipHelper membershipHelper) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) { + _membershipHelper = membershipHelper; } [HttpPost] @@ -32,7 +38,7 @@ namespace Umbraco.Web.Controllers return CurrentUmbracoPage(); } - if (Members.IsLoggedIn()) + if (_membershipHelper.IsLoggedIn()) { FormsAuthentication.SignOut(); } diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs index c922b4142f..c5a37e7629 100644 --- a/src/Umbraco.Web/Controllers/UmbProfileController.cs +++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs @@ -15,12 +15,18 @@ namespace Umbraco.Web.Controllers [MemberAuthorize] public class UmbProfileController : SurfaceController { + private readonly MembershipHelper _membershipHelper; + public UmbProfileController() { } - public UmbProfileController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) - : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) - { } + public UmbProfileController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, + MembershipHelper membershipHelper) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) + { + _membershipHelper = membershipHelper; + } [HttpPost] [ValidateAntiForgeryToken] @@ -32,7 +38,7 @@ namespace Umbraco.Web.Controllers return CurrentUmbracoPage(); } - var updateAttempt = Members.UpdateMemberProfile(model); + var updateAttempt = _membershipHelper.UpdateMemberProfile(model); if (updateAttempt.Success == false) { //don't add a field level error, just model level diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs index 4f4173a67d..1e83fadda6 100644 --- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs @@ -8,18 +8,24 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.Mvc; +using Umbraco.Web.Security; namespace Umbraco.Web.Controllers { public class UmbRegisterController : SurfaceController { + private readonly MembershipHelper _membershipHelper; + public UmbRegisterController() { } - public UmbRegisterController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) - : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + public UmbRegisterController(IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, + IProfilingLogger profilingLogger, MembershipHelper membershipHelper) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) { + _membershipHelper = membershipHelper; } [HttpPost] @@ -40,7 +46,7 @@ namespace Umbraco.Web.Controllers } MembershipCreateStatus status; - var member = Members.RegisterMember(model, out status, model.LoginOnSuccess); + var member = _membershipHelper.RegisterMember(model, out status, model.LoginOnSuccess); switch (status) { diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 434fadc716..6519cf4cf6 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -13,7 +13,6 @@ using Microsoft.AspNet.Identity.Owin; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; -using Umbraco.Core.Models.Identity; using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; @@ -24,9 +23,12 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; -using Umbraco.Web.Composing; using IUser = Umbraco.Core.Models.Membership.IUser; using Umbraco.Core.Mapping; +using Umbraco.Web.Models.Identity; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -42,11 +44,29 @@ namespace Umbraco.Web.Editors private BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; private readonly IUserPasswordConfiguration _passwordConfiguration; + private readonly IRuntimeState _runtimeState; + private readonly ISecuritySettings _securitySettings; + private readonly IIOHelper _ioHelper; - public AuthenticationController(IUserPasswordConfiguration passwordConfiguration, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper) + public AuthenticationController( + IUserPasswordConfiguration passwordConfiguration, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + UmbracoMapper umbracoMapper, + ISecuritySettings securitySettings, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) { _passwordConfiguration = passwordConfiguration ?? throw new ArgumentNullException(nameof(passwordConfiguration)); + _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); + _securitySettings = securitySettings ?? throw new ArgumentNullException(nameof(securitySettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); } protected BackOfficeUserManager UserManager => _userManager @@ -290,7 +310,7 @@ namespace Umbraco.Web.Editors { // If this feature is switched off in configuration the UI will be amended to not make the request to reset password available. // So this is just a server-side secondary check. - if (Current.Configs.Settings().Security.AllowPasswordReset == false) + if (_securitySettings.AllowPasswordReset == false) { throw new HttpResponseException(HttpStatusCode.BadRequest); } @@ -514,13 +534,13 @@ namespace Umbraco.Web.Editors var action = urlHelper.Action("ValidatePasswordResetCode", "BackOffice", new { - area = GlobalSettings.GetUmbracoMvcArea(Current.IOHelper), + area = _ioHelper.GetUmbracoMvcArea(), u = userId, r = code }); // Construct full URL using configured application URL (which will fall back to request) - var applicationUri = Current.RuntimeState.ApplicationUrl; + var applicationUri = _runtimeState.ApplicationUrl; var callbackUri = new Uri(applicationUri, action); return callbackUri.ToString(); } diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 63ab65e7f2..e39d1658a2 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -16,16 +16,20 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; -using Umbraco.Core.Models.Identity; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.Features; using Umbraco.Web.JavaScript; +using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using Constants = Umbraco.Core.Constants; using JArray = Newtonsoft.Json.Linq.JArray; +using Umbraco.Core.Configuration.Grid; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Web.Trees; namespace Umbraco.Web.Editors { @@ -43,14 +47,48 @@ namespace Umbraco.Web.Editors private BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; private readonly IUmbracoVersion _umbracoVersion; + private readonly IGridConfig _gridConfig; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + private readonly TreeCollection _treeCollection; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IRuntimeSettings _runtimeSettings; + private readonly ISecuritySettings _securitySettings; + + public BackOfficeController( + IManifestParser manifestParser, + UmbracoFeatures features, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IRuntimeState runtimeState, + IUmbracoVersion umbracoVersion, + IGridConfig gridConfig, + IContentSettings contentSettings, + IIOHelper ioHelper, + TreeCollection treeCollection, + IHostingEnvironment hostingEnvironment, + IHttpContextAccessor httpContextAccessor, + IRuntimeSettings settings, + ISecuritySettings securitySettings) + : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger) - public BackOfficeController(IManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IUmbracoVersion umbracoVersion) - : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger, umbracoHelper) { _manifestParser = manifestParser; _features = features; _runtimeState = runtimeState; _umbracoVersion = umbracoVersion; + _gridConfig = gridConfig ?? throw new ArgumentNullException(nameof(gridConfig)); + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _treeCollection = treeCollection ?? throw new ArgumentNullException(nameof(treeCollection)); + _hostingEnvironment = hostingEnvironment; + _httpContextAccessor = httpContextAccessor; + _runtimeSettings = settings; + _securitySettings = securitySettings; } protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager()); @@ -66,8 +104,8 @@ namespace Umbraco.Web.Editors public async Task Default() { return await RenderDefaultOrProcessExternalLoginAsync( - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion)), - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion))); + () => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings,_ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)), + () => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings))); } [HttpGet] @@ -150,7 +188,7 @@ namespace Umbraco.Web.Editors { return await RenderDefaultOrProcessExternalLoginAsync( //The default view to render when there is no external login info or errors - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion)), + () => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)), //The ActionResult to perform if external login is successful () => Redirect("/")); } @@ -205,7 +243,7 @@ namespace Umbraco.Web.Editors var initCss = new CssInitialization(_manifestParser); var files = initJs.OptimizeBackOfficeScriptFiles(HttpContext, JsInitialization.GetDefaultInitialization()); - var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco"); + var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco", GlobalSettings, _ioHelper); result += initCss.GetStylesheetInitialization(HttpContext); return JavaScript(result); @@ -230,7 +268,7 @@ namespace Umbraco.Web.Editors } //cache the result if debugging is disabled - var result = HttpContext.IsDebuggingEnabled + var result = _hostingEnvironment.IsDebugMode ? GetAssetList() : AppCaches.RuntimeCache.GetCacheItem( "Umbraco.Web.Editors.BackOfficeController.GetManifestAssetList", @@ -244,8 +282,7 @@ namespace Umbraco.Web.Editors [HttpGet] public JsonNetResult GetGridConfig() { - var gridConfig = Current.Configs.Grids(); - return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.None }; + return new JsonNetResult { Data = _gridConfig.EditorsConfig.Editors, Formatting = Formatting.None }; } @@ -258,10 +295,10 @@ namespace Umbraco.Web.Editors [MinifyJavaScriptResult(Order = 1)] public JavaScriptResult ServerVariables() { - var serverVars = new BackOfficeServerVariables(Url, _runtimeState, _features, GlobalSettings, _umbracoVersion); + var serverVars = new BackOfficeServerVariables(Url, _runtimeState, _features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings); //cache the result if debugging is disabled - var result = HttpContext.IsDebuggingEnabled + var result = _hostingEnvironment.IsDebugMode ? ServerVariablesParser.Parse(serverVars.GetServerVariables()) : AppCaches.RuntimeCache.GetCacheItem( typeof(BackOfficeController) + "ServerVariables", @@ -352,7 +389,7 @@ namespace Umbraco.Web.Editors if (defaultResponse == null) throw new ArgumentNullException("defaultResponse"); if (externalSignInResponse == null) throw new ArgumentNullException("externalSignInResponse"); - ViewData.SetUmbracoPath(GlobalSettings.GetUmbracoMvcArea(Current.IOHelper)); + ViewData.SetUmbracoPath(_ioHelper.GetUmbracoMvcArea()); //check if there is the TempData with the any token name specified, if so, assign to view bag and render the view if (ViewData.FromTempData(TempData, ViewDataExtensions.TokenExternalSignInError) || @@ -464,6 +501,7 @@ namespace Umbraco.Web.Editors var groups = Services.UserService.GetUserGroupsByAlias(autoLinkOptions.GetDefaultUserGroups(UmbracoContext, loginInfo)); var autoLinkUser = BackOfficeIdentityUser.CreateNew( + GlobalSettings, loginInfo.Email, loginInfo.Email, autoLinkOptions.GetDefaultCulture(UmbracoContext, loginInfo)); diff --git a/src/Umbraco.Web/Editors/BackOfficeModel.cs b/src/Umbraco.Web/Editors/BackOfficeModel.cs index 72c345dee8..3aefd58e8f 100644 --- a/src/Umbraco.Web/Editors/BackOfficeModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficeModel.cs @@ -1,20 +1,41 @@ using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; using Umbraco.Web.Features; +using Umbraco.Web.Trees; namespace Umbraco.Web.Editors { public class BackOfficeModel { - public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) + public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, + IContentSettings contentSettings, IIOHelper ioHelper, TreeCollection treeCollection, + IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, + IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings) { Features = features; GlobalSettings = globalSettings; UmbracoVersion = umbracoVersion; + ContentSettings = contentSettings; + IOHelper = ioHelper; + TreeCollection = treeCollection; + HttpContextAccessor = httpContextAccessor; + HostingEnvironment = hostingEnvironment; + RuntimeSettings = runtimeSettings; + SecuritySettings = securitySettings; } public UmbracoFeatures Features { get; } public IGlobalSettings GlobalSettings { get; } public IUmbracoVersion UmbracoVersion { get; } + public IContentSettings ContentSettings { get; } + public IIOHelper IOHelper { get; } + public TreeCollection TreeCollection { get; } + public IHttpContextAccessor HttpContextAccessor { get; } + public IHostingEnvironment HostingEnvironment { get; } + public IRuntimeSettings RuntimeSettings { get; set; } + public ISecuritySettings SecuritySettings { get; set; } } } diff --git a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs index 24548a8089..fe9e82c580 100644 --- a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs @@ -2,9 +2,11 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -19,8 +21,18 @@ namespace Umbraco.Web.Editors [PrefixlessBodyModelValidator] public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController { - protected BackOfficeNotificationsController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + protected BackOfficeNotificationsController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { } } diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs index 7b9ae1a1df..394a0366af 100644 --- a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs @@ -1,7 +1,11 @@ using System.Collections.Generic; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Web.Features; +using Umbraco.Web.Trees; namespace Umbraco.Web.Editors { @@ -10,7 +14,8 @@ namespace Umbraco.Web.Editors private readonly UmbracoFeatures _features; public IEnumerable Languages { get; } - public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable languages) : base(features, globalSettings, umbracoVersion) + public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable languages, IContentSettings contentSettings, IIOHelper ioHelper, TreeCollection treeCollection, IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings) + : base(features, globalSettings, umbracoVersion, contentSettings, ioHelper, treeCollection, httpContextAccessor, hostingEnvironment, runtimeSettings, securitySettings) { _features = features; Languages = languages; diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 010487b4c0..14f067a0fc 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -1,17 +1,13 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Configuration; using System.Linq; using System.Runtime.Serialization; using System.Web; -using System.Web.Configuration; using System.Web.Mvc; using ClientDependency.Core.Config; -using Microsoft.Owin; using Microsoft.Owin.Security; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Web.Features; using Umbraco.Web.HealthCheck; @@ -21,6 +17,9 @@ using Umbraco.Web.Profiling; using Umbraco.Web.PropertyEditors; using Umbraco.Web.Trees; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; namespace Umbraco.Web.Editors { @@ -33,19 +32,41 @@ namespace Umbraco.Web.Editors private readonly IRuntimeState _runtimeState; private readonly UmbracoFeatures _features; private readonly IGlobalSettings _globalSettings; - private readonly HttpContextBase _httpContext; - private readonly IOwinContext _owinContext; private readonly IUmbracoVersion _umbracoVersion; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + private readonly TreeCollection _treeCollection; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IRuntimeSettings _settings; + private readonly ISecuritySettings _securitySettings; - internal BackOfficeServerVariables(UrlHelper urlHelper, IRuntimeState runtimeState, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) + internal BackOfficeServerVariables( + UrlHelper urlHelper, + IRuntimeState runtimeState, + UmbracoFeatures features, + IGlobalSettings globalSettings, + IUmbracoVersion umbracoVersion, + IContentSettings contentSettings, + IIOHelper ioHelper, + TreeCollection treeCollection, + IHttpContextAccessor httpContextAccessor, + IHostingEnvironment hostingEnvironment, + IRuntimeSettings settings, + ISecuritySettings securitySettings) { _urlHelper = urlHelper; _runtimeState = runtimeState; _features = features; _globalSettings = globalSettings; - _httpContext = _urlHelper.RequestContext.HttpContext; - _owinContext = _httpContext.GetOwinContext(); _umbracoVersion = umbracoVersion; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _treeCollection = treeCollection ?? throw new ArgumentNullException(nameof(treeCollection)); + _httpContextAccessor = httpContextAccessor; + _hostingEnvironment = hostingEnvironment; + _settings = settings; + _securitySettings = securitySettings; } /// @@ -101,7 +122,7 @@ namespace Umbraco.Web.Editors /// internal Dictionary GetServerVariables() { - var globalSettings = Current.Configs.Global(); + var globalSettings = _globalSettings; var defaultVals = new Dictionary { { @@ -283,7 +304,7 @@ namespace Umbraco.Web.Editors controller => controller.DeleteById(int.MaxValue)) }, { - "nuCacheStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + "publishedSnapshotCacheStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetStatus()) }, { @@ -314,35 +335,39 @@ namespace Umbraco.Web.Editors "tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.UploadImage()) }, + { + "imageUrlGeneratorApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + controller => controller.GetCropUrl(null, null, null, null, null)) + }, } }, { "umbracoSettings", new Dictionary { - {"umbracoPath", _globalSettings.Path}, - {"mediaPath", Current.IOHelper.ResolveUrl(globalSettings.UmbracoMediaPath).TrimEnd('/')}, - {"appPluginsPath", Current.IOHelper.ResolveUrl(Constants.SystemDirectories.AppPlugins).TrimEnd('/')}, + {"umbracoPath", _ioHelper.BackOfficePath}, + {"mediaPath", _ioHelper.ResolveUrl(globalSettings.UmbracoMediaPath).TrimEnd('/')}, + {"appPluginsPath", _ioHelper.ResolveUrl(Constants.SystemDirectories.AppPlugins).TrimEnd('/')}, { "imageFileTypes", - string.Join(",", Current.Configs.Settings().Content.ImageFileTypes) + string.Join(",", _contentSettings.ImageFileTypes) }, { "disallowedUploadFiles", - string.Join(",", Current.Configs.Settings().Content.DisallowedUploadFiles) + string.Join(",", _contentSettings.DisallowedUploadFiles) }, { "allowedUploadFiles", - string.Join(",", Current.Configs.Settings().Content.AllowedUploadFiles) + string.Join(",", _contentSettings.AllowedUploadFiles) }, { "maxFileSize", GetMaxRequestLength() }, - {"keepUserLoggedIn", Current.Configs.Settings().Security.KeepUserLoggedIn}, - {"usernameIsEmail", Current.Configs.Settings().Security.UsernameIsEmail}, - {"cssPath", Current.IOHelper.ResolveUrl(globalSettings.UmbracoCssPath).TrimEnd('/')}, - {"allowPasswordReset", Current.Configs.Settings().Security.AllowPasswordReset}, - {"loginBackgroundImage", Current.Configs.Settings().Content.LoginBackgroundImage}, + {"keepUserLoggedIn", _securitySettings.KeepUserLoggedIn}, + {"usernameIsEmail", _securitySettings.UsernameIsEmail}, + {"cssPath", _ioHelper.ResolveUrl(globalSettings.UmbracoCssPath).TrimEnd('/')}, + {"allowPasswordReset", _securitySettings.AllowPasswordReset}, + {"loginBackgroundImage", _contentSettings.LoginBackgroundImage}, {"showUserInvite", EmailSender.CanSendRequiredEmail(globalSettings)}, {"canSendRequiredEmail", EmailSender.CanSendRequiredEmail(globalSettings)}, } @@ -357,7 +382,7 @@ namespace Umbraco.Web.Editors } }, { - "isDebuggingEnabled", _httpContext.IsDebuggingEnabled + "isDebuggingEnabled", _hostingEnvironment.IsDebugMode }, { "application", GetApplicationState() @@ -366,7 +391,7 @@ namespace Umbraco.Web.Editors "externalLogins", new Dictionary { { - "providers", _owinContext.Authentication.GetExternalAuthenticationTypes() + "providers", _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().Authentication.GetExternalAuthenticationTypes() .Where(p => p.Properties.ContainsKey("UmbracoBackOffice")) .Select(p => new { @@ -413,9 +438,8 @@ namespace Umbraco.Web.Editors // // do this instead // inheriting from TreeControllerBase and marked with TreeAttribute - var trees = Current.Factory.GetInstance(); - foreach (var tree in trees) + foreach (var tree in _treeCollection) { var treeType = tree.TreeControllerType; @@ -454,7 +478,7 @@ namespace Umbraco.Web.Editors app.Add("cacheBuster", $"{version}.{_runtimeState.Level}.{ClientDependencySettings.Instance.Version}".GenerateHash()); //useful for dealing with virtual paths on the client side when hosted in virtual directories especially - app.Add("applicationPath", _httpContext.Request.ApplicationPath.EnsureEndsWith('/')); + app.Add("applicationPath", _httpContextAccessor.GetRequiredHttpContext().Request.ApplicationPath.EnsureEndsWith('/')); //add the server's GMT time offset in minutes app.Add("serverTimeOffset", Convert.ToInt32(DateTimeOffset.Now.Offset.TotalMinutes)); @@ -462,11 +486,9 @@ namespace Umbraco.Web.Editors return app; } - private static string GetMaxRequestLength() + private string GetMaxRequestLength() { - return ConfigurationManager.GetSection("system.web/httpRuntime") is HttpRuntimeSection section - ? section.MaxRequestLength.ToString() - : string.Empty; + return _settings.MaxRequestLength.HasValue ? _settings.MaxRequestLength.Value.ToString() : string.Empty; } } } diff --git a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs index 11a876345d..a6cba6ce41 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs @@ -49,18 +49,15 @@ namespace Umbraco.Web.Editors.Binders { foreach (var variant in model.Variants) { - if (variant.Culture.IsNullOrWhiteSpace()) - { - //map the property dto collection (no culture is passed to the mapping context so it will be invariant) - variant.PropertyCollectionDto = Current.Mapper.Map(model.PersistedContent); - } - else - { - //map the property dto collection with the culture of the current variant - variant.PropertyCollectionDto = Current.Mapper.Map( - model.PersistedContent, - context => context.SetCulture(variant.Culture)); - } + //map the property dto collection with the culture of the current variant + variant.PropertyCollectionDto = Current.Mapper.Map( + model.PersistedContent, + context => + { + // either of these may be null and that is ok, if it's invariant they will be null which is what is expected + context.SetCulture(variant.Culture); + context.SetSegment(variant.Segment); + }); //now map all of the saved values to the dto _modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto); @@ -87,6 +84,5 @@ namespace Umbraco.Web.Editors.Binders model.ParentId, contentType); } - } } diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 96a99830d6..325b39e4f2 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -16,7 +16,6 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Strings.Css; -using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -24,6 +23,8 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Web.Trees; using Stylesheet = Umbraco.Core.Models.Stylesheet; using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -34,6 +35,9 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class CodeFileController : BackOfficeNotificationsController { + private readonly IIOHelper _ioHelper; + private readonly IFileSystems _fileSystems; + public CodeFileController( IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, @@ -42,11 +46,15 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) - + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IIOHelper ioHelper, + IFileSystems fileSystems, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { + _ioHelper = ioHelper; + _fileSystems = fileSystems; } /// @@ -129,11 +137,11 @@ namespace Umbraco.Web.Editors Services.FileService.CreatePartialViewMacroFolder(virtualPath); break; case Core.Constants.Trees.Scripts: - virtualPath = NormalizeVirtualPath(name, Current.Configs.Global().UmbracoScriptsPath); + virtualPath = NormalizeVirtualPath(name, GlobalSettings.UmbracoScriptsPath); Services.FileService.CreateScriptFolder(virtualPath); break; case Core.Constants.Trees.Stylesheets: - virtualPath = NormalizeVirtualPath(name, Current.Configs.Global().UmbracoCssPath); + virtualPath = NormalizeVirtualPath(name, GlobalSettings.UmbracoCssPath); Services.FileService.CreateStyleSheetFolder(virtualPath); break; @@ -273,11 +281,11 @@ namespace Umbraco.Web.Editors break; case Core.Constants.Trees.Scripts: codeFileDisplay = Mapper.Map(new Script(string.Empty)); - codeFileDisplay.VirtualPath = Current.Configs.Global().UmbracoScriptsPath; + codeFileDisplay.VirtualPath = GlobalSettings.UmbracoScriptsPath; break; case Core.Constants.Trees.Stylesheets: codeFileDisplay = Mapper.Map(new Stylesheet(string.Empty)); - codeFileDisplay.VirtualPath = Current.Configs.Global().UmbracoCssPath; + codeFileDisplay.VirtualPath = GlobalSettings.UmbracoCssPath; break; default: throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Unsupported editortype")); @@ -340,7 +348,7 @@ namespace Umbraco.Web.Editors return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path"); case Core.Constants.Trees.Scripts: - if (IsDirectory(virtualPath, Current.Configs.Global().UmbracoScriptsPath)) + if (IsDirectory(virtualPath, GlobalSettings.UmbracoScriptsPath)) { Services.FileService.DeleteScriptFolder(virtualPath); return Request.CreateResponse(HttpStatusCode.OK); @@ -353,7 +361,7 @@ namespace Umbraco.Web.Editors return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path"); case Core.Constants.Trees.Stylesheets: - if (IsDirectory(virtualPath, Current.Configs.Global().UmbracoCssPath)) + if (IsDirectory(virtualPath, GlobalSettings.UmbracoCssPath)) { Services.FileService.DeleteStyleSheetFolder(virtualPath); return Request.CreateResponse(HttpStatusCode.OK); @@ -516,7 +524,7 @@ namespace Umbraco.Web.Editors /// private IScript CreateOrUpdateScript(CodeFileDisplay display) { - return CreateOrUpdateFile(display, ".js", Current.FileSystems.ScriptsFileSystem, + return CreateOrUpdateFile(display, ".js", _fileSystems.ScriptsFileSystem, name => Services.FileService.GetScriptByName(name), (script, userId) => Services.FileService.SaveScript(script, userId), name => new Script(name)); @@ -524,7 +532,7 @@ namespace Umbraco.Web.Editors private IStylesheet CreateOrUpdateStylesheet(CodeFileDisplay display) { - return CreateOrUpdateFile(display, ".css", Current.FileSystems.StylesheetsFileSystem, + return CreateOrUpdateFile(display, ".css", _fileSystems.StylesheetsFileSystem, name => Services.FileService.GetStylesheetByName(name), (stylesheet, userId) => Services.FileService.SaveStylesheet(stylesheet, userId), name => new Stylesheet(name) @@ -646,7 +654,7 @@ namespace Umbraco.Web.Editors private bool IsDirectory(string virtualPath, string systemDirectory) { - var path = Current.IOHelper.MapPath(systemDirectory + "/" + virtualPath); + var path = _ioHelper.MapPath(systemDirectory + "/" + virtualPath); var dirInfo = new DirectoryInfo(path); return dirInfo.Attributes == FileAttributes.Directory; } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 952a924adb..2ea1ec3a24 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -38,6 +38,7 @@ using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Strings; +using Umbraco.Core.Mapping; namespace Umbraco.Web.Editors { @@ -68,9 +69,10 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, - IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); _allLangs = new Lazy>(() => Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); @@ -315,7 +317,7 @@ namespace Umbraco.Web.Editors } /// - /// Gets the content json for the content id + /// Gets the content json for the content guid /// /// /// @@ -335,7 +337,7 @@ namespace Umbraco.Web.Editors } /// - /// Gets the content json for the content id + /// Gets the content json for the content udi /// /// /// @@ -353,7 +355,7 @@ namespace Umbraco.Web.Editors } /// - /// Gets an empty content item for the + /// Gets an empty content item for the document type. /// /// /// @@ -408,7 +410,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetNiceUrl(int id) { - var url = UmbracoContext.Url(id); + var url = PublishedUrlProvider.GetUrl(id); var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(url, Encoding.UTF8, "text/plain"); return response; @@ -421,7 +423,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetNiceUrl(Guid id) { - var url = UmbracoContext.Url(id); + var url = PublishedUrlProvider.GetUrl(id); var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(url, Encoding.UTF8, "text/plain"); return response; @@ -1668,7 +1670,7 @@ namespace Umbraco.Web.Editors [HttpPost] public DomainSave PostSaveLanguageAndDomains(DomainSave model) { - foreach(var domain in model.Domains) + foreach (var domain in model.Domains) { try { @@ -1849,8 +1851,13 @@ namespace Umbraco.Web.Editors /// private void MapValuesForPersistence(ContentItemSave contentSave) { - // inline method to determine if a property type varies - bool Varies(IProperty property) => property.PropertyType.VariesByCulture(); + // inline method to determine the culture and segment to persist the property + (string culture, string segment) PropertyCultureAndSegment(IProperty property, ContentVariantSave variant) + { + var culture = property.PropertyType.VariesByCulture() ? variant.Culture : null; + var segment = property.PropertyType.VariesBySegment() ? variant.Segment : null; + return (culture, segment); + } var variantIndex = 0; @@ -1889,8 +1896,18 @@ namespace Umbraco.Web.Editors MapPropertyValuesForPersistence( contentSave, propertyCollection, - (save, property) => Varies(property) ? property.GetValue(variant.Culture) : property.GetValue(), //get prop val - (save, property, v) => { if (Varies(property)) property.SetValue(v, variant.Culture); else property.SetValue(v); }, //set prop val + (save, property) => + { + // Get property value + (var culture, var segment) = PropertyCultureAndSegment(property, variant); + return property.GetValue(culture, segment); + }, + (save, property, v) => + { + // Set property value + (var culture, var segment) = PropertyCultureAndSegment(property, variant); + property.SetValue(v, culture, segment); + }, variant.Culture); variantIndex++; @@ -2170,7 +2187,10 @@ namespace Umbraco.Web.Editors /// private ContentItemDisplay MapToDisplay(IContent content) { - var display = Mapper.Map(content); + var display = Mapper.Map(content, context => + { + context.Items["CurrentUser"] = Security.CurrentUser; + }); display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false; return display; } diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 4f9e41eeb0..a7949084b5 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Core.Persistence; @@ -16,6 +17,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -38,9 +40,10 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, - IShortStringHelper shortStringHelper) - :base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + :base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { CultureDictionary = cultureDictionary; } diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index e64485e5ab..506eac6aac 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -11,9 +11,9 @@ using System.Xml; using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; @@ -29,7 +29,8 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -50,22 +51,31 @@ namespace Umbraco.Web.Editors private readonly IGlobalSettings _globalSettings; private readonly PropertyEditorCollection _propertyEditors; private readonly IScopeProvider _scopeProvider; + private readonly IIOHelper _ioHelper; public ContentTypeController(IEntityXmlSerializer serializer, ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, PropertyEditorCollection propertyEditors, - ServiceContext services, AppCaches appCaches, - IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, + ISqlContext sqlContext, + PropertyEditorCollection propertyEditors, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, IScopeProvider scopeProvider, - IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider, + EditorValidatorCollection editorValidatorCollection) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) { _serializer = serializer; _globalSettings = globalSettings; _propertyEditors = propertyEditors; _scopeProvider = scopeProvider; + _ioHelper = ioHelper; } public int GetCount() @@ -73,6 +83,13 @@ namespace Umbraco.Web.Editors return Services.ContentTypeService.Count(); } + [HttpGet] + [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + public bool HasContentNodes(int id) + { + return Services.ContentTypeService.HasContentNodes(id); + } + public DocumentTypeDisplay GetById(int id) { var ct = Services.ContentTypeService.Get(id); @@ -428,11 +445,11 @@ namespace Umbraco.Web.Editors } var contentType = Services.ContentTypeBaseServices.GetContentTypeOf(contentItem); - var ids = contentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray(); + var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); if (ids.Any() == false) return Enumerable.Empty(); - types = Services.ContentTypeService.GetAll(ids).ToList(); + types = Services.ContentTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); } var basics = types.Where(type => type.IsElement == false).Select(Mapper.Map).ToList(); @@ -455,7 +472,7 @@ namespace Umbraco.Web.Editors } } - return basics; + return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty); } /// @@ -517,7 +534,7 @@ namespace Umbraco.Web.Editors [HttpPost] public HttpResponseMessage Import(string file) { - var filePath = Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Data), file); + var filePath = Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Data), file); if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(filePath)) { return Request.CreateResponse(HttpStatusCode.NotFound); @@ -555,7 +572,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = Current.IOHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "FileUploads"); + var root = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "FileUploads"); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -598,7 +615,7 @@ namespace Umbraco.Web.Editors } else { - model.Notifications.Add(new Notification( + model.Notifications.Add(new BackOfficeNotification( Services.TextService.Localize("speechBubbles/operationFailedHeader"), Services.TextService.Localize("media/disallowedFileType"), NotificationStyle.Warning)); diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 1e4500ddac..85ead9924d 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -5,18 +5,21 @@ using System.Net; using System.Net.Http; using System.Text; using System.Web.Http; +using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; namespace Umbraco.Web.Editors @@ -29,9 +32,24 @@ namespace Umbraco.Web.Editors public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController where TContentType : class, IContentTypeComposition { - protected ContentTypeControllerBase(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + private readonly EditorValidatorCollection _editorValidatorCollection; + + protected ContentTypeControllerBase( + ICultureDictionary cultureDictionary, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider, + EditorValidatorCollection editorValidatorCollection) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { + _editorValidatorCollection = editorValidatorCollection; CultureDictionary = cultureDictionary; } @@ -258,7 +276,7 @@ namespace Umbraco.Web.Editors } // execute the external validators - EditorValidator.Validate(ModelState, contentTypeSave); + ValidateExternalValidators(ModelState, contentTypeSave); if (ModelState.IsValid == false) { @@ -350,6 +368,20 @@ namespace Umbraco.Web.Editors } } + private void ValidateExternalValidators(ModelStateDictionary modelState, object model) + { + var modelType = model.GetType(); + + var validationResults = _editorValidatorCollection + .Where(x => x.ModelType == modelType) + .SelectMany(x => x.Validate(model)) + .Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) && x.MemberNames.Any()); + + foreach (var r in validationResults) + foreach (var m in r.MemberNames) + modelState.AddModelError(m, r.ErrorMessage); + } + /// /// Move /// @@ -549,6 +581,6 @@ namespace Umbraco.Web.Editors forDisplay.Errors = ModelState.ToErrorDictionary(); return new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); - } + } } } diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index b235aabf6a..384270743c 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -20,7 +20,10 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Strings; using Umbraco.Web.Security; using Umbraco.Web.WebApi.Filters; - +using Umbraco.Core.Mapping; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -31,6 +34,9 @@ namespace Umbraco.Web.Editors public class CurrentUserController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + private readonly IImageUrlGenerator _imageUrlGenerator; public CurrentUserController( IGlobalSettings globalSettings, @@ -40,12 +46,19 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IContentSettings contentSettings, + IIOHelper ioHelper, + IImageUrlGenerator imageUrlGenerator, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _mediaFileSystem = mediaFileSystem; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _imageUrlGenerator = imageUrlGenerator; } /// @@ -179,7 +192,7 @@ namespace Umbraco.Web.Editors public async Task PostSetAvatar() { //borrow the logic from the user controller - return await UsersController.PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, Security.GetUserId().ResultOr(0)); + return await UsersController.PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, _contentSettings, _ioHelper, _imageUrlGenerator, Security.GetUserId().ResultOr(0)); } /// diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 80d4172473..84aad2d0c0 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -20,6 +20,8 @@ using Umbraco.Core.Strings; using Umbraco.Core.Dashboards; using Umbraco.Core.Strings; using Umbraco.Web.Services; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -48,10 +50,11 @@ namespace Umbraco.Web.Editors IProfilingLogger logger, IRuntimeState runtimeState, IDashboardService dashboardService, - UmbracoHelper umbracoHelper, IUmbracoVersion umbracoVersion, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper,publishedUrlProvider) { _dashboardService = dashboardService; _umbracoVersion = umbracoVersion; diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 670c574d87..19ad546b2d 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -21,6 +21,10 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using System.Web.Http.Controllers; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -35,14 +39,42 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)] [EnableOverrideAuthorization] + [DataTypeControllerConfiguration] public class DataTypeController : BackOfficeNotificationsController { private readonly PropertyEditorCollection _propertyEditors; + private readonly IContentSettings _contentSettings; - public DataTypeController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public DataTypeController( + PropertyEditorCollection propertyEditors, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IContentSettings contentSettings, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _propertyEditors = propertyEditors; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + } + + /// + /// Configures this controller with a custom action selector + /// + private class DataTypeControllerConfigurationAttribute : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { + controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector( + new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)) + )); + } } /// @@ -71,6 +103,40 @@ namespace Umbraco.Web.Editors return Mapper.Map(dataType); } + /// + /// Gets the datatype json for the datatype guid + /// + /// + /// + public DataTypeDisplay GetById(Guid id) + { + var dataType = Services.DataTypeService.GetDataType(id); + if (dataType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + return Mapper.Map(dataType); + } + + /// + /// Gets the datatype json for the datatype udi + /// + /// + /// + public DataTypeDisplay GetById(Udi id) + { + var guidUdi = id as GuidUdi; + if (guidUdi == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + var dataType = Services.DataTypeService.GetDataType(guidUdi.Guid); + if (dataType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + return Mapper.Map(dataType); + } + /// /// Deletes a data type with a given ID /// @@ -379,7 +445,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 @@ -402,7 +468,7 @@ namespace Umbraco.Web.Editors public IDictionary> GetGroupedPropertyEditors() { var datatypes = new List(); - var showDeprecatedPropertyEditors = Current.Configs.Settings().Content.ShowDeprecatedPropertyEditors; + var showDeprecatedPropertyEditors = _contentSettings.ShowDeprecatedPropertyEditors; var propertyEditors = Current.PropertyEditors .Where(x=>x.IsDeprecated == false || showDeprecatedPropertyEditors); diff --git a/src/Umbraco.Web/Editors/DictionaryController.cs b/src/Umbraco.Web/Editors/DictionaryController.cs index ce808bab1e..56701d35ed 100644 --- a/src/Umbraco.Web/Editors/DictionaryController.cs +++ b/src/Umbraco.Web/Editors/DictionaryController.cs @@ -8,16 +8,17 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; namespace Umbraco.Web.Editors { @@ -34,8 +35,18 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class DictionaryController : BackOfficeNotificationsController { - public DictionaryController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public DictionaryController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { } @@ -187,7 +198,7 @@ namespace Umbraco.Web.Editors var model = Mapper.Map(dictionaryItem); - model.Notifications.Add(new Notification( + model.Notifications.Add(new BackOfficeNotification( Services.TextService.Localize("speechBubbles/dictionaryItemSaved", userCulture), string.Empty, NotificationStyle.Success)); diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs index 2b3eb278ee..df4c5e4b36 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; +using System.Collections.Generic; using System.Web.Http.Filters; using Umbraco.Core.Dashboards; using Umbraco.Core.Events; @@ -18,9 +16,6 @@ namespace Umbraco.Web.Editors public static event TypedEventHandler> SendingMemberModel; public static event TypedEventHandler> SendingUserModel; - [Obsolete("Please Use SendingDashboardSlimModel")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static event TypedEventHandler>>> SendingDashboardModel; public static event TypedEventHandler>>> SendingDashboardSlimModel; private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs>> e) diff --git a/src/Umbraco.Web/Editors/EditorValidator.cs b/src/Umbraco.Web/Editors/EditorValidator.cs deleted file mode 100644 index af355206a5..0000000000 --- a/src/Umbraco.Web/Editors/EditorValidator.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Linq; -using System.Web.Http.ModelBinding; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Editors -{ - /// - /// Provides a method to validate an object using validation. - /// - internal static class EditorValidator - { - /// - /// Validates an object. - /// - public static void Validate(ModelStateDictionary modelState, object model) - { - var modelType = model.GetType(); - - var validationResults = Current.EditorValidators // TODO: inject - .Where(x => x.ModelType == modelType) - .SelectMany(x => x.Validate(model)) - .Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) && x.MemberNames.Any()); - - foreach (var r in validationResults) - foreach (var m in r.MemberNames) - modelState.AddModelError(m, r.ErrorMessage); - } - } -} diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index c0a5212535..483834868f 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -30,6 +30,8 @@ using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -51,6 +53,7 @@ namespace Umbraco.Web.Editors private readonly ITreeService _treeService; private readonly UmbracoTreeSearcher _treeSearcher; private readonly SearchableTreeCollection _searchableTreeCollection; + private readonly IPublishedContentQuery _publishedContentQuery; public EntityController( IGlobalSettings globalSettings, @@ -61,16 +64,18 @@ namespace Umbraco.Web.Editors IProfilingLogger logger, IRuntimeState runtimeState, ITreeService treeService, - UmbracoHelper umbracoHelper, SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) - + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentQuery publishedContentQuery) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _treeService = treeService; _searchableTreeCollection = searchableTreeCollection; _treeSearcher = treeSearcher; + _publishedContentQuery = publishedContentQuery; } /// @@ -186,7 +191,7 @@ namespace Umbraco.Web.Editors { var foundContent = GetResultForId(id, type); - return foundContent.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + return foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); } /// @@ -218,6 +223,35 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } + /// + /// Gets the url of an entity + /// + /// UDI of the entity to fetch URL for + /// The culture to fetch the URL for + /// The URL or path to the item + public HttpResponseMessage GetUrl(Udi udi, string culture = "*") + { + var intId = Services.EntityService.GetId(udi); + if (!intId.Success) + throw new HttpResponseException(HttpStatusCode.NotFound); + UmbracoEntityTypes entityType; + switch (udi.EntityType) + { + case Constants.UdiEntityType.Document: + entityType = UmbracoEntityTypes.Document; + break; + case Constants.UdiEntityType.Media: + entityType = UmbracoEntityTypes.Media; + break; + case Constants.UdiEntityType.Member: + entityType = UmbracoEntityTypes.Member; + break; + default: + throw new HttpResponseException(HttpStatusCode.NotFound); + } + return GetUrl(intId.Result, entityType, culture); + } + /// /// Gets the url of an entity /// @@ -236,7 +270,7 @@ namespace Umbraco.Web.Editors if (type == UmbracoEntityTypes.Document) { - var foundUrl = UmbracoContext.Url(id, culture); + var foundUrl = PublishedUrlProvider.GetUrl(id, culture: culture); if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") { returnUrl = foundUrl; @@ -251,7 +285,8 @@ namespace Umbraco.Web.Editors var ancestors = GetResultForAncestors(id, type); //if content, skip the first node for replicating NiceUrl defaults - if(type == UmbracoEntityTypes.Document) { + if (type == UmbracoEntityTypes.Document) + { ancestors = ancestors.Skip(1); } @@ -281,7 +316,7 @@ namespace Umbraco.Web.Editors var q = ParseXPathQuery(query, nodeContextId); - var node = Umbraco.ContentSingleAtXPath(q); + var node = _publishedContentQuery.ContentSingleAtXPath(q); if (node == null) return null; @@ -300,7 +335,7 @@ namespace Umbraco.Web.Editors var ent = Services.EntityService.Get(nodeid); return ent.Path.Split(',').Reverse(); }, - publishedContentExists: i => Umbraco.Content(i) != null); + publishedContentExists: i => _publishedContentQuery.Content(i) != null); } [HttpGet] @@ -315,7 +350,9 @@ namespace Umbraco.Web.Editors [HttpGet] public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*") { - var url = UmbracoContext.UrlProvider.GetUrl(id); + culture = culture ?? ClientCulture(); + + var url = PublishedUrlProvider.GetUrl(id, culture: culture); var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture); return new UrlAndAnchors(url, anchorValues); } @@ -656,7 +693,7 @@ namespace Umbraco.Web.Editors case UmbracoEntityTypes.Media: return Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); default: - return Array.Empty(); + return Array.Empty(); } } @@ -750,7 +787,8 @@ namespace Umbraco.Web.Editors /// private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null, bool ignoreUserStartNodes = false) { - return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom, ignoreUserStartNodes); + var culture = ClientCulture(); + return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, culture, searchFrom, ignoreUserStartNodes); } private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) @@ -1074,7 +1112,7 @@ namespace Umbraco.Web.Editors case UmbracoEntityTypes.Language: - if (!postFilter.IsNullOrWhiteSpace() ) + if (!postFilter.IsNullOrWhiteSpace()) throw new NotSupportedException("Filtering on languages is not currently supported"); return Services.LocalizationService.GetAllLanguages().Select(MapEntities()); diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 16bf071445..2e1656b45d 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -5,7 +5,6 @@ using System.Net; using System.Net.Http; using System.Web.Http; using Examine; -using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.IO; @@ -24,17 +23,19 @@ namespace Umbraco.Web.Editors private readonly IExamineManager _examineManager; private readonly ILogger _logger; private readonly IIOHelper _ioHelper; + private readonly IIndexDiagnosticsFactory _indexDiagnosticsFactory; private readonly IAppPolicyCache _runtimeCache; private readonly IndexRebuilder _indexRebuilder; - public ExamineManagementController(IExamineManager examineManager, ILogger logger, IIOHelper ioHelper, + public ExamineManagementController(IExamineManager examineManager, ILogger logger, IIOHelper ioHelper, IIndexDiagnosticsFactory indexDiagnosticsFactory, AppCaches appCaches, IndexRebuilder indexRebuilder) { _examineManager = examineManager; _logger = logger; _ioHelper = ioHelper; + _indexDiagnosticsFactory = indexDiagnosticsFactory; _runtimeCache = appCaches.RuntimeCache; _indexRebuilder = indexRebuilder; } @@ -69,9 +70,8 @@ namespace Umbraco.Web.Editors if (!msg.IsSuccessStatusCode) throw new HttpResponseException(msg); - var results = Examine.ExamineExtensions.TryParseLuceneQuery(query) - ? searcher.CreateQuery().NativeQuery(query).Execute(maxResults: pageSize * (pageIndex + 1)) - : searcher.Search(query, maxResults: pageSize * (pageIndex + 1)); + // NativeQuery will work for a single word/phrase too (but depends on the implementation) the lucene one will work. + var results = searcher.CreateQuery().NativeQuery(query).Execute(maxResults: pageSize * (pageIndex + 1)); var pagedResults = results.Skip(pageIndex * pageSize); @@ -173,19 +173,11 @@ namespace Umbraco.Web.Editors } } - - private ExamineIndexModel CreateModel(IIndex index) { var indexName = index.Name; - if (!(index is IIndexDiagnostics indexDiag)) - { - if (index is LuceneIndex luceneIndex) - indexDiag = new LuceneIndexDiagnostics(luceneIndex, Logger, _ioHelper); - else - indexDiag = new GenericIndexDiagnostics(index); - } + var indexDiag = _indexDiagnosticsFactory.Create(index); var isHealth = indexDiag.IsHealthy(); var properties = new Dictionary diff --git a/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs index b94d4d43bc..de84d80074 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Editors.Filters /// /// /// - private bool ValidateUserAccess(ContentItemSave contentItem, HttpActionContext actionContext, WebSecurity webSecurity) + private bool ValidateUserAccess(ContentItemSave contentItem, HttpActionContext actionContext, IWebSecurity webSecurity) { //We now need to validate that the user is allowed to be doing what they are doing. diff --git a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs b/src/Umbraco.Web/Editors/Filters/DataTypeValidateAttribute.cs similarity index 99% rename from src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs rename to src/Umbraco.Web/Editors/Filters/DataTypeValidateAttribute.cs index 1337545ee0..efdcf93fff 100644 --- a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/DataTypeValidateAttribute.cs @@ -5,7 +5,7 @@ using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; diff --git a/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs b/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs index e1d6626055..daa3ae3491 100644 --- a/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs @@ -4,7 +4,7 @@ using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; namespace Umbraco.Web.Editors.Filters { @@ -33,7 +33,7 @@ namespace Umbraco.Web.Editors.Filters _paramName = paramName; } - private UmbracoContext GetUmbracoContext() + private IUmbracoContext GetUmbracoContext() { return _umbracoContextAccessor?.UmbracoContext ?? Composing.Current.UmbracoContext; } diff --git a/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs b/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs index 78cd8e6a4d..f17eddd44b 100644 --- a/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs @@ -5,7 +5,7 @@ using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core; using Umbraco.Core.Mapping; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; diff --git a/src/Umbraco.Web/Editors/ImageUrlGeneratorController.cs b/src/Umbraco.Web/Editors/ImageUrlGeneratorController.cs new file mode 100644 index 0000000000..87d7e29619 --- /dev/null +++ b/src/Umbraco.Web/Editors/ImageUrlGeneratorController.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Web.Editors +{ + /// + /// The API controller used for getting URLs for images with parameters + /// + /// + /// + /// This controller allows for retrieving URLs for processed images, such as resized, cropped, + /// or otherwise altered. These can be different based on the IImageUrlGenerator + /// implementation in use, and so the BackOffice could should not rely on hard-coded string + /// building to generate correct URLs + /// + /// + public class ImageUrlGeneratorController : UmbracoAuthorizedJsonController + { + private readonly IImageUrlGenerator _imageUrlGenerator; + + public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) + { + _imageUrlGenerator = imageUrlGenerator; + } + + public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null, string animationProcessMode = null) + { + return mediaPath.GetCropUrl(_imageUrlGenerator, null, width: width, height: height, imageCropMode: imageCropMode, animationProcessMode: animationProcessMode); + } + } +} diff --git a/src/Umbraco.Web/Editors/ImagesController.cs b/src/Umbraco.Web/Editors/ImagesController.cs index f6452ffba2..39fe619b9d 100644 --- a/src/Umbraco.Web/Editors/ImagesController.cs +++ b/src/Umbraco.Web/Editors/ImagesController.cs @@ -4,7 +4,7 @@ using System.Net; using System.Net.Http; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Web.Media; +using Umbraco.Core.Models; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -17,12 +17,14 @@ namespace Umbraco.Web.Editors public class ImagesController : UmbracoAuthorizedApiController { private readonly IMediaFileSystem _mediaFileSystem; - private readonly IContentSection _contentSection; + private readonly IContentSettings _contentSettings; + private readonly IImageUrlGenerator _imageUrlGenerator; - public ImagesController(IMediaFileSystem mediaFileSystem, IContentSection contentSection) + public ImagesController(IMediaFileSystem mediaFileSystem, IContentSettings contentSettings, IImageUrlGenerator imageUrlGenerator) { _mediaFileSystem = mediaFileSystem; - _contentSection = contentSection; + _contentSettings = contentSettings; + _imageUrlGenerator = imageUrlGenerator; } /// @@ -54,13 +56,30 @@ namespace Umbraco.Web.Editors var ext = Path.GetExtension(imagePath); // we need to check if it is an image by extension - if (_contentSection.IsImageFile(ext) == false) + if (_contentSettings.IsImageFile(ext) == false) return Request.CreateResponse(HttpStatusCode.NotFound); //redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file var response = Request.CreateResponse(HttpStatusCode.Found); - var imageLastModified = _mediaFileSystem.GetLastModified(imagePath); - response.Headers.Location = new Uri($"{imagePath}?rnd={imageLastModified:yyyyMMddHHmmss}&upscale=false&width={width}&animationprocessmode=first&mode=max", UriKind.Relative); + + DateTimeOffset? imageLastModified = null; + try + { + imageLastModified = _mediaFileSystem.GetLastModified(imagePath); + + } + catch (Exception) + { + // if we get an exception here it's probably because the image path being requested is an image that doesn't exist + // in the local media file system. This can happen if someone is storing an absolute path to an image online, which + // is perfectly legal but in that case the media file system isn't going to resolve it. + // so ignore and we won't set a last modified date. + } + + var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; + var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) { UpScale = false, Width = width, AnimationProcessMode = "first", ImageCropMode = "max", CacheBusterValue = rnd }); + + response.Headers.Location = new Uri(imageUrl, UriKind.RelativeOrAbsolute); return response; } diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs index f02a79b574..b502802775 100644 --- a/src/Umbraco.Web/Editors/LogController.cs +++ b/src/Umbraco.Web/Editors/LogController.cs @@ -6,12 +6,14 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors @@ -23,6 +25,7 @@ namespace Umbraco.Web.Editors public class LogController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; + private readonly IImageUrlGenerator _imageUrlGenerator; public LogController( IGlobalSettings globalSettings, @@ -32,12 +35,15 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IImageUrlGenerator imageUrlGenerator, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _mediaFileSystem = mediaFileSystem; + _imageUrlGenerator = imageUrlGenerator; } [UmbracoApplicationAuthorize(Core.Constants.Applications.Content, Core.Constants.Applications.Media)] @@ -92,7 +98,7 @@ namespace Umbraco.Web.Editors var mappedItems = items.ToList(); var userIds = mappedItems.Select(x => x.UserId).ToArray(); var userAvatars = Services.UserService.GetUsersById(userIds) - .ToDictionary(x => x.Id, x => x.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem)); + .ToDictionary(x => x.Id, x => x.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator)); var userNames = Services.UserService.GetUsersById(userIds).ToDictionary(x => x.Id, x => x.Name); foreach (var item in mappedItems) { diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 9e9b7efc1b..43b544cb1f 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -11,10 +11,16 @@ using System.Web.SessionState; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -30,18 +36,39 @@ namespace Umbraco.Web.Editors public class MacroRenderingController : UmbracoAuthorizedJsonController, IRequiresSessionState { private readonly IMacroService _macroService; - private readonly IContentService _contentService; - private readonly IShortStringHelper _shortStringHelper; private readonly IUmbracoComponentRenderer _componentRenderer; private readonly IVariationContextAccessor _variationContextAccessor; - public MacroRenderingController(IUmbracoComponentRenderer componentRenderer, IVariationContextAccessor variationContextAccessor, IMacroService macroService, IContentService contentService, IShortStringHelper shortStringHelper) + + public MacroRenderingController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IUmbracoComponentRenderer componentRenderer, + IVariationContextAccessor variationContextAccessor, + IMacroService macroService, + IPublishedUrlProvider publishedUrlProvider) + : base( + globalSettings, + umbracoContextAccessor, + sqlContext, + services, + appCaches, + logger, + runtimeState, + shortStringHelper, + umbracoMapper, + publishedUrlProvider) { _componentRenderer = componentRenderer; _variationContextAccessor = variationContextAccessor; _macroService = macroService; - _contentService = contentService; - _shortStringHelper = shortStringHelper; } /// @@ -162,12 +189,11 @@ namespace Umbraco.Web.Editors var macroName = model.Filename.TrimEnd(".cshtml"); - var macro = new Macro(_shortStringHelper) + var macro = new Macro(ShortStringHelper) { Alias = macroName.ToSafeAlias(ShortStringHelper), Name = macroName, - MacroSource = model.VirtualPath.EnsureStartsWith("~"), - MacroType = MacroTypes.PartialView + MacroSource = model.VirtualPath.EnsureStartsWith("~") }; _macroService.Save(macro); // may throw diff --git a/src/Umbraco.Web/Editors/MacrosController.cs b/src/Umbraco.Web/Editors/MacrosController.cs index 12f0933500..babeb921fa 100644 --- a/src/Umbraco.Web/Editors/MacrosController.cs +++ b/src/Umbraco.Web/Editors/MacrosController.cs @@ -13,12 +13,16 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Strings; -using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using System.Web.Http.Controllers; +using Umbraco.Core.IO; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -28,16 +32,46 @@ namespace Umbraco.Web.Editors /// [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.Macros)] + [MacrosControllerConfiguration] public class MacrosController : BackOfficeNotificationsController { + private readonly ParameterEditorCollection _parameterEditorCollection; + private readonly IIOHelper _ioHelper; private readonly IMacroService _macroService; - public MacrosController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public MacrosController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + ParameterEditorCollection parameterEditorCollection, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { + _parameterEditorCollection = parameterEditorCollection; + _ioHelper = ioHelper; _macroService = Services.MacroService; } + /// + /// Configures this controller with a custom action selector + /// + private class MacrosControllerConfigurationAttribute : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { + controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector( + new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)) + )); + } + } + /// /// Creates a new macro /// @@ -73,8 +107,7 @@ namespace Umbraco.Web.Editors { Alias = alias, Name = name, - MacroSource = string.Empty, - MacroType = MacroTypes.PartialView + MacroSource = string.Empty }; _macroService.Save(macro, this.Security.CurrentUser.Id); @@ -97,39 +130,43 @@ namespace Umbraco.Web.Editors return this.ReturnErrorResponse($"Macro with id {id} does not exist"); } - var macroDisplay = new MacroDisplay - { - Alias = macro.Alias, - Id = macro.Id, - Key = macro.Key, - Name = macro.Name, - CacheByPage = macro.CacheByPage, - CacheByUser = macro.CacheByMember, - CachePeriod = macro.CacheDuration, - View = macro.MacroSource, - RenderInEditor = !macro.DontRender, - UseInEditor = macro.UseInEditor, - Path = $"-1,{macro.Id}" - }; - - var parameters = new List(); - - foreach (var param in macro.Properties.Values.OrderBy(x => x.SortOrder)) - { - parameters.Add(new MacroParameterDisplay - { - Editor = param.EditorAlias, - Key = param.Alias, - Label = param.Name, - Id = param.Id - }); - } - - macroDisplay.Parameters = parameters; + var macroDisplay = MapToDisplay(macro); return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay); } + [HttpGet] + public HttpResponseMessage GetById(Guid id) + { + var macro = _macroService.GetById(id); + + if (macro == null) + { + return this.ReturnErrorResponse($"Macro with id {id} does not exist"); + } + + var macroDisplay = MapToDisplay(macro); + + return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay); + } + + [HttpGet] + public HttpResponseMessage GetById(Udi id) + { + var guidUdi = id as GuidUdi; + if (guidUdi == null) + this.ReturnErrorResponse($"Macro with id {id} does not exist"); + + var macro = _macroService.GetById(guidUdi.Guid); + if (macro == null) + { + return this.ReturnErrorResponse($"Macro with id {id} does not exist"); + } + + var macroDisplay = MapToDisplay(macro); + + return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay); + } [HttpPost] public HttpResponseMessage DeleteById(int id) @@ -184,7 +221,6 @@ namespace Umbraco.Web.Editors macro.DontRender = !macroDisplay.RenderInEditor; macro.UseInEditor = macroDisplay.UseInEditor; macro.MacroSource = macroDisplay.View; - macro.MacroType = MacroTypes.PartialView; macro.Properties.ReplaceAll(macroDisplay.Parameters.Select((x,i) => new MacroProperty(x.Key, x.Label, i, x.Editor))); try @@ -193,7 +229,7 @@ namespace Umbraco.Web.Editors macroDisplay.Notifications.Clear(); - macroDisplay.Notifications.Add(new Models.ContentEditing.Notification("Success", "Macro saved", NotificationStyle.Success)); + macroDisplay.Notifications.Add(new BackOfficeNotification("Success", "Macro saved", NotificationStyle.Success)); return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay); } @@ -226,7 +262,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetParameterEditors() { - return this.Request.CreateResponse(HttpStatusCode.OK, Current.ParameterEditors); + return this.Request.CreateResponse(HttpStatusCode.OK, _parameterEditorCollection); } /// @@ -237,7 +273,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetGroupedParameterEditors() { - var parameterEditors = Current.ParameterEditors.ToArray(); + var parameterEditors = _parameterEditorCollection.ToArray(); var grouped = parameterEditors .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group.ToLower()) @@ -255,7 +291,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetParameterEditorByAlias(string alias) { - var parameterEditors = Current.ParameterEditors.ToArray(); + var parameterEditors = _parameterEditorCollection.ToArray(); var parameterEditor = parameterEditors.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); @@ -311,7 +347,7 @@ namespace Umbraco.Web.Editors /// private IEnumerable FindPartialViewFilesInViewsFolder() { - var partialsDir = Current.IOHelper.MapPath(Constants.SystemDirectories.MacroPartials); + var partialsDir = _ioHelper.MapPath(Constants.SystemDirectories.MacroPartials); return this.FindPartialViewFilesInFolder( partialsDir, @@ -328,7 +364,7 @@ namespace Umbraco.Web.Editors { var files = new List(); - var appPluginsFolder = new DirectoryInfo(Current.IOHelper.MapPath(Constants.SystemDirectories.AppPlugins)); + var appPluginsFolder = new DirectoryInfo(_ioHelper.MapPath(Constants.SystemDirectories.AppPlugins)); if (!appPluginsFolder.Exists) { @@ -384,5 +420,29 @@ namespace Umbraco.Web.Editors return files; } + + /// + /// Used to map an instance to a + /// + /// + /// + private MacroDisplay MapToDisplay(IMacro macro) + { + var display = Mapper.Map(macro); + + var parameters = macro.Properties.Values + .OrderBy(x => x.SortOrder) + .Select(x => new MacroParameterDisplay() + { + Editor = x.EditorAlias, + Key = x.Alias, + Label = x.Name, + Id = x.Id + }); + + display.Parameters = parameters; + + return display; + } } } diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 421e861620..40f00d54e5 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -11,34 +11,33 @@ using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Dictionary; using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Validation; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; -using Umbraco.Core.Strings; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -51,6 +50,9 @@ namespace Umbraco.Web.Editors [MediaControllerControllerConfiguration] public class MediaController : ContentControllerBase { + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + public MediaController( ICultureDictionary cultureDictionary, PropertyEditorCollection propertyEditors, @@ -61,13 +63,18 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, - IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IContentSettings contentSettings, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); _mediaFileSystem = mediaFileSystem; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); } /// @@ -448,7 +455,7 @@ namespace Umbraco.Web.Editors if (sourceParentID == destinationParentID) { - return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new Notification("",Services.TextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error))); + return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new BackOfficeNotification("",Services.TextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error))); } if (moveResult == false) { @@ -638,7 +645,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = Current.IOHelper.MapPath(Constants.SystemDirectories.TempFileUploads); + var root = _ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -715,13 +722,13 @@ namespace Umbraco.Web.Editors var safeFileName = fileName.ToSafeFileName(ShortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext)) + if (_contentSettings.IsFileAllowedForUpload(ext)) { var mediaType = Constants.Conventions.MediaTypes.File; if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect) { - if (Current.Configs.Settings().Content.ImageFileTypes.Contains(ext)) + if (_contentSettings.ImageFileTypes.Contains(ext)) { mediaType = Constants.Conventions.MediaTypes.Image; } @@ -761,7 +768,7 @@ namespace Umbraco.Web.Editors } else { - tempFiles.Notifications.Add(new Notification( + tempFiles.Notifications.Add(new BackOfficeNotification( Services.TextService.Localize("speechBubbles/operationFailedHeader"), Services.TextService.Localize("media/disallowedFileType"), NotificationStyle.Warning)); @@ -852,7 +859,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(Request.CreateResponse( HttpStatusCode.Forbidden, - new SimpleNotificationModel(new Notification( + new SimpleNotificationModel(new BackOfficeNotification( Services.TextService.Localize("speechBubbles/operationFailedHeader"), Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), NotificationStyle.Warning)))); diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index b0f21e26e0..e6130ca1a9 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -21,6 +21,8 @@ using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Constants = Umbraco.Core.Constants; using IMediaType = Umbraco.Core.Models.IMediaType; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -38,11 +40,26 @@ namespace Umbraco.Web.Editors public class MediaTypeController : ContentTypeControllerBase { private readonly IShortStringHelper _shortStringHelper; + private readonly IEntityService _entityService; - public MediaTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public MediaTypeController( + ICultureDictionary cultureDictionary, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IEntityService entityService, + IPublishedUrlProvider publishedUrlProvider, + EditorValidatorCollection editorValidatorCollection) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) { _shortStringHelper = shortStringHelper; + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); } /// @@ -244,11 +261,11 @@ namespace Umbraco.Web.Editors } var contentType = Services.MediaTypeService.Get(contentItem.ContentTypeId); - var ids = contentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray(); + var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); if (ids.Any() == false) return Enumerable.Empty(); - types = Services.MediaTypeService.GetAll(ids).ToList(); + types = Services.MediaTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); } var basics = types.Select(Mapper.Map).ToList(); @@ -259,7 +276,7 @@ namespace Umbraco.Web.Editors basic.Description = TranslateItem(basic.Description); } - return basics.OrderBy(x => x.Name); + return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty); } /// @@ -269,7 +286,7 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] public IEnumerable GetAllowedChildren(Guid contentId) { - var entity = Current.Services.EntityService.Get(contentId); + var entity = _entityService.Get(contentId); if (entity != null) { return GetAllowedChildren(entity.Id); @@ -288,7 +305,7 @@ namespace Umbraco.Web.Editors var guidUdi = contentId as GuidUdi; if (guidUdi != null) { - var entity = Current.Services.EntityService.Get(guidUdi.Guid); + var entity = _entityService.Get(guidUdi.Guid); if (entity != null) { return GetAllowedChildren(entity.Id); diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 4c395a56f9..55a3e29c5c 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -32,6 +32,8 @@ using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Strings; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -55,9 +57,10 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, - IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _passwordConfig = passwordConfig ?? throw new ArgumentNullException(nameof(passwordConfig)); _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index b10fc23e50..63ec9c62c7 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -6,7 +6,7 @@ using System.Net.Http; using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; @@ -18,6 +18,8 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -28,8 +30,20 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] public class MemberTypeController : ContentTypeControllerBase { - public MemberTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public MemberTypeController( + ICultureDictionary cultureDictionary, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider, + EditorValidatorCollection editorValidatorCollection) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) { } diff --git a/src/Umbraco.Web/Editors/NuCacheStatusController.cs b/src/Umbraco.Web/Editors/NuCacheStatusController.cs deleted file mode 100644 index 86dbdd4e01..0000000000 --- a/src/Umbraco.Web/Editors/NuCacheStatusController.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Web.Http; -using Umbraco.Web.Cache; -using Umbraco.Web.Composing; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.PublishedCache.NuCache; -using Umbraco.Web.WebApi; - -namespace Umbraco.Web.Editors -{ - public class NuCacheStatusController : UmbracoAuthorizedApiController - { - private readonly IPublishedSnapshotService _publishedSnapshotService; - - public NuCacheStatusController(IPublishedSnapshotService publishedSnapshotService) - { - _publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService)); - } - - private PublishedSnapshotService PublishedSnapshotService - { - get - { - var svc = _publishedSnapshotService as PublishedSnapshotService; - if (svc == null) - throw new NotSupportedException("Not running NuCache."); - return svc; - } - } - - [HttpPost] - public string RebuildDbCache() - { - var service = PublishedSnapshotService; - service.RebuildContentDbCache(); - service.RebuildMediaDbCache(); - service.RebuildMemberDbCache(); - return service.GetStatus(); - } - - [HttpGet] - public string GetStatus() - { - var service = PublishedSnapshotService; - return service.GetStatus(); - } - - [HttpGet] - public string Collect() - { - var service = PublishedSnapshotService; - GC.Collect(); - service.Collect(); - return service.GetStatus(); - } - - [HttpPost] - public void ReloadCache() - { - Current.DistributedCache.RefreshAllPublishedSnapshot(); - } - } -} diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index d3ef290a72..6dc913237d 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -9,11 +8,18 @@ using System.Text; using System.Web; using System.Web.Http; using Semver; -using Umbraco.Core.Composing; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models.Packaging; -using Umbraco.Web.Models.ContentEditing; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -27,6 +33,25 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageController : UmbracoAuthorizedJsonController { + private readonly IIOHelper _ioHelper; + + public PackageController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) + { + _ioHelper = ioHelper; + } + public IEnumerable GetCreatedPackages() { return Services.PackagingService.GetAllCreatedPackages(); @@ -91,7 +116,7 @@ namespace Umbraco.Web.Editors if (package == null) return Request.CreateResponse(HttpStatusCode.NotFound); - var fullPath = Current.IOHelper.MapPath(package.PackagePath); + var fullPath = _ioHelper.MapPath(package.PackagePath); if (!File.Exists(fullPath)) return Request.CreateNotificationValidationErrorResponse("No file found for path " + package.PackagePath); diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 8b62b23688..672b47f1eb 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -8,23 +8,25 @@ using System.Web.Http; using Semver; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Packaging; 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; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using File = System.IO.File; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; namespace Umbraco.Web.Editors { @@ -37,13 +39,28 @@ namespace Umbraco.Web.Editors { private readonly IUmbracoVersion _umbracoVersion; - public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, - IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper, IUmbracoVersion umbracoVersion) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + private readonly IIOHelper _ioHelper; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + + public PackageInstallController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + IUmbracoVersion umbracoVersion, + UmbracoMapper umbracoMapper, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider, + IUmbracoApplicationLifetime umbracoApplicationLifetime) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _umbracoVersion = umbracoVersion; - + _ioHelper = ioHelper; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } /// @@ -94,7 +111,7 @@ namespace Umbraco.Web.Editors private void PopulateFromPackageData(LocalPackageInstallModel model) { - var zipFile = new FileInfo(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName)); + var zipFile = new FileInfo(Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName)); var ins = Services.PackagingService.GetCompiledPackageInfo(zipFile); @@ -107,8 +124,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; @@ -136,7 +153,7 @@ namespace Umbraco.Web.Editors if (Request.Content.IsMimeMultipartContent() == false) throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - var root = Current.IOHelper.MapPath(Core.Constants.SystemDirectories.TempFileUploads); + var root = _ioHelper.MapPath(Core.Constants.SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -163,7 +180,7 @@ namespace Umbraco.Web.Editors { //we always save package files to /App_Data/packages/package-guid.umb for processing as a standard so lets copy. - var packagesFolder = Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages); + var packagesFolder = _ioHelper.MapPath(Core.Constants.SystemDirectories.Packages); Directory.CreateDirectory(packagesFolder); var packageFile = Path.Combine(packagesFolder, model.PackageGuid + ".umb"); File.Copy(file.LocalFileName, packageFile); @@ -193,7 +210,7 @@ namespace Umbraco.Web.Editors } else { - model.Notifications.Add(new Notification( + model.Notifications.Add(new BackOfficeNotification( Services.TextService.Localize("speechBubbles/operationFailedHeader"), Services.TextService.Localize("media/disallowedFileType"), NotificationStyle.Warning)); @@ -215,7 +232,7 @@ namespace Umbraco.Web.Editors { //Default path string fileName = packageGuid + ".umb"; - if (File.Exists(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), fileName)) == false) + if (File.Exists(Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Packages), fileName)) == false) { var packageFile = await Services.PackagingService.FetchPackageFileAsync( Guid.Parse(packageGuid), @@ -255,7 +272,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var zipFile = new FileInfo(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName)); + var zipFile = new FileInfo(Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName)); var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile); @@ -311,7 +328,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; @@ -323,12 +340,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; } @@ -368,7 +381,7 @@ namespace Umbraco.Web.Editors zipFile.Delete(); //bump cdf to be safe - var clientDependencyConfig = new ClientDependencyConfiguration(Logger); + var clientDependencyConfig = new ClientDependencyConfiguration(Logger, _ioHelper); var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber( _umbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd"); diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index 6da4e364e1..179ef8fb45 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; using Umbraco.Core.Security; using Umbraco.Web.Models; +using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using IUser = Umbraco.Core.Models.Membership.IUser; diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index e5d7577163..3539047112 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -5,6 +5,10 @@ using System.Web.Mvc; using System.Web.UI; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Cookie; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Features; @@ -12,6 +16,7 @@ using Umbraco.Web.JavaScript; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Trees; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors @@ -25,6 +30,14 @@ namespace Umbraco.Web.Editors private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILocalizationService _localizationService; private readonly IUmbracoVersion _umbracoVersion; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + private readonly TreeCollection _treeCollection; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly ICookieManager _cookieManager; + private IRuntimeSettings _runtimeSettings; + private readonly ISecuritySettings _securitySettings; public PreviewController( UmbracoFeatures features, @@ -32,7 +45,15 @@ namespace Umbraco.Web.Editors IPublishedSnapshotService publishedSnapshotService, IUmbracoContextAccessor umbracoContextAccessor, ILocalizationService localizationService, - IUmbracoVersion umbracoVersion) + IUmbracoVersion umbracoVersion, + IContentSettings contentSettings, + IIOHelper ioHelper, + TreeCollection treeCollection, + IHttpContextAccessor httpContextAccessor, + IHostingEnvironment hostingEnvironment, + ICookieManager cookieManager, + IRuntimeSettings settings, + ISecuritySettings securitySettings) { _features = features; _globalSettings = globalSettings; @@ -40,6 +61,14 @@ namespace Umbraco.Web.Editors _umbracoContextAccessor = umbracoContextAccessor; _localizationService = localizationService; _umbracoVersion = umbracoVersion; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _treeCollection = treeCollection; + _httpContextAccessor = httpContextAccessor; + _hostingEnvironment = hostingEnvironment; + _cookieManager = cookieManager; + _runtimeSettings = settings; + _securitySettings = securitySettings; } [UmbracoAuthorize(redirectToUmbracoLogin: true)] @@ -48,7 +77,7 @@ namespace Umbraco.Web.Editors { var availableLanguages = _localizationService.GetAllLanguages(); - var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages); + var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { @@ -59,7 +88,7 @@ namespace Umbraco.Web.Editors } } - return View(_globalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + "Index.cshtml", model); + return View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Preview/" + "Index.cshtml", model); } /// @@ -71,7 +100,7 @@ namespace Umbraco.Web.Editors public JavaScriptResult Application() { var files = JsInitialization.OptimizeScriptFiles(HttpContext, JsInitialization.GetPreviewInitialization()); - var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco.preview"); + var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco.preview", _globalSettings, _ioHelper); return JavaScript(result); } @@ -98,7 +127,7 @@ namespace Umbraco.Web.Editors public ActionResult End(string redir = null) { - var previewToken = Request.GetPreviewCookieValue(); + var previewToken = _cookieManager.GetPreviewCookieValue(); var service = Current.PublishedSnapshotService; service.ExitPreview(previewToken); diff --git a/src/Umbraco.Web/Editors/PublishedSnapshotCacheStatusController.cs b/src/Umbraco.Web/Editors/PublishedSnapshotCacheStatusController.cs new file mode 100644 index 0000000000..6a701fdcfa --- /dev/null +++ b/src/Umbraco.Web/Editors/PublishedSnapshotCacheStatusController.cs @@ -0,0 +1,46 @@ +using System; +using System.Web.Http; +using Umbraco.Web.Cache; +using Umbraco.Web.Composing; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.WebApi; + +namespace Umbraco.Web.Editors +{ + public class PublishedSnapshotCacheStatusController : UmbracoAuthorizedApiController + { + private readonly IPublishedSnapshotService _publishedSnapshotService; + + public PublishedSnapshotCacheStatusController(IPublishedSnapshotService publishedSnapshotService) + { + _publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService)); + } + + [HttpPost] + public string RebuildDbCache() + { + _publishedSnapshotService.Rebuild(); + return _publishedSnapshotService.GetStatus(); + } + + [HttpGet] + public string GetStatus() + { + return _publishedSnapshotService.GetStatus(); + } + + [HttpGet] + public string Collect() + { + GC.Collect(); + _publishedSnapshotService.Collect(); + return _publishedSnapshotService.GetStatus(); + } + + [HttpPost] + public void ReloadCache() + { + Current.DistributedCache.RefreshAllPublishedSnapshot(); + } + } +} diff --git a/src/Umbraco.Web/Editors/PublishedStatusController.cs b/src/Umbraco.Web/Editors/PublishedStatusController.cs index a6272108e9..5ed70c4811 100644 --- a/src/Umbraco.Web/Editors/PublishedStatusController.cs +++ b/src/Umbraco.Web/Editors/PublishedStatusController.cs @@ -17,11 +17,10 @@ namespace Umbraco.Web.Editors [HttpGet] public string GetPublishedStatusUrl() { - //if (service is PublishedCache.PublishedNoCache.PublishedSnapshotService) - // return "views/dashboard/developer/nocache.html"; - - if (_publishedSnapshotService is PublishedCache.NuCache.PublishedSnapshotService) - return "views/dashboard/settings/nucache.html"; + if (!string.IsNullOrWhiteSpace(_publishedSnapshotService.StatusUrl)) + { + return _publishedSnapshotService.StatusUrl; + } throw new NotSupportedException("Not supported: " + _publishedSnapshotService.GetType().FullName); } diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index 9252899027..d1bb3f873c 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -1,7 +1,6 @@ using System; using System.Web.Http; using System.Xml; -using System.Collections.Generic; using System.Linq; using System.Security; using Umbraco.Core.Logging; @@ -11,7 +10,8 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using File = System.IO.File; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Web.Editors { @@ -19,10 +19,12 @@ namespace Umbraco.Web.Editors public class RedirectUrlManagementController : UmbracoAuthorizedApiController { private readonly ILogger _logger; + private readonly IWebRoutingSettings _webRoutingSettings; - public RedirectUrlManagementController(ILogger logger) + public RedirectUrlManagementController(ILogger logger, IWebRoutingSettings webRoutingSettings) { _logger = logger; + _webRoutingSettings = webRoutingSettings; } /// @@ -32,7 +34,7 @@ namespace Umbraco.Web.Editors [HttpGet] public IHttpActionResult GetEnableState() { - var enabled = Current.Configs.Settings().WebRouting.DisableRedirectUrlTracking == false; + var enabled = _webRoutingSettings.DisableRedirectUrlTracking == false; var userIsAdmin = UmbracoContext.Security.CurrentUser.IsAdmin(); return Ok(new { enabled, userIsAdmin }); } diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index 55b3ced23a..b5b983c9ff 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -17,6 +17,8 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -36,9 +38,10 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { } @@ -94,7 +97,6 @@ namespace Umbraco.Web.Editors new ObjectType{Id = UmbracoObjectTypes.MemberType.GetGuid(), Name = UmbracoObjectTypes.MemberType.GetFriendlyName()}, new ObjectType{Id = UmbracoObjectTypes.DataType.GetGuid(), Name = UmbracoObjectTypes.DataType.GetFriendlyName()}, new ObjectType{Id = UmbracoObjectTypes.MemberGroup.GetGuid(), Name = UmbracoObjectTypes.MemberGroup.GetFriendlyName()}, - new ObjectType{Id = UmbracoObjectTypes.Stylesheet.GetGuid(), Name = UmbracoObjectTypes.Stylesheet.GetFriendlyName()}, new ObjectType{Id = UmbracoObjectTypes.ROOT.GetGuid(), Name = UmbracoObjectTypes.ROOT.GetFriendlyName()}, new ObjectType{Id = UmbracoObjectTypes.RecycleBin.GetGuid(), Name = UmbracoObjectTypes.RecycleBin.GetFriendlyName()}, }; diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index 102f962ecb..f352d9154f 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -13,6 +13,8 @@ using Umbraco.Web.Trees; using Section = Umbraco.Web.Models.ContentEditing.Section; using Umbraco.Web.Models.Trees; using Umbraco.Web.Services; +using Umbraco.Core.Mapping; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -27,8 +29,8 @@ namespace Umbraco.Web.Editors private readonly ITreeService _treeService; public SectionController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, IShortStringHelper shortStringHelper, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _dashboardService = dashboardService; _sectionService = sectionService; @@ -43,7 +45,7 @@ namespace Umbraco.Web.Editors // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that // since tree's by nature are controllers and require request contextual data - var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContextAccessor, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, _sectionService, Umbraco) + var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContextAccessor, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, _sectionService, Mapper, PublishedUrlProvider) { ControllerContext = ControllerContext }; diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 524ecea1eb..a02536e2bb 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -9,12 +9,14 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; @@ -24,8 +26,18 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.Templates)] public class TemplateController : BackOfficeNotificationsController { - public TemplateController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + public TemplateController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { } @@ -196,7 +208,9 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Alias, display.Content, master); + // we need to pass the template name as alias to keep the template file casing consistent with templates created with content + // - see comment in FileService.CreateTemplateForContentType for additional details + var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Name, display.Content, master); Mapper.Map(template, display); } diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index ed737e7749..df7f8a0d96 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -4,10 +4,17 @@ using System.Diagnostics; using System.Linq; using System.Text; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.TemplateQuery; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; namespace Umbraco.Web.Editors @@ -19,6 +26,28 @@ namespace Umbraco.Web.Editors [JsonCamelCaseFormatter] public class TemplateQueryController : UmbracoAuthorizedJsonController { + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IPublishedContentQuery _publishedContentQuery; + + public TemplateQueryController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IVariationContextAccessor variationContextAccessor, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentQuery publishedContentQuery) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) + { + _variationContextAccessor = variationContextAccessor; + _publishedContentQuery = publishedContentQuery; + } + private IEnumerable Terms => new List { new OperatorTerm(Services.TextService.Localize("template/is"), Operator.Equals, new [] {"string"}), @@ -52,7 +81,7 @@ namespace Umbraco.Web.Editors if (model == null) { - contents = Umbraco.ContentAtRoot().FirstOrDefault().Children(); + contents = _publishedContentQuery.ContentAtRoot().FirstOrDefault().Children(_variationContextAccessor); queryExpression.Append("Umbraco.ContentAtRoot().FirstOrDefault().Children()"); } else @@ -83,7 +112,7 @@ namespace Umbraco.Web.Editors IPublishedContent sourceDocument; if (model.Source != null && model.Source.Id > 0) { - sourceDocument = Umbraco.Content(model.Source.Id); + sourceDocument = _publishedContentQuery.Content(model.Source.Id); if (sourceDocument == null) queryExpression.AppendFormat("Umbraco.Content({0})", model.Source.Id); @@ -92,7 +121,7 @@ namespace Umbraco.Web.Editors } else { - sourceDocument = Umbraco.ContentAtRoot().FirstOrDefault(); + sourceDocument = _publishedContentQuery.ContentAtRoot().FirstOrDefault(); queryExpression.Append("Umbraco.ContentAtRoot().FirstOrDefault()"); } @@ -110,7 +139,7 @@ namespace Umbraco.Web.Editors { contents = sourceDocument == null ? Enumerable.Empty() - : sourceDocument.Children(); + : sourceDocument.Children(_variationContextAccessor); queryExpression.Append(".Children()"); } diff --git a/src/Umbraco.Web/Editors/TinyMceController.cs b/src/Umbraco.Web/Editors/TinyMceController.cs index 65e3f7ea18..ff7fdd980e 100644 --- a/src/Umbraco.Web/Editors/TinyMceController.cs +++ b/src/Umbraco.Web/Editors/TinyMceController.cs @@ -6,12 +6,17 @@ using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Web.Composing; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; @@ -25,17 +30,31 @@ namespace Umbraco.Web.Editors Constants.Applications.Members)] public class TinyMceController : UmbracoAuthorizedApiController { - private readonly IMediaService _mediaService; - private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IIOHelper _ioHelper; private readonly IShortStringHelper _shortStringHelper; + private readonly IContentSettings _contentSettings; - public TinyMceController(IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IShortStringHelper shortStringHelper) + public TinyMceController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + UmbracoMapper umbracoMapper, + IShortStringHelper shortStringHelper, + IContentSettings contentSettings, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) { - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _shortStringHelper = shortStringHelper; + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); } + [HttpPost] public async Task UploadImage() { @@ -45,10 +64,10 @@ namespace Umbraco.Web.Editors } // Create an unique folder path to help with concurrent users to avoid filename clash - var imageTempPath = Current.IOHelper.MapPath(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid().ToString()); + var imageTempPath = _ioHelper.MapPath(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid().ToString()); // Temp folderpath (Files come in as bodypart & will need to move/saved into imgTempPath - var folderPath = Current.IOHelper.MapPath(Constants.SystemDirectories.TempFileUploads); + var folderPath = _ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads); // Ensure image temp path exists if(Directory.Exists(imageTempPath) == false) @@ -78,7 +97,7 @@ namespace Umbraco.Web.Editors var safeFileName = fileName.ToSafeFileName(_shortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext) == false || Current.Configs.Settings().Content.ImageFileTypes.Contains(ext) == false) + if (_contentSettings.IsFileAllowedForUpload(ext) == false || _contentSettings.ImageFileTypes.Contains(ext) == false) { // Throw some error - to say can't upload this IMG type return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "This is not an image filetype extension that is approved"); @@ -86,8 +105,8 @@ namespace Umbraco.Web.Editors //var mediaItemName = fileName.ToFriendlyName(); var currentFile = file.LocalFileName; - var newFilePath = imageTempPath + Current.IOHelper.DirSepChar + safeFileName; - var relativeNewFilePath = Current.IOHelper.GetRelativePath(newFilePath); + var newFilePath = imageTempPath + _ioHelper.DirSepChar + safeFileName; + var relativeNewFilePath = _ioHelper.GetRelativePath(newFilePath); try { diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index c7c60c502d..93ec4252df 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -4,11 +4,19 @@ using System.IO; using System.Linq; using Newtonsoft.Json; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; +using Umbraco.Core.Models.Identity; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.Tour; namespace Umbraco.Web.Editors @@ -17,20 +25,38 @@ namespace Umbraco.Web.Editors public class TourController : UmbracoAuthorizedJsonController { private readonly TourFilterCollection _filters; + private readonly IIOHelper _ioHelper; + private readonly ITourSettings _tourSettings; - public TourController(TourFilterCollection filters) + public TourController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + TourFilterCollection filters, + IIOHelper ioHelper, + IPublishedUrlProvider publishedUrlProvider, + ITourSettings tourSettings) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _filters = filters; + _ioHelper = ioHelper; + _tourSettings = tourSettings; } public IEnumerable GetTours() { var result = new List(); - if (Current.Configs.Settings().BackOffice.Tours.EnableTours == false) + if (_tourSettings.EnableTours == false) return result; - var user = Composing.Current.UmbracoContext.Security.CurrentUser; + var user = UmbracoContext.Security.CurrentUser; if (user == null) return result; @@ -41,7 +67,7 @@ namespace Umbraco.Web.Editors var nonPluginFilters = _filters.Where(x => x.PluginName == null).ToList(); //add core tour files - var coreToursPath = Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Config), "BackOfficeTours"); + var coreToursPath = Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Config), "BackOfficeTours"); if (Directory.Exists(coreToursPath)) { foreach (var tourFile in Directory.EnumerateFiles(coreToursPath, "*.json")) @@ -51,7 +77,7 @@ namespace Umbraco.Web.Editors } //collect all tour files in packages - var appPlugins = Current.IOHelper.MapPath(Core.Constants.SystemDirectories.AppPlugins); + var appPlugins = _ioHelper.MapPath(Core.Constants.SystemDirectories.AppPlugins); if (Directory.Exists(appPlugins)) { foreach (var plugin in Directory.EnumerateDirectories(appPlugins)) @@ -110,6 +136,39 @@ namespace Umbraco.Web.Editors return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } + /// + /// Gets a tours for a specific doctype + /// + /// The documenttype alias + /// A + public IEnumerable GetToursForDoctype(string doctypeAlias) + { + var tourFiles = this.GetTours(); + + var doctypeAliasWithCompositions = new List + { + doctypeAlias + }; + + var contentType = this.Services.ContentTypeService.Get(doctypeAlias); + + if (contentType != null) + { + doctypeAliasWithCompositions.AddRange(contentType.CompositionAliases()); + } + + return tourFiles.SelectMany(x => x.Tours) + .Where(x => + { + if (string.IsNullOrEmpty(x.ContentType)) + { + return false; + } + var contentTypes = x.ContentType.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(ct => ct.Trim()); + return contentTypes.Intersect(doctypeAliasWithCompositions).Any(); + }); + } + private void TryParseTourFile(string tourFile, ICollection result, List filters, diff --git a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs index 4f8876738b..64aba378f4 100644 --- a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs @@ -3,9 +3,12 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Web.Composing; +using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -24,10 +27,21 @@ namespace Umbraco.Web.Editors { protected UmbracoAuthorizedJsonController() { + ShortStringHelper = Current.ShortStringHelper; } - protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + protected UmbracoAuthorizedJsonController( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger logger, + IRuntimeState runtimeState, + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) { ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index 9206d1f71f..8518938a32 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -2,11 +2,14 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using System.Web.Http.Filters; +using Semver; using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -15,15 +18,19 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class UpdateCheckController : UmbracoAuthorizedJsonController { + private readonly IUpgradeService _upgradeService; private readonly IUmbracoVersion _umbracoVersion; - public UpdateCheckController (IUmbracoVersion umbracoVersion) + public UpdateCheckController() { } + + public UpdateCheckController(IUpgradeService upgradeService, IUmbracoVersion umbracoVersion) { + _upgradeService = upgradeService; _umbracoVersion = umbracoVersion; } [UpdateCheckResponseFilter] - public UpgradeCheckResponse GetCheck() + public async Task GetCheck() { var updChkCookie = Request.Headers.GetCookies("UMB_UPDCHK").FirstOrDefault(); var updateCheckCookie = updChkCookie != null ? updChkCookie["UMB_UPDCHK"].Value : ""; @@ -31,23 +38,15 @@ namespace Umbraco.Web.Editors { try { - var check = new org.umbraco.update.CheckForUpgrade { Timeout = 2000 }; + var version = new SemVersion(_umbracoVersion.Current.Major, _umbracoVersion.Current.Minor, + _umbracoVersion.Current.Build, _umbracoVersion.Comment); + var result = await _upgradeService.CheckUpgrade(version); - var result = check.CheckUpgrade(_umbracoVersion.Current.Major, - _umbracoVersion.Current.Minor, - _umbracoVersion.Current.Build, - _umbracoVersion.Comment); - - return new UpgradeCheckResponse(result.UpgradeType.ToString(), result.Comment, result.UpgradeUrl, _umbracoVersion); + return new UpgradeCheckResponse(result.UpgradeType, result.Comment, result.UpgradeUrl, _umbracoVersion); } - catch (System.Net.WebException) + catch { - //this occurs if the server is down or cannot be reached - return null; - } - catch (System.Web.Services.Protocols.SoapException) - { - //this occurs if the server has a timeout + //We don't want to crash due to this return null; } } diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs index 394a08433e..368a2ee535 100644 --- a/src/Umbraco.Web/Editors/UserGroupsController.cs +++ b/src/Umbraco.Web/Editors/UserGroupsController.cs @@ -4,8 +4,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; -using Umbraco.Core; -using Umbraco.Core.Composing; +using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index f26b2df355..9554906b0d 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -13,26 +13,29 @@ using System.Web.Mvc; using Microsoft.AspNet.Identity; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; -using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Editors.Filters; +using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Identity; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using IUser = Umbraco.Core.Models.Membership.IUser; using Task = System.Threading.Tasks.Task; +using Umbraco.Core.Mapping; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Web.Routing; namespace Umbraco.Web.Editors { @@ -43,6 +46,11 @@ namespace Umbraco.Web.Editors public class UsersController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + private readonly ISqlContext _sqlContext; + private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly ISecuritySettings _securitySettings; public UsersController( IGlobalSettings globalSettings, @@ -52,12 +60,22 @@ namespace Umbraco.Web.Editors AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, - IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + IShortStringHelper shortStringHelper, + UmbracoMapper umbracoMapper, + IContentSettings contentSettings, + IIOHelper ioHelper, + IImageUrlGenerator imageUrlGenerator, + IPublishedUrlProvider publishedUrlProvider, + ISecuritySettings securitySettings) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _mediaFileSystem = mediaFileSystem; + _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _ioHelper = ioHelper; + _sqlContext = sqlContext; + _imageUrlGenerator = imageUrlGenerator; + _securitySettings = securitySettings; } /// @@ -66,7 +84,7 @@ namespace Umbraco.Web.Editors /// public string[] GetCurrentUserAvatarUrls() { - var urls = UmbracoContext.Security.CurrentUser.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem); + var urls = UmbracoContext.Security.CurrentUser.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); if (urls == null) throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not access Gravatar endpoint")); @@ -78,17 +96,17 @@ namespace Umbraco.Web.Editors [AdminUsersAuthorize] public async Task PostSetAvatar(int id) { - return await PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, id); + return await PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, _contentSettings, _ioHelper, _imageUrlGenerator, id); } - internal static async Task PostSetAvatarInternal(HttpRequestMessage request, IUserService userService, IAppCache cache, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, int id) + internal static async Task PostSetAvatarInternal(HttpRequestMessage request, IUserService userService, IAppCache cache, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IContentSettings contentSettings, IIOHelper ioHelper, IImageUrlGenerator imageUrlGenerator, int id) { if (request.Content.IsMimeMultipartContent() == false) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = Current.IOHelper.MapPath(Constants.SystemDirectories.TempFileUploads); + var root = ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -116,14 +134,14 @@ namespace Umbraco.Web.Editors var safeFileName = fileName.ToSafeFileName(shortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (Current.Configs.Settings().Content.DisallowedUploadFiles.Contains(ext) == false) + if (contentSettings.DisallowedUploadFiles.Contains(ext) == false) { //generate a path of known data, we don't want this path to be guessable user.Avatar = "UserAvatars/" + (user.Id + safeFileName).GenerateHash() + "." + ext; using (var fs = System.IO.File.OpenRead(file.LocalFileName)) { - Current.MediaFileSystem.AddFile(user.Avatar, fs, true); + mediaFileSystem.AddFile(user.Avatar, fs, true); } userService.Save(user); @@ -135,7 +153,7 @@ namespace Umbraco.Web.Editors }); } - return request.CreateResponse(HttpStatusCode.OK, user.GetUserAvatarUrls(cache, mediaFileSystem)); + return request.CreateResponse(HttpStatusCode.OK, user.GetUserAvatarUrls(cache, mediaFileSystem, imageUrlGenerator)); } [AppendUserModifiedHeader("id")] @@ -165,11 +183,11 @@ namespace Umbraco.Web.Editors if (filePath.IsNullOrWhiteSpace() == false) { - if (Current.MediaFileSystem.FileExists(filePath)) - Current.MediaFileSystem.DeleteFile(filePath); + if (_mediaFileSystem.FileExists(filePath)) + _mediaFileSystem.DeleteFile(filePath); } - return Request.CreateResponse(HttpStatusCode.OK, found.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem)); + return Request.CreateResponse(HttpStatusCode.OK, found.GetUserAvatarUrls(AppCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator)); } /// @@ -215,7 +233,7 @@ namespace Umbraco.Web.Editors // so to do that here, we'll need to check if this current user is an admin and if not we should exclude all user who are // also admins - var hideDisabledUsers = Current.Configs.Settings().Security.HideDisabledUsersInBackoffice; + var hideDisabledUsers = _securitySettings.HideDisabledUsersInBackoffice; var excludeUserGroups = new string[0]; var isAdmin = Security.CurrentUser.IsAdmin(); if (isAdmin == false) @@ -224,7 +242,7 @@ namespace Umbraco.Web.Editors excludeUserGroups = new[] {Constants.Security.AdminGroupAlias}; } - var filterQuery = Current.SqlContext.Query(); + var filterQuery = _sqlContext.Query(); if (!Security.CurrentUser.IsSuper()) { @@ -273,7 +291,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } - if (Current.Configs.Settings().Security.UsernameIsEmail) + if (_securitySettings.UsernameIsEmail) { //ensure they are the same if we're using it userSave.Username = userSave.Email; @@ -295,7 +313,7 @@ namespace Umbraco.Web.Editors //we want to create the user with the UserManager, this ensures the 'empty' (special) password //format is applied without us having to duplicate that logic - var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); + var identityUser = BackOfficeIdentityUser.CreateNew(GlobalSettings, userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); identityUser.Name = userSave.Name; var created = await UserManager.CreateAsync(identityUser); @@ -365,7 +383,7 @@ namespace Umbraco.Web.Editors } IUser user; - if (Current.Configs.Settings().Security.UsernameIsEmail) + if (_securitySettings.UsernameIsEmail) { //ensure it's the same userSave.Username = userSave.Email; @@ -389,7 +407,7 @@ namespace Umbraco.Web.Editors { //we want to create the user with the UserManager, this ensures the 'empty' (special) password //format is applied without us having to duplicate that logic - var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); + var identityUser = BackOfficeIdentityUser.CreateNew(GlobalSettings, userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); identityUser.Name = userSave.Name; var created = await UserManager.CreateAsync(identityUser); @@ -439,7 +457,7 @@ namespace Umbraco.Web.Editors if (user != null && (extraCheck == null || extraCheck(user))) { ModelState.AddModelError( - Current.Configs.Settings().Security.UsernameIsEmail ? "Email" : "Username", + _securitySettings.UsernameIsEmail ? "Email" : "Username", "A user with the username already exists"); throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } @@ -469,7 +487,7 @@ namespace Umbraco.Web.Editors var action = urlHelper.Action("VerifyInvite", "BackOffice", new { - area = GlobalSettings.GetUmbracoMvcArea(Current.IOHelper), + area = _ioHelper.GetUmbracoMvcArea(), invite = inviteToken }); @@ -559,33 +577,11 @@ namespace Umbraco.Web.Editors // if the found user has their email for username, we want to keep this synced when changing the email. // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here. - if (Current.Configs.Settings().Security.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email) + if (_securitySettings.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email) { userSave.Username = userSave.Email; } - if (userSave.ChangePassword != null) - { - var passwordChanger = new PasswordChanger(Logger); - - //this will change the password and raise appropriate events - var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager); - if (passwordChangeResult.Success) - { - //need to re-get the user - found = Services.UserService.GetUserById(intId.Result); - } - else - { - hasErrors = true; - - foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames) - { - ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage); - } - } - } - if (hasErrors) throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); @@ -600,6 +596,51 @@ namespace Umbraco.Web.Editors return display; } + /// + /// + /// + /// + /// + public async Task> PostChangePassword(ChangingPasswordModel changingPasswordModel) + { + changingPasswordModel = changingPasswordModel ?? throw new ArgumentNullException(nameof(changingPasswordModel)); + + if (ModelState.IsValid == false) + { + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); + } + + var intId = changingPasswordModel.Id.TryConvertTo(); + if (intId.Success == false) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var found = Services.UserService.GetUserById(intId.Result); + if (found == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var passwordChanger = new PasswordChanger(Logger); + var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, changingPasswordModel, UserManager); + + if (passwordChangeResult.Success) + { + var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); + result.AddSuccessNotification(Services.TextService.Localize("general/success"), Services.TextService.Localize("user/passwordChangedGeneric")); + return result; + } + + foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames) + { + ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage); + } + + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + + /// /// Disables the users with the given user ids /// diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs deleted file mode 100644 index 9a9fa98d95..0000000000 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Examine; -using Umbraco.Core; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Web -{ - /// - /// Extension methods for Examine - /// - public static class ExamineExtensions - { - public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache) - { - var list = new List(); - - foreach (var result in results.OrderByDescending(x => x.Score)) - { - if (!int.TryParse(result.Id, out var intId)) continue; //invalid - var content = cache.GetById(intId); - if (content == null) continue; // skip if this doesn't exist in the cache - - list.Add(new PublishedSearchResult(content, result.Score)); - - } - - return list; - } - } -} diff --git a/src/Umbraco.Web/FormlessPage.cs b/src/Umbraco.Web/FormlessPage.cs deleted file mode 100644 index 9789479fb5..0000000000 --- a/src/Umbraco.Web/FormlessPage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Web.UI; - -namespace Umbraco.Web -{ - /// - /// A formless page for use with the rendering a control in a page via Server.Execute. - /// This ignores the check to check for a form control on the page. - /// - /// - /// UmbracoHelper currently uses this for rendering macros but could be used anywhere we want when rendering - /// a page with Server.Execute. - /// SD: I have a custom MVC engine that uses this in my own internal libs if we want to pull it out which is called ViewManager - /// and works really well for things like this. - /// - internal class FormlessPage : Page - { - public override void VerifyRenderingInServerForm(Control control) { } - - } -} diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs index 2f72b946de..251c5c0ae4 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs @@ -1,16 +1,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Configuration; using System.Linq; using System.Web.Http; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Web.Editors; using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.Configuration.HealthChecks; namespace Umbraco.Web.HealthCheck { @@ -24,12 +20,12 @@ namespace Umbraco.Web.HealthCheck private readonly IList _disabledCheckIds; private readonly ILogger _logger; - public HealthCheckController(HealthCheckCollection checks, ILogger logger) + public HealthCheckController(HealthCheckCollection checks, ILogger logger, IHealthChecks healthChecks) { _checks = checks ?? throw new ArgumentNullException(nameof(checks)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - var healthCheckConfig = Current.Configs.HealthChecks(); + var healthCheckConfig = healthChecks ?? throw new ArgumentNullException(nameof(healthChecks)); _disabledCheckIds = healthCheckConfig.DisabledChecks .Select(x => x.Id) .ToList(); diff --git a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs index d9c2e2b881..ee3ab51d47 100644 --- a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs @@ -5,14 +5,18 @@ using System.Web; using System.Web.Mvc; using Microsoft.Owin.Security; using Newtonsoft.Json; -using Umbraco.Core.Composing; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Web.Composing; using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.Models; +using Umbraco.Web.Trees; namespace Umbraco.Web { using Core.Configuration; + using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Web.JavaScript; /// @@ -36,9 +40,9 @@ namespace Umbraco.Web /// These are the bare minimal server variables that are required for the application to start without being authenticated, /// we will load the rest of the server vars after the user is authenticated. /// - public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) + public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IContentSettings contentSettings, IIOHelper ioHelper, TreeCollection treeCollection, IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, IRuntimeSettings settings, ISecuritySettings securitySettings) { - var serverVars = new BackOfficeServerVariables(uri, Current.RuntimeState, features, globalSettings, umbracoVersion); + var serverVars = new BackOfficeServerVariables(uri, Current.RuntimeState, features, globalSettings, umbracoVersion, contentSettings, ioHelper, treeCollection, httpContextAccessor, hostingEnvironment, settings, securitySettings); var minVars = serverVars.BareMinimumServerVariables(); var str = @"