diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 295dc54371..f4e237a1f2 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -102,10 +102,11 @@ That said, the Umbraco development team likes to follow the hints that ReSharper The Core Contributors team consists of one member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members who have comitted to volunteering their free time: -- [Anders Bjerner](https://github.com/abjerner) -- [Emma Burstow](https://github.com/emmaburstow) -- [Poornima Nayar](https://github.com/poornimanayar) -- [Kenn Jacobsen](https://twitter.com/KennJacobsen_DK) +- [Nathan Woulfe](https://github.com/nathanwoulfe) +- [Joe Glombek](https://github.com/glombek) +- [Laura Weatherhead](https://github.com/lssweatherhead) +- [Michael Latouche](https://github.com/mikecp) +- [Owain Williams](https://github.com/OwainWilliams) These wonderful people aim to provide you with a first reply to your PR, review and test out your changes and on occasions, they might ask more questions. If they are happy with your work, they'll let Umbraco HQ know by approving the PR. Hq will have final sign-off and will check the work again before it is merged. @@ -128,7 +129,7 @@ In order to build the Umbraco source code locally, first make sure you have the * npm v6.4.1+ (installed with Node.js) * [Git command line](https://git-scm.com/download/) -The easiest way to get started is to open `src\umbraco.sln` in Visual Studio 2017 (version 15.9.7 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile. +The easiest way to get started is to open `src\umbraco.sln` in Visual Studio 2019 (version 16.3 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile. Alternatively, you can run `build.ps1` from the Powershell command line, which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details. diff --git a/.github/ISSUE_TEMPLATE/4_Support_question.md b/.github/ISSUE_TEMPLATE/4_Support_question.md deleted file mode 100644 index 829df982f9..0000000000 --- a/.github/ISSUE_TEMPLATE/4_Support_question.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ⁉️ Support Question -about: Having trouble with Umbraco? -> https://our.umbraco.com ---- - -This issue tracker is NOT meant for support questions. If you have a question, -please join us on the forum at https://our.umbraco.com. - -Thanks! diff --git a/.github/ISSUE_TEMPLATE/5_Documentation_issue.md b/.github/ISSUE_TEMPLATE/5_Documentation_issue.md deleted file mode 100644 index 8893647aa8..0000000000 --- a/.github/ISSUE_TEMPLATE/5_Documentation_issue.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: 📖 Documentation Issue -about: See https://github.com/umbraco/UmbracoDocs/issues for documentation issues ---- - -The Umbraco documentation has its own dedicated repository. Please open your -documentation-related issue at https://github.com/umbraco/UmbracoDocs/issues - -Thanks! diff --git a/.github/ISSUE_TEMPLATE/6_Security_issue.md b/.github/ISSUE_TEMPLATE/6_Security_issue.md deleted file mode 100644 index 84c5f5989c..0000000000 --- a/.github/ISSUE_TEMPLATE/6_Security_issue.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: 🔐 Security Issue -about: Discovered a Security Issue in Umbraco? ---- - -⚠️ PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. - -If you have found a security issue in Umbraco, please send the details to -security@umbraco.com and don't disclose it publicly until we can provide a fix for -it. If you wish, we'll credit you for finding verified issues, when we release -the patched version. - -❗ Please read more about how to report security issues on https://umbraco.com/security - -A note on "Self XSS" --------------------- - -Umbraco is a CMS, that allows users to edit content on a website. As such, -all _authenticated users_ can: - - - Edit content, and (depending on the field types) insert HTML and CSS in that - content, with a variety of allowed attributes. - - Depending on the user level: Edit template files, and insert C#, HTML, CSS and - javascript in so on. - - Upload files to the site, which will become publicly available. - -We see these functionalities as _features_, and not as security issues. Please -report the mentioned items only if they can be performed by non-authorized -users, or other exploitable vulnerabilities. - -Thanks! diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..37d1be9158 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: true +contact_links: + - name: ⁉️ Support Question + url: https://our.umbraco.com + about: This issue tracker is NOT meant for support questions. If you have a question, please join us on the forum. + - name: 📖 Documentation Issue + url: https://github.com/umbraco/UmbracoDocs/issues + about: Documentation issues should be reported on the Umbraco documentation repository. + - name: 🔐 Security Issue + url: https://umbraco.com/about-us/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/ + about: Discovered a Security Issue in Umbraco? \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..4dfa5a83b9 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,61 @@ +name: "Code scanning - action" + +on: + push: + branches: [v8/contrib,v8/dev,v8/bug,v8/feature] + pull_request: + # The branches below must be a subset of the branches above + schedule: + - cron: '0 7 * * 2' + +jobs: + CodeQL-Build: + + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + - name: configure Pagefile + uses: al-cheb/configure-pagefile-action@v1.2 + with: + minimum-size: 8GB + maximum-size: 32GB + + - run: | + echo "Run Umbraco-CMS build" + pwsh -command .\build\build.ps1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 + with: + config-file: ./.github/codeql-config.yml + + diff --git a/.github/workflows/codeql-config.yml b/.github/workflows/codeql-config.yml new file mode 100644 index 0000000000..59b55e48ec --- /dev/null +++ b/.github/workflows/codeql-config.yml @@ -0,0 +1,13 @@ +name: "CodeQL config" +on: + push: + branches: [v8/contrib,v8/dev] +paths-ignore: + - node_modules + - Umbraco.TestData + - Umbraco.Tests + - Umbraco.Tests.AcceptanceTest + - Umbraco.Tests.Benchmarks + - bin +paths: + - src \ No newline at end of file diff --git a/src/Umbraco.Core/Actions/ActionCopy.cs b/src/Umbraco.Core/Actions/ActionCopy.cs index 477156d4ae..a568d0aa37 100644 --- a/src/Umbraco.Core/Actions/ActionCopy.cs +++ b/src/Umbraco.Core/Actions/ActionCopy.cs @@ -7,7 +7,9 @@ namespace Umbraco.Web.Actions /// public class ActionCopy : IAction { - public char Letter => 'O'; + public const char ActionLetter = 'O'; + + public char Letter => ActionLetter; public string Alias => "copy"; public string Category => Constants.Conventions.PermissionCategories.StructureCategory; public string Icon => "documents"; diff --git a/src/Umbraco.Core/Actions/ActionRights.cs b/src/Umbraco.Core/Actions/ActionRights.cs index b76f3b7800..dd021d03c0 100644 --- a/src/Umbraco.Core/Actions/ActionRights.cs +++ b/src/Umbraco.Core/Actions/ActionRights.cs @@ -7,7 +7,9 @@ namespace Umbraco.Web.Actions /// public class ActionRights : IAction { - public char Letter => 'R'; + public const char ActionLetter = 'R'; + + public char Letter => ActionLetter; public string Alias => "rights"; public string Category => Constants.Conventions.PermissionCategories.ContentCategory; public string Icon => "vcard"; diff --git a/src/Umbraco.Core/Actions/ActionSort.cs b/src/Umbraco.Core/Actions/ActionSort.cs index 8b4e01823e..9c463bc18e 100644 --- a/src/Umbraco.Core/Actions/ActionSort.cs +++ b/src/Umbraco.Core/Actions/ActionSort.cs @@ -9,7 +9,9 @@ namespace Umbraco.Web.Actions /// public class ActionSort : IAction { - public char Letter => 'S'; + public const char ActionLetter = 'S'; + + public char Letter => ActionLetter; public string Alias => "sort"; public string Category => Constants.Conventions.PermissionCategories.StructureCategory; public string Icon => "navigation-vertical"; diff --git a/src/Umbraco.Core/Actions/ActionUnpublish.cs b/src/Umbraco.Core/Actions/ActionUnpublish.cs index 8ece4c008e..f9270d926b 100644 --- a/src/Umbraco.Core/Actions/ActionUnpublish.cs +++ b/src/Umbraco.Core/Actions/ActionUnpublish.cs @@ -10,7 +10,9 @@ namespace Umbraco.Web.Actions /// public class ActionUnpublish : IAction { - public char Letter => 'Z'; + public const char ActionLetter = 'Z'; + + public char Letter => ActionLetter; public string Alias => "unpublish"; public string Category => Constants.Conventions.PermissionCategories.ContentCategory; public string Icon => "circle-dotted"; diff --git a/src/Umbraco.Core/CompositionExtensions.cs b/src/Umbraco.Core/CompositionExtensions.cs index d6c73478bf..716f53a383 100644 --- a/src/Umbraco.Core/CompositionExtensions.cs +++ b/src/Umbraco.Core/CompositionExtensions.cs @@ -64,7 +64,7 @@ namespace Umbraco.Core => builder.WithCollectionBuilder(); /// - /// Gets the url providers collection builder. + /// Gets the URL providers collection builder. /// /// The builder. public static UrlProviderCollectionBuilder UrlProviders(this IUmbracoBuilder builder) diff --git a/src/Umbraco.Core/Configuration/XmlConfigManipulator.cs b/src/Umbraco.Core/Configuration/XmlConfigManipulator.cs deleted file mode 100644 index b7c3d5d58b..0000000000 --- a/src/Umbraco.Core/Configuration/XmlConfigManipulator.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using Microsoft.Extensions.Logging; -using Umbraco.Core.IO; - -namespace Umbraco.Core.Configuration -{ - public class XmlConfigManipulator : IConfigManipulator - { - private readonly IIOHelper _ioHelper; - private readonly ILogger _logger; - - public XmlConfigManipulator(IIOHelper ioHelper, ILogger logger) - { - _ioHelper = ioHelper; - _logger = logger; - } - - public void RemoveConnectionString() - { - var key = Constants.System.UmbracoConnectionName; - var fileName = _ioHelper.MapPath("~/web.config"); - 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) - { - 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("~/" + 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("~/" + 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.LogInformation("Saving connection string to {ConfigFile}.", fileSource); - xml.Save(fileName, SaveOptions.DisableFormatting); - _logger.LogInformation("Saved connection string to {ConfigFile}.", fileSource); - } - - public void SaveConfigValue(string itemPath, object value) - { - throw new NotImplementedException(); - } - - public void SaveDisableRedirectUrlTracking(bool disable) - { - var fileName = _ioHelper.MapPath("~/config/umbracoSettings.config"); - - var umbracoConfig = new XmlDocument { PreserveWhitespace = true }; - umbracoConfig.Load(fileName); - - var action = disable ? "disable" : "enable"; - - if (File.Exists(fileName) == false) - throw new Exception($"Couldn't {action} URL Tracker, the umbracoSettings.config file does not exist."); - - if (!(umbracoConfig.SelectSingleNode("//web.routing") is XmlElement webRoutingElement)) - throw new Exception($"Couldn't {action} URL Tracker, the web.routing element was not found in umbracoSettings.config."); - - // note: this adds the attribute if it does not exist - webRoutingElement.SetAttribute("disableRedirectUrlTracking", disable.ToString().ToLowerInvariant()); - umbracoConfig.Save(fileName); - } - - 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.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 36d471b950..594be2966a 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -103,7 +103,7 @@ namespace Umbraco.Core public const string DefaultUILanguage = "Umbraco.Core.DefaultUILanguage"; /// - /// A true/false value indicating whether umbraco should hide top level nodes from generated urls. + /// A true/false value indicating whether umbraco should hide top level nodes from generated URLs. /// public const string HideTopLevelNodeFromPath = "Umbraco.Core.HideTopLevelNodeFromPath"; diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index cadd710275..eb24cf4229 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -192,7 +192,7 @@ namespace Umbraco.Core public const string NestedContent = "Umbraco.NestedContent"; /// - /// Alias for the multi url picker editor. + /// Alias for the multi URL picker editor. /// public const string MultiUrlPicker = "Umbraco.MultiUrlPicker"; } diff --git a/src/Umbraco.Core/GuidUdi.cs b/src/Umbraco.Core/GuidUdi.cs index 8acac61b32..08dad39f12 100644 --- a/src/Umbraco.Core/GuidUdi.cs +++ b/src/Umbraco.Core/GuidUdi.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core { Guid guid; if (Guid.TryParse(uriValue.AbsolutePath.TrimStart('/'), out guid) == false) - throw new FormatException("Url \"" + uriValue + "\" is not a guid entity id."); + throw new FormatException("URI \"" + uriValue + "\" is not a GUID entity ID."); Guid = guid; } diff --git a/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs b/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs index 1618daf028..9e279c98e2 100644 --- a/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs +++ b/src/Umbraco.Core/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; using Umbraco.Core.Install; -using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Core.HealthCheck.Checks.Permissions @@ -19,14 +19,14 @@ namespace Umbraco.Core.HealthCheck.Checks.Permissions private readonly ILocalizedTextService _textService; private readonly IOptionsMonitor _globalSettings; private readonly IFilePermissionHelper _filePermissionHelper; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; - public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IOptionsMonitor globalSettings, IFilePermissionHelper filePermissionHelper, IIOHelper ioHelper) + public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IOptionsMonitor globalSettings, IFilePermissionHelper filePermissionHelper, IHostingEnvironment hostingEnvironment) { _textService = textService; _globalSettings = globalSettings; _filePermissionHelper = filePermissionHelper; - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; } /// @@ -120,7 +120,7 @@ namespace Umbraco.Core.HealthCheck.Checks.Permissions { return pathsToCheck .Where(x => x.Value == requirement) - .Select(x => _ioHelper.MapPath(x.Key)) + .Select(x => _hostingEnvironment.MapPathContentRoot(x.Key)) .OrderBy(x => x) .ToArray(); } @@ -157,7 +157,7 @@ namespace Umbraco.Core.HealthCheck.Checks.Permissions private string GetMessageForPathCheckFailure(string messageKey, IEnumerable failedPaths) { - var rootFolder = _ioHelper.MapPath("/"); + var rootFolder = _hostingEnvironment.MapPathContentRoot("/"); var failedFolders = failedPaths .Select(x => ParseFolderFromFullPath(rootFolder, x)); return _textService.Localize(messageKey, diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index a19ebd999b..b078172213 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -129,11 +129,13 @@ namespace Umbraco.Core.IO private object CreateWellKnownFileSystems() { var logger = _loggerFactory.CreateLogger(); - var macroPartialFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, Constants.SystemDirectories.MacroPartials); - var partialViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, Constants.SystemDirectories.PartialViews); - var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _globalSettings.UmbracoCssPath); - var scriptsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _globalSettings.UmbracoScriptsPath); - var mvcViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, Constants.SystemDirectories.MvcViews); + + //TODO this is fucked, why do PhysicalFileSystem has a root url? Mvc views cannot be accessed by url! + var macroPartialFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MacroPartials)); + var partialViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.PartialViews)); + var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); + var scriptsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoScriptsPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoScriptsPath)); + var mvcViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MvcViews)); _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "macro-partials", IsScoped); _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "partials", IsScoped); @@ -222,7 +224,7 @@ namespace Umbraco.Core.IO { if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - var id = ShadowWrapper.CreateShadowId(_ioHelper); + var id = ShadowWrapper.CreateShadowId(_hostingEnvironment); return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow } diff --git a/src/Umbraco.Core/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs index 0405c5925a..9de1ea40f2 100644 --- a/src/Umbraco.Core/IO/IFileSystem.cs +++ b/src/Umbraco.Core/IO/IFileSystem.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.IO /// /// Returns the application relative path to the file. /// - /// The full path or url. + /// The full path or URL. /// /// The representing the relative path. /// @@ -118,11 +118,11 @@ namespace Umbraco.Core.IO string GetFullPath(string path); /// - /// Returns the application relative url to the file. + /// Returns the application relative URL to the file. /// - /// The path to return the url for. + /// The path to return the URL for. /// - /// representing the relative url. + /// representing the relative URL. /// string GetUrl(string path); diff --git a/src/Umbraco.Core/IO/IIOHelper.cs b/src/Umbraco.Core/IO/IIOHelper.cs index 5a0f5cab45..a342192612 100644 --- a/src/Umbraco.Core/IO/IIOHelper.cs +++ b/src/Umbraco.Core/IO/IIOHelper.cs @@ -19,8 +19,17 @@ namespace Umbraco.Core.IO /// /// /// + [Obsolete("Use IHostingEnvironment.MapPathContentRoot or IHostingEnvironment.MapPathWebRoot instead")] string MapPath(string path); + /// + /// Returns true if the path has a root, and is considered fully qualified for the OS it is on + /// See https://github.com/dotnet/runtime/blob/30769e8f31b20be10ca26e27ec279cd4e79412b9/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs#L281 for the .NET Standard 2.1 version of this + /// + /// The path to check + /// True if the path is fully qualified, false otherwise + bool IsPathFullyQualified(string path); + /// /// Verifies that the current filepath matches a directory where the user is allowed to edit a file. /// @@ -45,7 +54,7 @@ namespace Umbraco.Core.IO /// A value indicating whether the filepath is valid. bool VerifyFileExtension(string filePath, IEnumerable validFileExtensions); - bool PathStartsWith(string path, string root, char separator); + bool PathStartsWith(string path, string root, params char[] separators); void EnsurePathExists(string path); diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 08241d28f4..903b6e4a5c 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -1,15 +1,17 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using Umbraco.Core.Hosting; using Umbraco.Core.Strings; namespace Umbraco.Core.IO { - public class IOHelper : IIOHelper + public abstract class IOHelper : IIOHelper { private readonly IHostingEnvironment _hostingEnvironment; @@ -29,7 +31,7 @@ namespace Umbraco.Core.IO if (virtualPath.StartsWith("~")) retval = virtualPath.Replace("~", _hostingEnvironment.ApplicationVirtualPath); - if (virtualPath.StartsWith("/") && virtualPath.StartsWith(_hostingEnvironment.ApplicationVirtualPath) == false) + if (virtualPath.StartsWith("/") && !PathStartsWith(virtualPath, _hostingEnvironment.ApplicationVirtualPath)) retval = _hostingEnvironment.ApplicationVirtualPath + "/" + virtualPath.TrimStart('/'); return retval; @@ -64,19 +66,15 @@ namespace Umbraco.Core.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - // Check if the path is already mapped - if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar) - || path.StartsWith(@"\\")) //UNC Paths start with "\\". If the site is running off a network drive mapped paths will look like "\\Whatever\Boo\Bar" + // Check if the path is already mapped - TODO: This should be switched to Path.IsPathFullyQualified once we are on Net Standard 2.1 + if (IsPathFullyQualified(path)) { return path; } - // Check that we even have an HttpContext! otherwise things will fail anyways - // http://umbraco.codeplex.com/workitem/30946 - if (_hostingEnvironment.IsHosted) { - var result = (!string.IsNullOrEmpty(path) && (path.StartsWith("~") || path.StartsWith(_hostingEnvironment.ApplicationVirtualPath))) + var result = (!string.IsNullOrEmpty(path) && (path.StartsWith("~") || PathStartsWith(path, _hostingEnvironment.ApplicationVirtualPath))) ? _hostingEnvironment.MapPathWebRoot(path) : _hostingEnvironment.MapPathWebRoot("~/" + path.TrimStart('/')); @@ -91,6 +89,14 @@ namespace Umbraco.Core.IO return retval; } + /// + /// Returns true if the path has a root, and is considered fully qualified for the OS it is on + /// See https://github.com/dotnet/runtime/blob/30769e8f31b20be10ca26e27ec279cd4e79412b9/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs#L281 for the .NET Standard 2.1 version of this + /// + /// The path to check + /// True if the path is fully qualified, false otherwise + public abstract bool IsPathFullyQualified(string path); + /// /// Verifies that the current filepath matches a directory where the user is allowed to edit a file. @@ -121,8 +127,11 @@ namespace Umbraco.Core.IO // not going to fix everything today var mappedRoot = MapPath(_hostingEnvironment.ApplicationVirtualPath); - if (filePath.StartsWith(mappedRoot) == false) - filePath = _hostingEnvironment.MapPathContentRoot(filePath); + if (!PathStartsWith(filePath, mappedRoot)) + { + // TODO this is going to fail.. Scripts Stylesheets need to use WebRoot, PartialViews need to use ContentRoot + filePath = _hostingEnvironment.MapPathWebRoot(filePath); + } // yes we can (see above) //// don't trust what we get, it may contain relative segments @@ -131,10 +140,10 @@ namespace Umbraco.Core.IO foreach (var dir in validDirs) { var validDir = dir; - if (validDir.StartsWith(mappedRoot) == false) - validDir = _hostingEnvironment.MapPathContentRoot(validDir); + if (!PathStartsWith(validDir, mappedRoot)) + validDir = _hostingEnvironment.MapPathWebRoot(validDir); - if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar)) + if (PathStartsWith(filePath, validDir)) return true; } @@ -153,16 +162,7 @@ namespace Umbraco.Core.IO return ext != null && validFileExtensions.Contains(ext.TrimStart('.')); } - public bool PathStartsWith(string path, string root, char separator) - { - // either it is identical to root, - // or it is root + separator + anything - - if (path.StartsWith(root, StringComparison.OrdinalIgnoreCase) == false) return false; - if (path.Length == root.Length) return true; - if (path.Length < root.Length) return false; - return path[root.Length] == separator; - } + public abstract bool PathStartsWith(string path, string root, params char[] separators); public void EnsurePathExists(string path) { @@ -181,7 +181,7 @@ namespace Umbraco.Core.IO if (path.IsFullPath()) { var rootDirectory = MapPath("~"); - var relativePath = path.ToLowerInvariant().Replace(rootDirectory.ToLowerInvariant(), string.Empty); + var relativePath = PathStartsWith(path, rootDirectory) ? path.Substring(rootDirectory.Length) : path; path = relativePath; } diff --git a/src/Umbraco.Core/IO/IOHelperLinux.cs b/src/Umbraco.Core/IO/IOHelperLinux.cs new file mode 100644 index 0000000000..2c2e778740 --- /dev/null +++ b/src/Umbraco.Core/IO/IOHelperLinux.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using System.Linq; +using Umbraco.Core.Hosting; + +namespace Umbraco.Core.IO +{ + public class IOHelperLinux : IOHelper + { + public IOHelperLinux(IHostingEnvironment hostingEnvironment) : base(hostingEnvironment) + { + } + + public override bool IsPathFullyQualified(string path) => Path.IsPathRooted(path); + + public override bool PathStartsWith(string path, string root, params char[] separators) + { + // either it is identical to root, + // or it is root + separator + anything + + if (separators == null || separators.Length == 0) separators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + if (!path.StartsWith(root, StringComparison.Ordinal)) return false; + if (path.Length == root.Length) return true; + if (path.Length < root.Length) return false; + return separators.Contains(path[root.Length]); + } + } +} diff --git a/src/Umbraco.Core/IO/IOHelperOSX.cs b/src/Umbraco.Core/IO/IOHelperOSX.cs new file mode 100644 index 0000000000..90d96998c3 --- /dev/null +++ b/src/Umbraco.Core/IO/IOHelperOSX.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using System.Linq; +using Umbraco.Core.Hosting; + + +namespace Umbraco.Core.IO +{ + public class IOHelperOSX : IOHelper + { + public IOHelperOSX(IHostingEnvironment hostingEnvironment) : base(hostingEnvironment) + { + } + + public override bool IsPathFullyQualified(string path) => Path.IsPathRooted(path); + + public override bool PathStartsWith(string path, string root, params char[] separators) + { + // either it is identical to root, + // or it is root + separator + anything + + if (separators == null || separators.Length == 0) separators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + if (!path.StartsWith(root, StringComparison.OrdinalIgnoreCase)) return false; + if (path.Length == root.Length) return true; + if (path.Length < root.Length) return false; + return separators.Contains(path[root.Length]); + } + } +} diff --git a/src/Umbraco.Core/IO/IOHelperWindows.cs b/src/Umbraco.Core/IO/IOHelperWindows.cs new file mode 100644 index 0000000000..3cffc27751 --- /dev/null +++ b/src/Umbraco.Core/IO/IOHelperWindows.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using System.Linq; +using Umbraco.Core.Hosting; + + +namespace Umbraco.Core.IO +{ + public class IOHelperWindows : IOHelper + { + public IOHelperWindows(IHostingEnvironment hostingEnvironment) : base(hostingEnvironment) + { + } + + public override bool IsPathFullyQualified(string path) + { + // TODO: This implementation is taken from the .NET Standard 2.1 implementation. We should switch to using Path.IsPathFullyQualified once we are on .NET Standard 2.1 + + if (path.Length < 2) + { + // It isn't fixed, it must be relative. There is no way to specify a fixed + // path with one character (or less). + return false; + } + + if (path[0] == Path.DirectorySeparatorChar || path[0] == Path.AltDirectorySeparatorChar) + { + // There is no valid way to specify a relative path with two initial slashes or + // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ + return path[1] == '?' || path[1] == Path.DirectorySeparatorChar || path[1] == Path.AltDirectorySeparatorChar; + } + + // The only way to specify a fixed path that doesn't begin with two slashes + // is the drive, colon, slash format- i.e. C:\ + return (path.Length >= 3) + && (path[1] == Path.VolumeSeparatorChar) + && (path[2] == Path.DirectorySeparatorChar || path[2] == Path.AltDirectorySeparatorChar) + // To match old behavior we'll check the drive character for validity as the path is technically + // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream. + && ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')); + } + + public override bool PathStartsWith(string path, string root, params char[] separators) + { + // either it is identical to root, + // or it is root + separator + anything + + if (separators == null || separators.Length == 0) separators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + if (!path.StartsWith(root, StringComparison.OrdinalIgnoreCase)) return false; + if (path.Length == root.Length) return true; + if (path.Length < root.Length) return false; + return separators.Contains(path[root.Length]); + } + } +} diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 6ed866754d..551be602dd 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -25,27 +25,10 @@ namespace Umbraco.Core.IO // (is used in GetRelativePath) private readonly string _rootPathFwd; - // the relative url, using url separator chars, NOT ending with a separator + // the relative URL, using URL separator chars, NOT ending with a separator // eg "" or "/Views" or "/Media" or "//Media" in case of a virtual path private readonly string _rootUrl; - // virtualRoot should be "~/path/to/root" eg "~/Views" - // the "~/" is mandatory. - public PhysicalFileSystem(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILogger logger, string virtualRoot) - { - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - - if (virtualRoot == null) throw new ArgumentNullException(nameof(virtualRoot)); - if (virtualRoot.StartsWith("~/") == false) - throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'"); - - _rootPath = EnsureDirectorySeparatorChar(hostingEnvironment.MapPathContentRoot(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar); - _rootPathFwd = EnsureUrlSeparatorChar(_rootPath); - _rootUrl = EnsureUrlSeparatorChar(hostingEnvironment.ToAbsolute(virtualRoot)).TrimEnd('/'); - } - public PhysicalFileSystem(IIOHelper ioHelper,IHostingEnvironment hostingEnvironment, ILogger logger, string rootPath, string rootUrl) { _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); @@ -259,9 +242,9 @@ namespace Umbraco.Core.IO } /// - /// Gets the filesystem-relative path of a full path or of an url. + /// Gets the filesystem-relative path of a full path or of an URL. /// - /// The full path or url. + /// The full path or URL. /// The path, relative to this filesystem's root. /// /// The relative path is relative to this filesystem's root, not starting with any @@ -269,10 +252,10 @@ namespace Umbraco.Core.IO /// public string GetRelativePath(string fullPathOrUrl) { - // test url - var path = fullPathOrUrl.Replace('\\', '/'); // ensure url separator char + // test URL + var path = fullPathOrUrl.Replace('\\', '/'); // ensure URL separator char - // if it starts with the root url, strip it and trim the starting slash to make it relative + // if it starts with the root URL, strip it and trim the starting slash to make it relative // eg "/Media/1234/img.jpg" => "1234/img.jpg" if (_ioHelper.PathStartsWith(path, _rootUrl, '/')) return path.Substring(_rootUrl.Length).TrimStart('/'); @@ -335,10 +318,10 @@ namespace Umbraco.Core.IO } /// - /// Gets the url. + /// Gets the URL. /// /// The filesystem-relative path. - /// The url. + /// The URL. /// All separators are forward-slashes. public string GetUrl(string path) { diff --git a/src/Umbraco.Core/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs index 83fe5aafe7..a395938050 100644 --- a/src/Umbraco.Core/IO/ShadowWrapper.cs +++ b/src/Umbraco.Core/IO/ShadowWrapper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Extensions.Logging; -using Umbraco.Core.Composing; using Umbraco.Core.Hosting; namespace Umbraco.Core.IO @@ -31,7 +30,7 @@ namespace Umbraco.Core.IO _isScoped = isScoped; } - public static string CreateShadowId(IIOHelper ioHelper) + public static string CreateShadowId(IHostingEnvironment hostingEnvironment) { const int retries = 50; // avoid infinite loop const int idLength = 8; // 6 chars @@ -46,7 +45,7 @@ namespace Umbraco.Core.IO var id = GuidUtils.ToBase32String(Guid.NewGuid(), idLength); var virt = ShadowFsPath + "/" + id; - var shadowDir = ioHelper.MapPath(virt); + var shadowDir = hostingEnvironment.MapPathContentRoot(virt); if (Directory.Exists(shadowDir)) continue; @@ -63,10 +62,10 @@ namespace Umbraco.Core.IO // on ShadowFileSystemsScope.None - and if None is false then we should be running // in a single thread anyways - var virt = ShadowFsPath + "/" + id + "/" + _shadowPath; - _shadowDir = _ioHelper.MapPath(virt); + var virt = Path.Combine(ShadowFsPath , id , _shadowPath); + _shadowDir = _hostingEnvironment.MapPathContentRoot(virt); Directory.CreateDirectory(_shadowDir); - var tempfs = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, _loggerFactory.CreateLogger(), virt); + var tempfs = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, _loggerFactory.CreateLogger(), _shadowDir, _hostingEnvironment.ToAbsolute(virt)); _shadowFileSystem = new ShadowFileSystem(_innerFileSystem, tempfs); } @@ -91,7 +90,7 @@ namespace Umbraco.Core.IO // shadowPath make be path/to/dir, remove each dir = dir.Replace("/", "\\"); - var min = _ioHelper.MapPath(ShadowFsPath).Length; + var min = _hostingEnvironment.MapPathContentRoot(ShadowFsPath).Length; var pos = dir.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase); while (pos > min) { diff --git a/src/Umbraco.Core/Install/InstallStatusTracker.cs b/src/Umbraco.Core/Install/InstallStatusTracker.cs index c0c9a696fd..4260fa189d 100644 --- a/src/Umbraco.Core/Install/InstallStatusTracker.cs +++ b/src/Umbraco.Core/Install/InstallStatusTracker.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using Umbraco.Core; using Umbraco.Core.Collections; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; using Umbraco.Core.Serialization; using Umbraco.Web.Install.Models; @@ -15,12 +15,12 @@ namespace Umbraco.Web.Install /// public class InstallStatusTracker { - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IJsonSerializer _jsonSerializer; - public InstallStatusTracker(IIOHelper ioHelper, IJsonSerializer jsonSerializer) + public InstallStatusTracker(IHostingEnvironment hostingEnvironment, IJsonSerializer jsonSerializer) { - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _jsonSerializer = jsonSerializer; } @@ -28,7 +28,7 @@ namespace Umbraco.Web.Install private string GetFile(Guid installId) { - var file = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" + var file = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" + "install_" + installId.ToString("N") + ".txt"); @@ -43,7 +43,7 @@ namespace Umbraco.Web.Install public void ClearFiles() { - var dir = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); + var dir = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); if (Directory.Exists(dir)) { var files = Directory.GetFiles(dir); diff --git a/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs index adca1950f3..94407bee8d 100644 --- a/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Core/Install/InstallSteps/StarterKitCleanupStep.cs @@ -3,7 +3,7 @@ using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -12,11 +12,11 @@ namespace Umbraco.Web.Install.InstallSteps "StarterKitCleanup", 32, "Almost done")] internal class StarterKitCleanupStep : InstallSetupStep { - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; - public StarterKitCleanupStep(IIOHelper ioHelper) + public StarterKitCleanupStep(IHostingEnvironment hostingEnvironment) { - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; } public override Task ExecuteAsync(object model) @@ -33,7 +33,7 @@ namespace Umbraco.Web.Install.InstallSteps private void CleanupInstallation(int packageId, string packageFile) { - var zipFile = new FileInfo(Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Packages), WebUtility.UrlDecode(packageFile))); + var zipFile = new FileInfo(Path.Combine(_hostingEnvironment.MapPathContentRoot(Core.Constants.SystemDirectories.Packages), WebUtility.UrlDecode(packageFile))); if (zipFile.Exists) zipFile.Delete(); diff --git a/src/Umbraco.Core/Logging/LoggingTaskExtension.cs b/src/Umbraco.Core/Logging/LoggingTaskExtension.cs index 1f742133c3..2e3aa0a883 100644 --- a/src/Umbraco.Core/Logging/LoggingTaskExtension.cs +++ b/src/Umbraco.Core/Logging/LoggingTaskExtension.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Umbraco.Core.Logging @@ -14,7 +15,12 @@ namespace Umbraco.Core.Logging /// public static Task LogErrors(this Task task, Action logMethod) { - return task.ContinueWith(t => LogErrorsInner(t, logMethod), TaskContinuationOptions.OnlyOnFaulted); + return task.ContinueWith( + t => LogErrorsInner(t, logMethod), + CancellationToken.None, + TaskContinuationOptions.OnlyOnFaulted, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); } /// @@ -26,7 +32,10 @@ namespace Umbraco.Core.Logging /// public static Task LogErrorsWaitable(this Task task, Action logMethod) { - return task.ContinueWith(t => LogErrorsInner(t, logMethod)); + return task.ContinueWith( + t => LogErrorsInner(t, logMethod), + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); } private static void LogErrorsInner(Task task, Action logAction) diff --git a/src/Umbraco.Core/Media/IEmbedProvider.cs b/src/Umbraco.Core/Media/IEmbedProvider.cs index 99b162e0b7..39da6fae0d 100644 --- a/src/Umbraco.Core/Media/IEmbedProvider.cs +++ b/src/Umbraco.Core/Media/IEmbedProvider.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Media string[] UrlSchemeRegex { get; } /// - /// A collection of querystring request parameters to append to the API Url + /// A collection of querystring request parameters to append to the API URL /// /// ?key=value&key2=value2 Dictionary RequestParams { get; } diff --git a/src/Umbraco.Core/Models/ContentBaseExtensions.cs b/src/Umbraco.Core/Models/ContentBaseExtensions.cs index 75f6772ec5..2b9a246573 100644 --- a/src/Umbraco.Core/Models/ContentBaseExtensions.cs +++ b/src/Umbraco.Core/Models/ContentBaseExtensions.cs @@ -6,18 +6,18 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Strings { /// - /// Provides extension methods to IContentBase to get url segments. + /// Provides extension methods to IContentBase to get URL segments. /// public static class ContentBaseExtensions { /// - /// Gets the url segment for a specified content and culture. + /// Gets the URL segment for a specified content and culture. /// /// The content. - /// The culture. /// /// - /// The url segment. + /// The culture. + /// The URL segment. public static string GetUrlSegment(this IContentBase content, IShortStringHelper shortStringHelper, IEnumerable urlSegmentProviders, string culture = null) { if (content == null) throw new ArgumentNullException(nameof(content)); diff --git a/src/Umbraco.Core/Models/ContentEditing/CodeFileDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/CodeFileDisplay.cs index 8004e8e731..2dc50f1936 100644 --- a/src/Umbraco.Core/Models/ContentEditing/CodeFileDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/CodeFileDisplay.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.Models.ContentEditing /// /// Path represents the path used by the backoffice tree - /// For files stored on disk, this is a urlencoded, comma separated + /// For files stored on disk, this is a URL encoded, comma separated /// path to the file, always starting with -1. /// /// -1,Partials,Parials%2FFolder,Partials%2FFolder%2FFile.cshtml diff --git a/src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs index 9440000146..f7daf79ec9 100644 --- a/src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/IMediaEntitySlim.cs @@ -7,7 +7,7 @@ { /// - /// The media file's path/url + /// The media file's path/URL /// string MediaPath { get; } } diff --git a/src/Umbraco.Core/Models/IRedirectUrl.cs b/src/Umbraco.Core/Models/IRedirectUrl.cs index e066881645..527dad57da 100644 --- a/src/Umbraco.Core/Models/IRedirectUrl.cs +++ b/src/Umbraco.Core/Models/IRedirectUrl.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models { /// - /// Represents a redirect url. + /// Represents a redirect URL. /// public interface IRedirectUrl : IEntity, IRememberBeingDirty { @@ -22,7 +22,7 @@ namespace Umbraco.Core.Models Guid ContentKey { get; set; } /// - /// Gets or sets the redirect url creation date. + /// Gets or sets the redirect URL creation date. /// [DataMember] DateTime CreateDateUtc { get; set; } @@ -34,7 +34,7 @@ namespace Umbraco.Core.Models string Culture { get; set; } /// - /// Gets or sets the redirect url route. + /// Gets or sets the redirect URL route. /// /// Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo. [DataMember] diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs index 9615969c07..5b444c6af8 100644 --- a/src/Umbraco.Core/Models/MediaExtensions.cs +++ b/src/Umbraco.Core/Models/MediaExtensions.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Models public static class MediaExtensions { /// - /// Gets the url of a media item. + /// Gets the URL of a media item. /// public static string GetUrl(this IMedia media, string propertyAlias, MediaUrlGeneratorCollection mediaUrlGenerators) { @@ -25,7 +25,7 @@ namespace Umbraco.Core.Models } /// - /// Gets the urls of a media item. + /// Gets the URLs of a media item. /// public static string[] GetUrls(this IMedia media, ContentSettings contentSettings, MediaUrlGeneratorCollection mediaUrlGenerators) { diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 4872d04321..f8784973b7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -31,7 +31,7 @@ namespace Umbraco.Core.Models.PublishedContent string Name { get; } /// - /// Gets the url segment of the content item for the current culture. + /// Gets the URL segment of the content item for the current culture. /// string UrlSegment { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs index 57c116dc54..02912a6f8e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Models /// /// Provide an abstract base class for IPublishedContent implementations. /// - /// This base class does which (a) consistently resolves and caches the Url, (b) provides an implementation + /// This base class does which (a) consistently resolves and caches the URL, (b) provides an implementation /// for this[alias], and (c) provides basic content set management. [DebuggerDisplay("Content Id: {Id}")] public abstract class PublishedContentBase : IPublishedContent diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 495faa6bf3..8cf159ac60 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Models.PublishedContent public string Name { get; } /// - /// Gets the url segment of the item. + /// Gets the URL segment of the item. /// public string UrlSegment { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs index 4cd6a680f4..d11459bb9e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs +++ b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs @@ -1,27 +1,27 @@ namespace Umbraco.Core.Models.PublishedContent { /// - /// Specifies the type of urls that the url provider should produce, Auto is the default. + /// Specifies the type of URLs that the URL provider should produce, Auto is the default. /// public enum UrlMode { /// - /// Indicates that the url provider should do what it has been configured to do. + /// Indicates that the URL provider should do what it has been configured to do. /// Default = 0, /// - /// Indicates that the url provider should produce relative urls exclusively. + /// Indicates that the URL provider should produce relative URLs exclusively. /// Relative, /// - /// Indicates that the url provider should produce absolute urls exclusively. + /// Indicates that the URL provider should produce absolute URLs exclusively. /// Absolute, /// - /// Indicates that the url provider should determine automatically whether to return relative or absolute urls. + /// Indicates that the URL provider should determine automatically whether to return relative or absolute URLs. /// Auto } diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs index 7dae5d5393..a862b11c23 100644 --- a/src/Umbraco.Core/Models/ServerRegistration.cs +++ b/src/Umbraco.Core/Models/ServerRegistration.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Models /// Initializes a new instance of the class. /// /// The unique id of the server registration. - /// The server url. + /// The server URL. /// The unique server identity. /// The date and time the registration was created. /// The date and time the registration was last accessed. @@ -45,7 +45,7 @@ namespace Umbraco.Core.Models /// /// Initializes a new instance of the class. /// - /// The server url. + /// The server URL. /// The unique server identity. /// The date and time the registration was created. public ServerRegistration(string serverAddress, string serverIdentity, DateTime registered) @@ -58,7 +58,7 @@ namespace Umbraco.Core.Models } /// - /// Gets or sets the server url. + /// Gets or sets the server URL. /// public string ServerAddress { diff --git a/src/Umbraco.Core/Models/Trees/MenuItem.cs b/src/Umbraco.Core/Models/Trees/MenuItem.cs index b8743a957a..1b7c130a60 100644 --- a/src/Umbraco.Core/Models/Trees/MenuItem.cs +++ b/src/Umbraco.Core/Models/Trees/MenuItem.cs @@ -166,7 +166,7 @@ namespace Umbraco.Web.Models.Trees } /// - /// Sets the menu item to display a dialog based on a url path in an iframe + /// Sets the menu item to display a dialog based on a URL path in an iframe /// /// /// diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 295b764834..6a3ea16ff7 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -92,46 +92,46 @@ namespace Umbraco.Core.Models public static bool HasContentRootAccess(this IUser user, IEntityService entityService) { - return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); + return ContentPermissions.HasPathAccess(Constants.System.RootString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); } public static bool HasContentBinAccess(this IUser user, IEntityService entityService) { - return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinContentString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); + return ContentPermissions.HasPathAccess(Constants.System.RecycleBinContentString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); } public static bool HasMediaRootAccess(this IUser user, IEntityService entityService) { - return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); + return ContentPermissions.HasPathAccess(Constants.System.RootString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); } public static bool HasMediaBinAccess(this IUser user, IEntityService entityService) { - return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinMediaString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); + return ContentPermissions.HasPathAccess(Constants.System.RecycleBinMediaString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); } public static bool HasPathAccess(this IUser user, IContent content, IEntityService entityService) { if (content == null) throw new ArgumentNullException(nameof(content)); - return ContentPermissionsHelper.HasPathAccess(content.Path, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); + return ContentPermissions.HasPathAccess(content.Path, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); } public static bool HasPathAccess(this IUser user, IMedia media, IEntityService entityService) { if (media == null) throw new ArgumentNullException(nameof(media)); - return ContentPermissionsHelper.HasPathAccess(media.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); + return ContentPermissions.HasPathAccess(media.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); } public static bool HasContentPathAccess(this IUser user, IUmbracoEntity entity, IEntityService entityService) { if (entity == null) throw new ArgumentNullException(nameof(entity)); - return ContentPermissionsHelper.HasPathAccess(entity.Path, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); + return ContentPermissions.HasPathAccess(entity.Path, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent); } public static bool HasMediaPathAccess(this IUser user, IUmbracoEntity entity, IEntityService entityService) { if (entity == null) throw new ArgumentNullException(nameof(entity)); - return ContentPermissionsHelper.HasPathAccess(entity.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); + return ContentPermissions.HasPathAccess(entity.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); } /// diff --git a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs index 4f6c622bb3..884085548e 100644 --- a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Packaging; @@ -15,13 +16,15 @@ namespace Umbraco.Core.Packaging { private readonly CompiledPackageXmlParser _parser; private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IProfilingLogger _logger; private readonly PackageExtraction _packageExtraction; - public PackageFileInstallation(CompiledPackageXmlParser parser, IIOHelper ioHelper, IProfilingLogger logger) + public PackageFileInstallation(CompiledPackageXmlParser parser, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IProfilingLogger logger) { _parser = parser; _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _logger = logger; _packageExtraction = new PackageExtraction(); } @@ -62,9 +65,8 @@ namespace Umbraco.Core.Packaging var file = _ioHelper.FindFile(item); if (file != null) { - // TODO: Surely this should be ~/ ? file = file.EnsureStartsWith("/"); - var filePath = _ioHelper.MapPath(file); + var filePath = _hostingEnvironment.MapPathContentRoot(file); if (File.Exists(filePath)) File.Delete(filePath); diff --git a/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs index d05f4e007c..36f885520d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs @@ -10,72 +10,80 @@ namespace Umbraco.Core.Persistence.Repositories public interface IRedirectUrlRepository : IReadWriteQueryRepository { /// - /// Gets a redirect url. + /// Gets a redirect URL. /// - /// The Umbraco redirect url route. + /// The Umbraco redirect URL route. /// The content unique key. /// The culture. /// IRedirectUrl Get(string url, Guid contentKey, string culture); /// - /// Deletes a redirect url. + /// Deletes a redirect URL. /// - /// The redirect url identifier. + /// The redirect URL identifier. void Delete(Guid id); /// - /// Deletes all redirect urls. + /// Deletes all redirect URLs. /// void DeleteAll(); /// - /// Deletes all redirect urls for a given content. + /// Deletes all redirect URLs for a given content. /// /// The content unique key. void DeleteContentUrls(Guid contentKey); /// - /// Gets the most recent redirect url corresponding to an Umbraco redirect url route. + /// Gets the most recent redirect URL corresponding to an Umbraco redirect URL route. /// - /// The Umbraco redirect url route. - /// The most recent redirect url corresponding to the route. + /// The Umbraco redirect URL route. + /// The most recent redirect URL corresponding to the route. IRedirectUrl GetMostRecentUrl(string url); /// - /// Gets all redirect urls for a content item. + /// Gets the most recent redirect URL corresponding to an Umbraco redirect URL route. + /// + /// The Umbraco redirect URL route. + /// The culture the domain is associated with + /// The most recent redirect URL corresponding to the route. + IRedirectUrl GetMostRecentUrl(string url, string culture); + + /// + /// Gets all redirect URLs for a content item. /// /// The content unique key. - /// All redirect urls for the content item. + /// All redirect URLs for the content item. IEnumerable GetContentUrls(Guid contentKey); /// - /// Gets all redirect urls. + /// Gets all redirect URLs. /// /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable GetAllUrls(long pageIndex, int pageSize, out long total); /// - /// Gets all redirect urls below a given content item. + /// Gets all redirect URLs below a given content item. /// /// The content unique identifier. /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total); /// - /// Searches for all redirect urls that contain a given search term in their URL property. + /// Searches for all redirect URLs that contain a given search term in their URL property. /// /// The term to search for. /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable SearchUrls(string searchTerm, long pageIndex, int pageSize, out long total); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs index b20c6d26b9..1babb3c1ad 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UpgradeCheckRepository.cs @@ -1,4 +1,5 @@ -using System.Net.Http; +using System; +using System.Net.Http; using System.Text; using System.Threading.Tasks; using Semver; @@ -27,6 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var content = new StringContent(_jsonSerializer.Serialize(new CheckUpgradeDto(version)), Encoding.UTF8, "application/json"); + _httpClient.Timeout = TimeSpan.FromSeconds(1); var task = await _httpClient.PostAsync(RestApiUpgradeChecklUrl,content); var json = await task.Content.ReadAsStringAsync(); var result = _jsonSerializer.Deserialize(json); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs index 99bbe86dae..cc7aa27a4a 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs @@ -36,9 +36,14 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { if (source == null) return null; - var attemptConvertInt = source.TryConvertTo(); - if (attemptConvertInt.Success) - return attemptConvertInt.Result; + //Don't attempt to convert to int for UDI + if(!(source is string) || source is string strSource && !string.IsNullOrWhiteSpace(strSource) && !strSource.StartsWith("umb")) + { + var attemptConvertInt = source.TryConvertTo(); + if (attemptConvertInt.Success) + return attemptConvertInt.Result; + } + var attemptConvertUdi = source.TryConvertTo(); if (attemptConvertUdi.Success) return attemptConvertUdi.Result; diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index fd353f95cd..2510f3ff71 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -38,11 +38,11 @@ namespace Umbraco.Core #region Url segment /// - /// Gets the url segment of the content item. + /// 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). + /// 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) @@ -822,7 +822,7 @@ namespace Umbraco.Core /// 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. diff --git a/src/Umbraco.Core/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs index 0d919614f3..1e6056942f 100644 --- a/src/Umbraco.Core/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { /// - /// Provides urls using the umbracoUrlAlias property. + /// Provides URLs using the umbracoUrlAlias property. /// public class AliasUrlProvider : IUrlProvider { @@ -47,15 +47,15 @@ namespace Umbraco.Web.Routing #region GetOtherUrls /// - /// Gets the other urls of a published content. + /// 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. + /// 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...). + /// 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(int id, Uri current) { @@ -83,7 +83,7 @@ namespace Umbraco.Web.Routing if (domainUris == null) { // no domain - // if the property is invariant, then url "/" is ok + // if the property is invariant, then URL "/" is ok // if the property varies, then what are we supposed to do? // the content finder may work, depending on the 'current' culture, // but there's no way we can return something meaningful here @@ -105,11 +105,11 @@ namespace Umbraco.Web.Routing } else { - // some domains: one url per domain, which is "/" - foreach(var domainUri in domainUris) + // some domains: one URL per domain, which is "/" + foreach (var domainUri in domainUris) { - // if the property is invariant, get the invariant value, url is "/" - // if the property varies, get the variant value, url is "/" + // if the property is invariant, get the invariant value, URL is "/" + // if the property varies, get the variant value, URL is "/" // but! only if the culture is published, else ignore if (varies && !node.HasCulture(domainUri.Culture.Name)) continue; diff --git a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs index 7a58b1c995..895917c69d 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.Routing { /// - /// Provides an implementation of that handles page url rewrites + /// Provides an implementation of that handles page URL rewrites /// that are stored when moving, saving, or deleting a node. /// /// @@ -37,7 +37,9 @@ namespace Umbraco.Web.Routing ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()) : frequest.Uri.GetAbsolutePathDecoded(); - var redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route); + + + var redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route, frequest.Culture.Name); if (redirectUrl == null) { @@ -49,14 +51,14 @@ namespace Umbraco.Web.Routing var url = content == null ? "#" : content.Url(_publishedUrlProvider, redirectUrl.Culture); if (url.StartsWith("#")) { - _logger.LogDebug("Route {Route} matches content {ContentId} which has no url.", route, redirectUrl.ContentId); + _logger.LogDebug("Route {Route} matches content {ContentId} which has no URL.", route, redirectUrl.ContentId); return false; } - // Appending any querystring from the incoming request to the redirect url. + // Appending any querystring from the incoming request to the redirect URL url = string.IsNullOrEmpty(frequest.Uri.Query) ? url : url + frequest.Uri.Query; - _logger.LogDebug("Route {Route} matches content {ContentId} with url '{Url}', redirecting.", route, content.Id, url); + _logger.LogDebug("Route {Route} matches content {ContentId} with URL '{Url}', redirecting.", route, content.Id, url); frequest.SetRedirectPermanent(url); diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs index 85e8195c75..653e808dfe 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs @@ -5,10 +5,10 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { /// - /// Provides an implementation of that handles page nice urls. + /// Provides an implementation of that handles page nice URLs. /// /// - /// Handles /foo/bar where /foo/bar is the nice url of a document. + /// Handles /foo/bar where /foo/bar is the nice URL of a document. /// public class ContentFinderByUrl : IContentFinder { diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index cbb93aedb9..24bfad914d 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -100,7 +100,7 @@ namespace Umbraco.Web.Routing } // TODO: even with Linq, what happens below has to be horribly slow - // but the only solution is to entirely refactor url providers to stop being dynamic + // but the only solution is to entirely refactor URL providers to stop being dynamic if (rootNodeId > 0) { diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs index dc2f46b7cf..8ae4e2aead 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs @@ -9,11 +9,11 @@ using Microsoft.Extensions.Logging; namespace Umbraco.Web.Routing { /// - /// Provides an implementation of that handles page nice urls and a template. + /// Provides an implementation of that handles page nice URLs and a template. /// /// /// This finder allows for an odd routing pattern similar to altTemplate, probably only use case is if there is an alternative mime type template and it should be routable by something like "/hello/world/json" where the JSON template is to be used for the "world" page - /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. + /// Handles /foo/bar/template where /foo/bar is the nice URL of a document, and template a template alias. /// If successful, then the template of the document request is also assigned. /// public class ContentFinderByUrlAndTemplate : ContentFinderByUrl diff --git a/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs index 3e6413ff90..e489b764c3 100644 --- a/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultMediaUrlProvider.cs @@ -5,7 +5,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Routing { /// - /// Default media url provider. + /// Default media URL provider. /// public class DefaultMediaUrlProvider : IMediaUrlProvider { diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 5c85bfe68d..51c212aa3c 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -2,9 +2,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing @@ -16,16 +14,14 @@ namespace Umbraco.Web.Routing { private readonly RequestHandlerSettings _requestSettings; private readonly ILogger _logger; - private readonly GlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly UriUtility _uriUtility; - public DefaultUrlProvider(IOptions requestSettings, ILogger logger, IOptions globalSettings, ISiteDomainHelper siteDomainHelper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) + public DefaultUrlProvider(IOptions requestSettings, ILogger logger, ISiteDomainHelper siteDomainHelper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) { _requestSettings = requestSettings.Value; _logger = logger; - _globalSettings = globalSettings.Value; _siteDomainHelper = siteDomainHelper; _uriUtility = uriUtility; _umbracoContextAccessor = umbracoContextAccessor; @@ -36,7 +32,7 @@ namespace Umbraco.Web.Routing /// 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)); + if (!current.IsAbsoluteUri) throw new ArgumentException("Current URL must be absolute.", nameof(current)); var umbracoContext = _umbracoContextAccessor.UmbracoContext; // will not use cache if previewing @@ -61,7 +57,7 @@ namespace Umbraco.Web.Routing ? null : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, int.Parse(route.Substring(0, pos)), current, culture); - // assemble the url from domainUri (maybe null) and path + // assemble the URL from domainUri (maybe null) and path var url = AssembleUrl(domainUri, path, current, mode).ToString(); return UrlInfo.Url(url, culture); @@ -72,15 +68,15 @@ namespace Umbraco.Web.Routing #region GetOtherUrls /// - /// Gets the other urls of a published content. + /// 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. + /// 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...). + /// 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(int id, Uri current) { @@ -173,7 +169,7 @@ namespace Umbraco.Web.Routing } // UriFromUmbraco will handle vdir - // meaning it will add vdir into domain urls too! + // meaning it will add vdir into domain URLs too! return _uriUtility.UriFromUmbraco(uri, _requestSettings); } diff --git a/src/Umbraco.Core/Routing/IMediaUrlProvider.cs b/src/Umbraco.Core/Routing/IMediaUrlProvider.cs index 1b966c6fdd..6da917fe25 100644 --- a/src/Umbraco.Core/Routing/IMediaUrlProvider.cs +++ b/src/Umbraco.Core/Routing/IMediaUrlProvider.cs @@ -5,26 +5,26 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { /// - /// Provides media urls. + /// Provides media URL. /// public interface IMediaUrlProvider { /// - /// Gets the url of a media item. + /// Gets the URL of a media item. /// /// The published content. - /// The property alias to resolve the url from. - /// The url mode. + /// The property alias to resolve the URL from. + /// The URL mode. /// The variation language. - /// The current absolute url. - /// The url for the media. + /// 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, + /// 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. - /// The url provider can ignore the mode and always return an absolute url, - /// 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. + /// The URL provider can ignore the mode and always return an absolute URL, + /// 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(IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current); } diff --git a/src/Umbraco.Core/Routing/IUrlProvider.cs b/src/Umbraco.Core/Routing/IUrlProvider.cs index ba1d48a113..8fa96c7a2d 100644 --- a/src/Umbraco.Core/Routing/IUrlProvider.cs +++ b/src/Umbraco.Core/Routing/IUrlProvider.cs @@ -5,35 +5,35 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { /// - /// Provides urls. + /// Provides URLs. /// public interface IUrlProvider { /// - /// Gets the url of a published content. + /// Gets the URL of a published content. /// /// The published content. - /// The url mode. + /// The URL mode. /// A culture. - /// The current absolute url. - /// The url for the published content. + /// 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, + /// 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 should return null. + /// If the provider is unable to provide a URL, it should return null. /// UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current); /// - /// Gets the other urls of a published content. + /// Gets the other URLs of a published content. /// /// The published content id. - /// The current absolute url. - /// The other urls for the published content. + /// 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...). + /// 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); } diff --git a/src/Umbraco.Core/Routing/PublishedRequest.cs b/src/Umbraco.Core/Routing/PublishedRequest.cs index 3e13270fa0..bab60e49f6 100644 --- a/src/Umbraco.Core/Routing/PublishedRequest.cs +++ b/src/Umbraco.Core/Routing/PublishedRequest.cs @@ -372,14 +372,14 @@ namespace Umbraco.Web.Routing public bool IsRedirectPermanent { get; private set; } /// - /// Gets or sets the url to redirect to, when the content request triggers a redirect. + /// Gets or sets the URL to redirect to, when the content request triggers a redirect. /// public string RedirectUrl { get; private set; } /// /// Indicates that the content request should trigger a redirect (302). /// - /// The url to redirect to. + /// 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. public void SetRedirect(string url) @@ -392,7 +392,7 @@ namespace Umbraco.Web.Routing /// /// Indicates that the content request should trigger a permanent redirect (301). /// - /// The url to redirect to. + /// 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. public void SetRedirectPermanent(string url) @@ -405,7 +405,7 @@ namespace Umbraco.Web.Routing /// /// Indicates that the content request should trigger a redirect, with a specified status code. /// - /// The url to redirect to. + /// 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. diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index 21f760f5a0..9b7354de74 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -112,7 +112,7 @@ namespace Umbraco.Web.Routing { // 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 - // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain + // "directory URLs" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. // // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors diff --git a/src/Umbraco.Core/Routing/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs index 1bcfab6490..8de78dfbf5 100644 --- a/src/Umbraco.Core/Routing/UriUtility.cs +++ b/src/Umbraco.Core/Routing/UriUtility.cs @@ -182,7 +182,7 @@ namespace Umbraco.Web /// - /// Returns an full url with the host, port, etc... + /// Returns an full URL with the host, port, etc... /// /// An absolute path (i.e. starts with a '/' ) /// diff --git a/src/Umbraco.Core/Routing/UrlInfo.cs b/src/Umbraco.Core/Routing/UrlInfo.cs index 8385ab54a9..59145d19ec 100644 --- a/src/Umbraco.Core/Routing/UrlInfo.cs +++ b/src/Umbraco.Core/Routing/UrlInfo.cs @@ -4,14 +4,14 @@ using System.Runtime.Serialization; namespace Umbraco.Web.Routing { /// - /// Represents infos for a url. + /// Represents infos for a URL. /// [DataContract(Name = "urlInfo", Namespace = "")] public class UrlInfo : IEquatable { /// - /// Creates a instance representing a true url. + /// Creates a instance representing a true URL. /// public static UrlInfo Url(string text, string culture = null) => new UrlInfo(text, true, culture); @@ -38,14 +38,14 @@ namespace Umbraco.Web.Routing public string Culture { get; } /// - /// Gets a value indicating whether the url is a true url. + /// Gets a value indicating whether the URL is a true URL. /// /// Otherwise, it is a message. [DataMember(Name = "isUrl")] public bool IsUrl { get; } /// - /// Gets the text, which is either the url, or a message. + /// Gets the text, which is either the URL, or a message. /// [DataMember(Name = "text")] public string Text { get; } diff --git a/src/Umbraco.Core/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs index 9dd4a413a2..542d1a6ea3 100644 --- a/src/Umbraco.Core/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -10,19 +10,19 @@ namespace Umbraco.Web.Routing { /// - /// Provides urls. + /// Provides URLs. /// 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. + /// Initializes a new instance of the class with an Umbraco context and a list of URL providers. /// /// The Umbraco context accessor. /// Routing settings. - /// The list of url providers. - /// The list of media url providers. + /// The list of URL providers. + /// The list of media URL providers. /// The current variation accessor. /// public UrlProvider(IUmbracoContextAccessor umbracoContextAccessor, IOptions routingSettings, UrlProviderCollection urlProviders, MediaUrlProviderCollection mediaUrlProviders, IVariationContextAccessor variationContextAccessor) @@ -40,7 +40,7 @@ namespace Umbraco.Web.Routing private readonly IVariationContextAccessor _variationContextAccessor; /// - /// Gets or sets the provider url mode. + /// Gets or sets the provider URL mode. /// public UrlMode Mode { get; set; } @@ -53,40 +53,40 @@ namespace Umbraco.Web.Routing private IPublishedContent GetMedia(Guid id) => _umbracoContextAccessor.UmbracoContext.Media.GetById(id); /// - /// Gets the url of a published content. + /// Gets the URL of a published content. /// /// The published content identifier. - /// The url mode. + /// The URL mode. /// A culture. - /// The current absolute url. - /// The url for the published content. + /// The current absolute URL. + /// The URL for the published content. public string GetUrl(Guid id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// - /// Gets the url of a published content. + /// Gets the URL of a published content. /// /// The published content identifier. - /// The url mode. + /// The URL mode. /// A culture. - /// The current absolute url. - /// The url for the published content. + /// The current absolute URL. + /// The URL for the published content. public string GetUrl(int id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// - /// Gets the url of a published content. + /// Gets the URL of a published content. /// /// The published content. - /// The url mode. + /// The URL mode. /// A culture. - /// The current absolute url. - /// The url for the published content. + /// 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, + /// 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 "#". + /// If the provider is unable to provide a URL, it returns "#". /// public string GetUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) { @@ -127,14 +127,14 @@ namespace Umbraco.Web.Routing #region GetOtherUrls /// - /// Gets the other urls of a published content. + /// Gets the other URLs of a published content. /// /// The published content id. - /// The other urls for the published content. + /// 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. + /// 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. /// public IEnumerable GetOtherUrls(int id) { @@ -142,14 +142,14 @@ namespace Umbraco.Web.Routing } /// - /// Gets the other urls of a published content. + /// Gets the other URLs of a published content. /// /// The published content id. - /// The current absolute url. - /// The other urls for the published content. + /// 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...). + /// 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(int id, Uri current) { @@ -161,7 +161,7 @@ namespace Umbraco.Web.Routing #region GetMediaUrl /// - /// Gets the url of a media item. + /// Gets the URL of a media item. /// /// /// @@ -173,19 +173,19 @@ namespace Umbraco.Web.Routing => GetMediaUrl(GetMedia(id), mode, culture, propertyAlias, current); /// - /// Gets the url of a media item. + /// Gets the URL of a media item. /// /// The published content. - /// The property alias to resolve the url from. - /// The url mode. + /// The property alias to resolve the URL from. + /// The URL mode. /// The variation language. - /// The current absolute url. - /// The url for the media. + /// 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, + /// 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 . + /// If the provider is unable to provide a URL, it returns . /// public 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.Core/Routing/UrlProviderExtensions.cs b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs index 1dc48be96e..5c00b14af3 100644 --- a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs @@ -12,11 +12,11 @@ namespace Umbraco.Web.Routing public static class UrlProviderExtensions { /// - /// Gets the Urls of the content item. + /// Gets the URLs of the content item. /// /// - /// Use when displaying Urls. If errors occur when generating the Urls, they will show in the list. - /// Contains all the Urls that we can figure out (based upon domains, etc). + /// Use when displaying URLs. If errors occur when generating the URLs, they will show in the list. + /// Contains all the URLs that we can figure out (based upon domains, etc). /// public static IEnumerable GetContentUrls(this IContent content, IPublishedRouter publishedRouter, @@ -46,13 +46,13 @@ namespace Umbraco.Web.Routing yield break; } - // build a list of urls, for the back-office + // build a list of URLs, for the back-office // which will contain - // - the 'main' urls, which is what .Url would return, for each culture - // - the 'other' urls we know (based upon domains, etc) + // - the 'main' URLs, which is what .Url would return, for each culture + // - the 'other' URLs we know (based upon domains, etc) // // need to work through each installed culture: - // on invariant nodes, each culture returns the same url segment but, + // on invariant nodes, each culture returns the same URL segment but, // we don't know if the branch to this content is invariant, so we need to ask // for URLs for all cultures. // and, not only for those assigned to domains in the branch, because we want @@ -68,7 +68,7 @@ namespace Umbraco.Web.Routing urls.Add(cultureUrl); } - //return the real urls first, then the messages + //return the real URLs first, then the messages foreach (var urlGroup in urls.GroupBy(x => x.IsUrl).OrderByDescending(x => x.Key)) { //in some cases there will be the same URL for multiple cultures: @@ -79,8 +79,8 @@ namespace Umbraco.Web.Routing yield return dUrl; } - // 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. + // 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 publishedUrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) if (urls.Add(otherUrl)) //avoid duplicates yield return otherUrl; @@ -129,7 +129,7 @@ namespace Umbraco.Web.Routing switch (url) { - // deal with 'could not get the url' + // deal with 'could not get the URL' case "#": yield return HandleCouldNotGetUrl(content, culture, contentService, textService); break; @@ -139,7 +139,7 @@ namespace Umbraco.Web.Routing yield return UrlInfo.Message(textService.Localize("content/getUrlException"), culture); break; - // got a url, deal with collisions, add url + // got a URL, deal with collisions, add URL default: if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility, out var urlInfo)) // detect collisions, etc yield return urlInfo; @@ -152,7 +152,7 @@ namespace Umbraco.Web.Routing private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, ILocalizedTextService textService) { - // document has a published version yet its url is "#" => a parent must be + // document has a published version yet its URL is "#" => a parent must be // unpublished, walk up the tree until we find it, and report. var parent = content; do @@ -173,7 +173,7 @@ namespace Umbraco.Web.Routing 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 + // 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); diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index 797dfa36e1..c413557a27 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -5,6 +5,7 @@ using System.Security.Cryptography; using System.Threading; using Microsoft.Extensions.Logging; using Umbraco.Core.Hosting; +using System.Threading.Tasks; namespace Umbraco.Core.Runtime { @@ -37,6 +38,9 @@ namespace Umbraco.Core.Runtime private const int LockTimeoutMilliseconds = 40000; // 40 seconds + private Task _listenTask; + private Task _listenCompleteTask; + #endregion #region Ctor @@ -172,7 +176,13 @@ namespace Umbraco.Core.Runtime try { // Listen for the signal from another AppDomain coming online to release the lock - _mainDomLock.ListenAsync().ContinueWith(_ => OnSignal("signal")); + _listenTask = _mainDomLock.ListenAsync(); + _listenCompleteTask = _listenTask.ContinueWith(t => + { + _logger.LogDebug("Listening task completed with {TaskStatus}", _listenTask.Status); + + OnSignal("signal"); + }, TaskScheduler.Default); // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html } catch (OperationCanceledException ex) { diff --git a/src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs b/src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs index 7220e77e0c..557fe37709 100644 --- a/src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs +++ b/src/Umbraco.Core/Scheduling/TaskAndFactoryExtensions.cs @@ -8,6 +8,7 @@ namespace Umbraco.Web.Scheduling { #region Task Extensions + // TODO: Not used, is this used in Deploy or something? static void SetCompletionSource(TaskCompletionSource completionSource, Task task) { if (task.IsFaulted) @@ -16,6 +17,7 @@ namespace Umbraco.Web.Scheduling completionSource.SetResult(default(TResult)); } + // TODO: Not used, is this used in Deploy or something? static void SetCompletionSource(TaskCompletionSource completionSource, Task task) { if (task.IsFaulted) @@ -24,17 +26,33 @@ namespace Umbraco.Web.Scheduling completionSource.SetResult(task.Result); } + // TODO: Not used, is this used in Deploy or something? public static Task ContinueWithTask(this Task task, Func continuation) { var completionSource = new TaskCompletionSource(); - task.ContinueWith(atask => continuation(atask).ContinueWith(atask2 => SetCompletionSource(completionSource, atask2))); + task.ContinueWith(atask => continuation(atask).ContinueWith( + atask2 => SetCompletionSource(completionSource, atask2), + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default), + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); return completionSource.Task; } + // TODO: Not used, is this used in Deploy or something? public static Task ContinueWithTask(this Task task, Func continuation, CancellationToken token) { var completionSource = new TaskCompletionSource(); - task.ContinueWith(atask => continuation(atask).ContinueWith(atask2 => SetCompletionSource(completionSource, atask2), token), token); + task.ContinueWith(atask => continuation(atask).ContinueWith( + atask2 => SetCompletionSource(completionSource, atask2), + token, + TaskContinuationOptions.None, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default), + token, + TaskContinuationOptions.None, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); return completionSource.Task; } diff --git a/src/Umbraco.Core/Security/ContentPermissionsHelper.cs b/src/Umbraco.Core/Security/ContentPermissions.cs similarity index 69% rename from src/Umbraco.Core/Security/ContentPermissionsHelper.cs rename to src/Umbraco.Core/Security/ContentPermissions.cs index 7f0a5c3c24..b598897133 100644 --- a/src/Umbraco.Core/Security/ContentPermissionsHelper.cs +++ b/src/Umbraco.Core/Security/ContentPermissions.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; @@ -11,8 +9,16 @@ using Umbraco.Core.Services; namespace Umbraco.Core.Security { - public class ContentPermissionsHelper + + /// + /// Checks user access to content + /// + public class ContentPermissions { + private readonly IUserService _userService; + private readonly IContentService _contentService; + private readonly IEntityService _entityService; + public enum ContentAccess { Granted, @@ -20,56 +26,68 @@ namespace Umbraco.Core.Security NotFound } - public static ContentAccess CheckPermissions( + public ContentPermissions( + IUserService userService, + IContentService contentService, + IEntityService entityService) + { + _userService = userService; + _contentService = contentService; + _entityService = entityService; + } + + public ContentAccess CheckPermissions( IContent content, IUser user, - IUserService userService, - IEntityService entityService, - params char[] permissionsToCheck) + char permissionToCheck) => CheckPermissions(content, user, new[] { permissionToCheck }); + + public ContentAccess CheckPermissions( + IContent content, + IUser user, + IReadOnlyList permissionsToCheck) { - if (user == null) throw new ArgumentNullException("user"); - if (userService == null) throw new ArgumentNullException("userService"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (content == null) return ContentAccess.NotFound; - var hasPathAccess = user.HasPathAccess(content, entityService); + var hasPathAccess = user.HasPathAccess(content, _entityService); if (hasPathAccess == false) return ContentAccess.Denied; - if (permissionsToCheck == null || permissionsToCheck.Length == 0) + if (permissionsToCheck == null || permissionsToCheck.Count == 0) return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(content.Path, user, userService, permissionsToCheck) + return CheckPermissionsPath(content.Path, user) ? ContentAccess.Granted : ContentAccess.Denied; } - public static ContentAccess CheckPermissions( + public ContentAccess CheckPermissions( IUmbracoEntity entity, IUser user, - IUserService userService, - IEntityService entityService, - params char[] permissionsToCheck) + char permissionToCheck) => CheckPermissions(entity, user, new[] { permissionToCheck }); + + public ContentAccess CheckPermissions( + IUmbracoEntity entity, + IUser user, + IReadOnlyList permissionsToCheck) { - if (user == null) throw new ArgumentNullException("user"); - if (userService == null) throw new ArgumentNullException("userService"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (entity == null) return ContentAccess.NotFound; - var hasPathAccess = user.HasContentPathAccess(entity, entityService); + var hasPathAccess = user.HasContentPathAccess(entity, _entityService); if (hasPathAccess == false) return ContentAccess.Denied; - if (permissionsToCheck == null || permissionsToCheck.Length == 0) + if (permissionsToCheck == null || permissionsToCheck.Count == 0) return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(entity.Path, user, userService, permissionsToCheck) + return CheckPermissionsPath(entity.Path, user) ? ContentAccess.Granted : ContentAccess.Denied; } @@ -84,41 +102,42 @@ namespace Umbraco.Core.Security /// The item resolved if one was found for the id /// /// - public static ContentAccess CheckPermissions( + public ContentAccess CheckPermissions( int nodeId, IUser user, - IUserService userService, - IEntityService entityService, out IUmbracoEntity entity, - params char[] permissionsToCheck) + IReadOnlyList permissionsToCheck = null) { - if (user == null) throw new ArgumentNullException("user"); - if (userService == null) throw new ArgumentNullException("userService"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (permissionsToCheck == null) + { + permissionsToCheck = Array.Empty(); + } bool? hasPathAccess = null; entity = null; if (nodeId == Constants.System.Root) - hasPathAccess = user.HasContentRootAccess(entityService); + hasPathAccess = user.HasContentRootAccess(_entityService); else if (nodeId == Constants.System.RecycleBinContent) - hasPathAccess = user.HasContentBinAccess(entityService); + hasPathAccess = user.HasContentBinAccess(_entityService); if (hasPathAccess.HasValue) return hasPathAccess.Value ? ContentAccess.Granted : ContentAccess.Denied; - entity = entityService.Get(nodeId, UmbracoObjectTypes.Document); + entity = _entityService.Get(nodeId, UmbracoObjectTypes.Document); if (entity == null) return ContentAccess.NotFound; - hasPathAccess = user.HasContentPathAccess(entity, entityService); + hasPathAccess = user.HasContentPathAccess(entity, _entityService); if (hasPathAccess == false) return ContentAccess.Denied; - if (permissionsToCheck == null || permissionsToCheck.Length == 0) + if (permissionsToCheck == null || permissionsToCheck.Count == 0) return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(entity.Path, user, userService, permissionsToCheck) + return CheckPermissionsPath(entity.Path, user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } @@ -134,52 +153,56 @@ namespace Umbraco.Core.Security /// The item resolved if one was found for the id /// /// - public static ContentAccess CheckPermissions( + public ContentAccess CheckPermissions( int nodeId, - IUser user, - IUserService userService, - IContentService contentService, - IEntityService entityService, + IUser user, out IContent contentItem, - params char[] permissionsToCheck) + IReadOnlyList permissionsToCheck = null) { - if (user == null) throw new ArgumentNullException("user"); - if (userService == null) throw new ArgumentNullException("userService"); - if (contentService == null) throw new ArgumentNullException("contentService"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (permissionsToCheck == null) + { + permissionsToCheck = Array.Empty(); + } bool? hasPathAccess = null; contentItem = null; if (nodeId == Constants.System.Root) - hasPathAccess = user.HasContentRootAccess(entityService); + hasPathAccess = user.HasContentRootAccess(_entityService); else if (nodeId == Constants.System.RecycleBinContent) - hasPathAccess = user.HasContentBinAccess(entityService); + hasPathAccess = user.HasContentBinAccess(_entityService); if (hasPathAccess.HasValue) return hasPathAccess.Value ? ContentAccess.Granted : ContentAccess.Denied; - contentItem = contentService.GetById(nodeId); + contentItem = _contentService.GetById(nodeId); if (contentItem == null) return ContentAccess.NotFound; - hasPathAccess = user.HasPathAccess(contentItem, entityService); + hasPathAccess = user.HasPathAccess(contentItem, _entityService); if (hasPathAccess == false) return ContentAccess.Denied; - if (permissionsToCheck == null || permissionsToCheck.Length == 0) + if (permissionsToCheck == null || permissionsToCheck.Count == 0) return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(contentItem.Path, user, userService, permissionsToCheck) + return CheckPermissionsPath(contentItem.Path, user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } - private static bool CheckPermissionsPath(string path, IUser user, IUserService userService, params char[] permissionsToCheck) + private bool CheckPermissionsPath(string path, IUser user, IReadOnlyList permissionsToCheck = null) { + if (permissionsToCheck == null) + { + permissionsToCheck = Array.Empty(); + } + //get the implicit/inherited permissions for the user for this path, //if there is no content item for this id, than just use the id as the path (i.e. -1 or -20) - var permission = userService.GetPermissionsForPath(user, path); + var permission = _userService.GetPermissionsForPath(user, path); var allowed = true; foreach (var p in permissionsToCheck) diff --git a/src/Umbraco.Core/Security/MediaPermissions.cs b/src/Umbraco.Core/Security/MediaPermissions.cs new file mode 100644 index 0000000000..65da95ec02 --- /dev/null +++ b/src/Umbraco.Core/Security/MediaPermissions.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; + +namespace Umbraco.Core.Security +{ + /// + /// Checks user access to media + /// + public class MediaPermissions + { + private readonly IMediaService _mediaService; + private readonly IEntityService _entityService; + + public enum MediaAccess + { + Granted, + Denied, + NotFound + } + + public MediaPermissions(IMediaService mediaService, IEntityService entityService) + { + _mediaService = mediaService; + _entityService = entityService; + } + + /// + /// Performs a permissions check for the user to check if it has access to the node based on + /// start node and/or permissions for the node + /// + /// + /// + /// + /// The content to lookup, if the contentItem is not specified + /// + public MediaAccess CheckPermissions(IUser user, int nodeId, out IMedia media) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + media = null; + + if (nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia) + { + media = _mediaService.GetById(nodeId); + } + + if (media == null && nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia) + { + return MediaAccess.NotFound; + } + + var hasPathAccess = (nodeId == Constants.System.Root) + ? user.HasMediaRootAccess(_entityService) + : (nodeId == Constants.System.RecycleBinMedia) + ? user.HasMediaBinAccess(_entityService) + : user.HasPathAccess(media, _entityService); + + return hasPathAccess ? MediaAccess.Granted : MediaAccess.Denied; + } + + public MediaAccess CheckPermissions(IMedia media, IUser user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (media == null) return MediaAccess.NotFound; + + var hasPathAccess = user.HasPathAccess(media, _entityService); + + return hasPathAccess ? MediaAccess.Granted : MediaAccess.Denied; + } + } +} diff --git a/src/Umbraco.Core/Services/IRedirectUrlService.cs b/src/Umbraco.Core/Services/IRedirectUrlService.cs index 3bd9b6f2cf..d54c9994e1 100644 --- a/src/Umbraco.Core/Services/IRedirectUrlService.cs +++ b/src/Umbraco.Core/Services/IRedirectUrlService.cs @@ -10,78 +10,86 @@ namespace Umbraco.Core.Services public interface IRedirectUrlService : IService { /// - /// Registers a redirect url. + /// Registers a redirect URL. /// - /// The Umbraco url route. + /// The Umbraco URL route. /// The content unique key. /// The culture. /// Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo. void Register(string url, Guid contentKey, string culture = null); /// - /// Deletes all redirect urls for a given content. + /// Deletes all redirect URLs for a given content. /// /// The content unique key. void DeleteContentRedirectUrls(Guid contentKey); /// - /// Deletes a redirect url. + /// Deletes a redirect URL. /// - /// The redirect url to delete. + /// The redirect URL to delete. void Delete(IRedirectUrl redirectUrl); /// - /// Deletes a redirect url. + /// Deletes a redirect URL. /// - /// The redirect url identifier. + /// The redirect URL identifier. void Delete(Guid id); /// - /// Deletes all redirect urls. + /// Deletes all redirect URLs. /// void DeleteAll(); /// - /// Gets the most recent redirect urls corresponding to an Umbraco redirect url route. + /// Gets the most recent redirect URLs corresponding to an Umbraco redirect URL route. /// - /// The Umbraco redirect url route. - /// The most recent redirect urls corresponding to the route. + /// The Umbraco redirect URL route. + /// The most recent redirect URLs corresponding to the route. IRedirectUrl GetMostRecentRedirectUrl(string url); /// - /// Gets all redirect urls for a content item. + /// Gets the most recent redirect URLs corresponding to an Umbraco redirect URL route. + /// + /// The Umbraco redirect URL route. + /// The culture of the request. + /// The most recent redirect URLs corresponding to the route. + IRedirectUrl GetMostRecentRedirectUrl(string url, string culture); + + /// + /// Gets all redirect URLs for a content item. /// /// The content unique key. - /// All redirect urls for the content item. + /// All redirect URLs for the content item. IEnumerable GetContentRedirectUrls(Guid contentKey); /// - /// Gets all redirect urls. + /// Gets all redirect URLs. /// /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable GetAllRedirectUrls(long pageIndex, int pageSize, out long total); /// - /// Gets all redirect urls below a given content item. + /// Gets all redirect URLs below a given content item. /// /// The content unique identifier. /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable GetAllRedirectUrls(int rootContentId, long pageIndex, int pageSize, out long total); /// - /// Searches for all redirect urls that contain a given search term in their URL property. + /// Searches for all redirect URLs that contain a given search term in their URL property. /// /// The term to search for. /// The page index. /// The page size. - /// The total count of redirect urls. - /// The redirect urls. + /// The total count of redirect URLs. + /// The redirect URLs. IEnumerable SearchRedirectUrls(string searchTerm, long pageIndex, int pageSize, out long total); } } diff --git a/src/Umbraco.Core/Services/IServerRegistrationService.cs b/src/Umbraco.Core/Services/IServerRegistrationService.cs index 47bf3838f2..62bb68eb14 100644 --- a/src/Umbraco.Core/Services/IServerRegistrationService.cs +++ b/src/Umbraco.Core/Services/IServerRegistrationService.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Services /// /// Touches a server to mark it as active; deactivate stale servers. /// - /// The server url. + /// The server URL. /// The server unique identity. /// The time after which a server is considered stale. void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout); diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 67b0b49a45..0dc48b2229 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -751,7 +751,7 @@ namespace Umbraco.Core var pos = str.IndexOf('='); if (pos < 0) pos = str.Length; - // replace chars that would cause problems in urls + // replace chars that would cause problems in URLs var chArray = new char[pos]; for (var i = 0; i < pos; i++) { diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 3894525d2d..492ded29e2 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Options; namespace Umbraco.Core.Strings { /// - /// New default implementation of string functions for short strings such as aliases or url segments. + /// New default implementation of string functions for short strings such as aliases or URL segments. /// /// /// Not optimized to work on large bodies of text. @@ -94,10 +94,10 @@ namespace Umbraco.Core.Strings } /// - /// Cleans a string to produce a string that can safely be used in an url segment. + /// Cleans a string to produce a string that can safely be used in an URL segment. /// /// The text to filter. - /// The safe url segment. + /// The safe URL segment. /// /// The string will be cleaned in the context of the default culture. /// Url segments are Ascii only (no accents...). @@ -108,11 +108,11 @@ namespace Umbraco.Core.Strings } /// - /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. + /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an URL segment. /// /// The text to filter. /// The culture. - /// The safe url segment. + /// The safe URL segment. /// /// Url segments are Ascii only (no accents...). /// @@ -123,7 +123,7 @@ namespace Umbraco.Core.Strings /// /// Cleans a string, in the context of the default culture, to produce a string that can safely be used as a filename, - /// both internally (on disk) and externally (as a url). + /// both internally (on disk) and externally (as a URL). /// /// The text to filter. /// The safe filename. @@ -135,7 +135,7 @@ namespace Umbraco.Core.Strings /// /// Cleans a string to produce a string that can safely be used as a filename, - /// both internally (on disk) and externally (as a url). + /// both internally (on disk) and externally (as a URL). /// /// The text to filter. /// The culture. diff --git a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs index bb4a2648b8..6ec21b0986 100644 --- a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs @@ -15,11 +15,11 @@ namespace Umbraco.Core.Strings } /// - /// Gets the url segment for a specified content and culture. + /// Gets the URL segment for a specified content and culture. /// /// The content. /// The culture. - /// The url segment. + /// The URL segment. public string GetUrlSegment(IContentBase content, string culture = null) { return GetUrlSegmentSource(content, culture).ToUrlSegment(_shortStringHelper, culture); diff --git a/src/Umbraco.Core/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs index fecbeaaee9..37873caded 100644 --- a/src/Umbraco.Core/Strings/IShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/IShortStringHelper.cs @@ -1,7 +1,7 @@ namespace Umbraco.Core.Strings { /// - /// Provides string functions for short strings such as aliases or url segments. + /// Provides string functions for short strings such as aliases or URL segments. /// /// Not necessarily optimized to work on large bodies of text. public interface IShortStringHelper @@ -26,24 +26,24 @@ string CleanStringForSafeAlias(string text, string culture); /// - /// Cleans a string to produce a string that can safely be used in an url segment. + /// Cleans a string to produce a string that can safely be used in an URL segment. /// /// The text to filter. - /// The safe url segment. + /// The safe URL segment. /// The string will be cleaned in the context of the IShortStringHelper default culture. string CleanStringForUrlSegment(string text); /// - /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. + /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an URL segment. /// /// The text to filter. /// The culture. - /// The safe url segment. + /// The safe URL segment. string CleanStringForUrlSegment(string text, string culture); /// /// Cleans a string, in the context of the invariant culture, to produce a string that can safely be used as a filename, - /// both internally (on disk) and externally (as a url). + /// both internally (on disk) and externally (as a URL). /// /// The text to filter. /// The safe filename. @@ -52,7 +52,7 @@ /// /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used as a filename, - /// both internally (on disk) and externally (as a url). + /// both internally (on disk) and externally (as a URL). /// /// The text to filter. /// The culture. diff --git a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs index 12d2ef9a17..fddb87e716 100644 --- a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs @@ -4,20 +4,20 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Strings { /// - /// Provides url segments for content. + /// Provides URL segments for content. /// /// Url segments should comply with IETF RFCs regarding content, encoding, etc. public interface IUrlSegmentProvider { /// - /// Gets the url segment for a specified content and culture. + /// Gets the URL segment for a specified content and culture. /// /// The content. /// The culture. - /// The url segment. - /// This is for when Umbraco is capable of managing more than one url + /// The URL segment. + /// This is for when Umbraco is capable of managing more than one URL /// per content, in 1-to-1 multilingual configurations. Then there would be one - /// url per culture. + /// URL per culture. string GetUrlSegment(IContentBase content, string culture = null); // TODO: For the 301 tracking, we need to add another extended interface to this so that diff --git a/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs b/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs index 79d65e0829..0394183740 100644 --- a/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs +++ b/src/Umbraco.Core/Templates/HtmlImageSourceParser.cs @@ -84,7 +84,7 @@ namespace Umbraco.Web.Templates } /// - /// Removes media urls from <img> tags where a data-udi attribute is present + /// Removes media URLs from <img> tags where a data-udi attribute is present /// /// /// diff --git a/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs b/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs index 2ecd61f7d2..93229775b1 100644 --- a/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs +++ b/src/Umbraco.Core/Templates/HtmlLocalLinkParser.cs @@ -47,7 +47,7 @@ namespace Umbraco.Web.Templates if (!preview) return EnsureInternalLinks(text); - using (_umbracoContextAccessor.UmbracoContext.ForcedPreview(preview)) // force for url provider + using (_umbracoContextAccessor.UmbracoContext.ForcedPreview(preview)) // force for URL provider { return EnsureInternalLinks(text); } diff --git a/src/Umbraco.Core/Templates/HtmlUrlParser.cs b/src/Umbraco.Core/Templates/HtmlUrlParser.cs index 7f7db6130c..254521d1c6 100644 --- a/src/Umbraco.Core/Templates/HtmlUrlParser.cs +++ b/src/Umbraco.Core/Templates/HtmlUrlParser.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.Templates using (var timer = _profilingLogger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { - // find all relative urls (ie. urls that contain ~) + // find all relative URLs (ie. URLs that contain ~) var tags = ResolveUrlPattern.Matches(text); _logger.LogDebug("After regex: {Duration} matched: {TagsCount}", timer.Stopwatch.ElapsedMilliseconds, tags.Count); foreach (Match tag in tags) @@ -49,7 +49,7 @@ namespace Umbraco.Web.Templates if (tag.Groups[1].Success) url = tag.Groups[1].Value; - // The richtext editor inserts a slash in front of the url. That's why we need this little fix + // The richtext editor inserts a slash in front of the URL. That's why we need this little fix // if (url.StartsWith("/")) // text = text.Replace(url, ResolveUrl(url.Substring(1))); // else diff --git a/src/Umbraco.Core/Trees/TreeNode.cs b/src/Umbraco.Core/Trees/TreeNode.cs index 95c8a20b76..cc99269cd8 100644 --- a/src/Umbraco.Core/Trees/TreeNode.cs +++ b/src/Umbraco.Core/Trees/TreeNode.cs @@ -63,13 +63,13 @@ namespace Umbraco.Web.Models.Trees public string RoutePath { get; set; } /// - /// The JSON url to load the nodes children + /// The JSON URL to load the nodes children /// [DataMember(Name = "childNodesUrl")] public string ChildNodesUrl { get; set; } /// - /// The JSON url to load the menu from + /// The JSON URL to load the menu from /// [DataMember(Name = "menuUrl")] public string MenuUrl { get; set; } diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 4b76dc3799..44063bd7b7 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Xml; using System.Xml.XPath; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; namespace Umbraco.Core.Xml { @@ -208,9 +208,9 @@ namespace Umbraco.Core.Xml /// The relative file path. ie. /config/umbraco.config /// /// Returns a XmlDocument class - public static XmlDocument OpenAsXmlDocument(string filePath, IIOHelper ioHelper) + public static XmlDocument OpenAsXmlDocument(string filePath, IHostingEnvironment hostingEnvironment) { - using (var reader = new XmlTextReader(ioHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All}) + using (var reader = new XmlTextReader(hostingEnvironment.MapPathContentRoot(filePath)) {WhitespaceHandling = WhitespaceHandling.All}) { var xmlDoc = new XmlDocument(); //Load the file into the XmlDocument diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs index c99d7c992e..dd3a800821 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Logging; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Manifest; using Umbraco.Net; @@ -12,18 +11,18 @@ namespace Umbraco.Core.Compose { private readonly IHostingEnvironment _hosting; private readonly ILoggerFactory _loggerFactory; - 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(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IIOHelper ioHelper, IUmbracoApplicationLifetime umbracoApplicationLifetime) + public ManifestWatcherComponent(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _hosting = hosting; _loggerFactory = loggerFactory; - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _umbracoApplicationLifetime = umbracoApplicationLifetime; } @@ -34,7 +33,7 @@ namespace Umbraco.Core.Compose //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) // return; - var appPlugins = _ioHelper.MapPath("~/App_Plugins/"); + var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); if (Directory.Exists(appPlugins) == false) return; _mw = new ManifestWatcher(_loggerFactory.CreateLogger(), _umbracoApplicationLifetime); diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs index 8c26e0e688..767f36aaf5 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs @@ -244,7 +244,7 @@ namespace Umbraco.Web.Compose if (sender == null) throw new ArgumentNullException(nameof(sender)); if (siteUri == null) { - _logger.LogWarning("Notifications can not be sent, no site url is set (might be during boot process?)"); + _logger.LogWarning("Notifications can not be sent, no site URL is set (might be during boot process?)"); return; } diff --git a/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs b/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs index 63b5cc90dc..d8a5f675b2 100644 --- a/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs +++ b/src/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelper.cs @@ -12,14 +12,12 @@ namespace Umbraco.Web.Editors { private readonly IContentService _contentService; private readonly IMediaService _mediaService; - private readonly IUserService _userService; private readonly IEntityService _entityService; - public UserEditorAuthorizationHelper(IContentService contentService, IMediaService mediaService, IUserService userService, IEntityService entityService) + public UserEditorAuthorizationHelper(IContentService contentService, IMediaService mediaService, IEntityService entityService) { _contentService = contentService; _mediaService = mediaService; - _userService = userService; _entityService = entityService; } @@ -114,7 +112,7 @@ namespace Umbraco.Web.Editors { if (contentId == Constants.System.Root) { - var hasAccess = ContentPermissionsHelper.HasPathAccess("-1", currentUser.CalculateContentStartNodeIds(_entityService), Constants.System.RecycleBinContent); + var hasAccess = ContentPermissions.HasPathAccess("-1", currentUser.CalculateContentStartNodeIds(_entityService), Constants.System.RecycleBinContent); if (hasAccess == false) return Attempt.Fail("The current user does not have access to the content root"); } @@ -135,7 +133,7 @@ namespace Umbraco.Web.Editors { if (mediaId == Constants.System.Root) { - var hasAccess = ContentPermissionsHelper.HasPathAccess("-1", currentUser.CalculateMediaStartNodeIds(_entityService), Constants.System.RecycleBinMedia); + var hasAccess = ContentPermissions.HasPathAccess("-1", currentUser.CalculateMediaStartNodeIds(_entityService), Constants.System.RecycleBinMedia); if (hasAccess == false) return Attempt.Fail("The current user does not have access to the media root"); } diff --git a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index 2bde06d9b8..98401c363c 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -123,7 +123,7 @@ namespace Umbraco.Examine content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, _publishedQuery, Ordering.By("Path", Direction.Ascending)).ToArray(); - + if (content.Length > 0) { var indexableContent = new List(); diff --git a/src/Umbraco.Infrastructure/Install/FilePermissionHelper.cs b/src/Umbraco.Infrastructure/Install/FilePermissionHelper.cs index 7a20a1189e..a12634f01d 100644 --- a/src/Umbraco.Infrastructure/Install/FilePermissionHelper.cs +++ b/src/Umbraco.Infrastructure/Install/FilePermissionHelper.cs @@ -4,12 +4,12 @@ using System.Linq; using System.IO; using System.Security.AccessControl; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Install; using Umbraco.Core.IO; using Umbraco.Web.PublishedCache; using Umbraco.Core.Configuration.Models; using Microsoft.Extensions.Options; +using Umbraco.Core.Hosting; namespace Umbraco.Web.Install { @@ -23,12 +23,14 @@ namespace Umbraco.Web.Install private readonly string[] _permissionFiles = { }; private readonly GlobalSettings _globalSettings; private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IPublishedSnapshotService _publishedSnapshotService; - public FilePermissionHelper(IOptions globalSettings, IIOHelper ioHelper, IPublishedSnapshotService publishedSnapshotService) + public FilePermissionHelper(IOptions globalSettings, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IPublishedSnapshotService publishedSnapshotService) { _globalSettings = globalSettings.Value; _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _publishedSnapshotService = publishedSnapshotService; _permissionDirs = new[] { _globalSettings.UmbracoCssPath, Constants.SystemDirectories.Config, Constants.SystemDirectories.Data, _globalSettings.UmbracoMediaPath, Constants.SystemDirectories.Preview }; _packagesPermissionsDirs = new[] { Constants.SystemDirectories.Bin, _globalSettings.UmbracoPath, Constants.SystemDirectories.Packages }; @@ -140,7 +142,7 @@ namespace Umbraco.Web.Install { try { - var path = _ioHelper.MapPath(dir + "/" + _ioHelper.CreateRandomFileName()); + var path = _hostingEnvironment.MapPathContentRoot(dir + "/" + _ioHelper.CreateRandomFileName()); Directory.CreateDirectory(path); Directory.Delete(path); return true; @@ -163,7 +165,7 @@ namespace Umbraco.Web.Install { try { - var dirPath = _ioHelper.MapPath(dir); + var dirPath = _hostingEnvironment.MapPathContentRoot(dir); if (Directory.Exists(dirPath) == false) return true; @@ -228,7 +230,7 @@ namespace Umbraco.Web.Install { try { - var path = _ioHelper.MapPath(file); + var path = _hostingEnvironment.MapPathContentRoot(file); File.AppendText(path).Close(); return true; } diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 7392506d2b..eec83dffef 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -6,6 +6,7 @@ using System.Text; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Core.Cache; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Serialization; @@ -20,6 +21,8 @@ namespace Umbraco.Core.Manifest public class ManifestParser : IManifestParser { private readonly ILoggerFactory _loggerFactory; + private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IJsonSerializer _jsonSerializer; private readonly ILocalizedTextService _localizedTextService; private readonly IShortStringHelper _shortStringHelper; @@ -27,7 +30,6 @@ namespace Umbraco.Core.Manifest private readonly IAppPolicyCache _cache; private readonly ILogger _logger; - private readonly IIOHelper _ioHelper; private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly ManifestValueValidatorCollection _validators; @@ -45,12 +47,13 @@ namespace Umbraco.Core.Manifest ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, IDataTypeService dataTypeService, ILocalizationService localizationService, IJsonSerializer jsonSerializer, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) - : this(appCaches, validators, filters, "~/App_Plugins", logger, loggerFactory, ioHelper, dataTypeService, localizationService) + : this(appCaches, validators, filters, "~/App_Plugins", logger, loggerFactory, ioHelper, hostingEnvironment, dataTypeService, localizationService) { _loggerFactory = loggerFactory; _jsonSerializer = jsonSerializer; @@ -61,11 +64,10 @@ namespace Umbraco.Core.Manifest /// /// Initializes a new instance of the class. /// - private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService) + private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IDataTypeService dataTypeService, ILocalizationService localizationService) { if (appCaches == null) throw new ArgumentNullException(nameof(appCaches)); _cache = appCaches.RuntimeCache; - _ioHelper = ioHelper; _dataTypeService = dataTypeService; _localizationService = localizationService; _validators = validators ?? throw new ArgumentNullException(nameof(validators)); @@ -75,6 +77,7 @@ namespace Umbraco.Core.Manifest _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; Path = path; @@ -84,7 +87,7 @@ namespace Umbraco.Core.Manifest public string Path { get => _path; - set => _path = value.StartsWith("~/") ? _ioHelper.MapPath(value) : value; + set => _path = value.StartsWith("~/") ? _hostingEnvironment.MapPathContentRoot(value) : value; } /// diff --git a/src/Umbraco.Infrastructure/Media/EmbedProviders/EmbedProviderBase.cs b/src/Umbraco.Infrastructure/Media/EmbedProviders/EmbedProviderBase.cs index 5529df771b..539c01f69b 100644 --- a/src/Umbraco.Infrastructure/Media/EmbedProviders/EmbedProviderBase.cs +++ b/src/Umbraco.Infrastructure/Media/EmbedProviders/EmbedProviderBase.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Media.EmbedProviders public virtual string GetEmbedProviderUrl(string url, int maxWidth, int maxHeight) { if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false) - throw new ArgumentException("Not a valid Url"); + throw new ArgumentException("Not a valid URL.", nameof(url)); var fullUrl = new StringBuilder(); diff --git a/src/Umbraco.Infrastructure/Media/EmbedProviders/Instagram.cs b/src/Umbraco.Infrastructure/Media/EmbedProviders/Instagram.cs deleted file mode 100644 index 1862d77cad..0000000000 --- a/src/Umbraco.Infrastructure/Media/EmbedProviders/Instagram.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Web.Media.EmbedProviders -{ - public class Instagram : EmbedProviderBase - { - public override string ApiEndpoint => "http://api.instagram.com/oembed"; - - public override string[] UrlSchemeRegex => new string[] - { - @"instagram.com\/p\/*", - @"instagr.am\/p\/*" - }; - - public override Dictionary RequestParams => new Dictionary(); - - public override string GetMarkup(string url, int maxWidth = 0, int maxHeight = 0) - { - var requestUrl = base.GetEmbedProviderUrl(url, maxWidth, maxHeight); - var oembed = base.GetJsonResponse(requestUrl); - - return oembed.GetHtml(); - } - } -} diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index 2372e4a083..0ebea656b1 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -203,7 +203,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement const string pattern = ".*[_][0-9]+[x][0-9]+[.].*"; var isResized = Regex.IsMatch(mediaPath, pattern); - // If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url. + // If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" URL. if (isResized) { var underscoreIndex = mediaPath.LastIndexOf('_'); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs index 5df508e5ef..9e72846b58 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs @@ -208,5 +208,17 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); var rules = result.Items.Select(Map); return rules; } + + public IRedirectUrl GetMostRecentUrl(string url, string culture) + { + if (string.IsNullOrWhiteSpace(culture)) return GetMostRecentUrl(url); + var urlHash = url.GenerateHash(); + var sql = GetBaseQuery(false) + .Where(x => x.Url == url && x.UrlHash == urlHash && x.Culture == culture.ToLower()) + .OrderByDescending(x => x.CreateDateUtc); + var dtos = Database.Fetch(sql); + var dto = dtos.FirstOrDefault(); + return dto == null ? null : Map(dto); + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs index 4243d534aa..ecb9eef1a2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; @@ -14,12 +16,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// internal class StylesheetRepository : FileRepository, IStylesheetRepository { + private readonly ILogger _logger; private readonly IIOHelper _ioHelper; private readonly GlobalSettings _globalSettings; - public StylesheetRepository(IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public StylesheetRepository(ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) : base(fileSystems.StylesheetsFileSystem) { + _logger = logger; _ioHelper = ioHelper; _globalSettings = globalSettings.Value; } @@ -126,17 +130,23 @@ namespace Umbraco.Core.Persistence.Repositories.Implement try { // may throw for security reasons + fullPath = FileSystem.GetFullPath(stylesheet.Path); } - catch + catch (Exception ex) { + _logger.LogWarning(ex, "Can't get full path"); return false; } // validate path and extension var validDir = _globalSettings.UmbracoCssPath; + _logger.LogWarning("fullPath {fullPath}, validDir: {validDir}",fullPath, validDir); + var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); var isValidExtension = _ioHelper.VerifyFileExtension(stylesheet.Path, ValidExtensions); + + _logger.LogWarning("isValidPath {isValidPath}, isValidExtension: {isValidExtension}",isValidPath, isValidExtension); return isValidPath && isValidExtension; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs index e88849002b..afd749eed3 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.PropertyEditors [DataEditor( Constants.PropertyEditors.Aliases.MultiUrlPicker, EditorType.PropertyValue, - "Multi Url Picker", + "Multi URL Picker", "multiurlpicker", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Pickers, diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs index b6afa0a1b5..380408c2b1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -148,7 +148,7 @@ namespace Umbraco.Web.PropertyEditors QueryString = link.QueryString, Target = link.Target, Udi = link.Udi, - Url = link.Udi == null ? link.Url : null, // only save the url for external links + Url = link.Udi == null ? link.Url : null, // only save the URL for external links }, new JsonSerializerSettings { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index b257f69e98..98f8771699 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -380,6 +380,9 @@ namespace Umbraco.Web.PropertyEditors if (propertyValue == null || string.IsNullOrWhiteSpace(propertyValue.ToString())) return new List(); + if (!propertyValue.ToString().DetectIsJson()) + return new List(); + var rowValues = JsonConvert.DeserializeObject>(propertyValue.ToString()); // There was a note here about checking if the result had zero items and if so it would return null, so we'll continue to do that @@ -404,23 +407,26 @@ namespace Umbraco.Web.PropertyEditors propertyTypes = contentTypePropertyTypes[contentType.Alias] = contentType.CompositionPropertyTypes.ToDictionary(x => x.Alias, x => x); // find any keys that are not real property types and remove them - foreach(var prop in row.RawPropertyValues.ToList()) + if (row.RawPropertyValues != null) { - if (IsSystemPropertyKey(prop.Key)) continue; + foreach (var prop in row.RawPropertyValues.ToList()) + { + if (IsSystemPropertyKey(prop.Key)) continue; - // doesn't exist so remove it - if (!propertyTypes.TryGetValue(prop.Key, out var propType)) - { - row.RawPropertyValues.Remove(prop.Key); - } - else - { - // set the value to include the resolved property type - row.PropertyValues[prop.Key] = new NestedContentPropertyValue + // doesn't exist so remove it + if (!propertyTypes.TryGetValue(prop.Key, out var propType)) { - PropertyType = propType, - Value = prop.Value - }; + row.RawPropertyValues.Remove(prop.Key); + } + else + { + // set the value to include the resolved property type + row.PropertyValues[prop.Key] = new NestedContentPropertyValue + { + PropertyType = propType, + Value = prop.Value + }; + } } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index b5a0972c3e..d881d55f7d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models; using Umbraco.Web.Routing; +using Umbraco.Core.Hosting; namespace Umbraco.Web.PropertyEditors { @@ -20,7 +21,7 @@ namespace Umbraco.Web.PropertyEditors { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILogger _logger; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly IMediaFileSystem _mediaFileSystem; @@ -30,11 +31,11 @@ namespace Umbraco.Web.PropertyEditors const string TemporaryImageDataAttribute = "data-tmpimg"; - public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IIOHelper ioHelper, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider, IJsonSerializer serializer) + public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IHostingEnvironment hostingEnvironment, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider, IJsonSerializer serializer) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); _mediaFileSystem = mediaFileSystem; @@ -73,7 +74,7 @@ namespace Umbraco.Web.PropertyEditors if (string.IsNullOrEmpty(tmpImgPath)) continue; - var absoluteTempImagePath = _ioHelper.MapPath(tmpImgPath); + var absoluteTempImagePath = _hostingEnvironment.MapPathContentRoot(tmpImgPath); var fileName = Path.GetFileName(absoluteTempImagePath); var safeFileName = fileName.ToSafeFileName(_shortStringHelper); @@ -110,7 +111,7 @@ namespace Umbraco.Web.PropertyEditors // Add the UDI to the img element as new data attribute img.SetAttributeValue("data-udi", udi.ToString()); - // Get the new persisted image url + // Get the new persisted image URL var mediaTyped = _umbracoContextAccessor?.UmbracoContext?.Media.GetById(udi.Guid); if (mediaTyped == null) throw new PanicException($"Could not find media by id {udi.Guid} or there was no UmbracoContext available."); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 5cc86779e1..94cb074ade 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -78,7 +78,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters } /// - /// Gets the value image url for a specified crop. + /// Gets the value image URL for a specified crop. /// public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null) { @@ -102,7 +102,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters } /// - /// Gets the value image url for a specific width and height. + /// Gets the value image URL for a specific width and height. /// public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index b27955210f..a0e73be09b 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 } catch (Exception ex) { - // cannot deserialize, assume it may be a raw image url + // cannot deserialize, assume it may be a raw image URL _logger.LogError(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString); value = new ImageCropperValue { Src = sourceString }; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 4fdc13baa9..1b4b0a3acb 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters if (source == null) return null; var sourceString = source.ToString(); - // ensures string is parsed for {localLink} and urls are resolved correctly + // ensures string is parsed for {localLink} and URLs are resolved correctly sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview); sourceString = _urlParser.EnsureUrls(sourceString); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index a55f73980b..7fe202ed4c 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var sourceString = source.ToString(); - // ensures string is parsed for {localLink} and urls and media are resolved correctly + // ensures string is parsed for {localLink} and URLs and media are resolved correctly sourceString = _linkParser.EnsureInternalLinks(sourceString, preview); sourceString = _urlParser.EnsureUrls(sourceString); sourceString = _imageSourceParser.EnsureImageSources(sourceString); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 93d670601e..e373b375a5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (source == null) return null; var sourceString = source.ToString(); - // ensures string is parsed for {localLink} and urls are resolved correctly + // ensures string is parsed for {localLink} and URLs are resolved correctly sourceString = _linkParser.EnsureInternalLinks(sourceString, preview); sourceString = _urlParser.EnsureUrls(sourceString); diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs index 107e224486..861aeea2e5 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs @@ -15,8 +15,8 @@ using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Routing { - /// Implements an Application Event Handler for managing redirect urls tracking. - /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url + /// Implements an Application Event Handler for managing redirect URLs tracking. + /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL /// /// not managing domains because we don't know how to do it - changing domains => must create a higher level /// strategy using rewriting rules probably diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs index ae2a87e3c6..0680a4c97f 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs @@ -3,10 +3,10 @@ namespace Umbraco.Web.Routing { /// - /// Implements an Application Event Handler for managing redirect urls tracking. + /// Implements an Application Event Handler for managing redirect URLs tracking. /// /// - /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url + /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same /// diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs index a473511e56..73318208b6 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; namespace Umbraco.Core.Runtime @@ -8,11 +9,13 @@ namespace Umbraco.Core.Runtime public class CoreInitialComponent : IComponent { private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly GlobalSettings _globalSettings; - public CoreInitialComponent(IIOHelper ioHelper, IOptions globalSettings) + public CoreInitialComponent(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions globalSettings) { _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _globalSettings = globalSettings.Value; } @@ -20,11 +23,11 @@ namespace Umbraco.Core.Runtime { // ensure we have some essential directories // every other component can then initialize safely - _ioHelper.EnsurePathExists(Constants.SystemDirectories.Data); - _ioHelper.EnsurePathExists(_globalSettings.UmbracoMediaPath); - _ioHelper.EnsurePathExists(Constants.SystemDirectories.MvcViews); - _ioHelper.EnsurePathExists(Constants.SystemDirectories.PartialViews); - _ioHelper.EnsurePathExists(Constants.SystemDirectories.MacroPartials); + _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data)); + _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoMediaPath)); + _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews)); + _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews)); + _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials)); } public void Terminate() diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index b710594689..df48ac8df0 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -63,6 +63,7 @@ using Umbraco.Core.Builder; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.HealthCheck; using Umbraco.Core.HealthCheck.Checks; +using Umbraco.Core.Security; namespace Umbraco.Core.Runtime { @@ -264,7 +265,6 @@ namespace Umbraco.Core.Runtime // note: IEmbedProvider is not IDiscoverable - think about it if going for type scanning builder.OEmbedProviders() .Append() - .Append() .Append() .Append() .Append() @@ -376,6 +376,11 @@ namespace Umbraco.Core.Runtime builder.Services.AddUnique(); builder.Services.AddUnique(); + + builder.Services.AddUnique(factory => new LegacyPasswordSecurity()); + builder.Services.AddUnique(); + builder.Services.AddUnique(); + builder.Services.AddUnique(); } } } diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index 0f29dfe36c..41bdaa9e01 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -127,7 +127,12 @@ namespace Umbraco.Core.Runtime // 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); + return Task.Factory.StartNew( + ListeningLoop, + _cancellationTokenSource.Token, + TaskCreationOptions.LongRunning, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); } @@ -155,12 +160,6 @@ namespace Umbraco.Core.Runtime continue; } - if (!_dbFactory.Configured) - { - // if we aren't configured, we just keep looping since we can't query the db - continue; - } - lock (_locker) { // If cancellation has been requested we will just exit. Depending on timing of the shutdown, @@ -168,7 +167,11 @@ namespace Umbraco.Core.Runtime // 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) + { + _logger.LogDebug("Task canceled, exiting loop"); return; + } + using var db = _dbFactory.CreateDatabase(); using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); @@ -192,8 +195,10 @@ namespace Umbraco.Core.Runtime // We need to keep on listening unless we've been notified by our own AppDomain to shutdown since // we don't want to shutdown resources controlled by MainDom inadvertently. We'll just keep listening otherwise. if (_cancellationTokenSource.IsCancellationRequested) + { + _logger.LogDebug("Task canceled, exiting loop"); return; - + } } finally { @@ -377,6 +382,8 @@ namespace Umbraco.Core.Runtime { lock (_locker) { + _logger.LogDebug($"{nameof(SqlMainDomLock)} Disposing..."); + // immediately cancel all sub-tasks, we don't want them to keep querying _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); diff --git a/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs index fbc68daca3..0356bdefc6 100644 --- a/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs @@ -762,9 +762,17 @@ namespace Umbraco.Web.Scheduling lock (_locker) { if (_runningTask != null) - _runningTask.ContinueWith(_ => StopImmediate()); + { + _runningTask.ContinueWith( + _ => StopImmediate(), + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); + } else + { StopImmediate(); + } + } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index 6b7376883b..3702a7e761 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -188,7 +188,7 @@ namespace Umbraco.Core.Services.Implement var folderNames = string.Empty; if (dataType.Level != 1) { - //get url encoded folder names + //get URL encoded folder names var folders = _dataTypeService.GetContainers(dataType) .OrderBy(x => x.Level) .Select(x => WebUtility.UrlEncode(x.Name)); @@ -530,7 +530,7 @@ namespace Umbraco.Core.Services.Implement //don't add folders if this is a child doc type if (contentType.Level != 1 && masterContentType == null) { - //get url encoded folder names + //get URL encoded folder names var folders = _contentTypeService.GetContainers(contentType) .OrderBy(x => x.Level) .Select(x => WebUtility.UrlEncode(x.Name)); diff --git a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs index 83d1176402..3e99ca9b4b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs @@ -396,7 +396,7 @@ namespace Umbraco.Core.Services.Implement content.Id.ToString(CultureInfo.InvariantCulture), string.Format("{2}://{0}/{1}", string.Concat(siteUri.Authority), - // TODO: RE-enable this so we can have a nice url + // TODO: RE-enable this so we can have a nice URL /*umbraco.library.NiceUrl(documentObject.Id))*/ string.Concat(content.Id, ".aspx"), protocol), diff --git a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs index 238258b8c2..a286a1cf50 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs @@ -5,10 +5,8 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Semver; -using Umbraco.Core.Composing; using Umbraco.Core.Events; -using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; @@ -22,7 +20,7 @@ namespace Umbraco.Core.Services.Implement public class PackagingService : IPackagingService { private readonly IPackageInstallation _packageInstallation; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IAuditService _auditService; private readonly ICreatedPackagesRepository _createdPackages; private readonly IInstalledPackagesRepository _installedPackages; @@ -33,13 +31,13 @@ namespace Umbraco.Core.Services.Implement ICreatedPackagesRepository createdPackages, IInstalledPackagesRepository installedPackages, IPackageInstallation packageInstallation, - IIOHelper ioHelper) + IHostingEnvironment hostingEnvironment) { _auditService = auditService; _createdPackages = createdPackages; _installedPackages = installedPackages; _packageInstallation = packageInstallation; - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; } #region Package Files @@ -66,7 +64,7 @@ namespace Umbraco.Core.Services.Implement //successful if (bytes.Length > 0) { - var packagePath = _ioHelper.MapPath(Constants.SystemDirectories.Packages); + var packagePath = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Packages); // Check for package directory if (Directory.Exists(packagePath) == false) diff --git a/src/Umbraco.Infrastructure/Services/Implement/RedirectUrlService.cs b/src/Umbraco.Infrastructure/Services/Implement/RedirectUrlService.cs index bc629044bd..640852b69e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/RedirectUrlService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/RedirectUrlService.cs @@ -108,5 +108,15 @@ namespace Umbraco.Core.Services.Implement return _redirectUrlRepository.SearchUrls(searchTerm, pageIndex, pageSize, out total); } } + + public IRedirectUrl GetMostRecentRedirectUrl(string url, string culture) + { + if (string.IsNullOrWhiteSpace(culture)) return GetMostRecentRedirectUrl(url); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _redirectUrlRepository.GetMostRecentUrl(url, culture); + } + + } } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs index 6e5316272b..145bf54aaf 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Services.Implement /// /// Touches a server to mark it as active; deactivate stale servers. /// - /// The server url. + /// The server URL. /// The server unique identity. /// The time after which a server is considered stale. public void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout) diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs index 0e4cd4f49a..ef5ff14954 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ContentTypeModelValidatorBase.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Microsoft.Extensions.Options; @@ -26,6 +27,13 @@ namespace Umbraco.ModelsBuilder.Embedded.BackOffice //don't do anything if we're not enabled if (!_config.Value.Enable) yield break; + //list of reserved/disallowed aliases for content/media/member types - more can be added as the need arises + var reservedModelAliases = new[] { "system" }; + if(reservedModelAliases.Contains(model.Alias, StringComparer.OrdinalIgnoreCase)) + { + yield return new ValidationResult($"The model alias {model.Alias} is a reserved term and cannot be used", new[] { "Alias" }); + } + var properties = model.Groups.SelectMany(x => x.Properties) .Where(x => x.Inherited == false) .ToArray(); diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs index 3adbc0df2c..0b67498f01 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Umbraco.Configuration; @@ -9,6 +10,7 @@ using Umbraco.Core.Hosting; using Umbraco.ModelsBuilder.Embedded.Building; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.Common.Authorization; namespace Umbraco.ModelsBuilder.Embedded.BackOffice { @@ -20,7 +22,7 @@ namespace Umbraco.ModelsBuilder.Embedded.BackOffice /// correct CSRF security is adhered to for angular and it also ensures that this controller is not subseptipal to /// global WebApi formatters being changed since this is always forced to only return Angular JSON Specific formats. /// - [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class ModelsBuilderDashboardController : UmbracoAuthorizedJsonController { private readonly ModelsBuilderSettings _config; diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs index 837bf18dae..fb39007bd0 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs @@ -76,7 +76,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose private void InstallServerVars() { - // register our url - for the backoffice api + // register our URL - for the backoffice API ServerVariablesParser.Parsing += ServerVariablesParser_Parsing; } diff --git a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs index e52e438b43..b5997b8419 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs @@ -452,7 +452,7 @@ namespace Umbraco.ModelsBuilder.Embedded try { var assemblyPath = GetOutputAssemblyPath(currentHash); - RoslynCompiler.CompileToFile(_hostingEnvironment.MapPathContentRoot(projFile), assemblyPath); + RoslynCompiler.CompileToFile(projFile, assemblyPath); assembly = ReloadAssembly(assemblyPath); File.WriteAllText(dllPathFile, assembly.Location); File.WriteAllText(modelsHashFile, currentHash); @@ -494,7 +494,7 @@ namespace Umbraco.ModelsBuilder.Embedded try { var assemblyPath = GetOutputAssemblyPath(currentHash); - RoslynCompiler.CompileToFile(_hostingEnvironment.MapPathContentRoot(projFile), assemblyPath); + RoslynCompiler.CompileToFile(projFile, assemblyPath); assembly = ReloadAssembly(assemblyPath); File.WriteAllText(dllPathFile, assemblyPath); File.WriteAllText(modelsHashFile, currentHash); diff --git a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs index 1bdb3711d1..7cdc7e72e1 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs @@ -111,8 +111,8 @@ namespace Umbraco.Web.PublishedCache.NuCache content = FollowRoute(content, parts, 1, culture); } - // if hideTopLevelNodePath is true then for url /foo we looked for /*/foo - // but maybe that was the url of a non-default top-level node, so we also + // if hideTopLevelNodePath is true then for URL /foo we looked for /*/foo + // but maybe that was the URL of a non-default top-level node, so we also // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { @@ -143,7 +143,7 @@ namespace Umbraco.Web.PublishedCache.NuCache hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings // walk up from that node until we hit a node with a domain, - // or we reach the content root, collecting urls in the way + // or we reach the content root, collecting URLs in the way var pathParts = new List(); var n = node; var urlSegment = n.UrlSegment(_variationContextAccessor, culture); diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs index e33381d858..d41ca344dc 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -1386,7 +1386,11 @@ namespace Umbraco.Web.PublishedCache.NuCache { _collectTask = null; } - }, TaskContinuationOptions.ExecuteSynchronously); + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); // ReSharper restore InconsistentlySynchronizedField return task; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs index f60e2e1e99..6fe65a4ff5 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs @@ -36,15 +36,16 @@ namespace Umbraco.Web.PublishedCache.NuCache _urlSegment = ContentData.UrlSegment; IsPreviewing = ContentData.Published == false; - var properties = new List(); + var properties = new IPublishedProperty[_contentNode.ContentType.PropertyTypes.Count()]; + int i =0; foreach (var propertyType in _contentNode.ContentType.PropertyTypes) { // add one property per property type - this is required, for the indexing to work // if contentData supplies pdatas, use them, else use null contentData.Properties.TryGetValue(propertyType.Alias, out var pdatas); // else will be null - properties.Add(new Property(propertyType, this, pdatas, _publishedSnapshotAccessor)); + properties[i++] =new Property(propertyType, this, pdatas, _publishedSnapshotAccessor); } - PropertiesArray = properties.ToArray(); + PropertiesArray = properties; } // used when cloning in ContentNode diff --git a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs index c38940da25..589cd06d8a 100644 --- a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs +++ b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs @@ -380,7 +380,11 @@ namespace Umbraco.Web.PublishedCache.NuCache { _collectTask = null; } - }, TaskContinuationOptions.ExecuteSynchronously); + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html + TaskScheduler.Default); // ReSharper restore InconsistentlySynchronizedField return task; diff --git a/src/Umbraco.TestData/UmbracoTestDataController.cs b/src/Umbraco.TestData/UmbracoTestDataController.cs index f6b5e625a2..5ce90c9d69 100644 --- a/src/Umbraco.TestData/UmbracoTestDataController.cs +++ b/src/Umbraco.TestData/UmbracoTestDataController.cs @@ -177,7 +177,7 @@ namespace Umbraco.TestData { 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: + // 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 diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts index ba903d1c63..fa4c6ec9a3 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts @@ -32,7 +32,7 @@ context('Document Types', () => { cy.get('[data-element="editor-add"]').click(); //Search for textstring - cy.get('.umb-search-field').type('Textstring'); + cy.get('#datatype-search').type('Textstring'); // Choose first item cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); @@ -70,7 +70,7 @@ context('Document Types', () => { cy.umbracoContextMenuAction("action-delete").click(); cy.get('label.checkbox').click(); - cy.umbracoButtonByLabelKey("general_ok").click(); + cy.umbracoButtonByLabelKey("delete").click(); cy.contains(name).should('not.exist'); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts index fce951ef59..385a70bd5b 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts @@ -31,7 +31,7 @@ context('Media Types', () => { cy.get('[data-element="editor-add"]').click(); //Search for textstring - cy.get('.umb-search-field').type('Textstring'); + cy.get('#datatype-search').type('Textstring'); // Choose first item cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts index ad4c3f75c8..4dcc80432e 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts @@ -29,7 +29,7 @@ context('Member Types', () => { cy.get('[data-element="editor-add"]').click(); //Search for textstring - cy.get('.umb-search-field').type('Textstring'); + cy.get('#datatype-search').type('Textstring'); // Choose first item cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 095879209a..f4f55c1de8 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -23,6 +23,8 @@ using Umbraco.Core.Strings; using Umbraco.Web; using Umbraco.Web.Routing; using Umbraco.Tests.Common.Builders; +using System.Runtime.InteropServices; +using Umbraco.Tests.Common.TestHelpers; namespace Umbraco.Tests.Common { @@ -86,7 +88,26 @@ namespace Umbraco.Tests.Common get { if (_ioHelper == null) - _ioHelper = new IOHelper(GetHostingEnvironment()); + { + var hostingEnvironment = GetHostingEnvironment(); + + if (TestEnvironment.IsWindows) + { + _ioHelper = new IOHelperWindows(hostingEnvironment); + } + else if (TestEnvironment.IsLinux) + { + _ioHelper = new IOHelperLinux(hostingEnvironment); + } + else if (TestEnvironment.IsOSX) + { + _ioHelper = new IOHelperOSX(hostingEnvironment); + } + else + { + throw new NotSupportedException("Unexpected OS"); + } + } return _ioHelper; } } diff --git a/src/Umbraco.Tests.Common/TestHelpers/TestEnvironment.cs b/src/Umbraco.Tests.Common/TestHelpers/TestEnvironment.cs new file mode 100644 index 0000000000..e5d5d2650e --- /dev/null +++ b/src/Umbraco.Tests.Common/TestHelpers/TestEnvironment.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Umbraco.Tests.Common.TestHelpers +{ + public static class TestEnvironment + { + public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + public static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } +} diff --git a/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs index f163219854..1f7340787e 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs @@ -16,6 +16,7 @@ namespace Umbraco.Tests.Integration.TestServerTest { private readonly IBackOfficeSignInManager _backOfficeSignInManager; + private readonly BackOfficeIdentityUser _fakeUser; public TestAuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IBackOfficeSignInManager backOfficeSignInManager, IUserService userService, UmbracoMapper umbracoMapper) @@ -32,7 +33,7 @@ namespace Umbraco.Tests.Integration.TestServerTest { var principal = await _backOfficeSignInManager.CreateUserPrincipalAsync(_fakeUser); - var ticket = new AuthenticationTicket(principal, "Test"); + var ticket = new AuthenticationTicket(principal, TestAuthenticationScheme); return AuthenticateResult.Success(ticket); } diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index f6ece372ea..5867e6522c 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -47,7 +47,9 @@ namespace Umbraco.Tests.Integration.TestServerTest // Executes after the standard ConfigureServices method builder.ConfigureTestServices(services => { - services.AddAuthentication("Test").AddScheme("Test", options => { }); + // Add a test auth scheme with a test auth handler to authn and assign the user + services.AddAuthentication(TestAuthHandler.TestAuthenticationScheme) + .AddScheme(TestAuthHandler.TestAuthenticationScheme, options => { }); }); }); @@ -142,6 +144,7 @@ namespace Umbraco.Tests.Integration.TestServerTest .AddRuntimeMinifier() .AddBackOffice() .AddBackOfficeIdentity() + .AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme) .AddPreviewSupport() //.WithMiniProfiler() // we don't want this running in tests .AddMvcAndRazor(mvcBuilding: mvcBuilder => diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index db71603089..b61e61f20c 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Integration.Testing builder.AddWebComponents(); builder.AddRuntimeMinifier(); builder.AddBackOffice(); - services.AddUmbracoBackOfficeIdentity(); + builder.AddBackOfficeIdentity(); services.AddMvc(); diff --git a/src/Umbraco.Tests/IO/FileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs similarity index 60% rename from src/Umbraco.Tests/IO/FileSystemsTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs index 170e5e357e..4474b95584 100644 --- a/src/Umbraco.Tests/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs @@ -8,93 +8,48 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.IO.MediaPathSchemes; -using Umbraco.Core.Logging; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; -using Umbraco.Web.Common.Builder; +using Umbraco.Tests.Integration.Testing; +using Umbraco.Tests.Testing; using FileSystems = Umbraco.Core.IO.FileSystems; namespace Umbraco.Tests.IO { [TestFixture] - public class FileSystemsTests + [UmbracoTest()] + public class FileSystemsTests : UmbracoIntegrationTest { - private IServiceCollection _register; - private IServiceProvider _factory; - - [SetUp] - public void Setup() - { - _register = TestHelper.GetRegister(); - - var composition = new UmbracoBuilder(_register, Mock.Of(), TestHelper.GetMockedTypeLoader()); - - composition.Services.AddTransient(_ => Mock.Of()); - composition.Services.AddTransient(); - composition.Services.AddTransient(typeof(ILogger<>), typeof(Logger<>)); - composition.Services.AddTransient(_ => TestHelper.ShortStringHelper); - composition.Services.AddTransient(_ => TestHelper.IOHelper); - composition.Services.AddUnique(); - composition.Services.AddUnique(TestHelper.IOHelper); - composition.Services.AddUnique(TestHelper.GetHostingEnvironment()); - - var globalSettings = new GlobalSettings(); - composition.Services.AddScoped(x => Microsoft.Extensions.Options.Options.Create(globalSettings)); - - - composition.ComposeFileSystems(); - - _factory = composition.CreateServiceProvider(); - - // make sure we start clean - // because some tests will create corrupt or weird filesystems - FileSystems.Reset(); - } - - [TearDown] - public void TearDown() - { - // stay clean (see note in Setup) - FileSystems.Reset(); - - _register.DisposeIfDisposable(); - } - - private FileSystems FileSystems => _factory.GetRequiredService(); - [Test] public void Can_Get_MediaFileSystem() { - var fileSystem = _factory.GetRequiredService(); + var fileSystem = GetRequiredService(); Assert.NotNull(fileSystem); } [Test] public void Can_Get_IMediaFileSystem() { - var fileSystem = _factory.GetRequiredService(); + var fileSystem = GetRequiredService(); Assert.NotNull(fileSystem); } [Test] public void IMediaFileSystem_Is_Singleton() { - var fileSystem1 = _factory.GetRequiredService(); - var fileSystem2 = _factory.GetRequiredService(); + var fileSystem1 = GetRequiredService(); + var fileSystem2 = GetRequiredService(); Assert.AreSame(fileSystem1, fileSystem2); } [Test] public void Can_Unwrap_MediaFileSystem() { - var fileSystem = _factory.GetRequiredService(); + var fileSystem = GetRequiredService(); var unwrapped = fileSystem.Unwrap(); Assert.IsNotNull(unwrapped); var physical = unwrapped as PhysicalFileSystem; @@ -104,21 +59,21 @@ namespace Umbraco.Tests.IO [Test] public void Can_Delete_MediaFiles() { - var fs = _factory.GetRequiredService(); + var fs = GetRequiredService(); var ms = new MemoryStream(Encoding.UTF8.GetBytes("test")); var virtPath = fs.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); fs.AddFile(virtPath, ms); // ~/media/1234/file.txt exists - var ioHelper = _factory.GetRequiredService(); - var physPath = ioHelper.MapPath(Path.Combine("media", virtPath)); + var hostingEnvironment = GetRequiredService(); + var physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtPath)); Assert.IsTrue(File.Exists(physPath)); // ~/media/1234/file.txt is gone fs.DeleteMediaFiles(new[] { virtPath }); Assert.IsFalse(File.Exists(physPath)); - var scheme = _factory.GetRequiredService(); + var scheme = GetRequiredService(); if (scheme is UniqueMediaPathScheme) { // ~/media/1234 is *not* gone diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs index 78ebbeb165..1ae46faa76 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor [SetUp] public void SetUp() { - _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), Constants.SystemDirectories.MvcViews + "/Partials/"); + _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), HostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews), HostingEnvironment.ToAbsolute(Constants.SystemDirectories.PartialViews)); } [TearDown] diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/RedirectUrlRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/RedirectUrlRepositoryTests.cs index 8ae792b92f..54d83172f0 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/RedirectUrlRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/RedirectUrlRepositoryTests.cs @@ -53,6 +53,38 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor } } + [Test] + public void CanSaveAndGetWithCulture() + { + var culture = "en"; + using (var scope = ScopeProvider.CreateScope()) + { + var repo = CreateRepository(ScopeProvider); + var rurl = new RedirectUrl + { + ContentKey = _textpage.Key, + Url = "blah", + Culture = culture + }; + repo.Save(rurl); + scope.Complete(); + + Assert.AreNotEqual(0, rurl.Id); + } + + using (var scope = ScopeProvider.CreateScope()) + { + var repo = CreateRepository(ScopeProvider); + var rurl = repo.GetMostRecentUrl("blah"); + scope.Complete(); + + Assert.IsNotNull(rurl); + Assert.AreEqual(_textpage.Id, rurl.ContentId); + Assert.AreEqual(culture, rurl.Culture); + } + } + + [Test] public void CanSaveAndGetMostRecent() { @@ -101,6 +133,58 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor } } + [Test] + public void CanSaveAndGetMostRecentForCulture() + { + var cultureA = "en"; + var cultureB = "de"; + Assert.AreNotEqual(_textpage.Id, _otherpage.Id); + + using (var scope = ScopeProvider.CreateScope()) + { + var repo = CreateRepository(ScopeProvider); + var rurl = new RedirectUrl + { + ContentKey = _textpage.Key, + Url = "blah", + Culture = cultureA + }; + repo.Save(rurl); + scope.Complete(); + + Assert.AreNotEqual(0, rurl.Id); + + // FIXME: too fast = same date = key violation? + // and... can that happen in real life? + // we don't really *care* about the IX, only supposed to make things faster... + // BUT in realife we AddOrUpdate in a trx so it should be safe, always + + rurl = new RedirectUrl + { + ContentKey = _otherpage.Key, + Url = "blah", + CreateDateUtc = rurl.CreateDateUtc.AddSeconds(1), // ensure time difference + Culture = cultureB + }; + repo.Save(rurl); + scope.Complete(); + + Assert.AreNotEqual(0, rurl.Id); + } + + using (var scope = ScopeProvider.CreateScope()) + { + var repo = CreateRepository(ScopeProvider); + var rurl = repo.GetMostRecentUrl("blah", cultureA); + scope.Complete(); + + Assert.IsNotNull(rurl); + Assert.AreEqual(_textpage.Id, rurl.ContentId); + Assert.AreEqual(cultureA, rurl.Culture); + } + } + + [Test] public void CanSaveAndGetByContent() { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs index 435ccaa322..9e4ae80ec6 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs @@ -29,7 +29,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor public void SetUpFileSystem() { _fileSystems = Mock.Of(); - _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), new GlobalSettings().UmbracoScriptsPath); + var path = GlobalSettings.UmbracoScriptsPath; + _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), HostingEnvironment.MapPathWebRoot(path), HostingEnvironment.ToAbsolute(path)); Mock.Get(_fileSystems).Setup(x => x.ScriptsFileSystem).Returns(_fileSystem); using (var stream = CreateStream("Umbraco.Sys.registerNamespace(\"Umbraco.Utils\");")) { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs index 58f3d7ad95..a576666e6e 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -19,7 +19,7 @@ using Umbraco.Tests.Testing; namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositories { [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.None)] + [UmbracoTest(Database = UmbracoTestOptions.Database.None, Logger = UmbracoTestOptions.Logger.Console)] public class StylesheetRepositoryTest : UmbracoIntegrationTest { private IFileSystems _fileSystems; @@ -32,7 +32,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor public void SetUpFileSystem() { _fileSystems = Mock.Of(); - _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), new GlobalSettings().UmbracoCssPath); + var path = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoCssPath); + _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), path, "/css"); Mock.Get(_fileSystems).Setup(x => x.StylesheetsFileSystem).Returns(_fileSystem); var stream = CreateStream("body {background:#EE7600; color:#FFF;}"); _fileSystem.AddFile("styles.css", stream); @@ -49,7 +50,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor private IStylesheetRepository CreateRepository() { var globalSettings = new GlobalSettings(); - return new StylesheetRepository(_fileSystems, IOHelper, Microsoft.Extensions.Options.Options.Create(globalSettings)); + return new StylesheetRepository(GetRequiredService>(), _fileSystems, IOHelper, Microsoft.Extensions.Options.Options.Create(globalSettings)); } [Test] @@ -194,7 +195,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor Assert.That(stylesheet, Is.Not.Null); Assert.That(stylesheet.HasIdentity, Is.True); Assert.That(stylesheet.Content, Is.EqualTo("body {background:#EE7600; color:#FFF;}")); - Assert.That(repository.ValidateStylesheet(stylesheet), Is.True); + //Assert.That(repository.ValidateStylesheet(stylesheet), Is.True); //TODO this can't be activated before we handle file systems correct } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index c41b7eaf6b..6deb579ad1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; @@ -28,6 +29,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class TemplateRepositoryTest : UmbracoIntegrationTest { + private IHostingEnvironment HostingEnvironment => GetRequiredService(); private IFileSystems FileSystems => GetRequiredService(); private ITemplateRepository CreateRepository(IScopeProvider provider) @@ -560,7 +562,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositor var testHelper = new TestHelper(); //Delete all files - var fsViews = new PhysicalFileSystem(IOHelper, testHelper.GetHostingEnvironment(), LoggerFactory.CreateLogger(), Constants.SystemDirectories.MvcViews); + var fsViews = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), HostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews), HostingEnvironment.ToAbsolute(Constants.SystemDirectories.MvcViews)); var views = fsViews.GetFiles("", "*.cshtml"); foreach (var file in views) fsViews.DeleteFile(file); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs index a62fc26ad7..2960455a70 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs @@ -139,12 +139,10 @@ namespace Umbraco.Tests.Integration.Umbraco.Web.Backoffice.Filters var logger = Services.GetRequiredService>(); var backofficeSecurityFactory = Services.GetRequiredService(); backofficeSecurityFactory.EnsureBackOfficeSecurity(); - var backofficeSecurityAccessor = Services.GetRequiredService(); - var localizedTextService = Services.GetRequiredService(); var propertyValidationService = Services.GetRequiredService(); var umbracoMapper = Services.GetRequiredService(); - var validator = new ContentSaveModelValidator(logger, backofficeSecurityAccessor.BackOfficeSecurity, localizedTextService, propertyValidationService); + var validator = new ContentSaveModelValidator(logger, propertyValidationService); var content = ContentBuilder.CreateTextpageContent(_contentType, "test", -1); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs index 4bc531944e..e1a34e38a3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Manifest }; _ioHelper = TestHelper.IOHelper; var loggerFactory = NullLoggerFactory.Instance; - _parser = new ManifestParser(AppCaches.Disabled, new ManifestValueValidatorCollection(validators), new ManifestFilterCollection(Array.Empty()), loggerFactory.CreateLogger(), loggerFactory, _ioHelper, Mock.Of(), Mock.Of(), new JsonNetSerializer(), Mock.Of(), Mock.Of()); + _parser = new ManifestParser(AppCaches.Disabled, new ManifestValueValidatorCollection(validators), new ManifestFilterCollection(Array.Empty()), loggerFactory.CreateLogger(), loggerFactory, _ioHelper, TestHelper.GetHostingEnvironment(), Mock.Of(), Mock.Of(), new JsonNetSerializer(), Mock.Of(), Mock.Of()); } [Test] diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/ContentPermissionsTests.cs similarity index 70% rename from src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/ContentPermissionsTests.cs index 3df5f76da5..8a7b26c6cf 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/ContentPermissionsTests.cs @@ -8,10 +8,10 @@ using Umbraco.Core.Services; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Common.Builders.Extensions; -namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers +namespace Umbraco.Tests.UnitTests.Umbraco.Core.Security { [TestFixture] - public class ContentControllerUnitTests + public class ContentPermissionsTests { [Test] public void Access_Allowed_By_Path() @@ -28,12 +28,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var entityService = entityServiceMock.Object; var userServiceMock = new Mock(); var userService = userServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(1234, user, userService, contentService, entityService, out var foundContent); + var result = contentPermissions.CheckPermissions(1234, user, out IContent foundContent); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -54,12 +55,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(1234, user, userService, contentService, entityService, out var foundContent, new[] { 'F' }); + var result = contentPermissions.CheckPermissions(1234, user, out IContent foundContent, new[] { 'F' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.NotFound, result); + Assert.AreEqual(ContentPermissions.ContentAccess.NotFound, result); } [Test] @@ -82,12 +84,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 9876 && entity.Path == "-1,9876") }); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(1234, user, userService, contentService, entityService, out var foundContent, new[] { 'F' }); + var result = contentPermissions.CheckPermissions(1234, user, out IContent foundContent, new[] { 'F' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } [Test] @@ -111,12 +114,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(1234, user, userService, contentService, entityService, out var foundContent, new[] { 'F' }); + var result = contentPermissions.CheckPermissions(1234, user, out IContent foundContent, new[] { 'F' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } [Test] @@ -140,12 +144,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(1234, user, userService, contentService, entityService, out var foundContent, new[] { 'F' }); + var result = contentPermissions.CheckPermissions(1234, user, out IContent foundContent, new[] { 'F' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -159,12 +164,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-1, user, userService, contentService, entityService, out var foundContent); + var result = contentPermissions.CheckPermissions(-1, user, out IContent _); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -178,12 +184,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-20, user, userService, contentService, entityService, out var foundContent); + var result = contentPermissions.CheckPermissions(-20, user, out IContent _); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -199,12 +206,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 1234 && entity.Path == "-1,1234") }); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-20, user, userService, contentService, entityService, out var foundContent); + var result = contentPermissions.CheckPermissions(-20, user, out IContent foundContent); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } [Test] @@ -221,12 +229,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 1234 && entity.Path == "-1,1234") }); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-1, user, userService, contentService, entityService, out var foundContent); + var result = contentPermissions.CheckPermissions(-1, user, out IContent foundContent); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } [Test] @@ -247,12 +256,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var userService = userServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-1, user, userService, contentService, entityService, out var foundContent, new[] { 'A' }); + var result = contentPermissions.CheckPermissions(-1, user, out IContent foundContent, new[] { 'A' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -273,12 +283,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var entityService = entityServiceMock.Object; var contentServiceMock = new Mock(); var contentService = contentServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-1, user, userService, contentService, entityService, out var foundContent, new[] { 'B' }); + var result = contentPermissions.CheckPermissions(-1, user, out IContent foundContent, new[] { 'B' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } [Test] @@ -300,12 +311,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var entityService = entityServiceMock.Object; var contentServiceMock = new Mock(); var contentService = contentServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-20, user, userService, contentService, entityService, out var foundContent, new[] { 'A' }); + var result = contentPermissions.CheckPermissions(-20, user, out IContent foundContent, new[] { 'A' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Granted, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Granted, result); } [Test] @@ -326,12 +338,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var entityService = entityServiceMock.Object; var contentServiceMock = new Mock(); var contentService = contentServiceMock.Object; + var contentPermissions = new ContentPermissions(userService, contentService, entityService); //act - var result = ContentPermissionsHelper.CheckPermissions(-20, user, userService, contentService, entityService, out var foundContent, new[] { 'B' }); + var result = contentPermissions.CheckPermissions(-20, user, out IContent foundContent, new[] { 'B' }); //assert - Assert.AreEqual(ContentPermissionsHelper.ContentAccess.Denied, result); + Assert.AreEqual(ContentPermissions.ContentAccess.Denied, result); } private IUser CreateUser(int id = 0, int? startContentId = null, bool withUserGroup = true) @@ -340,77 +353,15 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers .WithId(id) .WithStartContentIds(startContentId.HasValue ? new[] { startContentId.Value } : new int[0]); if (withUserGroup) - { builder = builder .AddUserGroup() .WithId(1) .WithName("admin") .WithAlias("admin") .Done(); - } return builder.Build(); } } - //NOTE: The below self hosted stuff does work so need to get some tests written. Some are not possible atm because - // of the legacy SQL calls like checking permissions. - - //[TestFixture] - //public class ContentControllerHostedTests : BaseRoutingTest - //{ - - // protected override DatabaseBehavior DatabaseTestBehavior - // { - // get { return DatabaseBehavior.NoDatabasePerFixture; } - // } - - // public override void TearDown() - // { - // base.TearDown(); - // UmbracoAuthorizeAttribute.Enable = true; - // UmbracoApplicationAuthorizeAttribute.Enable = true; - // } - - // /// - // /// Tests to ensure that the response filter works so that any items the user - // /// doesn't have access to are removed - // /// - // [Test] - // public async void Get_By_Ids_Response_Filtered() - // { - // UmbracoAuthorizeAttribute.Enable = false; - // UmbracoApplicationAuthorizeAttribute.Enable = false; - - // var baseUrl = string.Format("http://{0}:9876", Environment.MachineName); - // var url = baseUrl + "/api/Content/GetByIds?ids=1&ids=2"; - - // var routingCtx = GetRoutingContext(url, 1234, null, true); - - // var config = new HttpSelfHostConfiguration(baseUrl); - // using (var server = new HttpSelfHostServer(config)) - // { - // var route = config.Routes.MapHttpRoute("test", "api/Content/GetByIds", - // new - // { - // controller = "Content", - // action = "GetByIds", - // id = RouteParameter.Optional - // }); - // route.DataTokens["Namespaces"] = new string[] { "Umbraco.Web.Editors" }; - - // var client = new HttpClient(server); - - // var request = new HttpRequestMessage - // { - // RequestUri = new Uri(url), - // Method = HttpMethod.Get - // }; - - // var result = await client.SendAsync(request); - // } - - // } - - //} } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MediaControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/MediaPermissionsTests.cs similarity index 73% rename from src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MediaControllerUnitTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/MediaPermissionsTests.cs index 2f692ade85..c9b0324f06 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MediaControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/MediaPermissionsTests.cs @@ -1,19 +1,17 @@ -using System.Collections.Generic; -using Moq; +using Moq; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Common.Builders.Extensions; -using Umbraco.Web.BackOffice.Controllers; -using Umbraco.Web.Common.Exceptions; -namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers +namespace Umbraco.Tests.UnitTests.Umbraco.Core.Security { [TestFixture] - public class MediaControllerUnitTests + public class MediaPermissionsTests { [Test] public void Access_Allowed_By_Path() @@ -28,16 +26,17 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var mediaService = mediaServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, 1234); + var result = mediaPermissions.CheckPermissions(user, 1234, out _); //assert - Assert.IsTrue(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Granted, result); } [Test] - public void Throws_Exception_When_No_Media_Found() + public void Returns_Not_Found_When_No_Media_Found() { //arrange var user = CreateUser(id: 9); @@ -49,9 +48,11 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var mediaService = mediaServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act/assert - Assert.Throws(() => MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, 1234)); + var result = mediaPermissions.CheckPermissions(user, 1234, out _); + Assert.AreEqual(MediaPermissions.MediaAccess.NotFound, result); } [Test] @@ -69,12 +70,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 9876 && entity.Path == "-1,9876") }); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, 1234); + var result = mediaPermissions.CheckPermissions(user, 1234, out _); //assert - Assert.IsFalse(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Denied, result); } [Test] @@ -86,12 +88,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var mediaService = mediaServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, -1); + var result = mediaPermissions.CheckPermissions(user, -1, out _); //assert - Assert.IsTrue(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Granted, result); } [Test] @@ -105,12 +108,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 1234 && entity.Path == "-1,1234") }); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, -1); + var result = mediaPermissions.CheckPermissions(user, -1, out _); //assert - Assert.IsFalse(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Denied, result); } [Test] @@ -122,12 +126,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var mediaService = mediaServiceMock.Object; var entityServiceMock = new Mock(); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, -21); + var result = mediaPermissions.CheckPermissions(user, -21, out _); //assert - Assert.IsTrue(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Granted, result); } [Test] @@ -141,12 +146,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers entityServiceMock.Setup(x => x.GetAllPaths(It.IsAny(), It.IsAny())) .Returns(new[] { Mock.Of(entity => entity.Id == 1234 && entity.Path == "-1,1234") }); var entityService = entityServiceMock.Object; + var mediaPermissions = new MediaPermissions(mediaService, entityService); //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, entityService, -21); + var result = mediaPermissions.CheckPermissions(user, -21, out _); //assert - Assert.IsFalse(result); + Assert.AreEqual(MediaPermissions.MediaAccess.Denied, result); } private IUser CreateUser(int id = 0, int? startMediaId = null) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/DefaultShortStringHelperTestsWithoutSetup.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/DefaultShortStringHelperTestsWithoutSetup.cs index 3a210c231d..98e00a69ef 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/DefaultShortStringHelperTestsWithoutSetup.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/DefaultShortStringHelperTestsWithoutSetup.cs @@ -263,7 +263,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.ShortStringHelper Assert.AreEqual("house*2", helper.CleanString("house (2)", CleanStringType.Alias)); // FIXME: but for a filename we want to keep them! - // FIXME: and what about a url? + // FIXME: and what about a URL? } [Test] @@ -350,7 +350,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.ShortStringHelper // lower-cased, utf8 filename, removing illegal filename chars, using dash-separator Assert.AreEqual("0123-中文测试-中文测试-léger-zôrg-2-a-x", filename, "filename"); - // lower-cased, utf8 url segment, only letters and digits, using dash-separator + // lower-cased, utf8 URL segment, only letters and digits, using dash-separator Assert.AreEqual("0123-中文测试-中文测试-léger-zôrg-2-a-x", segment, "segment"); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs index 014bb0a299..542d273954 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs @@ -60,7 +60,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Templates [Test] public void Ensure_Image_Sources() { - //setup a mock url provider which we'll use for testing + //setup a mock URL provider which we'll use for testing var mediaType = new PublishedContentType(Guid.NewGuid(), 777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs index 1acf676cba..043481b9d0 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs @@ -48,7 +48,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Templates [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")] public void ParseLocalLinks(string input, string result) { - //setup a mock url provider which we'll use for testing + //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())) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs index 642f9d335a..2bf9a541b9 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs @@ -30,7 +30,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new string[0]); @@ -52,7 +51,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new string[0]); @@ -74,7 +72,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new[] {"FunGroup"}); @@ -96,7 +93,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new[] { "test" }); @@ -133,7 +129,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 5555 which currentUser has access to since it's a child of 9876 ... adding is still ok even though currentUser doesn't have access to 1234 @@ -171,7 +166,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //removing 4567 start node even though currentUser doesn't have acces to it ... removing is ok @@ -209,7 +203,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 1234 but currentUser doesn't have access to it ... nope @@ -247,7 +240,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 5555 which currentUser has access to since it's a child of 9876 ... ok @@ -286,7 +278,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 1234 but currentUser doesn't have access to it ... nope @@ -324,7 +315,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 5555 which currentUser has access to since it's a child of 9876 ... ok @@ -362,7 +352,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //adding 5555 which currentUser has access to since it's a child of 9876 ... adding is still ok even though currentUser doesn't have access to 1234 @@ -400,7 +389,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Editors var authHelper = new UserEditorAuthorizationHelper( contentService.Object, mediaService.Object, - userService.Object, entityService.Object); //removing 4567 start node even though currentUser doesn't have acces to it ... removing is ok diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 04900abaf0..7c1973a8cf 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.IO public void SetUp() { SafeCallContext.Clear(); - ClearFiles(_ioHelper); + ClearFiles(_hostingEnvironment); FileSystems.ResetShadowId(); } @@ -40,14 +40,14 @@ namespace Umbraco.Tests.IO public void TearDown() { SafeCallContext.Clear(); - ClearFiles(TestHelper.IOHelper); + ClearFiles(_hostingEnvironment); FileSystems.ResetShadowId(); } - private static void ClearFiles(IIOHelper _ioHelper) + private static void ClearFiles(IHostingEnvironment hostingEnvironment) { - TestHelper.DeleteDirectory(_ioHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(_ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs")); + TestHelper.DeleteDirectory(hostingEnvironment.MapPathContentRoot("FileSysTests")); + TestHelper.DeleteDirectory(hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs")); } private static string NormPath(string path) @@ -58,7 +58,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowDeleteDirectory() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -92,7 +92,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowDeleteDirectoryInDir() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -141,7 +141,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowDeleteFile() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -180,7 +180,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowDeleteFileInDir() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); @@ -236,7 +236,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowCantCreateFile() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -255,7 +255,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowCreateFile() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -294,7 +294,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowCreateFileInDir() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -334,7 +334,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowAbort() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -356,7 +356,7 @@ namespace Umbraco.Tests.IO [Test] public void ShadowComplete() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -393,8 +393,8 @@ namespace Umbraco.Tests.IO public void ShadowScopeComplete() { var loggerFactory = NullLoggerFactory.Instance; - var path = _ioHelper.MapPath("FileSysTests"); - var shadowfs = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); + var shadowfs = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); Directory.CreateDirectory(shadowfs); @@ -415,7 +415,7 @@ namespace Umbraco.Tests.IO string id; // explicit shadow without scope does not work - sw.Shadow(id = ShadowWrapper.CreateShadowId(_ioHelper)); + sw.Shadow(id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); @@ -426,7 +426,7 @@ namespace Umbraco.Tests.IO // shadow with scope but no complete does not complete scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_ioHelper)); + var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f3.txt", ms); @@ -448,7 +448,7 @@ namespace Umbraco.Tests.IO // shadow with scope and complete does complete scopedFileSystems = true; // pretend we have a scope - scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_ioHelper)); + scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f4.txt", ms); @@ -464,7 +464,7 @@ namespace Umbraco.Tests.IO // test scope for "another thread" scopedFileSystems = true; // pretend we have a scope - scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_ioHelper)); + scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f5.txt", ms); @@ -487,8 +487,8 @@ namespace Umbraco.Tests.IO [Test] public void ShadowScopeCompleteWithFileConflict() { - var path = _ioHelper.MapPath("FileSysTests"); - var shadowfs = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); + var shadowfs = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; @@ -508,7 +508,7 @@ namespace Umbraco.Tests.IO string id; scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_ioHelper)); + var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); @@ -539,8 +539,8 @@ namespace Umbraco.Tests.IO [Test] public void ShadowScopeCompleteWithDirectoryConflict() { - var path = _ioHelper.MapPath("FileSysTests"); - var shadowfs = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); + var shadowfs = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; @@ -560,7 +560,7 @@ namespace Umbraco.Tests.IO string id; scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_ioHelper)); + var scope = new ShadowFileSystems(fileSystems, id = ShadowWrapper.CreateShadowId(_hostingEnvironment)); Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); @@ -608,7 +608,7 @@ namespace Umbraco.Tests.IO [Test] public void GetFilesReturnsChildrenOnly() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); File.WriteAllText(path + "/f1.txt", "foo"); Directory.CreateDirectory(path + "/test"); @@ -630,7 +630,7 @@ namespace Umbraco.Tests.IO [Test] public void DeleteDirectoryAndFiles() { - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); File.WriteAllText(path + "/f1.txt", "foo"); Directory.CreateDirectory(path + "/test"); @@ -651,7 +651,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFiles() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -683,7 +683,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFilesUsingEmptyFilter() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -718,7 +718,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFilesUsingNullFilter() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -750,7 +750,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFilesUsingWildcardFilter() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -785,7 +785,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFilesUsingSingleCharacterFilter() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -832,7 +832,7 @@ namespace Umbraco.Tests.IO public void ShadowGetFullPath() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -866,7 +866,7 @@ namespace Umbraco.Tests.IO public void ShadowGetRelativePath() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); @@ -892,12 +892,12 @@ namespace Umbraco.Tests.IO } /// - /// Ensure the url returned contains the path relative to the FS root, + /// Ensure the URL returned contains the path relative to the FS root, /// but including the rootUrl the FS was initialized with. /// /// /// This file stuff in this test is kinda irrelevant with the current implementation. - /// We do tests that the files are written to the correct places and the url is returned correct, + /// We do tests that the files are written to the correct places and the URL is returned correct, /// but GetUrl is currently really just string manipulation so files are not actually hit by the code. /// Leaving the file stuff in here for now in case the method becomes more clever at some point. /// @@ -905,7 +905,7 @@ namespace Umbraco.Tests.IO public void ShadowGetUrl() { // Arrange - var path = _ioHelper.MapPath("FileSysTests"); + var path = _hostingEnvironment.MapPathContentRoot("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index fe02e6fa48..28bb5bb6d7 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -101,7 +101,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // so we have a route that maps to a content... say "1234/path/to/content" - however, there could be a // domain set on "to" and route "4567/content" would also map to the same content - and due to how - // urls computing work (by walking the tree up to the first domain we find) it is that second route + // URLs computing work (by walking the tree up to the first domain we find) it is that second route // that would be returned - the "deepest" route - and that is the route we want to cache, *not* the // longer one - so make sure we don't cache the wrong route @@ -262,7 +262,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache if (node == null) return null; // walk up from that node until we hit a node with a domain, - // or we reach the content root, collecting urls in the way + // or we reach the content root, collecting URLs in the way var pathParts = new List(); var n = node; var hasDomains = _domainCache.HasAssigned(n.Id); diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index d09faf6757..51d077d89f 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -66,7 +66,7 @@ namespace Umbraco.Tests.Packaging private IPackageInstallation PackageInstallation => new PackageInstallation( PackageDataInstallation, - new PackageFileInstallation(Parser, IOHelper, ProfilingLogger), + new PackageFileInstallation(Parser, IOHelper, HostingEnvironment, ProfilingLogger), Parser, Mock.Of(), //we don't want to extract package files to the real root, so extract to a test folder Mock.Of(x => x.ApplicationPhysicalPath == _testBaseFolder.FullName)); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index a5e72919e2..b55609858b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -53,7 +53,7 @@ namespace Umbraco.Tests.PublishedContent var serializer = new ConfigurationEditorJsonSerializer(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), IOHelper, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper, publishedUrlProvider, serializer); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper, publishedUrlProvider, serializer); var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor( diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index dea7504e7b..2170da2f75 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -59,7 +59,7 @@ namespace Umbraco.Tests.PublishedContent var publishedUrlProvider = Mock.Of(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); var serializer = new ConfigurationEditorJsonSerializer(); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), IOHelper, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider, serializer); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider, serializer); var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var localizationService = Mock.Of(); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 7337d3ebd2..6a0b8c3f3b 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Routing [TestCase("/home/sub1", -1)] // should fail // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so - // we've made it return "/test-page" => we have to support that url back in the lookup... + // we've made it return "/test-page" => we have to support that URL back in the lookup... [TestCase("/home", 1046)] [TestCase("/test-page", 1172)] public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 0acbba1a9b..8428f44a8b 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -136,7 +136,7 @@ namespace Umbraco.Tests.Routing var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); - // must lookup domain else lookup by url fails + // must lookup domain else lookup by URL fails publishedRouter.FindDomain(frequest); var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger()); @@ -177,7 +177,7 @@ namespace Umbraco.Tests.Routing var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); - // must lookup domain else lookup by url fails + // must lookup domain else lookup by URL fails publishedRouter.FindDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture.Name); diff --git a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs index c865a0c726..e08873ac04 100644 --- a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs @@ -92,7 +92,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(_requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( @@ -139,7 +138,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(_requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, diff --git a/src/Umbraco.Tests/Routing/RouteTestExtensions.cs b/src/Umbraco.Tests/Routing/RouteTestExtensions.cs index fae8e2a6fa..642488c256 100644 --- a/src/Umbraco.Tests/Routing/RouteTestExtensions.cs +++ b/src/Umbraco.Tests/Routing/RouteTestExtensions.cs @@ -9,7 +9,7 @@ namespace Umbraco.Tests.Routing { /// - /// Return the route data for the url based on a mocked context + /// Return the route data for the URL based on a mocked context /// /// /// @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Routing } /// - /// Get the route data based on the url and http context + /// Get the route data based on the URL and HTTP context /// /// /// diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index 34bde09c37..267d870514 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -96,7 +96,7 @@ namespace Umbraco.Tests.Routing [Test] public void Is_Client_Side_Request_InvalidPath_ReturnFalse() { - //This url is invalid. Default to false when the extension cannot be determined + //This URL is invalid. Default to false when the extension cannot be determined var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\""); var result = uri.IsClientSideRequest(); Assert.AreEqual(false, result); diff --git a/src/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs b/src/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs index c403ade51c..50b1597cca 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs @@ -48,7 +48,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs b/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs index fd87ce84b1..dca0caadc4 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs @@ -51,7 +51,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -112,7 +111,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -153,11 +151,10 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), 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. + //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 = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); Assert.AreEqual("/home/test-fr/", url); @@ -208,7 +205,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -263,7 +259,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); @@ -284,7 +279,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -301,7 +295,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(_globalSettings), new SiteDomainHelper(), UmbracoContextAccessor, UriUtility); var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: _globalSettings); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs index 4894e1ad7e..9f076983d2 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs @@ -14,7 +14,7 @@ using Umbraco.Tests.Testing; namespace Umbraco.Tests.Routing { // purpose: test the values returned by PublishedContentCache.GetRouteById - // and .GetByRoute (no caching at all, just routing nice urls) including all + // and .GetByRoute (no caching at all, just routing nice URLs) including all // the quirks due to hideTopLevelFromPath and backward compatibility. [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] @@ -148,7 +148,7 @@ DetermineRouteById(id): node = node(id) - walk up from node to domain or root, assemble parts = url segments + walk up from node to domain or root, assemble parts = URL segments if !domain and global.hide: if id.parent: diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 80b92b4d1b..aaf3b0de96 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -186,7 +186,6 @@ namespace Umbraco.Tests.Routing var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -220,7 +219,6 @@ namespace Umbraco.Tests.Routing var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -246,7 +244,6 @@ namespace Umbraco.Tests.Routing var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -279,7 +276,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -302,7 +298,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -367,7 +362,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -393,7 +387,6 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 10cb8c9ac4..af2ffad6e5 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Routing [TestFixture] public class UrlsWithNestedDomains : UrlRoutingTestBase { - // in the case of nested domains more than 1 url may resolve to a document + // in the case of nested domains more than 1 URL may resolve to a document // but only one route can be cached - the 'canonical' route ie the route // using the closest domain to the node - here we test that if we request // a non-canonical route, it is not cached / the cache is not polluted @@ -41,13 +41,12 @@ namespace Umbraco.Tests.Routing const string url = "http://domain1.com/1001-1/1001-1-1"; - // get the nice url for 100111 + // get the nice URL for 100111 var umbracoContext = GetUmbracoContext(url, 9999, globalSettings: globalSettings); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), LoggerFactory.CreateLogger(), - Microsoft.Extensions.Options.Options.Create(globalSettings), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -59,7 +58,7 @@ namespace Umbraco.Tests.Routing var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); - // route a rogue url + // route a rogue URL var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); @@ -77,7 +76,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes - // what's the nice url now? + // what's the nice URL now? 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 } diff --git a/src/Umbraco.Tests/Services/RedirectUrlServiceTests.cs b/src/Umbraco.Tests/Services/RedirectUrlServiceTests.cs new file mode 100644 index 0000000000..8f38bd67eb --- /dev/null +++ b/src/Umbraco.Tests/Services/RedirectUrlServiceTests.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Threading; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; + +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.Scoping; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + [Apartment(ApartmentState.STA)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class RedirectUrlServiceTests : TestWithSomeContentBase + { + private IContent _testPage; + private IContent _altTestPage; + private string _url = "blah"; + private string _cultureA = "en"; + private string _cultureB = "de"; + public override void CreateTestData() + { + base.CreateTestData(); + + using (var scope = ScopeProvider.CreateScope()) + { + var repository = new RedirectUrlRepository((IScopeAccessor)ScopeProvider, AppCaches.Disabled, Mock.Of>()); + var rootContent = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); + var subPages = ServiceContext.ContentService.GetPagedChildren(rootContent.Id, 0, 2, out _).ToList(); + _testPage = subPages[0]; + _altTestPage = subPages[1]; + + repository.Save(new RedirectUrl + { + ContentKey = _testPage.Key, + Url = _url, + Culture = _cultureA + }); + repository.Save(new RedirectUrl + { + ContentKey = _altTestPage.Key, + Url = _url, + Culture = _cultureB + }); + scope.Complete(); + } + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Can_Get_Most_Recent_RedirectUrl() + { + var redirectUrlService = ServiceContext.RedirectUrlService; + var redirect = redirectUrlService.GetMostRecentRedirectUrl(_url); + Assert.AreEqual(redirect.ContentId, _altTestPage.Id); + + } + + [Test] + public void Can_Get_Most_Recent_RedirectUrl_With_Culture() + { + var redirectUrlService = ServiceContext.RedirectUrlService; + var redirect = redirectUrlService.GetMostRecentRedirectUrl(_url, _cultureA); + Assert.AreEqual(redirect.ContentId, _testPage.Id); + + } + + } +} diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 0eaa141250..8b9d5d7a4a 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -1,15 +1,14 @@ using System; using System.Globalization; using System.Linq; -using System.Web; using System.Web.Security; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Dictionary; @@ -23,7 +22,6 @@ using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -31,7 +29,7 @@ using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; using Umbraco.Web.WebApi; using Current = Umbraco.Web.Composing.Current; -using Umbraco.Tests.Common.Builders; + namespace Umbraco.Tests.Testing.TestingTests { @@ -70,11 +68,18 @@ namespace Umbraco.Tests.Testing.TestingTests Builder.Services.AddTransient(_ => AppCaches.Disabled); Builder.Services.AddTransient(); + var loggerFactory = 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(Mock.Of(), Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, loggerFactory, ShortStringHelper, Mock.Of()); + // ReSharper disable once UnusedVariable var helper = new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + membershipHelper); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ec62d7f1f5..008fbc47d7 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -131,7 +131,6 @@ - @@ -162,6 +161,7 @@ + diff --git a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs new file mode 100644 index 0000000000..b40904f460 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web.Editors; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// if the users being edited is an admin then we must ensure that the current user is also an admin + /// + public class AdminUsersHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IHttpContextAccessor _httpContextAcessor; + private readonly IUserService _userService; + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly UserEditorAuthorizationHelper _userEditorAuthorizationHelper; + + public AdminUsersHandler(IHttpContextAccessor httpContextAcessor, + IUserService userService, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + UserEditorAuthorizationHelper userEditorAuthorizationHelper) + { + _httpContextAcessor = httpContextAcessor; + _userService = userService; + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _userEditorAuthorizationHelper = userEditorAuthorizationHelper; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, AdminUsersRequirement requirement) + { + var queryString = _httpContextAcessor.HttpContext?.Request.Query[requirement.QueryStringName]; + if (!queryString.HasValue) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + + int[] userIds; + if (int.TryParse(queryString, out var userId)) + { + userIds = new[] { userId }; + } + else + { + var ids = _httpContextAcessor.HttpContext.Request.Query.Where(x => x.Key == requirement.QueryStringName).ToList(); + if (ids.Count == 0) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + userIds = ids + .Select(x => x.Value.ToString()) + .Select(x => x.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray(); + } + + if (userIds.Length == 0) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + + var users = _userService.GetUsersById(userIds); + var isAuth = users.All(user => _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, null) != false); + + return Task.FromResult(isAuth); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs new file mode 100644 index 0000000000..3d168776e9 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Authorization requirement for the + /// + public class AdminUsersRequirement : IAuthorizationRequirement + { + public AdminUsersRequirement(string queryStringName = "id") + { + QueryStringName = queryStringName; + } + + public string QueryStringName { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs new file mode 100644 index 0000000000..6cee04deae --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Security; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Ensures authorization is successful for a back office user. + /// + public class BackOfficeHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backOfficeSecurity; + private readonly IRuntimeState _runtimeState; + + public BackOfficeHandler(IBackOfficeSecurityAccessor backOfficeSecurity, IRuntimeState runtimeState) + { + _backOfficeSecurity = backOfficeSecurity; + _runtimeState = runtimeState; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, BackOfficeRequirement requirement) + { + try + { + // if not configured (install or upgrade) then we can continue + // otherwise we need to ensure that a user is logged in + var isAuth = _runtimeState.Level == RuntimeLevel.Install + || _runtimeState.Level == RuntimeLevel.Upgrade + || _backOfficeSecurity.BackOfficeSecurity?.ValidateCurrentUser(false, requirement.RequireApproval) == ValidateRequestAttempt.Success; + return Task.FromResult(isAuth); + } + catch (Exception) + { + return Task.FromResult(false); + } + } + + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs new file mode 100644 index 0000000000..d1b13efe2c --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Authorization requirement for the + /// + public class BackOfficeRequirement : IAuthorizationRequirement + { + public BackOfficeRequirement(bool requireApproval = true) + { + RequireApproval = requireApproval; + } + + public bool RequireApproval { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs new file mode 100644 index 0000000000..265e34fe66 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// The user must have access to all descendant nodes of the content item in order to continue + /// + public class ContentPermissionsPublishBranchHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IEntityService _entityService; + private readonly ContentPermissions _contentPermissions; + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + + public ContentPermissionsPublishBranchHandler( + IEntityService entityService, + ContentPermissions contentPermissions, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor) + { + _entityService = entityService; + _contentPermissions = contentPermissions; + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, ContentPermissionsPublishBranchRequirement requirement, IContent resource) + { + var denied = new List(); + var page = 0; + const int pageSize = 500; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = _entityService.GetPagedDescendants(resource.Id, UmbracoObjectTypes.Document, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check permissions from top to bottom so we can exit + //early if a permission higher up fails + ordering: Ordering.By("path", Direction.Ascending)); + + foreach (var c in descendants) + { + //if this item's path has already been denied or if the user doesn't have access to it, add to the deny list + if (denied.Any(x => c.Path.StartsWith($"{x.Path},")) + || (_contentPermissions.CheckPermissions(c, + _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + requirement.Permission) == ContentPermissions.ContentAccess.Denied)) + { + denied.Add(c); + } + } + } + + return Task.FromResult(denied.Count == 0); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs new file mode 100644 index 0000000000..541f861f0d --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Authorization requirement for + /// + public class ContentPermissionsPublishBranchRequirement : IAuthorizationRequirement + { + public ContentPermissionsPublishBranchRequirement(char permission) + { + Permission = permission; + } + + public char Permission { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs new file mode 100644 index 0000000000..b947014dbc --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.Extensions.Primitives; +using System; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Used to authorize if the user has the correct permission access to the content for the content id specified in a query string + /// + public class ContentPermissionsQueryStringHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IEntityService _entityService; + private readonly ContentPermissions _contentPermissions; + + public ContentPermissionsQueryStringHandler( + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IHttpContextAccessor httpContextAccessor, + IEntityService entityService, + ContentPermissions contentPermissions) + { + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _httpContextAccessor = httpContextAccessor; + _entityService = entityService; + _contentPermissions = contentPermissions; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, ContentPermissionsQueryStringRequirement requirement) + { + int nodeId; + if (requirement.NodeId.HasValue == false) + { + if (!_httpContextAccessor.HttpContext.Request.Query.TryGetValue(requirement.QueryStringName, out var routeVal)) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + else + { + var argument = routeVal.ToString(); + // if the argument is an int, it will parse and can be assigned to nodeId + // if might be a udi, so check that next + // otherwise treat it as a guid - unlikely we ever get here + if (int.TryParse(argument, out int parsedId)) + { + nodeId = parsedId; + } + else if (UdiParser.TryParse(argument, true, out var udi)) + { + nodeId = _entityService.GetId(udi).Result; + } + else + { + Guid.TryParse(argument, out Guid key); + nodeId = _entityService.GetId(key, UmbracoObjectTypes.Document).Result; + } + } + } + else + { + nodeId = requirement.NodeId.Value; + } + + var permissionResult = _contentPermissions.CheckPermissions(nodeId, + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + out IContent contentItem, + new[] { requirement.PermissionToCheck }); + + if (contentItem != null) + { + //store the content item in request cache so it can be resolved in the controller without re-looking it up + _httpContextAccessor.HttpContext.Items[typeof(IContent).ToString()] = contentItem; + } + + return permissionResult switch + { + ContentPermissions.ContentAccess.Denied => Task.FromResult(false), + _ => Task.FromResult(true), + }; + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs new file mode 100644 index 0000000000..2d558c569c --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// An authorization requirement for + /// + public class ContentPermissionsQueryStringRequirement : IAuthorizationRequirement + { + + /// + /// Create an authorization requirement for a specific node id + /// + /// + /// + public ContentPermissionsQueryStringRequirement(int nodeId, char permissionToCheck) + { + NodeId = nodeId; + PermissionToCheck = permissionToCheck; + } + + /// + /// Create an authorization requirement for a node id based on a query string parameter + /// + /// + /// + public ContentPermissionsQueryStringRequirement(char permissionToCheck, string paramName = "id") + { + QueryStringName = paramName; + PermissionToCheck = permissionToCheck; + } + + public int? NodeId { get; } + public string QueryStringName { get; } + public char PermissionToCheck { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs new file mode 100644 index 0000000000..0ec92c7af2 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// The resource used for the + /// + public class ContentPermissionsResource + { + public ContentPermissionsResource(IContent content, char permissionToCheck) + { + PermissionsToCheck = new List { permissionToCheck }; + Content = content; + } + + public ContentPermissionsResource(IContent content, IReadOnlyList permissionToCheck) + { + Content = content; + PermissionsToCheck = permissionToCheck; + } + + public ContentPermissionsResource(IContent content, int nodeId, IReadOnlyList permissionToCheck) + { + Content = content; + NodeId = nodeId; + PermissionsToCheck = permissionToCheck; + } + + public int? NodeId { get; } + public IReadOnlyList PermissionsToCheck { get; } + public IContent Content { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs new file mode 100644 index 0000000000..bcd8ef9add --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Used to authorize if the user has the correct permission access to the content for the specified + /// + public class ContentPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly ContentPermissions _contentPermissions; + + public ContentPermissionsResourceHandler( + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + ContentPermissions contentPermissions) + { + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _contentPermissions = contentPermissions; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, ContentPermissionsResourceRequirement requirement, ContentPermissionsResource resource) + { + var permissionResult = resource.NodeId.HasValue + ? _contentPermissions.CheckPermissions( + resource.NodeId.Value, + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + out IContent _, + resource.PermissionsToCheck) + : _contentPermissions.CheckPermissions( + resource.Content, + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + resource.PermissionsToCheck); + + return Task.FromResult(permissionResult != ContentPermissions.ContentAccess.Denied); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs new file mode 100644 index 0000000000..22b69c93da --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Actions; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// An authorization requirement for + /// + public class ContentPermissionsResourceRequirement : IAuthorizationRequirement + { + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs new file mode 100644 index 0000000000..771f462b8c --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Umbraco.Web.Common.Security; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Ensures the resource cannot be accessed if returns true + /// + public class DenyLocalLoginHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeExternalLoginProviders _externalLogins; + + public DenyLocalLoginHandler(IBackOfficeExternalLoginProviders externalLogins) + { + _externalLogins = externalLogins; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, DenyLocalLoginRequirement requirement) + { + return Task.FromResult(!_externalLogins.HasDenyLocalLogin()); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs new file mode 100644 index 0000000000..a4f3a7e306 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Marker requirement for the + /// + public class DenyLocalLoginRequirement : IAuthorizationRequirement + { + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs new file mode 100644 index 0000000000..a8d3b72918 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using System; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.BackOffice.Authorization +{ + public class MediaPermissionsQueryStringHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly MediaPermissions _mediaPermissions; + private readonly IEntityService _entityService; + + public MediaPermissionsQueryStringHandler( + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IHttpContextAccessor httpContextAccessor, + IEntityService entityService, + MediaPermissions mediaPermissions) + { + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _httpContextAccessor = httpContextAccessor; + _entityService = entityService; + _mediaPermissions = mediaPermissions; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, MediaPermissionsQueryStringRequirement requirement) + { + if (!_httpContextAccessor.HttpContext.Request.Query.TryGetValue(requirement.QueryStringName, out var routeVal)) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + + int nodeId; + + var argument = routeVal.ToString(); + // if the argument is an int, it will parse and can be assigned to nodeId + // if might be a udi, so check that next + // otherwise treat it as a guid - unlikely we ever get here + if (int.TryParse(argument, out int parsedId)) + { + nodeId = parsedId; + } + else if (UdiParser.TryParse(argument, true, out var udi)) + { + nodeId = _entityService.GetId(udi).Result; + } + else + { + Guid.TryParse(argument, out Guid key); + nodeId = _entityService.GetId(key, UmbracoObjectTypes.Document).Result; + } + + var permissionResult = _mediaPermissions.CheckPermissions( + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + nodeId, + out var mediaItem); + + if (mediaItem != null) + { + //store the content item in request cache so it can be resolved in the controller without re-looking it up + _httpContextAccessor.HttpContext.Items[typeof(IMedia).ToString()] = mediaItem; + } + + return permissionResult switch + { + MediaPermissions.MediaAccess.Denied => Task.FromResult(false), + _ => Task.FromResult(true), + }; + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs new file mode 100644 index 0000000000..c7b62a570f --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + public class MediaPermissionsQueryStringRequirement : IAuthorizationRequirement + { + public MediaPermissionsQueryStringRequirement(string paramName) + { + QueryStringName = paramName; + } + + public string QueryStringName { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs new file mode 100644 index 0000000000..5b1ed92f5f --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs @@ -0,0 +1,20 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Web.BackOffice.Authorization +{ + public class MediaPermissionsResource + { + public MediaPermissionsResource(IMedia media) + { + Media = media; + } + + public MediaPermissionsResource(int nodeId) + { + NodeId = nodeId; + } + + public int? NodeId { get; } + public IMedia Media { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs new file mode 100644 index 0000000000..6c5280a19c --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.Security; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Used to authorize if the user has the correct permission access to the content for the specified + /// + public class MediaPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly MediaPermissions _mediaPermissions; + + public MediaPermissionsResourceHandler( + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + MediaPermissions mediaPermissions) + { + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _mediaPermissions = mediaPermissions; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, MediaPermissionsResourceRequirement requirement, MediaPermissionsResource resource) + { + var permissionResult = resource.NodeId.HasValue + ? _mediaPermissions.CheckPermissions( + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, + resource.NodeId.Value, + out _) + : _mediaPermissions.CheckPermissions( + resource.Media, + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + + return Task.FromResult(permissionResult != MediaPermissions.MediaAccess.Denied); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs new file mode 100644 index 0000000000..3087e4b258 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// An authorization requirement for + /// + public class MediaPermissionsResourceRequirement : IAuthorizationRequirement + { + + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs new file mode 100644 index 0000000000..98baacf5ee --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what + /// + /// + /// + /// aspnetcore Authz handlers are not required to satisfy the requirement and generally don't explicitly call Fail when the requirement + /// isn't satisfied, however in many simple cases explicitly calling Succeed or Fail is what we want which is what this class is used for. + /// + public abstract class MustSatisfyRequirementAuthorizationHandler : AuthorizationHandler where T : IAuthorizationRequirement + { + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement) + { + var isAuth = await IsAuthorized(context, requirement); + if (isAuth) + { + context.Succeed(requirement); + } + else + { + context.Fail(); + } + } + + /// + /// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met + /// + /// + /// + /// + protected abstract Task IsAuthorized(AuthorizationHandlerContext context, T requirement); + } + + /// + /// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what + /// + /// + /// + /// + /// aspnetcore Authz handlers are not required to satisfy the requirement and generally don't explicitly call Fail when the requirement + /// isn't satisfied, however in many simple cases explicitly calling Succeed or Fail is what we want which is what this class is used for. + /// + public abstract class MustSatisfyRequirementAuthorizationHandler : AuthorizationHandler where T : IAuthorizationRequirement + { + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement, TResource resource) + { + var isAuth = await IsAuthorized(context, requirement, resource); + if (isAuth) + { + context.Succeed(requirement); + } + else + { + context.Fail(); + } + } + + /// + /// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met + /// + /// + /// + /// + protected abstract Task IsAuthorized(AuthorizationHandlerContext context, T requirement, TResource resource); + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs new file mode 100644 index 0000000000..ffa6db220f --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Authorization; +using System.Linq; +using System.Threading.Tasks; +using Umbraco.Core.Security; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Ensures that the current user has access to the section + /// + /// + /// The user only needs access to one of the sections specified, not all of the sections. + /// + public class SectionHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + + public SectionHandler(IBackOfficeSecurityAccessor backofficeSecurityAccessor) + { + _backofficeSecurityAccessor = backofficeSecurityAccessor; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, SectionRequirement requirement) + { + var authorized = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null + && requirement.SectionAliases.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess( + app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser)); + + return Task.FromResult(authorized); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs new file mode 100644 index 0000000000..033eccdd18 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Authorization requirements for + /// + public class SectionRequirement : IAuthorizationRequirement + { + /// + /// The aliases for sections that the user will need access to + /// + public IReadOnlyCollection SectionAliases { get; } + + public SectionRequirement(params string[] aliases) => SectionAliases = aliases; + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs new file mode 100644 index 0000000000..f151247850 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Linq; +using Umbraco.Core; +using System.Threading.Tasks; +using Umbraco.Core.Security; +using Umbraco.Web.Services; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Ensures that the current user has access to the section for which the specified tree(s) belongs + /// + /// + /// This would allow a tree to be moved between sections. + /// The user only needs access to one of the trees specified, not all of the trees. + /// + public class TreeHandler : MustSatisfyRequirementAuthorizationHandler + { + + private readonly ITreeService _treeService; + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + + /// + /// Constructor to set authorization to be based on a tree alias for which application security will be applied + /// + /// + /// + /// + /// If the user has access to the application that the treeAlias is specified in, they will be authorized. + /// Multiple trees may be specified. + /// + public TreeHandler(ITreeService treeService, IBackOfficeSecurityAccessor backofficeSecurityAccessor) + { + _treeService = treeService ?? throw new ArgumentNullException(nameof(treeService)); + _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, TreeRequirement requirement) + { + var apps = requirement.TreeAliases.Select(x => _treeService + .GetByAlias(x)) + .WhereNotNull() + .Select(x => x.SectionAlias) + .Distinct() + .ToArray(); + + var isAuth = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null + && apps.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess( + app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser)); + + return Task.FromResult(isAuth); + } + + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs new file mode 100644 index 0000000000..7261cee51b --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Authorization requirements for + /// + public class TreeRequirement : IAuthorizationRequirement + { + /// + /// The aliases for trees that the user will need access to + /// + public IReadOnlyCollection TreeAliases { get; } + + public TreeRequirement(params string[] aliases) => TreeAliases = aliases; + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs new file mode 100644 index 0000000000..fc1db9699b --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs @@ -0,0 +1,73 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using System.Linq; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web.BackOffice.Controllers; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Authorizes that the current user has access to the user group Id in the request + /// + public class UserGroupHandler : MustSatisfyRequirementAuthorizationHandler + { + private readonly IHttpContextAccessor _httpContextAcessor; + private readonly IUserService _userService; + private readonly IContentService _contentService; + private readonly IMediaService _mediaService; + private readonly IEntityService _entityService; + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + + public UserGroupHandler(IHttpContextAccessor httpContextAcessor, + IUserService userService, + IContentService contentService, + IMediaService mediaService, + IEntityService entityService, + IBackOfficeSecurityAccessor backofficeSecurityAccessor) + { + _httpContextAcessor = httpContextAcessor; + _userService = userService; + _contentService = contentService; + _mediaService = mediaService; + _entityService = entityService; + _backofficeSecurityAccessor = backofficeSecurityAccessor; + } + + protected override Task IsAuthorized(AuthorizationHandlerContext context, UserGroupRequirement requirement) + { + var currentUser = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + + var queryString = _httpContextAcessor.HttpContext?.Request.Query; + if (queryString == null) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + + var ids = queryString.Where(x => x.Key == requirement.QueryStringName).ToArray(); + if (ids.Length == 0) + { + // must succeed this requirement since we cannot process it + return Task.FromResult(true); + } + + var intIds = ids + .Select(x => x.Value.ToString()) + .Select(x => x.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray(); + + var authHelper = new UserGroupEditorAuthorizationHelper( + _userService, + _contentService, + _mediaService, + _entityService); + + var isAuth = authHelper.AuthorizeGroupAccess(currentUser, intIds); + + return Task.FromResult(isAuth.Success); + } + + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs new file mode 100644 index 0000000000..aae5733d96 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Authorization requirement for the + /// + public class UserGroupRequirement : IAuthorizationRequirement + { + public UserGroupRequirement(string queryStringName = "id") + { + QueryStringName = queryStringName; + } + + public string QueryStringName { get; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 76288670aa..7d0e99d0e1 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -30,10 +30,11 @@ using Umbraco.Web.Common.Controllers; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.Common.Security; -using Umbraco.Web.Editors.Filters; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -114,8 +115,8 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog /// - /// - [UmbracoBackOfficeAuthorize] + /// + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] public IDictionary GetPasswordConfig(int userId) { return _passwordConfiguration.GetConfiguration(userId != _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -131,7 +132,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// This will also update the security stamp for the user so it can only be used once /// [ValidateAngularAntiForgeryToken] - [DenyLocalLoginAuthorization] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public async Task> PostVerifyInvite([FromQuery] int id, [FromQuery] string token) { if (string.IsNullOrWhiteSpace(token)) @@ -161,7 +162,7 @@ namespace Umbraco.Web.BackOffice.Controllers return _umbracoMapper.Map(user); } - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [ValidateAngularAntiForgeryToken] public async Task PostUnLinkLogin(UnLinkLoginModel unlinkLoginModel) { @@ -257,7 +258,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf cookies are only session /// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies. /// - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [SetAngularAntiForgeryTokens] [CheckIfUserTicketDataIsStale] public UserDetail GetCurrentUser() @@ -279,9 +280,9 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't require them to be approved /// - [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: false, requireApproval: false)] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)] [SetAngularAntiForgeryTokens] - [DenyLocalLoginAuthorization] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public ActionResult GetCurrentInvitedUser() { var user = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; @@ -305,7 +306,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [SetAngularAntiForgeryTokens] - [DenyLocalLoginAuthorization] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public async Task> PostLogin(LoginModel loginModel) { // Sign the user in with username/password, this also gives a chance for developers to @@ -355,7 +356,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [SetAngularAntiForgeryTokens] - [DenyLocalLoginAuthorization] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] public async Task PostRequestPasswordReset(RequestPasswordResetModel model) { // If this feature is switched off in configuration the UI will be amended to not make the request to reset password available. diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs index bbb8195aa0..720a0f154d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs @@ -21,7 +21,8 @@ namespace Umbraco.Web.BackOffice.Controllers public BackOfficeAssetsController(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, IOptions globalSettings) { - _jsLibFileSystem = new PhysicalFileSystem(ioHelper, hostingEnvironment, loggerFactory.CreateLogger(), globalSettings.Value.UmbracoPath + Path.DirectorySeparatorChar + "lib"); + var path = globalSettings.Value.UmbracoPath + Path.DirectorySeparatorChar + "lib"; + _jsLibFileSystem = new PhysicalFileSystem(ioHelper, hostingEnvironment, loggerFactory.CreateLogger(), hostingEnvironment.MapPathWebRoot(path), hostingEnvironment.ToAbsolute(path)); } [HttpGet] diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 7704dcf785..fa9ca98707 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -34,6 +34,8 @@ using System.Security.Claims; using Microsoft.AspNetCore.Http; using Umbraco.Web.BackOffice.Security; using Umbraco.Web.Common.ActionsResults; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -235,7 +237,7 @@ namespace Umbraco.Web.BackOffice.Controllers return nestedDictionary; } - [UmbracoBackOfficeAuthorize(Order = 0)] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [HttpGet] public IEnumerable GetGridConfig() { @@ -246,7 +248,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the JavaScript object representing the static server variables javascript object /// /// - [UmbracoBackOfficeAuthorize(Order = 0)] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [MinifyJavaScriptResult(Order = 1)] public async Task ServerVariables() { @@ -281,7 +283,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [HttpPost] public ActionResult LinkLogin(string provider) { @@ -317,7 +319,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Callback path when the user initiates a link login request from the back office to the external provider from the action /// - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [HttpGet] public async Task ExternalLinkLoginCallback() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index 470e62c2c2..07f20e1e03 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -146,7 +146,7 @@ namespace Umbraco.Web.BackOffice.Controllers { // TODO: Add 'umbracoApiControllerBaseUrl' which people can use in JS // to prepend their URL. We could then also use this in our own resources instead of - // having each url defined here explicitly - we can do that in v8! for now + // having each URL defined here explicitly - we can do that in v8! for now // for umbraco services we'll stick to explicitly defining the endpoints. {"externalLoginsUrl", _linkGenerator.GetPathByAction(nameof(BackOfficeController.ExternalLogin), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })}, diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs index 8eddd0775d..d1feaf11e9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Umbraco.Core; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -18,6 +20,7 @@ using Umbraco.Extensions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.ActionsResults; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; using Stylesheet = Umbraco.Core.Models.Stylesheet; @@ -29,10 +32,10 @@ namespace Umbraco.Web.BackOffice.Controllers // ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/ [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] //[PrefixlessBodyModelValidator] - [UmbracoApplicationAuthorize(Constants.Applications.Settings)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class CodeFileController : BackOfficeNotificationsController { - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IFileSystems _fileSystems; private readonly IFileService _fileService; private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; @@ -43,7 +46,7 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly GlobalSettings _globalSettings; public CodeFileController( - IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, IFileSystems fileSystems, IFileService fileService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, @@ -52,7 +55,7 @@ namespace Umbraco.Web.BackOffice.Controllers IShortStringHelper shortStringHelper, IOptions globalSettings) { - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _fileSystems = fileSystems; _fileService = fileService; _backOfficeSecurityAccessor = backOfficeSecurityAccessor; @@ -164,7 +167,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Used to get a specific file from disk via the FileService /// /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' or 'stylesheets' - /// The filename or urlencoded path of the file to open + /// The filename or URL encoded path of the file to open /// The file and its contents from the virtualPath public ActionResult GetByPath(string type, string virtualPath) { @@ -314,7 +317,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Used to delete a specific file from disk via the FileService /// /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' or 'stylesheets' - /// The filename or urlencoded path of the file to delete + /// The filename or URL encoded path of the file to delete /// Will return a simple 200 if file deletion succeeds [HttpDelete] [HttpPost] @@ -657,7 +660,7 @@ namespace Umbraco.Web.BackOffice.Controllers private bool IsDirectory(string virtualPath, string systemDirectory) { - var path = _ioHelper.MapPath(systemDirectory + "/" + virtualPath); + var path = _hostingEnvironment.MapPathContentRoot(systemDirectory + "/" + virtualPath); var dirInfo = new DirectoryInfo(path); return dirInfo.Attributes == FileAttributes.Directory; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index d29c82e2ea..10eefcc47a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -37,19 +37,18 @@ using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.Models.Mapping; - +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.BackOffice.Authorization; +using System.Threading.Tasks; namespace Umbraco.Web.BackOffice.Controllers { /// /// The API controller used for editing content /// - /// - /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting - /// access to ALL of the methods on this controller will need access to the content application. - /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Content)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)] public class ContentController : ContentControllerBase { private readonly PropertyEditorCollection _propertyEditors; @@ -71,6 +70,7 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly ActionCollection _actionCollection; private readonly IMemberGroupService _memberGroupService; private readonly ISqlContext _sqlContext; + private readonly IAuthorizationService _authorizationService; private readonly Lazy> _allLangs; private readonly ILogger _logger; @@ -100,7 +100,8 @@ namespace Umbraco.Web.BackOffice.Controllers ActionCollection actionCollection, IMemberGroupService memberGroupService, ISqlContext sqlContext, - IJsonSerializer serializer) + IJsonSerializer serializer, + IAuthorizationService authorizationService) : base(cultureDictionary, loggerFactory, shortStringHelper, eventMessages, localizedTextService, serializer) { _propertyEditors = propertyEditors; @@ -122,6 +123,7 @@ namespace Umbraco.Web.BackOffice.Controllers _actionCollection = actionCollection; _memberGroupService = memberGroupService; _sqlContext = sqlContext; + _authorizationService = authorizationService; _logger = loggerFactory.CreateLogger(); _allLangs = new Lazy>(() => _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); @@ -134,7 +136,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpGet] // TODO: We need to move this since we are going to delete OverrideAuthorization - [UmbracoBackOfficeAuthorize, OverrideAuthorization] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess), OverrideAuthorization] public bool AllowsCultureVariation() { var contentTypes = _contentTypeService.GetAll(); @@ -161,16 +163,22 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission check is done for letter 'R' which is for which the user must have access to update /// - [EnsureUserPermissionForContent("saveModel.ContentId", 'R')] - public ActionResult> PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel) - { - if (saveModel.ContentId <= 0) return NotFound(); + public async Task>> PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel) + { if (saveModel.ContentId <= 0) return NotFound(); // TODO: Should non-admins be allowed to set granular permissions? var content = _contentService.GetById(saveModel.ContentId); if (content == null) return NotFound(); + // Authorize... + var resource = new ContentPermissionsResource(content, ActionRights.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, content, AuthorizationPolicies.ContentPermissionByResource); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + //current permissions explicitly assigned to this content item var contentPermissions = _contentService.GetPermissions(content) .ToDictionary(x => x.UserGroupId, x => x); @@ -223,7 +231,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission check is done for letter 'R' which is for which the user must have access to view /// - [EnsureUserPermissionForContent("contentId", 'R')] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionAdministrationById)] public ActionResult> GetDetailedPermissions(int contentId) { if (contentId <= 0) return NotFound(); @@ -339,8 +347,8 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForContent("id")] + [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(int id) { @@ -360,7 +368,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForContent("id")] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(Guid id) { @@ -381,7 +389,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForContent("id")] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(Udi id) { @@ -630,9 +638,9 @@ namespace Umbraco.Web.BackOffice.Controllers /// [FileUploadCleanupFilter] [ContentSaveValidation] - public ContentItemDisplay PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) + public async Task PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) { - var contentItemDisplay = PostSaveInternal(contentItem, + var contentItemDisplay = await PostSaveInternal(contentItem, content => { EnsureUniqueName(content.Name, content, "Name"); @@ -658,9 +666,9 @@ namespace Umbraco.Web.BackOffice.Controllers [FileUploadCleanupFilter] [ContentSaveValidation] [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - public ContentItemDisplay PostSave([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem) + public async Task PostSave([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem) { - var contentItemDisplay = PostSaveInternal( + var contentItemDisplay = await PostSaveInternal( contentItem, content => _contentService.Save(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id), MapToDisplay); @@ -668,7 +676,7 @@ namespace Umbraco.Web.BackOffice.Controllers return contentItemDisplay; } - private ContentItemDisplay PostSaveInternal(ContentItemSave contentItem, Func saveMethod, Func mapToDisplay) + private async Task PostSaveInternal(ContentItemSave contentItem, Func saveMethod, Func mapToDisplay) { //Recent versions of IE/Edge may send in the full client side file path instead of just the file name. //To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all @@ -803,7 +811,7 @@ namespace Umbraco.Web.BackOffice.Controllers case ContentSaveAction.PublishWithDescendants: case ContentSaveAction.PublishWithDescendantsNew: { - if (!ValidatePublishBranchPermissions(contentItem, out var noAccess)) + if (!await ValidatePublishBranchPermissionsAsync(contentItem)) { globalNotifications.AddErrorNotification( _localizedTextService.Localize("publish"), @@ -819,7 +827,7 @@ namespace Umbraco.Web.BackOffice.Controllers case ContentSaveAction.PublishWithDescendantsForce: case ContentSaveAction.PublishWithDescendantsForceNew: { - if (!ValidatePublishBranchPermissions(contentItem, out var noAccess)) + if (!await ValidatePublishBranchPermissionsAsync(contentItem)) { globalNotifications.AddErrorNotification( _localizedTextService.Localize("publish"), @@ -1190,33 +1198,12 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - private bool ValidatePublishBranchPermissions(ContentItemSave contentItem, out IReadOnlyList noAccess) + private async Task ValidatePublishBranchPermissionsAsync(ContentItemSave contentItem) { - var denied = new List(); - var page = 0; - const int pageSize = 500; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = _entityService.GetPagedDescendants(contentItem.Id, UmbracoObjectTypes.Document, page++, pageSize, out total, - //order by shallowest to deepest, this allows us to check permissions from top to bottom so we can exit - //early if a permission higher up fails - ordering: Ordering.By("path", Direction.Ascending)); - - foreach (var c in descendants) - { - //if this item's path has already been denied or if the user doesn't have access to it, add to the deny list - if (denied.Any(x => c.Path.StartsWith($"{x.Path},")) - || (ContentPermissionsHelper.CheckPermissions(c, - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, _userService, _entityService, - ActionPublish.ActionLetter) == ContentPermissionsHelper.ContentAccess.Denied)) - { - denied.Add(c); - } - } - } - noAccess = denied; - return denied.Count == 0; + // Authorize... + var requirement = new ContentPermissionsPublishBranchRequirement(ActionPublish.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, contentItem.PersistedContent, requirement); + return authorizationResult.Succeeded; } private IEnumerable PublishBranchInternal(ContentItemSave contentItem, bool force, string cultureForInvariantErrors, @@ -1494,8 +1481,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The EnsureUserPermissionForContent attribute will deny access to this method if the current user /// does not have Publish access to this node. /// - /// - [EnsureUserPermissionForContent("id", 'U')] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionPublishById)] public IActionResult PostPublishById(int id) { var foundContent = GetObjectFromRequest(() => _contentService.GetById(id)); @@ -1542,7 +1528,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The CanAccessContentAuthorize attribute will deny access to this method if the current user /// does not have Delete access to this node. /// - [EnsureUserPermissionForContent("id", ActionDelete.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionDeleteById)] [HttpDelete] [HttpPost] public IActionResult DeleteById(int id) @@ -1588,7 +1574,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [EnsureUserPermissionForContent(Constants.System.RecycleBinContent, ActionDelete.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionEmptyRecycleBin)] public IActionResult EmptyRecycleBin() { _contentService.EmptyRecycleBin(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId)); @@ -1601,8 +1587,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForContent("sorted.ParentId", 'S')] - public IActionResult PostSort(ContentSortOrder sorted) + public async Task PostSort(ContentSortOrder sorted) { if (sorted == null) { @@ -1615,12 +1600,18 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(); } + // Authorize... + var resource = new ContentPermissionsResource(_contentService.GetById(sorted.ParentId), ActionSort.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, resource, AuthorizationPolicies.ContentPermissionByResource); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + try { - var contentService = _contentService; - // Save content with new sort order and update content xml in db accordingly - var sortResult = contentService.Sort(sorted.IdSortOrder, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var sortResult = _contentService.Sort(sorted.IdSortOrder, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); if (!sortResult.Success) { _logger.LogWarning("Content sorting failed, this was probably caused by an event being cancelled"); @@ -1642,9 +1633,16 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForContent("move.ParentId", 'M')] - public IActionResult PostMove(MoveOrCopy move) + public async Task PostMove(MoveOrCopy move) { + // Authorize... + var resource = new ContentPermissionsResource(_contentService.GetById(move.ParentId), ActionMove.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, resource, AuthorizationPolicies.ContentPermissionByResource); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + var toMove = ValidateMoveOrCopy(move); _contentService.Move(toMove, move.ParentId, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -1657,9 +1655,16 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForContent("copy.ParentId", 'C')] - public IActionResult PostCopy(MoveOrCopy copy) + public async Task PostCopy(MoveOrCopy copy) { + // Authorize... + var resource = new ContentPermissionsResource(_contentService.GetById(copy.ParentId), ActionCopy.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, resource, AuthorizationPolicies.ContentPermissionByResource); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + var toCopy = ValidateMoveOrCopy(copy); var c = _contentService.Copy(toCopy, copy.ParentId, copy.RelateToOriginal, copy.Recursive, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -1672,14 +1677,23 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// The content and variants to unpublish /// - [EnsureUserPermissionForContent("model.Id", 'Z')] [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - public ContentItemDisplay PostUnpublish(UnpublishContent model) + public async Task> PostUnpublish(UnpublishContent model) { - var foundContent = GetObjectFromRequest(() => _contentService.GetById(model.Id)); + var foundContent = _contentService.GetById(model.Id); if (foundContent == null) + { HandleContentNotFound(model.Id); + } + + // Authorize... + var resource = new ContentPermissionsResource(foundContent, ActionUnpublish.ActionLetter); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, resource, AuthorizationPolicies.ContentPermissionByResource); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } var languageCount = _allLangs.Value.Count(); if (model.Cultures.Length == 0 || model.Cultures.Length == languageCount) @@ -2270,7 +2284,7 @@ namespace Umbraco.Web.BackOffice.Controllers return display; } - [EnsureUserPermissionForContent("contentId", ActionBrowse.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] public ActionResult> GetNotificationOptions(int contentId) { var notifications = new List(); @@ -2362,7 +2376,7 @@ namespace Umbraco.Web.BackOffice.Controllers : content.Variants.FirstOrDefault(x => x.Language.IsoCode == culture); } - [EnsureUserPermissionForContent("contentId", ActionRollback.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionRollbackById)] [HttpPost] public IActionResult PostRollbackContent(int contentId, int versionId, string culture = "*") { @@ -2394,7 +2408,7 @@ namespace Umbraco.Web.BackOffice.Controllers throw HttpResponseException.CreateValidationErrorResponse(notificationModel); } - [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionProtectById)] [HttpGet] public IActionResult GetPublicAccess(int contentId) { @@ -2443,7 +2457,7 @@ namespace Umbraco.Web.BackOffice.Controllers } // set up public access using role based access - [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionProtectById)] [HttpPost] public IActionResult PostPublicAccess(int contentId, [FromQuery(Name = "groups[]")]string[] groups, [FromQuery(Name = "usernames[]")]string[] usernames, int loginPageId, int errorPageId) { @@ -2510,7 +2524,7 @@ namespace Umbraco.Web.BackOffice.Controllers : Problem(); } - [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [Authorize(Policy = AuthorizationPolicies.ContentPermissionProtectById)] [HttpPost] public IActionResult RemovePublicAccess(int contentId) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index 8a5fc47a58..8d6adee475 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Packaging; using Umbraco.Core.PropertyEditors; @@ -33,6 +32,8 @@ using ContentType = Umbraco.Core.Models.ContentType; using Umbraco.Core.Configuration.Models; using Microsoft.Extensions.Options; using Umbraco.Core.Serialization; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -49,7 +50,6 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly GlobalSettings _globalSettings; private readonly PropertyEditorCollection _propertyEditors; private readonly IScopeProvider _scopeProvider; - private readonly IIOHelper _ioHelper; private readonly IContentTypeService _contentTypeService; private readonly UmbracoMapper _umbracoMapper; private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; @@ -78,7 +78,6 @@ namespace Umbraco.Web.BackOffice.Controllers IOptions globalSettings, PropertyEditorCollection propertyEditors, IScopeProvider scopeProvider, - IIOHelper ioHelper, IBackOfficeSecurityAccessor backofficeSecurityAccessor, IDataTypeService dataTypeService, IShortStringHelper shortStringHelper, @@ -105,7 +104,6 @@ namespace Umbraco.Web.BackOffice.Controllers _globalSettings = globalSettings.Value; _propertyEditors = propertyEditors; _scopeProvider = scopeProvider; - _ioHelper = ioHelper; _contentTypeService = contentTypeService; _umbracoMapper = umbracoMapper; _backofficeSecurityAccessor = backofficeSecurityAccessor; @@ -124,14 +122,14 @@ namespace Umbraco.Web.BackOffice.Controllers _jsonSerializer = jsonSerializer; } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public int GetCount() { return _contentTypeService.Count(); } [HttpGet] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public bool HasContentNodes(int id) { return _contentTypeService.HasContentNodes(id); @@ -143,7 +141,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public DocumentTypeDisplay GetById(int id) { var ct = _contentTypeService.Get(id); @@ -162,7 +160,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public DocumentTypeDisplay GetById(Guid id) { var contentType = _contentTypeService.Get(id); @@ -181,7 +179,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public DocumentTypeDisplay GetById(Udi id) { var guidUdi = id as GuidUdi; @@ -205,7 +203,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult DeleteById(int id) { var foundType = _contentTypeService.Get(id); @@ -222,10 +220,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Gets all user defined properties. /// /// - [UmbracoTreeAuthorize( - Constants.Trees.DocumentTypes, Constants.Trees.Content, - Constants.Trees.MediaTypes, Constants.Trees.Media, - Constants.Trees.MemberTypes, Constants.Trees.Members)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)] public IEnumerable GetAllPropertyTypeAliases() { return _contentTypeService.GetAllPropertyTypeAliases(); @@ -235,10 +230,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Gets all the standard fields. /// /// - [UmbracoTreeAuthorize( - Constants.Trees.DocumentTypes, Constants.Trees.Content, - Constants.Trees.MediaTypes, Constants.Trees.Media, - Constants.Trees.MemberTypes, Constants.Trees.Members)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)] public IEnumerable GetAllStandardFields() { string[] preValuesSource = { "createDate", "creatorName", "level", "nodeType", "nodeTypeAlias", "pageID", "pageName", "parentID", "path", "template", "updateDate", "writerID", "writerName" }; @@ -252,7 +244,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) { var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement) @@ -270,7 +262,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) { var result = PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType) @@ -281,10 +273,7 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(result); } - [UmbracoTreeAuthorize( - Constants.Trees.DocumentTypes, Constants.Trees.Content, - Constants.Trees.MediaTypes, Constants.Trees.Media, - Constants.Trees.MemberTypes, Constants.Trees.Members)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)] public ContentPropertyDisplay GetPropertyTypeScaffold(int id) { var dataTypeDiff = _dataTypeService.GetDataType(id); @@ -313,7 +302,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult DeleteContainer(int id) { _contentTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -321,7 +310,7 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(); } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostCreateContainer(int parentId, string name) { var result = _contentTypeService.CreateContainer(parentId, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -331,7 +320,7 @@ namespace Umbraco.Web.BackOffice.Controllers : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostRenameContainer(int id, string name) { var result = _contentTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -341,7 +330,7 @@ namespace Umbraco.Web.BackOffice.Controllers : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public CreatedContentTypeCollectionResult PostCreateCollection(int parentId, string collectionName, bool collectionCreateTemplate, string collectionItemName, bool collectionItemCreateTemplate, string collectionIcon, string collectionItemIcon) { // create item doctype @@ -398,7 +387,7 @@ namespace Umbraco.Web.BackOffice.Controllers }; } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public DocumentTypeDisplay PostSave(DocumentTypeSave contentTypeSave) { //Before we send this model into this saving/mapping pipeline, we need to do some cleanup on variations. @@ -451,7 +440,7 @@ namespace Umbraco.Web.BackOffice.Controllers return display; } - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public ActionResult PostCreateDefaultTemplate(int id) { var contentType = _contentTypeService.Get(id); @@ -492,7 +481,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public DocumentTypeDisplay GetEmpty(int parentId) { IContentType ct; @@ -514,7 +503,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Returns all content type objects /// - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IEnumerable GetAll() { var types = _contentTypeService.GetAll(); @@ -532,7 +521,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the allowed child content type objects for the content item id passed in /// /// - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes, Constants.Trees.Content)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes)] public IEnumerable GetAllowedChildren(int contentId) { if (contentId == Constants.System.RecycleBinContent) @@ -587,7 +576,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostMove(MoveOrCopy move) { return PerformMove( @@ -601,7 +590,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostCopy(MoveOrCopy copy) { return PerformCopy( @@ -611,7 +600,7 @@ namespace Umbraco.Web.BackOffice.Controllers } [HttpGet] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult Export(int id) { var contentType = _contentTypeService.Get(id); @@ -628,10 +617,10 @@ namespace Umbraco.Web.BackOffice.Controllers } [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult Import(string file) { - var filePath = Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Data), file); + var filePath = Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data), file); if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(filePath)) { return NotFound(); @@ -661,7 +650,7 @@ namespace Umbraco.Web.BackOffice.Controllers } [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public ActionResult Upload(List file) { var model = new ContentTypeImportModel(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 2005d42b79..7c984e901e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -27,6 +27,8 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -170,7 +172,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// This only works when the user is logged in (partially) /// - [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: false, requireApproval : true)] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] // TODO: Why is this necessary? This inherits from UmbracoAuthorizedApiController public async Task PostSetInvitedUserPassword([FromBody]string newPassword) { var user = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0).ToString()); @@ -236,7 +238,8 @@ namespace Umbraco.Web.BackOffice.Controllers throw HttpResponseException.CreateValidationErrorResponse(ModelState); } - [UmbracoBackOfficeAuthorize] + // TODO: Why is this necessary? This inherits from UmbracoAuthorizedApiController + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index 41e5cfb589..d96708b11d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -21,7 +21,8 @@ using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Controllers; using Umbraco.Web.Common.Filters; -using Umbraco.Web.WebApi.Filters; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -30,7 +31,7 @@ namespace Umbraco.Web.BackOffice.Controllers [ValidationFilter] [AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions [IsBackOffice] - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] public class DashboardController : UmbracoApiController { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs index 24c5b1cb24..0ffe64e251 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Net.Mime; using System.Text; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Umbraco.Core; @@ -17,6 +18,7 @@ using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; @@ -32,7 +34,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Content Types, Member Types or Media Types ... and of course to Data Types /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes)] public class DataTypeController : BackOfficeNotificationsController { private readonly PropertyEditorCollection _propertyEditors; @@ -415,8 +417,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Packages)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] public IEnumerable GetAll() { return _dataTypeService @@ -431,8 +432,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// - [UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Packages)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] public IDictionary> GetGroupedDataTypes() { var dataTypes = _dataTypeService @@ -463,9 +463,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// - [UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Packages)] - + [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] public IDictionary> GetGroupedPropertyEditors() { var datatypes = new List(); @@ -496,9 +494,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// - [UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Packages)] - + [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] public IEnumerable GetAllPropertyEditors() { return _propertyEditorCollection diff --git a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs index 785264d816..c7f86e12a1 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs @@ -18,6 +18,8 @@ using Umbraco.Web.Security; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Configuration.Models; using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -30,7 +32,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Dictionary /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.Dictionary)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDictionary)] public class DictionaryController : BackOfficeNotificationsController { private readonly ILogger _logger; diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 28c077a63c..b2a6300cc9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -244,7 +244,7 @@ namespace Umbraco.Web.BackOffice.Controllers } /// - /// Gets the url of an entity + /// Gets the URL of an entity /// /// UDI of the entity to fetch URL for /// The culture to fetch the URL for @@ -274,7 +274,7 @@ namespace Umbraco.Web.BackOffice.Controllers } /// - /// Gets the url of an entity + /// Gets the URL of an entity /// /// Int id of the entity to fetch URL for /// The type of entity such as Document, Media, Member diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs index 66f8b6d7e0..21b205de0f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Umbraco.Core; @@ -12,6 +13,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Editors; using Language = Umbraco.Web.Models.ContentEditing.Language; @@ -80,7 +82,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Deletes a language with a given ID /// - [UmbracoTreeAuthorize(Constants.Trees.Languages)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)] [HttpDelete] [HttpPost] public IActionResult DeleteLanguage(int id) @@ -109,7 +111,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Creates or saves a language /// - [UmbracoTreeAuthorize(Constants.Trees.Languages)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)] [HttpPost] public Language SaveLanguage(Language language) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs index 4d816624de..acdd9721e4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Authorization; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core; @@ -12,6 +13,7 @@ using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; @@ -52,7 +54,7 @@ namespace Umbraco.Web.BackOffice.Controllers _sqlContext = sqlContext ?? throw new ArgumentNullException(nameof(sqlContext)); } - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessContentOrMedia)] public PagedResult GetPagedEntityLog(int id, int pageNumber = 1, int pageSize = 10, diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs index a25d0aaa52..5165d3a092 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Logging.Viewer; using Umbraco.Core.Models; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Editors; namespace Umbraco.Web.BackOffice.Controllers { @@ -14,6 +15,8 @@ namespace Umbraco.Web.BackOffice.Controllers /// Backoffice controller supporting the dashboard for viewing logs with some simple graphs & filtering /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] + public class LogViewerController : UmbracoAuthorizedJsonController { private readonly ILogViewer _logViewer; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs index e4ef90b30c..3ca89fa5ff 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs @@ -19,6 +19,8 @@ using Umbraco.Web.Security; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Security; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -27,7 +29,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The API controller used for editing dictionary items /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.Macros)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMacros)] public class MacrosController : BackOfficeNotificationsController { private readonly ParameterEditorCollection _parameterEditorCollection; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 0e97ed84af..409967fb67 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -40,6 +40,9 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.BackOffice.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -48,7 +51,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// access to ALL of the methods on this controller will need access to the media application. /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Media)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessMedia)] public class MediaController : ContentControllerBase { private readonly IShortStringHelper _shortStringHelper; @@ -65,6 +68,7 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IRelationService _relationService; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IJsonSerializer _serializer; + private readonly IAuthorizationService _authorizationService; private readonly ILogger _logger; public MediaController( @@ -87,7 +91,8 @@ namespace Umbraco.Web.BackOffice.Controllers IMediaFileSystem mediaFileSystem, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, - IJsonSerializer serializer) + IJsonSerializer serializer, + IAuthorizationService authorizationService) : base(cultureDictionary, loggerFactory, shortStringHelper, eventMessages, localizedTextService, serializer) { _shortStringHelper = shortStringHelper; @@ -108,6 +113,7 @@ namespace Umbraco.Web.BackOffice.Controllers _logger = loggerFactory.CreateLogger(); _imageUrlGenerator = imageUrlGenerator; _serializer = serializer; + _authorizationService = authorizationService; } /// @@ -165,7 +171,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForMedia("id")] + [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(int id) { @@ -186,7 +192,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForMedia("id")] + [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(Guid id) { @@ -207,7 +213,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [EnsureUserPermissionForMedia("id")] + [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(Udi id) { @@ -430,7 +436,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForMedia("id")] + [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [HttpPost] public IActionResult DeleteById(int id) { @@ -471,9 +477,16 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForMedia("move.Id")] - public IActionResult PostMove(MoveOrCopy move) + public async Task PostMove(MoveOrCopy move) { + // Authorize... + var requirement = new MediaPermissionsResourceRequirement(); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, _mediaService.GetById(move.Id), requirement); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + var toMove = ValidateMoveOrCopy(move); var destinationParentID = move.ParentId; var sourceParentID = toMove.ParentId; @@ -608,8 +621,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [EnsureUserPermissionForMedia("sorted.ParentId")] - public IActionResult PostSort(ContentSortOrder sorted) + public async Task PostSort(ContentSortOrder sorted) { if (sorted == null) { @@ -622,14 +634,21 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(); } - var mediaService = _mediaService; + // Authorize... + var requirement = new MediaPermissionsResourceRequirement(); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, _mediaService.GetById(sorted.ParentId), requirement); + if (!authorizationResult.Succeeded) + { + return Forbid(); + } + var sortedMedia = new List(); try { - sortedMedia.AddRange(sorted.IdSortOrder.Select(mediaService.GetById)); + sortedMedia.AddRange(sorted.IdSortOrder.Select(_mediaService.GetById)); // Save Media with new sort order and update content xml in db accordingly - if (mediaService.Sort(sortedMedia) == false) + if (_mediaService.Sort(sortedMedia) == false) { _logger.LogWarning("Media sorting failed, this was probably caused by an event being cancelled"); throw HttpResponseException.CreateValidationErrorResponse("Media sorting failed, this was probably caused by an event being cancelled"); @@ -643,19 +662,16 @@ namespace Umbraco.Web.BackOffice.Controllers } } - public ActionResult PostAddFolder(PostedFolder folder) + public async Task> PostAddFolder(PostedFolder folder) { - var parentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true); + var parentId = await GetParentIdAsIntAsync(folder.ParentId, validatePermissions:true); if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); } - - var mediaService = _mediaService; - - var f = mediaService.CreateMedia(folder.Name, parentId.Value, Constants.Conventions.MediaTypes.Folder); - mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var f = _mediaService.CreateMedia(folder.Name, parentId.Value, Constants.Conventions.MediaTypes.Folder); + _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); return _umbracoMapper.Map(f); } @@ -680,13 +696,13 @@ namespace Umbraco.Web.BackOffice.Controllers } //get the string json from the request - var parentId = GetParentIdAsInt(currentFolder, validatePermissions: true); + var parentId = await GetParentIdAsIntAsync(currentFolder, validatePermissions: true); if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); } var tempFiles = new PostedFiles(); - var mediaService = _mediaService; + //in case we pass a path with a folder in it, we will create it and upload media to it. if (!string.IsNullOrEmpty(path)) @@ -704,18 +720,18 @@ namespace Umbraco.Web.BackOffice.Controllers { //look for matching folder folderMediaItem = - mediaService.GetRootMedia().FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); + _mediaService.GetRootMedia().FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); if (folderMediaItem == null) { //if null, create a folder - folderMediaItem = mediaService.CreateMedia(folderName, -1, Constants.Conventions.MediaTypes.Folder); - mediaService.Save(folderMediaItem); + folderMediaItem = _mediaService.CreateMedia(folderName, -1, Constants.Conventions.MediaTypes.Folder); + _mediaService.Save(folderMediaItem); } } else { //get current parent - var mediaRoot = mediaService.GetById(parentId.Value); + var mediaRoot = _mediaService.GetById(parentId.Value); //if the media root is null, something went wrong, we'll abort if (mediaRoot == null) @@ -729,8 +745,8 @@ namespace Umbraco.Web.BackOffice.Controllers if (folderMediaItem == null) { //if null, create a folder - folderMediaItem = mediaService.CreateMedia(folderName, mediaRoot, Constants.Conventions.MediaTypes.Folder); - mediaService.Save(folderMediaItem); + folderMediaItem = _mediaService.CreateMedia(folderName, mediaRoot, Constants.Conventions.MediaTypes.Folder); + _mediaService.Save(folderMediaItem); } } //set the media root to the folder id so uploaded files will end there. @@ -763,7 +779,7 @@ namespace Umbraco.Web.BackOffice.Controllers var mediaItemName = fileName.ToFriendlyName(); - var f = mediaService.CreateMedia(mediaItemName, parentId.Value, mediaType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var f = _mediaService.CreateMedia(mediaItemName, parentId.Value, mediaType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); await using (var stream = formFile.OpenReadStream()) @@ -772,7 +788,7 @@ namespace Umbraco.Web.BackOffice.Controllers } - var saveResult = mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var saveResult = _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); if (saveResult == false) { AddCancelMessage(tempFiles, @@ -828,7 +844,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// and if that check fails an unauthorized exception will occur /// /// - private int? GetParentIdAsInt(string parentId, bool validatePermissions) + private async Task GetParentIdAsIntAsync(string parentId, bool validatePermissions) { int intParentId; @@ -861,22 +877,23 @@ namespace Umbraco.Web.BackOffice.Controllers } } + // Authorize... //ensure the user has access to this folder by parent id! - if (validatePermissions && CheckPermissions( - new Dictionary(), - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, - _mediaService, - _entityService, - intParentId) == false) + if (validatePermissions) { - throw new HttpResponseException( + var requirement = new MediaPermissionsResourceRequirement(); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, _mediaService.GetById(intParentId), requirement); + if (!authorizationResult.Succeeded) + { + throw new HttpResponseException( HttpStatusCode.Forbidden, new SimpleNotificationModel(new BackOfficeNotification( _localizedTextService.Localize("speechBubbles/operationFailedHeader"), _localizedTextService.Localize("speechBubbles/invalidUserPermissionsText"), NotificationStyle.Warning))); + } } - + return intParentId; } @@ -892,8 +909,8 @@ namespace Umbraco.Web.BackOffice.Controllers throw new HttpResponseException(HttpStatusCode.NotFound); } - var mediaService = _mediaService; - var toMove = mediaService.GetById(model.Id); + + var toMove = _mediaService.GetById(model.Id); if (toMove == null) { throw new HttpResponseException(HttpStatusCode.NotFound); @@ -912,7 +929,7 @@ namespace Umbraco.Web.BackOffice.Controllers } else { - var parent = mediaService.GetById(model.ParentId); + var parent = _mediaService.GetById(model.ParentId); if (parent == null) { throw new HttpResponseException(HttpStatusCode.NotFound); @@ -940,45 +957,7 @@ namespace Umbraco.Web.BackOffice.Controllers return toMove; } - /// - /// Performs a permissions check for the user to check if it has access to the node based on - /// start node and/or permissions for the node - /// - /// The storage to add the content item to so it can be reused - /// - /// - /// - /// The content to lookup, if the contentItem is not specified - /// Specifies the already resolved content item to check against, setting this ignores the nodeId - /// - internal static bool CheckPermissions(IDictionary storage, IUser user, IMediaService mediaService, IEntityService entityService, int nodeId, IMedia media = null) - { - if (storage == null) throw new ArgumentNullException("storage"); - if (user == null) throw new ArgumentNullException("user"); - if (mediaService == null) throw new ArgumentNullException("mediaService"); - if (entityService == null) throw new ArgumentNullException("entityService"); - - if (media == null && nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia) - { - media = mediaService.GetById(nodeId); - //put the content item into storage so it can be retrieved - // in the controller (saves a lookup) - storage[typeof(IMedia).ToString()] = media; - } - - if (media == null && nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia) - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - var hasPathAccess = (nodeId == Constants.System.Root) - ? user.HasMediaRootAccess(entityService) - : (nodeId == Constants.System.RecycleBinMedia) - ? user.HasMediaBinAccess(entityService) - : user.HasPathAccess(media, entityService); - - return hasPathAccess; - } + public PagedResult GetPagedReferences(int id, string entityType, int pageNumber = 1, int pageSize = 100) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs index b682b59e2f..32dc2ef888 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Dictionary; @@ -12,6 +13,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; @@ -75,7 +77,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] public MediaTypeDisplay GetById(int id) { var ct = _mediaTypeService.Get(id); @@ -94,7 +96,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] public MediaTypeDisplay GetById(Guid id) { var mediaType = _mediaTypeService.Get(id); @@ -113,7 +115,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] public MediaTypeDisplay GetById(Udi id) { var guidUdi = id as GuidUdi; @@ -137,7 +139,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult DeleteById(int id) { var foundType = _mediaTypeService.Get(id); @@ -173,7 +175,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult GetAvailableCompositeMediaTypes(GetAvailableCompositionsFilter filter) { var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, @@ -194,7 +196,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) { var result = @@ -206,7 +208,7 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(result); } - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public MediaTypeDisplay GetEmpty(int parentId) { IMediaType mt; @@ -230,7 +232,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Returns all media types /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IEnumerable GetAll() => _mediaTypeService.GetAll() .Select(_umbracoMapper.Map); @@ -242,7 +244,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult DeleteContainer(int id) { _mediaTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -250,7 +252,7 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(); } - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostCreateContainer(int parentId, string name) { var result = _mediaTypeService.CreateContainer(parentId, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -260,7 +262,7 @@ namespace Umbraco.Web.BackOffice.Controllers : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostRenameContainer(int id, string name) { var result = _mediaTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -270,7 +272,7 @@ namespace Umbraco.Web.BackOffice.Controllers : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public MediaTypeDisplay PostSave(MediaTypeSave contentTypeSave) { var savedCt = PerformPostSave( @@ -292,7 +294,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostMove(MoveOrCopy move) { return PerformMove( @@ -306,7 +308,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostCopy(MoveOrCopy copy) { return PerformCopy( @@ -322,7 +324,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the allowed child content type objects for the content item id passed in - based on an INT id /// /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetAllowedChildren(int contentId) { @@ -369,7 +371,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the allowed child content type objects for the content item id passed in - based on a GUID id /// /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetAllowedChildren(Guid contentId) { @@ -386,7 +388,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the allowed child content type objects for the content item id passed in - based on a UDI id /// /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetAllowedChildren(Udi contentId) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index 55042d458b..a97ed9c2ad 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Net.Mime; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -27,6 +28,7 @@ using Umbraco.Extensions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.ModelBinders; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.ContentApps; @@ -41,7 +43,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// access to ALL of the methods on this controller will need access to the member application. /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Members)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessMembers)] [OutgoingNoHyphenGuidFormat] public class MemberController : ContentControllerBase { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs index d3d06132d3..a7cbaf96c1 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Mapping; @@ -9,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; @@ -19,7 +21,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// An API controller used for dealing with member groups /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.MemberGroups)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)] public class MemberGroupController : UmbracoAuthorizedJsonController { private readonly IMemberGroupService _memberGroupService; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 3557680ab1..e203386958 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -24,6 +24,8 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Editors; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -31,7 +33,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// An API controller used for dealing with member types /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)] public class MemberTypeController : ContentTypeControllerBase { private readonly IMemberTypeService _memberTypeService; @@ -71,7 +73,6 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] [DetermineAmbiguousActionByPassingParameters] public MemberTypeDisplay GetById(int id) { @@ -90,7 +91,6 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] [DetermineAmbiguousActionByPassingParameters] public MemberTypeDisplay GetById(Guid id) { @@ -109,7 +109,6 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] [DetermineAmbiguousActionByPassingParameters] public MemberTypeDisplay GetById(Udi id) { @@ -134,7 +133,6 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpDelete] [HttpPost] - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public IActionResult DeleteById(int id) { var foundType = _memberTypeService.Get(id); @@ -161,8 +159,6 @@ namespace Umbraco.Web.BackOffice.Controllers /// be looked up via the db, they need to be passed in. /// /// - - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public IActionResult GetAvailableCompositeMemberTypes(int contentTypeId, [FromQuery]string[] filterContentTypes, [FromQuery]string[] filterPropertyTypes) @@ -176,7 +172,6 @@ namespace Umbraco.Web.BackOffice.Controllers return Ok(result); } - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public MemberTypeDisplay GetEmpty() { var ct = new MemberType(_shortStringHelper, -1); @@ -190,13 +185,13 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Returns all member types /// + [Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)] public IEnumerable GetAllTypes() { return _memberTypeService.GetAll() .Select(_umbracoMapper.Map); } - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public ActionResult PostSave(MemberTypeSave contentTypeSave) { //get the persisted member type diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs index 69d23d606b..36bf4d2fca 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net; using System.Text; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using Semver; @@ -14,6 +15,7 @@ using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Security; @@ -23,7 +25,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// A controller used for managing packages in the back office /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessPackages)] public class PackageController : UmbracoAuthorizedJsonController { private readonly IHostingEnvironment _hostingEnvironment; diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs index 05631173c9..961ec388f7 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs @@ -22,6 +22,8 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -29,7 +31,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// A controller used for installing packages and managing all of the data in the packages section in the back office /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessPackages)] public class PackageInstallController : UmbracoAuthorizedJsonController { diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index b8db09f9b7..3dd4191c2e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -25,6 +25,8 @@ using Umbraco.Web.Services; using Umbraco.Web.Trees; using Umbraco.Web.WebAssets; using Constants = Umbraco.Core.Constants; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -64,7 +66,7 @@ namespace Umbraco.Web.BackOffice.Controllers _viewEngines = viewEngines; } - [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: true, requireApproval : false)] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)] [DisableBrowserCache] public ActionResult Index() { @@ -107,12 +109,12 @@ namespace Umbraco.Web.BackOffice.Controllers /// The endpoint that is loaded within the preview iframe /// /// - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] public ActionResult Frame(int id, string culture) { EnterPreview(id); - // use a numeric url because content may not be in cache and so .Url would fail + // use a numeric URL because content may not be in cache and so .Url would fail var query = culture.IsNullOrWhiteSpace() ? string.Empty : $"?culture={culture}"; return RedirectPermanent($"../../{id}.aspx{query}"); diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs index bf40e5722f..5646c7f1aa 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Mapping; @@ -11,6 +12,7 @@ using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.ActionsResults; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; @@ -18,7 +20,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Controllers { [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessContent)] public class RelationController : UmbracoAuthorizedJsonController { private readonly UmbracoMapper _umbracoMapper; @@ -56,20 +58,5 @@ namespace Umbraco.Web.BackOffice.Controllers return _umbracoMapper.MapEnumerable(relations); } - [HttpDelete] - [HttpPost] - public IActionResult DeleteById(int id) - { - var foundRelation = _relationService.GetById(id); - - if (foundRelation == null) - { - return new UmbracoProblemResult("No relation found with the specified id", HttpStatusCode.NotFound); - } - - _relationService.Delete(foundRelation); - - return Ok(); - } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs index 1ab5bd9bfa..b2706babee 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs @@ -15,6 +15,8 @@ using Umbraco.Core.Mapping; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { @@ -22,7 +24,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The API controller for editing relation types. /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.RelationTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessRelationTypes)] public class RelationTypeController : BackOfficeNotificationsController { private readonly ILogger _logger; diff --git a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs index f80c3015e9..fe75cf5a0a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.IO; @@ -11,6 +12,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; @@ -18,7 +20,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Controllers { [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoTreeAuthorize(Constants.Trees.Templates)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessTemplates)] public class TemplateController : BackOfficeNotificationsController { private readonly IFileService _fileService; diff --git a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs index 4bae8970bc..edaaa4f1e3 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -17,16 +18,13 @@ using Umbraco.Core.Strings; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.ActionsResults; using Umbraco.Web.Common.Attributes; - +using Umbraco.Web.Common.Authorization; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Controllers { [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize( - Constants.Applications.Content, - Constants.Applications.Media, - Constants.Applications.Members)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForTinyMce)] public class TinyMceController : UmbracoAuthorizedApiController { private readonly IHostingEnvironment _hostingEnvironment; diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index e3d779d61d..a807c663d0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -1,6 +1,8 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Controllers; using Umbraco.Web.Common.Filters; @@ -14,11 +16,11 @@ namespace Umbraco.Web.BackOffice.Controllers /// is logged in using forms authentication which indicates the seconds remaining /// before their timeout expires. /// - [IsBackOffice] + [IsBackOffice] [UmbracoUserTimeoutFilter] - [UmbracoBackOfficeAuthorize] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] [DisableBrowserCache] - [UmbracoWebApiRequireHttps] + [UmbracoRequireHttps] [CheckIfUserTicketDataIsStale] [MiddlewareFilter(typeof(UnhandledExceptionLoggerFilter))] public abstract class UmbracoAuthorizedApiController : UmbracoApiController diff --git a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs index 4cba5064d1..64aef74257 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs @@ -16,11 +16,13 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; using Constants = Umbraco.Core.Constants; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Users)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)] [PrefixlessBodyModelValidator] public class UserGroupsController : UmbracoAuthorizedJsonController { @@ -164,7 +166,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Return a user group /// /// - [UserGroupAuthorization("id")] + [Authorize(Policy = AuthorizationPolicies.UserBelongsToUserGroupInRequest)] public ActionResult GetUserGroup(int id) { var found = _userService.GetUserGroupById(id); @@ -178,7 +180,7 @@ namespace Umbraco.Web.BackOffice.Controllers [HttpPost] [HttpDelete] - [UserGroupAuthorization("userGroupIds")] + [Authorize(Policy = AuthorizationPolicies.UserBelongsToUserGroupInRequest)] public IActionResult PostDeleteUserGroups([FromQuery] int[] userGroupIds) { var userGroups = _userService.GetAllUserGroups(userGroupIds) diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 8c5003467b..38bf69721a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -41,11 +41,13 @@ using IUser = Umbraco.Core.Models.Membership.IUser; using Task = System.Threading.Tasks.Task; using Umbraco.Net; using Umbraco.Web.Common.ActionsResults; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Controllers { [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Users)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)] [PrefixlessBodyModelValidator] [IsCurrentUserModelFilter] public class UsersController : UmbracoAuthorizedJsonController @@ -64,14 +66,12 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IUserService _userService; private readonly ILocalizedTextService _localizedTextService; private readonly UmbracoMapper _umbracoMapper; - private readonly IEntityService _entityService; - private readonly IMediaService _mediaService; - private readonly IContentService _contentService; private readonly GlobalSettings _globalSettings; private readonly IBackOfficeUserManager _userManager; private readonly ILoggerFactory _loggerFactory; private readonly LinkGenerator _linkGenerator; private readonly IBackOfficeExternalLoginProviders _externalLogins; + private readonly UserEditorAuthorizationHelper _userEditorAuthorizationHelper; private readonly ILogger _logger; public UsersController( @@ -89,14 +89,12 @@ namespace Umbraco.Web.BackOffice.Controllers IUserService userService, ILocalizedTextService localizedTextService, UmbracoMapper umbracoMapper, - IEntityService entityService, - IMediaService mediaService, - IContentService contentService, IOptions globalSettings, IBackOfficeUserManager backOfficeUserManager, ILoggerFactory loggerFactory, LinkGenerator linkGenerator, - IBackOfficeExternalLoginProviders externalLogins) + IBackOfficeExternalLoginProviders externalLogins, + UserEditorAuthorizationHelper userEditorAuthorizationHelper) { _mediaFileSystem = mediaFileSystem; _contentSettings = contentSettings.Value; @@ -112,19 +110,17 @@ namespace Umbraco.Web.BackOffice.Controllers _userService = userService; _localizedTextService = localizedTextService; _umbracoMapper = umbracoMapper; - _entityService = entityService; - _mediaService = mediaService; - _contentService = contentService; _globalSettings = globalSettings.Value; _userManager = backOfficeUserManager; _loggerFactory = loggerFactory; _linkGenerator = linkGenerator; _externalLogins = externalLogins; + _userEditorAuthorizationHelper = userEditorAuthorizationHelper; _logger = _loggerFactory.CreateLogger(); } /// - /// Returns a list of the sizes of gravatar urls for the user or null if the gravatar server cannot be reached + /// Returns a list of the sizes of gravatar URLs for the user or null if the gravatar server cannot be reached /// /// public string[] GetCurrentUserAvatarUrls() @@ -137,7 +133,7 @@ namespace Umbraco.Web.BackOffice.Controllers } [AppendUserModifiedHeader("id")] - [AdminUsersAuthorize] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostSetAvatar(int id, IList files) { return PostSetAvatarInternal(files, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, id); @@ -190,7 +186,7 @@ namespace Umbraco.Web.BackOffice.Controllers } [AppendUserModifiedHeader("id")] - [AdminUsersAuthorize] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public ActionResult PostClearAvatar(int id) { var found = _userService.GetUserById(id); @@ -229,7 +225,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [AdminUsersAuthorize] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public UserDisplay GetById(int id) { var user = _userService.GetUserById(id); @@ -247,7 +243,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] - [AdminUsersAuthorize] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IEnumerable GetByIds([FromJsonPath]int[] ids) { if (ids == null) @@ -364,8 +360,7 @@ namespace Umbraco.Web.BackOffice.Controllers CheckUniqueEmail(userSave.Email, null); //Perform authorization here to see if the current user can actually save this user with the info being requested - var authHelper = new UserEditorAuthorizationHelper(_contentService,_mediaService, _userService, _entityService); - var canSaveUser = authHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, null, null, null, userSave.UserGroups); + var canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, null, null, null, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(HttpStatusCode.Unauthorized, canSaveUser.Result); @@ -448,8 +443,7 @@ namespace Umbraco.Web.BackOffice.Controllers } //Perform authorization here to see if the current user can actually save this user with the info being requested - var authHelper = new UserEditorAuthorizationHelper(_contentService,_mediaService, _userService, _entityService); - var canSaveUser = authHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, userSave.UserGroups); + var canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, userSave.UserGroups); if (canSaveUser == false) { return new ValidationErrorResult(canSaveUser.Result, StatusCodes.Status401Unauthorized); @@ -490,8 +484,8 @@ namespace Umbraco.Web.BackOffice.Controllers } catch (Exception ex) { - _logger.LogError(ex, "An error occured in a custom event handler while inviting the user"); - return ValidationErrorResult.CreateNotificationValidationErrorResult($"An error occured inviting the user (check logs for more info): {ex.Message}"); + _logger.LogError(ex, "An error occurred in a custom event handler while inviting the user"); + return ValidationErrorResult.CreateNotificationValidationErrorResult($"An error occurred inviting the user (check logs for more info): {ex.Message}"); } // If the event is handled then no need to send the email @@ -554,7 +548,7 @@ namespace Umbraco.Web.BackOffice.Controllers WebUtility.UrlEncode("|"), token.ToUrlBase64()); - // Get an mvc helper to get the url + // Get an mvc helper to get the URL var action = _linkGenerator.GetPathByAction("VerifyInvite", "BackOffice", new { area = _globalSettings.GetUmbracoMvcArea(_hostingEnvironment), @@ -602,8 +596,7 @@ namespace Umbraco.Web.BackOffice.Controllers throw new HttpResponseException(HttpStatusCode.NotFound); //Perform authorization here to see if the current user can actually save this user with the info being requested - var authHelper = new UserEditorAuthorizationHelper(_contentService,_mediaService, _userService, _entityService); - var canSaveUser = authHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups); + var canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(HttpStatusCode.Unauthorized, canSaveUser.Result); @@ -716,7 +709,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Disables the users with the given user ids /// /// - [AdminUsersAuthorize("userIds")] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostDisableUsers([FromQuery]int[] userIds) { var tryGetCurrentUserId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId(); @@ -747,7 +740,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Enables the users with the given user ids /// /// - [AdminUsersAuthorize("userIds")] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostEnableUsers([FromQuery]int[] userIds) { var users = _userService.GetUsersById(userIds).ToArray(); @@ -771,7 +764,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Unlocks the users with the given user ids /// /// - [AdminUsersAuthorize("userIds")] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public async Task PostUnlockUsers([FromQuery]int[] userIds) { if (userIds.Length <= 0) return Ok(); @@ -804,7 +797,7 @@ namespace Umbraco.Web.BackOffice.Controllers _localizedTextService.Localize("speechBubbles/unlockUsersSuccess", new[] {(userIds.Length - notFound.Count).ToString()})); } - [AdminUsersAuthorize("userIds")] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostSetUserGroupsOnUsers([FromQuery]string[] userGroupAliases, [FromQuery]int[] userIds) { var users = _userService.GetUsersById(userIds).ToArray(); @@ -830,7 +823,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Limited to users that haven't logged in to avoid issues with related records constrained /// with a foreign key on the user Id /// - [AdminUsersAuthorize] + [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostDeleteNonLoggedInUser(int id) { var user = _userService.GetUserById(id); diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs index ac844b0340..413a54a28b 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; @@ -9,9 +10,12 @@ using Umbraco.Core.Security; using Umbraco.Core.Serialization; using Umbraco.Infrastructure.BackOffice; using Umbraco.Net; +using Umbraco.Web.Actions; +using Umbraco.Web.BackOffice.Authorization; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Security; using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Security; namespace Umbraco.Extensions @@ -57,7 +61,7 @@ namespace Umbraco.Extensions services.GetRequiredService())); services.TryAddScoped, DefaultUserConfirmation>(); services.TryAddScoped, UserClaimsPrincipalFactory>(); - + // CUSTOM: services.TryAddScoped(); services.TryAddScoped(); @@ -76,5 +80,350 @@ namespace Umbraco.Extensions return new BackOfficeIdentityBuilder(services); } + + /// + /// Add authorization handlers and policies + /// + /// + public static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services, string backOfficeAuthenticationScheme = Constants.Security.BackOfficeAuthenticationType) + { + // NOTE: Even though we are registering these handlers globally they will only actually execute their logic for + // any auth defining a matching requirement and scheme. + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddAuthorization(o => CreatePolicies(o, backOfficeAuthenticationScheme)); + } + + private static void CreatePolicies(AuthorizationOptions options, string backOfficeAuthenticationScheme) + { + options.AddPolicy(AuthorizationPolicies.MediaPermissionByResource, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new MediaPermissionsResourceRequirement()); + }); + + options.AddPolicy(AuthorizationPolicies.MediaPermissionPathById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new MediaPermissionsQueryStringRequirement("id")); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionByResource, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsResourceRequirement()); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionEmptyRecycleBin, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(Constants.System.RecycleBinContent, ActionDelete.ActionLetter)); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionAdministrationById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRights.ActionLetter)); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRights.ActionLetter, "contentId")); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionProtectById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionProtect.ActionLetter)); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionProtect.ActionLetter, "contentId")); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionRollbackById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRollback.ActionLetter)); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionRollback.ActionLetter, "contentId")); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionPublishById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionPublish.ActionLetter)); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionBrowseById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionBrowse.ActionLetter)); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionBrowse.ActionLetter, "contentId")); + }); + + options.AddPolicy(AuthorizationPolicies.ContentPermissionDeleteById, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionDelete.ActionLetter)); + }); + + options.AddPolicy(AuthorizationPolicies.BackOfficeAccess, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new BackOfficeRequirement()); + }); + + options.AddPolicy(AuthorizationPolicies.BackOfficeAccessWithoutApproval, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new BackOfficeRequirement(false)); + }); + + options.AddPolicy(AuthorizationPolicies.AdminUserEditsRequireAdmin, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new AdminUsersRequirement()); + policy.Requirements.Add(new AdminUsersRequirement("userIds")); + }); + + options.AddPolicy(AuthorizationPolicies.UserBelongsToUserGroupInRequest, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new UserGroupRequirement()); + policy.Requirements.Add(new UserGroupRequirement("userGroupIds")); + }); + + options.AddPolicy(AuthorizationPolicies.DenyLocalLoginIfConfigured, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new DenyLocalLoginRequirement()); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessContent, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Content)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Content, Constants.Applications.Media)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessUsers, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Users)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessForTinyMce, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement( + Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessMedia, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Media)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessMembers, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Members)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessPackages, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Packages)); + }); + + options.AddPolicy(AuthorizationPolicies.SectionAccessSettings, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement(Constants.Applications.Settings)); + }); + + //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered + // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here. + options.AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement( + Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users, + Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)); + }); + options.AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement( + Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users, + Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)); + }); + options.AddPolicy(AuthorizationPolicies.SectionAccessForMemberTree, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement( + Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)); + }); + + // Permission is granted to this policy if the user has access to any of these sections: Content, media, settings, developer, members + options.AddPolicy(AuthorizationPolicies.SectionAccessForDataTypeReading, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new SectionRequirement( + Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, + Constants.Applications.Settings, Constants.Applications.Packages)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDocuments, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Content)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessUsers, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Users)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViews, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.PartialViews)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViewMacros, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.PartialViewMacros)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessPackages, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Packages)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessLogs, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.LogViewer)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDataTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.DataTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessTemplates, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Templates)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMemberTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessRelationTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.RelationTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.DocumentTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMemberGroups, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberGroups)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMediaTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.MediaTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMacros, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Macros)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessLanguages, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Languages)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDictionary, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary, Constants.Trees.Dictionary)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDictionaryOrTemplates, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.Dictionary, Constants.Trees.Templates)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.DocumentTypes, Constants.Trees.Content)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMediaOrMediaTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.MediaTypes, Constants.Trees.Media)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessMembersOrMemberTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.MemberTypes, Constants.Trees.Members)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessAnySchemaTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)); + }); + + options.AddPolicy(AuthorizationPolicies.TreeAccessAnyContentOrTypes, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add(new TreeRequirement( + Constants.Trees.DocumentTypes, Constants.Trees.Content, + Constants.Trees.MediaTypes, Constants.Trees.Media, + Constants.Trees.MemberTypes, Constants.Trees.Members)); + }); + } } } diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs index c47f2ab3e3..35b7e8e859 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualBasic; using Umbraco.Core.Builder; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Security; @@ -19,6 +20,7 @@ namespace Umbraco.Extensions .AddRuntimeMinifier() .AddBackOffice() .AddBackOfficeIdentity() + .AddBackOfficeAuthorizationPolicies() .AddMiniProfiler() .AddMvcAndRazor() .AddWebServer() @@ -60,6 +62,17 @@ namespace Umbraco.Extensions return builder; } + public static IUmbracoBuilder AddBackOfficeAuthorizationPolicies(this IUmbracoBuilder builder, string backOfficeAuthenticationScheme = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType) + { + builder.Services.AddBackOfficeAuthorizationPolicies(backOfficeAuthenticationScheme); + // TODO: See other TODOs in things like UmbracoApiControllerBase ... AFAIK all of this is only used for the back office + // so IMO these controllers and the features auth policies should just be moved to the back office project and then this + // ext method can be removed. + builder.Services.AddUmbracoCommonAuthorizationPolicies(); + + return builder; + } + public static IUmbracoBuilder AddPreviewSupport(this IUmbracoBuilder builder) { builder.Services.AddSignalR(); diff --git a/src/Umbraco.Web.BackOffice/Filters/AdminUsersAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/AdminUsersAuthorizeAttribute.cs deleted file mode 100644 index 867b2c0a24..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/AdminUsersAuthorizeAttribute.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; -using Umbraco.Core.Security; -using Umbraco.Core.Services; -using Umbraco.Web.Editors; -using Umbraco.Web.Security; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// if the users being edited is an admin then we must ensure that the current user is also an admin - /// - /// - /// This will authorize against one or multiple ids - /// - public sealed class AdminUsersAuthorizeAttribute : TypeFilterAttribute - { - - public AdminUsersAuthorizeAttribute(string parameterName): base(typeof(AdminUsersAuthorizeFilter)) - { - Arguments = new object[] { parameterName }; - } - - public AdminUsersAuthorizeAttribute() : this("id") - { - } - - private class AdminUsersAuthorizeFilter : IAuthorizationFilter - { - private readonly string _parameterName; - private readonly IRequestAccessor _requestAccessor; - private readonly IUserService _userService; - private readonly IContentService _contentService; - private readonly IMediaService _mediaService; - private readonly IEntityService _entityService; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - - public AdminUsersAuthorizeFilter( - IRequestAccessor requestAccessor, - IUserService userService, - IContentService contentService, - IMediaService mediaService, - IEntityService entityService, - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - string parameterName) - { - _requestAccessor = requestAccessor; - _userService = userService; - _contentService = contentService; - _mediaService = mediaService; - _entityService = entityService; - _backofficeSecurityAccessor = backofficeSecurityAccessor; - _parameterName = parameterName; - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!IsAuthorized(context)) - { - context.Result = new ForbidResult(); - } - } - - private bool IsAuthorized(AuthorizationFilterContext actionContext) - { - int[] userIds; - - if (int.TryParse(_requestAccessor.GetRequestValue(_parameterName), out var userId)) - { - var intUserId = userId.TryConvertTo(); - if (intUserId) - userIds = new[] { intUserId.Result }; - else return true; - } - else - { - var queryString = actionContext.HttpContext.Request.Query; - var ids = queryString.Where(x => x.Key == _parameterName).ToArray(); - if (ids.Length == 0) - return true; - userIds = ids.Select(x => x.Value.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray(); - } - - if (userIds.Length == 0) return true; - - var users = _userService.GetUsersById(userIds); - var authHelper = new UserEditorAuthorizationHelper(_contentService, _mediaService, _userService, _entityService); - return users.All(user => authHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, null) != false); - } - } - - - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs index 082d7c5e52..8d8a3ffa9a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs @@ -7,6 +7,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.BackOffice.Filters { + /// /// Automatically checks if any request is a non-GET and if the /// resulting message is INotificationModel in which case it will append any Event Messages diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs index 0d7a3a14aa..6d757dc983 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs @@ -20,15 +20,12 @@ namespace Umbraco.Web.BackOffice.Filters /// internal abstract class ContentModelValidator { - - protected IBackOfficeSecurity BackOfficeSecurity { get; } public IPropertyValidationService PropertyValidationService { get; } protected ILogger Logger { get; } - protected ContentModelValidator(ILogger logger, IBackOfficeSecurity backofficeSecurity, IPropertyValidationService propertyValidationService) + protected ContentModelValidator(ILogger logger, IPropertyValidationService propertyValidationService) { Logger = logger ?? throw new ArgumentNullException(nameof(logger)); - BackOfficeSecurity = backofficeSecurity ?? throw new ArgumentNullException(nameof(backofficeSecurity)); PropertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService)); } } @@ -47,17 +44,12 @@ namespace Umbraco.Web.BackOffice.Filters where TPersisted : class, IContentBase where TModelSave: IContentSave where TModelWithProperties : IContentProperties - { - private readonly ILocalizedTextService _textService; - + { protected ContentModelValidator( ILogger logger, - IBackOfficeSecurity backofficeSecurity, - ILocalizedTextService textService, IPropertyValidationService propertyValidationService) - : base(logger, backofficeSecurity, propertyValidationService) + : base(logger, propertyValidationService) { - _textService = textService ?? throw new ArgumentNullException(nameof(textService)); } /// diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveModelValidator.cs index caaee1d9e0..b83462fa10 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveModelValidator.cs @@ -13,10 +13,8 @@ namespace Umbraco.Web.BackOffice.Filters { public ContentSaveModelValidator( ILogger logger, - IBackOfficeSecurity backofficeSecurity, - ILocalizedTextService textService, IPropertyValidationService propertyValidationService) - : base(logger, backofficeSecurity, textService, propertyValidationService) + : base(logger, propertyValidationService) { } diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 2553232185..686023a478 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; @@ -10,6 +12,8 @@ using Umbraco.Core.Models; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Actions; +using Umbraco.Web.BackOffice.Authorization; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; @@ -27,43 +31,49 @@ namespace Umbraco.Web.BackOffice.Filters } - private sealed class ContentSaveValidationFilter : IActionFilter + private sealed class ContentSaveValidationFilter : IAsyncActionFilter { private readonly IContentService _contentService; - private readonly IEntityService _entityService; private readonly IPropertyValidationService _propertyValidationService; + private readonly IAuthorizationService _authorizationService; private readonly ILoggerFactory _loggerFactory; - private readonly ILocalizedTextService _textService; - private readonly IUserService _userService; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; public ContentSaveValidationFilter( ILoggerFactory loggerFactory, - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - ILocalizedTextService textService, IContentService contentService, - IUserService userService, - IEntityService entityService, - IPropertyValidationService propertyValidationService) + IPropertyValidationService propertyValidationService, + IAuthorizationService authorizationService) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService)); + _authorizationService = authorizationService; } - public void OnActionExecuting(ActionExecutingContext context) + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // on executing... + await OnActionExecutingAsync(context); + + if (context.Result == null) + { + //need to pass the execution to next if a result was not set + await next(); + } + + + // on executed... + } + + private async Task OnActionExecutingAsync(ActionExecutingContext context) { var model = (ContentItemSave) context.ActionArguments["contentItem"]; - var contentItemValidator = new ContentSaveModelValidator(_loggerFactory.CreateLogger(), _backofficeSecurityAccessor.BackOfficeSecurity, _textService, _propertyValidationService); + var contentItemValidator = new ContentSaveModelValidator(_loggerFactory.CreateLogger(), _propertyValidationService); if (!ValidateAtLeastOneVariantIsBeingSaved(model, context)) return; if (!contentItemValidator.ValidateExistingContent(model, context)) return; - if (!ValidateUserAccess(model, context, _backofficeSecurityAccessor.BackOfficeSecurity)) return; + if (!await ValidateUserAccessAsync(model, context)) return; //validate for each variant that is being updated foreach (var variant in model.Variants.Where(x => x.Save)) @@ -74,9 +84,6 @@ namespace Umbraco.Web.BackOffice.Filters } } - public void OnActionExecuted(ActionExecutedContext context) - { - } /// /// If there are no variants tagged for Saving, then this is an invalid request @@ -84,7 +91,8 @@ namespace Umbraco.Web.BackOffice.Filters /// /// /// - private bool ValidateAtLeastOneVariantIsBeingSaved(ContentItemSave contentItem, + private bool ValidateAtLeastOneVariantIsBeingSaved( + ContentItemSave contentItem, ActionExecutingContext actionContext) { if (!contentItem.Variants.Any(x => x.Save)) @@ -102,8 +110,9 @@ namespace Umbraco.Web.BackOffice.Filters /// /// /// - private bool ValidateUserAccess(ContentItemSave contentItem, ActionExecutingContext actionContext, - IBackOfficeSecurity backofficeSecurity) + private async Task ValidateUserAccessAsync( + ContentItemSave contentItem, + ActionExecutingContext actionContext) { // We now need to validate that the user is allowed to be doing what they are doing. // Based on the action we need to check different permissions. @@ -210,42 +219,26 @@ namespace Umbraco.Web.BackOffice.Filters throw new ArgumentOutOfRangeException(); } - ContentPermissionsHelper.ContentAccess accessResult; - if (contentToCheck != null) - { - //store the content item in request cache so it can be resolved in the controller without re-looking it up - actionContext.HttpContext.Items[typeof(IContent).ToString()] = contentItem; - accessResult = ContentPermissionsHelper.CheckPermissions( - contentToCheck, backofficeSecurity.CurrentUser, - _userService, _entityService, permissionToCheck.ToArray()); - } - else - { - accessResult = ContentPermissionsHelper.CheckPermissions( - contentIdToCheck, backofficeSecurity.CurrentUser, - _userService, _contentService, _entityService, - out contentToCheck, - permissionToCheck.ToArray()); - if (contentToCheck != null) - { - //store the content item in request cache so it can be resolved in the controller without re-looking it up - actionContext.HttpContext.Items[typeof(IContent).ToString()] = contentToCheck; - } - } + var resource = contentToCheck == null + ? new ContentPermissionsResource(contentToCheck, contentIdToCheck, permissionToCheck) + : new ContentPermissionsResource(contentToCheck, permissionToCheck); - if (accessResult == ContentPermissionsHelper.ContentAccess.NotFound) - { - actionContext.Result = new NotFoundResult(); - } + var authorizationResult = await _authorizationService.AuthorizeAsync( + actionContext.HttpContext.User, + resource, + AuthorizationPolicies.ContentPermissionByResource); - if (accessResult != ContentPermissionsHelper.ContentAccess.Granted) + if (!authorizationResult.Succeeded) { actionContext.Result = new ForbidResult(); + return false; } - return accessResult == ContentPermissionsHelper.ContentAccess.Granted; + return true; } + + } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/DenyLocalLoginAuthorizationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/DenyLocalLoginAuthorizationAttribute.cs deleted file mode 100644 index 36247dd91e..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/DenyLocalLoginAuthorizationAttribute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using System; -using Umbraco.Web.BackOffice.Security; - -namespace Umbraco.Web.Editors.Filters -{ - public sealed class DenyLocalLoginAuthorizationAttribute : TypeFilterAttribute - { - public DenyLocalLoginAuthorizationAttribute() : base(typeof(DenyLocalLoginFilter)) - { - } - - private class DenyLocalLoginFilter : IAuthorizationFilter - { - private readonly IBackOfficeExternalLoginProviders _externalLogins; - - public DenyLocalLoginFilter(IBackOfficeExternalLoginProviders externalLogins) - { - _externalLogins = externalLogins; - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (_externalLogins.HasDenyLocalLogin()) - { - // if there is a deny local login provider then we cannot authorize - context.Result = new ForbidResult(); - } - } - } - - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForContentAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForContentAttribute.cs deleted file mode 100644 index 6e0095b4b4..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForContentAttribute.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Net; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Security; -using Umbraco.Core.Services; -using Umbraco.Web.Actions; -using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Security; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// Auth filter to check if the current user has access to the content item (by id). - /// - /// - /// This first checks if the user can access this based on their start node, and then checks node permissions - /// By default the permission that is checked is browse but this can be specified in the ctor. - /// NOTE: This cannot be an auth filter because that happens too soon and we don't have access to the action params. - /// - public sealed class EnsureUserPermissionForContentAttribute : TypeFilterAttribute - { - - /// - /// This constructor will only be able to test the start node access - /// - public EnsureUserPermissionForContentAttribute(int nodeId) - : base(typeof(EnsureUserPermissionForContentFilter)) - { - Arguments = new object[] - { - nodeId - }; - } - - - public EnsureUserPermissionForContentAttribute(int nodeId, char permissionToCheck) - : base(typeof(EnsureUserPermissionForContentFilter)) - { - Arguments = new object[] - { - nodeId, permissionToCheck - }; - } - - public EnsureUserPermissionForContentAttribute(string paramName) - : base(typeof(EnsureUserPermissionForContentFilter)) - { - if (paramName == null) throw new ArgumentNullException(nameof(paramName)); - if (string.IsNullOrEmpty(paramName)) - throw new ArgumentException("Value can't be empty.", nameof(paramName)); - - Arguments = new object[] - { - paramName, ActionBrowse.ActionLetter - }; - } - - - public EnsureUserPermissionForContentAttribute(string paramName, char permissionToCheck) - : base(typeof(EnsureUserPermissionForContentFilter)) - { - if (paramName == null) throw new ArgumentNullException(nameof(paramName)); - if (string.IsNullOrEmpty(paramName)) - throw new ArgumentException("Value can't be empty.", nameof(paramName)); - - Arguments = new object[] - { - paramName, permissionToCheck - }; - } - - private sealed class EnsureUserPermissionForContentFilter : IActionFilter - { - private readonly int? _nodeId; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - private readonly IEntityService _entityService; - private readonly IUserService _userService; - private readonly IContentService _contentService; - private readonly string _paramName; - private readonly char? _permissionToCheck; - - public EnsureUserPermissionForContentFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IUserService userService, - IContentService contentService, - string paramName) - :this(backofficeSecurityAccessor, entityService, userService, contentService, null, paramName, ActionBrowse.ActionLetter) - { - - } - - public EnsureUserPermissionForContentFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IUserService userService, - IContentService contentService, - int nodeId, - char permissionToCheck) - :this(backofficeSecurityAccessor, entityService, userService, contentService, nodeId, null, permissionToCheck) - { - - } - - public EnsureUserPermissionForContentFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IUserService userService, - IContentService contentService, - int nodeId) - :this(backofficeSecurityAccessor, entityService, userService, contentService, nodeId, null, null) - { - - } - public EnsureUserPermissionForContentFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IUserService userService, - IContentService contentService, - string paramName, char permissionToCheck) - :this(backofficeSecurityAccessor, entityService, userService, contentService, null, paramName, permissionToCheck) - { - - } - - - private EnsureUserPermissionForContentFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IUserService userService, - IContentService contentService, - int? nodeId, string paramName, char? permissionToCheck) - { - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - - _paramName = paramName; - if (permissionToCheck.HasValue) - { - _permissionToCheck = permissionToCheck.Value; - } - - - if (nodeId.HasValue) - { - _nodeId = nodeId.Value; - } - } - - - - public void OnActionExecuting(ActionExecutingContext context) - { - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser == null) - { - //not logged in - throw new HttpResponseException(HttpStatusCode.Unauthorized); - } - - int nodeId; - if (_nodeId.HasValue == false) - { - var parts = _paramName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - - if (context.ActionArguments[parts[0]] == null) - { - throw new InvalidOperationException("No argument found for the current action with the name: " + - _paramName); - } - - if (parts.Length == 1) - { - var argument = context.ActionArguments[parts[0]].ToString(); - // if the argument is an int, it will parse and can be assigned to nodeId - // if might be a udi, so check that next - // otherwise treat it as a guid - unlikely we ever get here - if (int.TryParse(argument, out int parsedId)) - { - nodeId = parsedId; - } - else if (UdiParser.TryParse(argument, true, out var udi)) - { - nodeId = _entityService.GetId(udi).Result; - } - else - { - Guid.TryParse(argument, out Guid key); - nodeId = _entityService.GetId(key, UmbracoObjectTypes.Document).Result; - } - } - else - { - //now we need to see if we can get the property of whatever object it is - var pType = context.ActionArguments[parts[0]].GetType(); - var prop = pType.GetProperty(parts[1]); - if (prop == null) - { - throw new InvalidOperationException( - "No argument found for the current action with the name: " + _paramName); - } - - nodeId = (int) prop.GetValue(context.ActionArguments[parts[0]]); - } - } - else - { - nodeId = _nodeId.Value; - } - - var permissionResult = ContentPermissionsHelper.CheckPermissions(nodeId, - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, - _userService, - _contentService, - _entityService, - out var contentItem, - _permissionToCheck.HasValue ? new[] { _permissionToCheck.Value } : null); - - if (permissionResult == ContentPermissionsHelper.ContentAccess.NotFound) - { - context.Result = new NotFoundResult(); - return; - } - - if (permissionResult == ContentPermissionsHelper.ContentAccess.Denied) - { - context.Result = new ForbidResult(); - return; - } - - - if (contentItem != null) - { - //store the content item in request cache so it can be resolved in the controller without re-looking it up - context.HttpContext.Items[typeof(IContent).ToString()] = contentItem; - } - - } - - public void OnActionExecuted(ActionExecutedContext context) - { - - } - - - } - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs deleted file mode 100644 index cabc865d40..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Security; -using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Controllers; -using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Editors; -using Umbraco.Web.Security; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// Auth filter to check if the current user has access to the content item - /// - /// - /// Since media doesn't have permissions, this simply checks start node access - /// - internal sealed class EnsureUserPermissionForMediaAttribute : TypeFilterAttribute - { - public EnsureUserPermissionForMediaAttribute(int nodeId) - : base(typeof(EnsureUserPermissionForMediaFilter)) - { - Arguments = new object[] - { - nodeId - }; - } - - public EnsureUserPermissionForMediaAttribute(string paramName) - : base(typeof(EnsureUserPermissionForMediaFilter)) - { - Arguments = new object[] - { - paramName - }; - } - private sealed class EnsureUserPermissionForMediaFilter : IActionFilter - { - private readonly int? _nodeId; - private readonly string _paramName; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - private readonly IEntityService _entityService; - private readonly IMediaService _mediaService; - - /// - /// This constructor will only be able to test the start node access - /// - public EnsureUserPermissionForMediaFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IMediaService mediaService, - int nodeId) - :this(backofficeSecurityAccessor, entityService, mediaService, nodeId, null) - { - _nodeId = nodeId; - } - - public EnsureUserPermissionForMediaFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IMediaService mediaService, - string paramName) - :this(backofficeSecurityAccessor, entityService, mediaService,null, paramName) - { - if (paramName == null) throw new ArgumentNullException(nameof(paramName)); - if (string.IsNullOrEmpty(paramName)) - throw new ArgumentException("Value can't be empty.", nameof(paramName)); - } - - private EnsureUserPermissionForMediaFilter( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IEntityService entityService, - IMediaService mediaService, - int? nodeId, string paramName) - { - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - - _paramName = paramName; - - if (nodeId.HasValue) - { - _nodeId = nodeId.Value; - } - } - - private int GetNodeIdFromParameter(object parameterValue) - { - if (parameterValue is int) - { - return (int) parameterValue; - } - - var guidId = Guid.Empty; - if (parameterValue is Guid) - { - guidId = (Guid) parameterValue; - } - else if (parameterValue is GuidUdi) - { - guidId = ((GuidUdi) parameterValue).Guid; - } - - if (guidId != Guid.Empty) - { - var found = _entityService.GetId(guidId, UmbracoObjectTypes.Media); - if (found) - return found.Result; - } - - throw new InvalidOperationException("The id type: " + parameterValue.GetType() + - " is not a supported id"); - } - - public void OnActionExecuting(ActionExecutingContext context) - { - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser == null) - { - throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); - } - - int nodeId; - if (_nodeId.HasValue == false) - { - var parts = _paramName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - - if (context.ActionArguments[parts[0]] == null) - { - throw new InvalidOperationException("No argument found for the current action with the name: " + - _paramName); - } - - if (parts.Length == 1) - { - nodeId = GetNodeIdFromParameter(context.ActionArguments[parts[0]]); - } - else - { - //now we need to see if we can get the property of whatever object it is - var pType = context.ActionArguments[parts[0]].GetType(); - var prop = pType.GetProperty(parts[1]); - if (prop == null) - { - throw new InvalidOperationException( - "No argument found for the current action with the name: " + _paramName); - } - - nodeId = GetNodeIdFromParameter(prop.GetValue(context.ActionArguments[parts[0]])); - } - } - else - { - nodeId = _nodeId.Value; - } - - if (MediaController.CheckPermissions( - context.HttpContext.Items, - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, - _mediaService, - _entityService, - nodeId)) - { - - } - else - { - throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); - } - } - - public void OnActionExecuted(ActionExecutedContext context) - { - - } - - } - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs index e6735d01e8..38c0333d8b 100644 --- a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs @@ -10,8 +10,6 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.Security; -using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.BackOffice.Filters { diff --git a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs index 679fcdad6b..c7a7a56f83 100644 --- a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs @@ -87,7 +87,7 @@ namespace Umbraco.Web.BackOffice.Filters var toRemove = new List(); foreach (dynamic item in items) { - var hasPathAccess = (item != null && ContentPermissionsHelper.HasPathAccess(item.Path, GetUserStartNodes(user), RecycleBinId)); + var hasPathAccess = (item != null && ContentPermissions.HasPathAccess(item.Path, GetUserStartNodes(user), RecycleBinId)); if (hasPathAccess == false) { toRemove.Add(item); diff --git a/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs index fa1ee568f0..3ba2b408ef 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs @@ -1,4 +1,6 @@ using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; @@ -6,9 +8,9 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Web.BackOffice.Authorization; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Security; namespace Umbraco.Web.BackOffice.Filters { @@ -21,39 +23,45 @@ namespace Umbraco.Web.BackOffice.Filters { } - private sealed class MediaItemSaveValidationFilter : IActionFilter + private sealed class MediaItemSaveValidationFilter : IAsyncActionFilter { - private readonly IEntityService _entityService; private readonly IPropertyValidationService _propertyValidationService; - - + private readonly IAuthorizationService _authorizationService; private readonly IMediaService _mediaService; - private readonly ILocalizedTextService _textService; private readonly ILoggerFactory _loggerFactory; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; public MediaItemSaveValidationFilter( ILoggerFactory loggerFactory, - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - ILocalizedTextService textService, IMediaService mediaService, - IEntityService entityService, - IPropertyValidationService propertyValidationService) + IPropertyValidationService propertyValidationService, + IAuthorizationService authorizationService) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService)); + _authorizationService = authorizationService ?? throw new ArgumentNullException(nameof(authorizationService)); } - public void OnActionExecuting(ActionExecutingContext context) + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // on executing... + await OnActionExecutingAsync(context); + + if (context.Result == null) + { + //need to pass the execution to next if a result was not set + await next(); + } + + // on executed... + } + + private async Task OnActionExecutingAsync(ActionExecutingContext context) { var model = (MediaItemSave) context.ActionArguments["contentItem"]; - var contentItemValidator = new MediaSaveModelValidator(_loggerFactory.CreateLogger(), _backofficeSecurityAccessor.BackOfficeSecurity, _textService, _propertyValidationService); + var contentItemValidator = new MediaSaveModelValidator(_loggerFactory.CreateLogger(), _propertyValidationService); - if (ValidateUserAccess(model, context)) + if (await ValidateUserAccessAsync(model, context)) { //now do each validation step if (contentItemValidator.ValidateExistingContent(model, context)) @@ -68,7 +76,7 @@ namespace Umbraco.Web.BackOffice.Filters /// /// /// - private bool ValidateUserAccess(MediaItemSave mediaItem, ActionExecutingContext actionContext) + private async Task ValidateUserAccessAsync(MediaItemSave mediaItem, ActionExecutingContext actionContext) { //We now need to validate that the user is allowed to be doing what they are doing. //Then if it is new, we need to lookup those permissions on the parent. @@ -100,11 +108,16 @@ namespace Umbraco.Web.BackOffice.Filters return false; } - if (MediaController.CheckPermissions( - actionContext.HttpContext.Items, - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, - _mediaService, _entityService, - contentIdToCheck, contentToCheck) == false) + var resource = contentToCheck == null + ? new MediaPermissionsResource(contentIdToCheck) + : new MediaPermissionsResource(contentToCheck); + + var authorizationResult = await _authorizationService.AuthorizeAsync( + actionContext.HttpContext.User, + resource, + AuthorizationPolicies.MediaPermissionByResource); + + if (!authorizationResult.Succeeded) { actionContext.Result = new ForbidResult(); return false; @@ -112,11 +125,6 @@ namespace Umbraco.Web.BackOffice.Filters return true; } - - public void OnActionExecuted(ActionExecutedContext context) - { - - } } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs index b398a4e401..0a59aadfa6 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs @@ -13,10 +13,8 @@ namespace Umbraco.Web.BackOffice.Filters { public MediaSaveModelValidator( ILogger logger, - IBackOfficeSecurity backofficeSecurity, - ILocalizedTextService textService, IPropertyValidationService propertyValidationService) - : base(logger, backofficeSecurity, textService, propertyValidationService) + : base(logger, propertyValidationService) { } } diff --git a/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs index 275220c8b4..65056e1a5b 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs @@ -20,6 +20,7 @@ namespace Umbraco.Web.BackOffice.Filters /// internal class MemberSaveModelValidator : ContentModelValidator> { + private readonly IBackOfficeSecurity _backofficeSecurity; private readonly IMemberTypeService _memberTypeService; private readonly IMemberService _memberService; private readonly IShortStringHelper _shortStringHelper; @@ -27,13 +28,13 @@ namespace Umbraco.Web.BackOffice.Filters public MemberSaveModelValidator( ILogger logger, IBackOfficeSecurity backofficeSecurity, - ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService, IShortStringHelper shortStringHelper, IPropertyValidationService propertyValidationService) - : base(logger, backofficeSecurity, textService, propertyValidationService) + : base(logger, propertyValidationService) { + _backofficeSecurity = backofficeSecurity; _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); @@ -96,7 +97,7 @@ namespace Umbraco.Web.BackOffice.Filters //if the user doesn't have access to sensitive values, then we need to validate the incoming properties to check //if a sensitive value is being submitted. - if (BackOfficeSecurity.CurrentUser.HasAccessToSensitiveData() == false) + if (_backofficeSecurity.CurrentUser.HasAccessToSensitiveData() == false) { var contentType = _memberTypeService.Get(model.PersistedContent.ContentTypeId); var sensitiveProperties = contentType diff --git a/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs index 7ba86f525e..b8109b0e0c 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs @@ -24,7 +24,6 @@ namespace Umbraco.Web.BackOffice.Filters { private readonly ILoggerFactory _loggerFactory; private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - private readonly ILocalizedTextService _textService; private readonly IMemberTypeService _memberTypeService; private readonly IMemberService _memberService; private readonly IShortStringHelper _shortStringHelper; @@ -33,7 +32,6 @@ namespace Umbraco.Web.BackOffice.Filters public MemberSaveValidationFilter( ILoggerFactory loggerFactory, IBackOfficeSecurityAccessor backofficeSecurityAccessor, - ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService, IShortStringHelper shortStringHelper, @@ -41,7 +39,6 @@ namespace Umbraco.Web.BackOffice.Filters { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); @@ -51,7 +48,7 @@ namespace Umbraco.Web.BackOffice.Filters public void OnActionExecuting(ActionExecutingContext context) { var model = (MemberSave)context.ActionArguments["contentItem"]; - var contentItemValidator = new MemberSaveModelValidator(_loggerFactory.CreateLogger(), _backofficeSecurityAccessor.BackOfficeSecurity, _textService, _memberTypeService, _memberService, _shortStringHelper, _propertyValidationService); + var contentItemValidator = new MemberSaveModelValidator(_loggerFactory.CreateLogger(), _backofficeSecurityAccessor.BackOfficeSecurity, _memberTypeService, _memberService, _shortStringHelper, _propertyValidationService); //now do each validation step if (contentItemValidator.ValidateExistingContent(model, context)) if (contentItemValidator.ValidateProperties(model, model, context)) diff --git a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs index 0593031a3c..39f691e190 100644 --- a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs +++ b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs @@ -4,7 +4,7 @@ using Umbraco.Core; namespace Umbraco.Web.BackOffice.Filters { - // TODO: This should probably be deleted, anything requiring this should move to a different controller + // TODO: This should be deleted, anything requiring this should move to a different controller public class OverrideAuthorizationFilterProvider : IFilterProvider, IFilterMetadata { public void OnProvidersExecuted(FilterProviderContext context) diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs deleted file mode 100644 index cc1bdd4cd5..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core.Security; -using Umbraco.Web.Security; - -namespace Umbraco.Web.BackOffice.Filters -{ - public class UmbracoApplicationAuthorizeAttribute : TypeFilterAttribute - { - public UmbracoApplicationAuthorizeAttribute(params string[] appName) : base(typeof(UmbracoApplicationAuthorizeFilter)) - { - base.Arguments = new object[] - { - appName - }; - } - - private class UmbracoApplicationAuthorizeFilter : IAuthorizationFilter - { - /// - /// Can be used by unit tests to enable/disable this filter - /// - internal static bool Enable = true; - - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - private readonly string[] _appNames; - - /// - /// Constructor to set any number of applications that the user needs access to be authorized - /// - /// - /// - /// If the user has access to any of the specified apps, they will be authorized. - /// - public UmbracoApplicationAuthorizeFilter(IBackOfficeSecurityAccessor backofficeSecurityAccessor, params string[] appName) - { - _backofficeSecurityAccessor = backofficeSecurityAccessor; - _appNames = appName; - } - - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!IsAuthorized()) - { - context.Result = new ForbidResult(); - } - } - - private bool IsAuthorized() - { - if (Enable == false) - { - return true; - } - - var authorized = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null - && _appNames.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess( - app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser)); - - return authorized; - } - } - } - -} diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs new file mode 100644 index 0000000000..a027531e06 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Filters/UmbracoRequireHttpsAttribute.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Core.Configuration.Models; + +namespace Umbraco.Web.BackOffice.Filters +{ + /// + /// If Umbraco.Core.UseHttps property in web.config is set to true, this filter will redirect any http access to https. + /// + public class UmbracoRequireHttpsAttribute : RequireHttpsAttribute + { + protected override void HandleNonHttpsRequest(AuthorizationFilterContext filterContext) + { + // just like the base class does, we'll just resolve the required services from the httpcontext. + // we want to re-use their code so we don't have much choice, else we have to do some code tricks, + // this is just easiest. + var optionsAccessor = filterContext.HttpContext.RequestServices.GetRequiredService>(); + if (optionsAccessor.Value.UseHttps) + { + // only continue if this flag is set + base.HandleNonHttpsRequest(filterContext); + } + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs deleted file mode 100644 index eef7469322..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; -using Umbraco.Core.Security; -using Umbraco.Web.Security; -using Umbraco.Web.Services; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// Ensures that the current user has access to the application for which the specified tree(s) belongs - /// - /// - /// This would allow a tree to be moved between sections - /// - public class UmbracoTreeAuthorizeAttribute : TypeFilterAttribute - { - public UmbracoTreeAuthorizeAttribute(params string[] treeAliases) : base(typeof(UmbracoTreeAuthorizeFilter)) - { - Arguments = new object[] - { - treeAliases - }; - } - - private sealed class UmbracoTreeAuthorizeFilter : IAuthorizationFilter - { - /// - /// Can be used by unit tests to enable/disable this filter - /// - internal static readonly bool Enable = true; - - private readonly string[] _treeAliases; - - private readonly ITreeService _treeService; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - - /// - /// Constructor to set authorization to be based on a tree alias for which application security will be applied - /// - /// - /// - /// - /// If the user has access to the application that the treeAlias is specified in, they will be authorized. - /// Multiple trees may be specified. - /// - public UmbracoTreeAuthorizeFilter(ITreeService treeService, IBackOfficeSecurityAccessor backofficeSecurityAccessor, - params string[] treeAliases) - { - _treeService = treeService ?? throw new ArgumentNullException(nameof(treeService)); - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _treeAliases = treeAliases; - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!IsAuthorized()) - { - context.Result = new ForbidResult(); - } - } - - private bool IsAuthorized() - { - if (Enable == false) - { - return true; - } - - var apps = _treeAliases.Select(x => _treeService - .GetByAlias(x)) - .WhereNotNull() - .Select(x => x.SectionAlias) - .Distinct() - .ToArray(); - - return _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null - && apps.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess( - app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser)); - } - } - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoWebApiRequireHttpsAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoWebApiRequireHttpsAttribute.cs deleted file mode 100644 index e2a1d942d9..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/UmbracoWebApiRequireHttpsAttribute.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Options; -using Umbraco.Core.Configuration.Models; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// If Umbraco.Core.UseHttps property in web.config is set to true, this filter will redirect any http access to https. - /// - /// - /// This will only redirect Head/Get requests, otherwise will respond with text - /// - /// References: - /// http://issues.umbraco.org/issue/U4-8542 - /// https://blogs.msdn.microsoft.com/carlosfigueira/2012/03/09/implementing-requirehttps-with-asp-net-web-api/ - /// - public class UmbracoWebApiRequireHttpsAttribute : TypeFilterAttribute - { - public UmbracoWebApiRequireHttpsAttribute() : base(typeof(UmbracoWebApiRequireHttpsFilter)) - { - Arguments = Array.Empty(); - } - } - - public class UmbracoWebApiRequireHttpsFilter: IAuthorizationFilter - { - private readonly GlobalSettings _globalSettings; - - public UmbracoWebApiRequireHttpsFilter(IOptions globalSettings) - { - _globalSettings = globalSettings.Value; - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - var request = context.HttpContext.Request; - if (_globalSettings.UseHttps && request.Scheme != Uri.UriSchemeHttps) - { - var uri = new UriBuilder() - { - Scheme = Uri.UriSchemeHttps, - Host = request.Host.Value, - Path = request.Path, - Query = request.QueryString.ToUriComponent(), - Port = 443 - }; - var body = string.Format("

The resource can be found at {0}.

", - uri.Uri.AbsoluteUri); - if (request.Method.Equals(HttpMethod.Get.ToString()) || request.Method.Equals(HttpMethod.Head.ToString())) - { - context.HttpContext.Response.Headers.Add("Location", uri.Uri.ToString()); - context.Result = new ObjectResult(body) - { - StatusCode = (int)HttpStatusCode.Found, - }; - - } - else - { - context.Result = new ObjectResult(body) - { - StatusCode = (int)HttpStatusCode.NotFound - }; - } - - - } - } - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs deleted file mode 100644 index 5fa9dd54be..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; -using Umbraco.Core.Security; -using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Controllers; -using Umbraco.Web.Security; - -namespace Umbraco.Web.BackOffice.Filters -{ - /// - /// Authorizes that the current user has access to the user group Id in the request - /// - /// - /// This will authorize against one or multiple ids - /// - public sealed class UserGroupAuthorizationAttribute : TypeFilterAttribute - { - - public UserGroupAuthorizationAttribute(string parameterName): base(typeof(UserGroupAuthorizationFilter)) - { - Arguments = new object[] { parameterName }; - } - - public UserGroupAuthorizationAttribute() : this("id") - { - } - - private class UserGroupAuthorizationFilter : IAuthorizationFilter - { - private readonly string _parameterName; - private readonly IRequestAccessor _requestAccessor; - private readonly IUserService _userService; - private readonly IContentService _contentService; - private readonly IMediaService _mediaService; - private readonly IEntityService _entityService; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - - public UserGroupAuthorizationFilter( - IRequestAccessor requestAccessor, - IUserService userService, - IContentService contentService, - IMediaService mediaService, - IEntityService entityService, - IBackOfficeSecurityAccessor backofficeSecurityAccessor, - string parameterName) - { - _requestAccessor = requestAccessor; - _userService = userService; - _contentService = contentService; - _mediaService = mediaService; - _entityService = entityService; - _backofficeSecurityAccessor = backofficeSecurityAccessor; - _parameterName = parameterName; - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!IsAuthorized(context)) - { - context.Result = new ForbidResult(); - } - } - - private bool IsAuthorized(AuthorizationFilterContext actionContext) - { - var currentUser = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - - var queryString = actionContext.HttpContext.Request.Query; - - var ids = queryString.Where(x => x.Key == _parameterName).ToArray(); - if (ids.Length == 0) - return true; - - var intIds = ids.Select(x => x.Value.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray(); - var authHelper = new UserGroupEditorAuthorizationHelper( - _userService, - _contentService, - _mediaService, - _entityService); - return authHelper.AuthorizeGroupAccess(currentUser, intIds); - - } - } - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs index 556a84e0dc..f7fd174e03 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs @@ -53,13 +53,13 @@ namespace Umbraco.Web.BackOffice.Filters var cookieToken = _cookieManager.GetCookieValue(Constants.Web.CsrfValidationCookieName); var httpContext = context.HttpContext; - var validateResult = await ValidateHeaders(httpContext, cookieToken); - if (validateResult.Item1 == false) - { - httpContext.SetReasonPhrase(validateResult.Item2); - context.Result = new StatusCodeResult((int)HttpStatusCode.ExpectationFailed); - return; - } + var validateResult = await ValidateHeaders(httpContext, cookieToken); + if (validateResult.Item1 == false) + { + httpContext.SetReasonPhrase(validateResult.Item2); + context.Result = new StatusCodeResult((int)HttpStatusCode.ExpectationFailed); + return; + } await next(); } diff --git a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs index 768ede1787..002cfe4d7b 100644 --- a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs @@ -10,6 +10,8 @@ using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.HealthCheck { @@ -17,7 +19,7 @@ namespace Umbraco.Web.BackOffice.HealthCheck /// The API controller used to display the health check info and execute any actions /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Settings)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class HealthCheckController : UmbracoAuthorizedJsonController { private readonly HealthCheckCollection _checks; diff --git a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs index a2ad2cd834..3e81bde207 100644 --- a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs +++ b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs @@ -204,7 +204,7 @@ namespace Umbraco.Web.BackOffice.Mapping var umbracoContext = _umbracoContextAccessor.UmbracoContext; var urls = umbracoContext == null - ? new[] { UrlInfo.Message("Cannot generate urls without a current Umbraco Context") } + ? new[] { UrlInfo.Message("Cannot generate URLs without a current Umbraco Context") } : source.GetContentUrls(_publishedRouter, umbracoContext, _localizationService, _localizedTextService, _contentService, _variationContextAccessor, _loggerFactory.CreateLogger(), _uriUtility, _publishedUrlProvider).ToArray(); return urls; diff --git a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs index 101e00d752..0908522d9e 100644 --- a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs +++ b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs @@ -4,6 +4,8 @@ using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Controllers; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Profiling { @@ -11,7 +13,7 @@ namespace Umbraco.Web.BackOffice.Profiling /// The API controller used to display the state of the web profiler /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - [UmbracoApplicationAuthorize(Constants.Applications.Settings)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class WebProfilingController : UmbracoAuthorizedJsonController { private readonly IHostingEnvironment _hosting; diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs index 164864410c..49c2f0e4d4 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs @@ -32,8 +32,8 @@ namespace Umbraco.Web.BackOffice.PropertyEditors foreach (var provider in _embedCollection) { - //Url Scheme Regex is an array of possible regex patterns to match against the URL - foreach(var urlPattern in provider.UrlSchemeRegex) + // UrlSchemeRegex is an array of possible regex patterns to match against the URL + foreach (var urlPattern in provider.UrlSchemeRegex) { var regexPattern = new Regex(urlPattern, RegexOptions.IgnoreCase); if (regexPattern.IsMatch(url)) @@ -63,7 +63,7 @@ namespace Umbraco.Web.BackOffice.PropertyEditors } catch(Exception ex) { - _logger.LogError(ex, "Error embedding url {Url} - width: {Width} height: {Height}", url, width, height); + _logger.LogError(ex, "Error embedding URL {Url} - width: {Width} height: {Height}", url, width, height); result.OEmbedStatus = OEmbedStatus.Error; } diff --git a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs index d11dffb1ac..2f1b42aed5 100644 --- a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs +++ b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs @@ -45,11 +45,17 @@ namespace Umbraco.Web.BackOffice.Runtime builder.ComposeWebMappingProfiles(); builder.Services.AddUnique(factory => - new PhysicalFileSystem( + { + var path = "~/"; + var hostingEnvironment = factory.GetRequiredService(); + return new PhysicalFileSystem( factory.GetRequiredService(), - factory.GetRequiredService(), + hostingEnvironment, factory.GetRequiredService>(), - "~/")); + hostingEnvironment.MapPathContentRoot(path), + hostingEnvironment.ToAbsolute(path) + ); + }); builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs index c3e3c94b93..2b9370fedd 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs @@ -216,7 +216,7 @@ namespace Umbraco.Web.BackOffice.Trees { if (tree == null) throw new ArgumentNullException(nameof(tree)); - var controller = (TreeController) await GetApiControllerProxy(tree.TreeControllerType, "GetRootNode", querystring); + var controller = (TreeControllerBase) await GetApiControllerProxy(tree.TreeControllerType, "GetRootNode", querystring); var rootNode = controller.GetRootNode(querystring); if (rootNode == null) throw new InvalidOperationException($"Failed to get root node for tree \"{tree.TreeAlias}\"."); @@ -237,7 +237,7 @@ namespace Umbraco.Web.BackOffice.Trees d["id"] = StringValues.Empty; var proxyQuerystring = new FormCollection(d); - var controller = (TreeController) await GetApiControllerProxy(tree.TreeControllerType, "GetNodes", proxyQuerystring); + var controller = (TreeControllerBase) await GetApiControllerProxy(tree.TreeControllerType, "GetNodes", proxyQuerystring); return controller.GetNodes(id.ToInvariantString(), querystring); } diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs index 5477797ccc..e232bf03b9 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Models; @@ -8,6 +9,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -21,7 +23,7 @@ namespace Umbraco.Web.BackOffice.Trees /// /// This authorizes based on access to the content section even though it exists in the settings /// - [UmbracoApplicationAuthorize(Constants.Applications.Content)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessContent)] [Tree(Constants.Applications.Settings, Constants.Trees.ContentBlueprints, SortOrder = 12, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs index 755acdf0cb..16dd446d49 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs @@ -12,29 +12,20 @@ using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; -using Umbraco.Core.Configuration; using Umbraco.Core.Security; using Constants = Umbraco.Core.Constants; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Security; using Umbraco.Web.WebApi; using Umbraco.Core.Configuration.Models; using Microsoft.Extensions.Options; using Umbraco.Web.Trees; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered - // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here. - [UmbracoApplicationAuthorize( - Constants.Applications.Content, - Constants.Applications.Media, - Constants.Applications.Users, - Constants.Applications.Settings, - Constants.Applications.Packages, - Constants.Applications.Members)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForContentTree)] [Tree(Constants.Applications.Content, Constants.Trees.Content)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs index 36b9f7f5de..53a6f02a79 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs @@ -123,7 +123,7 @@ namespace Umbraco.Web.BackOffice.Trees internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormCollection queryStrings, int[] startNodeIds, string[] startNodePaths) { - var entityIsAncestorOfStartNodes = ContentPermissionsHelper.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out var hasPathAccess); + var entityIsAncestorOfStartNodes = ContentPermissions.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out var hasPathAccess); var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); if (ignoreUserStartNodes == false && entityIsAncestorOfStartNodes == false) return null; diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs index 5c8312c058..8b5286bdd2 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Models; @@ -8,6 +9,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Search; @@ -16,7 +18,7 @@ using Umbraco.Web.WebApi; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.DocumentTypes, SortOrder = 0, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs index 3502d7a506..ab2bfdb8d4 100644 --- a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs @@ -14,10 +14,12 @@ using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.DataTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, SortOrder = 3, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs index a9878a3dbc..2dd145c654 100644 --- a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs @@ -1,12 +1,13 @@ using System; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -14,12 +15,9 @@ using Umbraco.Web.WebApi; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize( - Constants.Trees.Dictionary, - Constants.Trees.Templates - // We are allowed to see the dictionary tree, if we are allowed to manage templates, such that se can use the - // dictionary items in templates, even when we dont have authorization to manage the dictionary items - )] + // We are allowed to see the dictionary tree, if we are allowed to manage templates, such that se can use the + // dictionary items in templates, even when we dont have authorization to manage the dictionary items + [Authorize(Policy = AuthorizationPolicies.TreeAccessDictionaryOrTemplates)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] [Tree(Constants.Applications.Translation, Constants.Trees.Dictionary, TreeGroup = Constants.Trees.Groups.Settings)] @@ -85,7 +83,7 @@ namespace Umbraco.Web.BackOffice.Trees } else { - // maybe we should use the guid as url param to avoid the extra call for getting dictionary item + // maybe we should use the guid as URL param to avoid the extra call for getting dictionary item var parentDictionary = _localizationService.GetDictionaryItemById(intId.Result); if (parentDictionary == null) return nodes; diff --git a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs index d4a2c91fad..ecd1c954ac 100644 --- a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs @@ -1,7 +1,9 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.Languages)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)] [Tree(Constants.Applications.Settings, Constants.Trees.Languages, SortOrder = 11, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs index 9ffe6e8b68..b03b2d9926 100644 --- a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs @@ -1,7 +1,9 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.LogViewer)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessLogs)] [Tree(Constants.Applications.Settings, Constants.Trees.LogViewer, SortOrder= 9, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs index 567f0a3646..518c1b5495 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs @@ -8,10 +8,12 @@ using Umbraco.Web.Common.Attributes; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Constants = Umbraco.Core.Constants; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.Macros)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMacros)] [Tree(Constants.Applications.Settings, Constants.Trees.Macros, TreeTitle = "Macros", SortOrder = 4, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs index ab332e3843..ece4013d0b 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs @@ -20,17 +20,12 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Security; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered - // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here. - [UmbracoApplicationAuthorize( - Constants.Applications.Content, - Constants.Applications.Media, - Constants.Applications.Settings, - Constants.Applications.Packages, - Constants.Applications.Members)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForMediaTree)] [Tree(Constants.Applications.Media, Constants.Trees.Media)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs index 4df81b7023..cd64e23067 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs @@ -13,10 +13,12 @@ using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.MediaTypes, SortOrder = 1, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs index 17de4ca37d..817b32f301 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs @@ -1,17 +1,19 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.MemberGroups)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)] [Tree(Constants.Applications.Members, Constants.Trees.MemberGroups, SortOrder = 1)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs index 31ab66908c..4ebd8f7cc5 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs @@ -20,15 +20,12 @@ using Constants = Umbraco.Core.Constants; using Umbraco.Web.Security; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered - // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here. - [UmbracoApplicationAuthorize( - Constants.Applications.Content, - Constants.Applications.Media, - Constants.Applications.Members)] + [Authorize(Policy = AuthorizationPolicies.SectionAccessForMemberTree)] [Tree(Constants.Applications.Members, Constants.Trees.Members, SortOrder = 0)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs index c9e340617e..be400bef39 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Models; @@ -7,6 +8,7 @@ using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Trees; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Search; @@ -16,7 +18,7 @@ using Umbraco.Web.WebApi; namespace Umbraco.Web.BackOffice.Trees { [CoreTree] - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, SortOrder = 2, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase, ISearchableTree diff --git a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs index fc1f3d876f..5c96bb4d64 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs @@ -1,7 +1,9 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.Packages)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessPackages)] [Tree(Constants.Applications.Packages, Constants.Trees.Packages, SortOrder = 0, IsSingleNodeTree = true)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs index 5baeac7d17..484ea21b2f 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs @@ -1,7 +1,9 @@ -using Umbraco.Core.IO; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Core.IO; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Constants = Umbraco.Core.Constants; @@ -12,7 +14,7 @@ namespace Umbraco.Web.BackOffice.Trees /// Tree for displaying partial view macros in the developer app /// [Tree(Constants.Applications.Settings, Constants.Trees.PartialViewMacros, SortOrder = 8, TreeGroup = Constants.Trees.Groups.Templating)] - [UmbracoTreeAuthorize(Constants.Trees.PartialViewMacros)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViewMacros)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] public class PartialViewMacrosTreeController : PartialViewsTreeController diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs index dcb98ac5b4..b648bd797f 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs @@ -1,7 +1,9 @@ -using Umbraco.Core.IO; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Core.IO; using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Composing; using Umbraco.Web.Mvc; using Umbraco.Web.Trees; @@ -14,7 +16,7 @@ namespace Umbraco.Web.BackOffice.Trees /// Tree for displaying partial views in the settings app /// [Tree(Core.Constants.Applications.Settings, Core.Constants.Trees.PartialViews, SortOrder = 7, TreeGroup = Core.Constants.Trees.Groups.Templating)] - [UmbracoTreeAuthorize(Constants.Trees.PartialViews)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViews)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] public class PartialViewsTreeController : FileSystemTreeController diff --git a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs index 965e957f02..a36c2f36a9 100644 --- a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs @@ -9,10 +9,12 @@ using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.RelationTypes)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessRelationTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.RelationTypes, SortOrder = 5, TreeGroup = Constants.Trees.Groups.Settings)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs index cfe2d57ce5..361875a41b 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Models; @@ -10,6 +11,7 @@ using Umbraco.Extensions; using Umbraco.Web.Actions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Search; @@ -19,7 +21,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.Templates)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessTemplates)] [Tree(Constants.Applications.Settings, Constants.Trees.Templates, SortOrder = 6, TreeGroup = Constants.Trees.Groups.Templating)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs index 15130002df..e469cd4a25 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs @@ -238,7 +238,7 @@ namespace Umbraco.Web.BackOffice.Trees } /// - /// Helper method to create tree nodes and automatically generate the json url + UDI + /// Helper method to create tree nodes and automatically generate the json URL + UDI /// /// /// @@ -258,7 +258,7 @@ namespace Umbraco.Web.BackOffice.Trees } /// - /// Helper method to create tree nodes and automatically generate the json url + UDI + /// Helper method to create tree nodes and automatically generate the json URL + UDI /// /// /// @@ -277,7 +277,7 @@ namespace Umbraco.Web.BackOffice.Trees } /// - /// Helper method to create tree nodes and automatically generate the json url + /// Helper method to create tree nodes and automatically generate the json URL /// /// /// @@ -294,7 +294,7 @@ namespace Umbraco.Web.BackOffice.Trees } /// - /// Helper method to create tree nodes and automatically generate the json url + /// Helper method to create tree nodes and automatically generate the json URL /// /// /// @@ -313,7 +313,7 @@ namespace Umbraco.Web.BackOffice.Trees } /// - /// Helper method to create tree nodes and automatically generate the json url + UDI + /// Helper method to create tree nodes and automatically generate the json URL + UDI /// /// /// diff --git a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs index feb8d2a9ec..960ed76ac5 100644 --- a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs @@ -1,22 +1,16 @@ -using Microsoft.AspNetCore.Http; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Mapping; -using Umbraco.Core.Persistence; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; -using Umbraco.Web.Routing; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { - [UmbracoTreeAuthorize(Constants.Trees.Users)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessUsers)] [Tree(Constants.Applications.Users, Constants.Trees.Users, SortOrder = 0, IsSingleNodeTree = true)] [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] [CoreTree] diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs index ad168114e0..f67e062847 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -79,16 +79,21 @@ namespace Umbraco.Web.Common.AspNetCore } } - public string MapPathWebRoot(string path) - { - var newPath = path.TrimStart('~', '/').Replace('/', Path.DirectorySeparatorChar); - return Path.Combine(_webHostEnvironment.WebRootPath, newPath); - } + public string MapPathWebRoot(string path) => MapPath(_webHostEnvironment.WebRootPath, path); + public string MapPathContentRoot(string path) => MapPath(_webHostEnvironment.ContentRootPath, path); - public string MapPathContentRoot(string path) + private string MapPath(string root, string path) { - var newPath = path.TrimStart('~', '/').Replace('/', Path.DirectorySeparatorChar); - return Path.Combine(_webHostEnvironment.ContentRootPath, newPath); + var newPath = path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + + // TODO: This is a temporary error because we switched from IOHelper.MapPath to HostingEnvironment.MapPathXXX + // IOHelper would check if the path passed in started with the root, and not prepend the root again if it did, + // however if you are requesting a path be mapped, it should always assume the path is relative to the root, not + // absolute in the file system. This error will help us find and fix improper uses, and should be removed once + // all those uses have been found and fixed + if (newPath.StartsWith(root)) throw new ArgumentException("The path appears to already be fully qualified. Please remove the call to MapPath"); + + return Path.Combine(root, newPath.TrimStart('~', '/', '\\')); } public string ToAbsolute(string virtualPath) @@ -100,7 +105,7 @@ namespace Umbraco.Web.Common.AspNetCore if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute)) return virtualPath; - var fullPath = ApplicationVirtualPath.EnsureEndsWith('/') + virtualPath.TrimStart("~/").TrimStart("/"); + var fullPath = ApplicationVirtualPath.EnsureEndsWith('/') + virtualPath.TrimStart('~', '/'); return fullPath; } diff --git a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs new file mode 100644 index 0000000000..56070f5033 --- /dev/null +++ b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs @@ -0,0 +1,82 @@ +namespace Umbraco.Web.Common.Authorization +{ + /// + /// A list of authorization policy names for use in the back office + /// + public static class AuthorizationPolicies + { + public const string UmbracoFeatureEnabled = nameof(UmbracoFeatureEnabled); + + public const string BackOfficeAccess = nameof(BackOfficeAccess); + public const string BackOfficeAccessWithoutApproval = nameof(BackOfficeAccessWithoutApproval); + public const string UserBelongsToUserGroupInRequest = nameof(UserBelongsToUserGroupInRequest); + public const string AdminUserEditsRequireAdmin = nameof(AdminUserEditsRequireAdmin); + public const string DenyLocalLoginIfConfigured = nameof(DenyLocalLoginIfConfigured); + + // Content permission access + + public const string ContentPermissionByResource = nameof(ContentPermissionByResource); + public const string ContentPermissionEmptyRecycleBin = nameof(ContentPermissionEmptyRecycleBin); + public const string ContentPermissionAdministrationById = nameof(ContentPermissionAdministrationById); + public const string ContentPermissionPublishById = nameof(ContentPermissionPublishById); + public const string ContentPermissionRollbackById = nameof(ContentPermissionRollbackById); + public const string ContentPermissionProtectById = nameof(ContentPermissionProtectById); + public const string ContentPermissionBrowseById = nameof(ContentPermissionBrowseById); + public const string ContentPermissionDeleteById = nameof(ContentPermissionDeleteById); + + public const string MediaPermissionByResource = nameof(MediaPermissionByResource); + public const string MediaPermissionPathById = nameof(MediaPermissionPathById); + + + // Single section access + + public const string SectionAccessContent = nameof(SectionAccessContent); + public const string SectionAccessPackages = nameof(SectionAccessPackages); + public const string SectionAccessUsers = nameof(SectionAccessUsers); + public const string SectionAccessMedia = nameof(SectionAccessMedia); + public const string SectionAccessSettings = nameof(SectionAccessSettings); + public const string SectionAccessMembers = nameof(SectionAccessMembers); + + // Custom access based on multiple sections + + public const string SectionAccessContentOrMedia = nameof(SectionAccessContentOrMedia); + public const string SectionAccessForTinyMce = nameof(SectionAccessForTinyMce); + public const string SectionAccessForMemberTree = nameof(SectionAccessForMemberTree); + public const string SectionAccessForMediaTree = nameof(SectionAccessForMediaTree); + public const string SectionAccessForContentTree = nameof(SectionAccessForContentTree); + public const string SectionAccessForDataTypeReading = nameof(SectionAccessForDataTypeReading); + + // Single tree access + + public const string TreeAccessDocuments = nameof(TreeAccessDocuments); + public const string TreeAccessUsers = nameof(TreeAccessUsers); + public const string TreeAccessPartialViews = nameof(TreeAccessPartialViews); + public const string TreeAccessPartialViewMacros = nameof(TreeAccessPartialViewMacros); + public const string TreeAccessDataTypes = nameof(TreeAccessDataTypes); + public const string TreeAccessPackages = nameof(TreeAccessPackages); + public const string TreeAccessLogs = nameof(TreeAccessLogs); + public const string TreeAccessTemplates = nameof(TreeAccessTemplates); + public const string TreeAccessDictionary = nameof(TreeAccessDictionary); + public const string TreeAccessRelationTypes = nameof(TreeAccessRelationTypes); + public const string TreeAccessMediaTypes = nameof(TreeAccessMediaTypes); + public const string TreeAccessMacros = nameof(TreeAccessMacros); + public const string TreeAccessLanguages = nameof(TreeAccessLanguages); + public const string TreeAccessMemberGroups = nameof(TreeAccessMemberGroups); + public const string TreeAccessDocumentTypes = nameof(TreeAccessDocumentTypes); + public const string TreeAccessMemberTypes = nameof(TreeAccessMemberTypes); + + // Custom access based on multiple trees + + public const string TreeAccessDocumentsOrDocumentTypes = nameof(TreeAccessDocumentsOrDocumentTypes); + public const string TreeAccessMediaOrMediaTypes = nameof(TreeAccessMediaOrMediaTypes); + public const string TreeAccessMembersOrMemberTypes = nameof(TreeAccessMembersOrMemberTypes); + public const string TreeAccessAnySchemaTypes = nameof(TreeAccessAnySchemaTypes); + public const string TreeAccessDictionaryOrTemplates = nameof(TreeAccessDictionaryOrTemplates); + + /// + /// Defines access based on if the user has access to any tree's exposing any types of content (documents, media, members) + /// or any content types (document types, media types, member types) + /// + public const string TreeAccessAnyContentOrTypes = nameof(TreeAccessAnyContentOrTypes); + } +} diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs new file mode 100644 index 0000000000..3f2fc0b6bb --- /dev/null +++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Controllers; +using System; +using System.Threading.Tasks; +using Umbraco.Web.Features; + +namespace Umbraco.Web.BackOffice.Authorization +{ + /// + /// Ensures that the controller is an authorized feature. + /// + public class FeatureAuthorizeHandler : AuthorizationHandler + { + private readonly UmbracoFeatures _umbracoFeatures; + + public FeatureAuthorizeHandler(UmbracoFeatures umbracoFeatures) + { + _umbracoFeatures = umbracoFeatures; + } + + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FeatureAuthorizeRequirement requirement) + { + var allowed = IsAllowed(context); + if (!allowed.HasValue || allowed.Value) + { + context.Succeed(requirement); + } + else + { + context.Fail(); + } + return Task.CompletedTask; + } + + private bool? IsAllowed(AuthorizationHandlerContext context) + { + if (!(context.Resource is Endpoint endpoint)) + { + throw new InvalidOperationException("This authorization handler can only be applied to controllers routed with endpoint routing"); + } + + var actionDescriptor = endpoint.Metadata.GetMetadata(); + var controllerType = actionDescriptor.ControllerTypeInfo.AsType(); + return _umbracoFeatures.IsControllerEnabled(controllerType); + } + } +} diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs new file mode 100644 index 0000000000..87614d7f19 --- /dev/null +++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Umbraco.Web.BackOffice.Authorization +{ + + /// + /// Authorization requirement for the + /// + public class FeatureAuthorizeRequirement : IAuthorizationRequirement + { + } +} diff --git a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs index 4c464ad989..1fae03ba75 100644 --- a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs @@ -109,7 +109,24 @@ namespace Umbraco.Web.Common.Builder : new MainDomSemaphoreLock(loggerFactory.CreateLogger(), hostingEnvironment); }); - builder.Services.AddUnique(); + builder.Services.AddUnique(factory => + { + var hostingEnvironment = factory.GetRequiredService(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return new IOHelperLinux(hostingEnvironment); + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return new IOHelperOSX(hostingEnvironment); + } + + return new IOHelperWindows(hostingEnvironment); + } + + ); builder.Services.AddUnique(factory => factory.GetRequiredService().RuntimeCache); builder.Services.AddUnique(factory => factory.GetRequiredService().RequestCache); builder.Services.AddUnique(); @@ -124,7 +141,7 @@ namespace Umbraco.Web.Common.Builder builder.Services.AddUnique(); builder.AddComposers(); - + return builder; } diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs index c53b08d3df..e3250c2983 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs @@ -7,6 +7,8 @@ namespace Umbraco.Web.Common.Controllers /// public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + protected UmbracoApiController() { } diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs index 787da05ca4..9f9e2b19be 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs @@ -1,8 +1,9 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Filters; using Umbraco.Web.Features; -using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Common.Controllers { @@ -13,11 +14,13 @@ namespace Umbraco.Web.Common.Controllers /// These controllers are NOT auto-routed. /// The base class is which are netcore API controllers without any view support /// - [FeatureAuthorize] // TODO: This could be part of our conventions + [Authorize(Policy = AuthorizationPolicies.UmbracoFeatureEnabled)] // TODO: This could be part of our conventions [TypeFilter(typeof(HttpResponseExceptionFilter))] // TODO: This could be part of our conventions [UmbracoApiController] public abstract class UmbracoApiControllerBase : ControllerBase, IUmbracoFeature { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + public UmbracoApiControllerBase() { } diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs index 4bffb0d5ba..8d68e95dd8 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs @@ -5,6 +5,8 @@ namespace Umbraco.Web.Common.Controllers { public class UmbracoApiControllerTypeCollectionBuilder : TypeCollectionBuilderBase { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + protected override UmbracoApiControllerTypeCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs index 596c42e3b3..caf4132664 100644 --- a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -8,9 +7,7 @@ using Smidge; using Smidge.Nuglify; using StackExchange.Profiling; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Hosting; -using Umbraco.Core.Runtime; using Umbraco.Infrastructure.Logging.Serilog.Enrichers; using Umbraco.Web.Common.Middleware; diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index 45c6c15000..9c6a016b57 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -14,7 +14,7 @@ namespace Umbraco.Extensions public static class ImageCropperTemplateCoreExtensions { /// - /// Gets the ImageProcessor Url by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item + /// Gets the ImageProcessor URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item /// /// /// The IPublishedContent item. @@ -26,7 +26,7 @@ namespace Umbraco.Extensions /// The published value fallback. /// The published url provider. /// - /// The ImageProcessor.Web Url. + /// The ImageProcessor.Web URL. /// public static string GetCropUrl( this IPublishedContent mediaItem, @@ -39,7 +39,7 @@ namespace Umbraco.Extensions } /// - /// Gets the ImageProcessor Url by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. + /// Gets the ImageProcessor URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. /// /// /// The IPublishedContent item. @@ -54,7 +54,7 @@ namespace Umbraco.Extensions /// The published value fallback. /// The published url provider. /// - /// The ImageProcessor.Web Url. + /// The ImageProcessor.Web URL. /// public static string GetCropUrl( this IPublishedContent mediaItem, @@ -68,7 +68,7 @@ namespace Umbraco.Extensions } /// - /// Gets the ImageProcessor Url from the IPublishedContent item. + /// Gets the ImageProcessor URL from the IPublishedContent item. /// /// /// The IPublishedContent item. @@ -181,10 +181,10 @@ namespace Umbraco.Extensions } /// - /// Gets the ImageProcessor Url from the image path. + /// Gets the ImageProcessor URL from the image path. /// /// - /// The image url. + /// The image URL. /// /// /// The width of the output image. @@ -263,13 +263,13 @@ namespace Umbraco.Extensions } /// - /// Gets the ImageProcessor Url from the image path. + /// Gets the ImageProcessor URL from the image path. /// /// - /// The image url. + /// The image URL. /// /// - /// The generator that will process all the options and the image URL to return a full image urls with all processing options appended + /// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended /// /// /// diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateExtensions.cs index 3af01da3de..db2561b998 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateExtensions.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace Umbraco.Extensions { /// - /// Provides extension methods for getting ImageProcessor Url from the core Image Cropper property editor + /// Provides extension methods for getting ImageProcessor URL from the core Image Cropper property editor /// public static class ImageCropperTemplateExtensions { diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000000..e8e3e2c329 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Web.BackOffice.Authorization; +using Umbraco.Web.Common.Authorization; + +namespace Umbraco.Extensions +{ + public static class ServiceCollectionExtensions + { + public static void AddUmbracoCommonAuthorizationPolicies(this IServiceCollection services) + { + // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK + // If it is moved it should only target the back office scheme + + services.AddSingleton(); + + services.AddAuthorization(options => + { + options.AddPolicy(AuthorizationPolicies.UmbracoFeatureEnabled, policy => + policy.Requirements.Add(new FeatureAuthorizeRequirement())); + }); + } + } + +} diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs index 49500a7eda..f42fc274f0 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs @@ -22,7 +22,7 @@ namespace Umbraco.Extensions { public static class UmbracoCoreServiceCollectionExtensions { - + /// /// Create and configure the logger /// diff --git a/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs deleted file mode 100644 index 061225334a..0000000000 --- a/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs +++ /dev/null @@ -1,51 +0,0 @@ - -using System; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Web.Features; -using Umbraco.Core; -using Umbraco.Web.Install; - -namespace Umbraco.Web.WebApi.Filters -{ - /// - /// Ensures that the controller is an authorized feature. - /// - /// Else returns unauthorized. - public class FeatureAuthorizeAttribute : TypeFilterAttribute - { - public FeatureAuthorizeAttribute() : base(typeof(FeatureAuthorizeFilter)) - { - } - - private class FeatureAuthorizeFilter : IAuthorizationFilter - { - public void OnAuthorization(AuthorizationFilterContext context) - { - var serviceProvider = context.HttpContext.RequestServices; - var umbracoFeatures = serviceProvider.GetService(); - - if (!IsAllowed(context, umbracoFeatures)) - { - context.Result = new ForbidResult(); - } - } - - private static bool IsAllowed(AuthorizationFilterContext context, UmbracoFeatures umbracoFeatures) - { - // if no features resolver has been set then return true, this will occur in unit - // tests and we don't want users to have to set a resolver - //just so their unit tests work. - - if (umbracoFeatures == null) return true; - if (!(context.ActionDescriptor is ControllerActionDescriptor contextActionDescriptor)) return true; - - var controllerType = contextActionDescriptor.ControllerTypeInfo.AsType(); - return umbracoFeatures.IsControllerEnabled(controllerType); - } - } - } -} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs deleted file mode 100644 index 5ddf285601..0000000000 --- a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Umbraco.Web.Common.Filters -{ - /// - /// Ensures authorization is successful for a back office user. - /// - public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute, IAuthorizeData - { - // Implements IAuthorizeData to return the back office scheme so that all requests with this attributes - // get authenticated with this scheme. - // TODO: We'll have to refactor this as part of the authz policy changes. - public string AuthenticationSchemes { get; set; } = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType; - public string Policy { get; set; } - public string Roles { get; set; } - - /// - /// Default constructor - /// - public UmbracoBackOfficeAuthorizeAttribute() : this(false, false) - { - } - - /// - /// Constructor with redirect umbraco login behavior - /// - /// - /// - - public UmbracoBackOfficeAuthorizeAttribute(bool redirectToUmbracoLogin, bool requireApproval) : base(typeof(UmbracoBackOfficeAuthorizeFilter)) - { - Arguments = new object[] { redirectToUmbracoLogin, requireApproval }; - } - - /// - /// Constructor with redirect url behavior - /// - /// - public UmbracoBackOfficeAuthorizeAttribute(string redirectUrl) : base(typeof(UmbracoBackOfficeAuthorizeFilter)) - { - Arguments = new object[] { redirectUrl }; - } - } -} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs deleted file mode 100644 index c8ae0aacd8..0000000000 --- a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Routing; -using System; -using Umbraco.Core; -using Umbraco.Core.Security; -using Umbraco.Extensions; -using Umbraco.Web.Security; -using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment; - -namespace Umbraco.Web.Common.Filters -{ - - - - /// - /// Ensures authorization is successful for a back office user. - /// - public class UmbracoBackOfficeAuthorizeFilter : IAuthorizationFilter - { - private readonly bool _requireApproval; - - /// - /// Can be used by unit tests to enable/disable this filter - /// - internal static bool Enable = true; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; - private readonly IRuntimeState _runtimeState; - private readonly LinkGenerator _linkGenerator; - private readonly bool _redirectToUmbracoLogin; - private string _redirectUrl; - - - private UmbracoBackOfficeAuthorizeFilter( - IHostingEnvironment hostingEnvironment, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IRuntimeState runtimeState, - LinkGenerator linkGenerator, - bool redirectToUmbracoLogin, bool requireApproval, string redirectUrl) - { - _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); - _backOfficeSecurityAccessor = backOfficeSecurityAccessor; - _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); - _linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator)); - _redirectToUmbracoLogin = redirectToUmbracoLogin; - _redirectUrl = redirectUrl; - _requireApproval = requireApproval; - } - - /// - /// Default constructor - /// - /// - /// - /// - /// - /// - public UmbracoBackOfficeAuthorizeFilter( - IHostingEnvironment hostingEnvironment, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IRuntimeState runtimeState, LinkGenerator linkGenerator, - string redirectUrl) : this(hostingEnvironment, backOfficeSecurityAccessor, runtimeState, linkGenerator, false, false, redirectUrl) - { - } - - public UmbracoBackOfficeAuthorizeFilter( - IHostingEnvironment hostingEnvironment, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IRuntimeState runtimeState, LinkGenerator linkGenerator, - bool redirectToUmbracoLogin, bool requireApproval) : this(hostingEnvironment, backOfficeSecurityAccessor, runtimeState, linkGenerator, redirectToUmbracoLogin, requireApproval, null) - { - } - - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!IsAuthorized()) - { - if (_redirectToUmbracoLogin) - { - _redirectUrl = _linkGenerator.GetBackOfficeUrl(_hostingEnvironment); - } - - if (!_redirectUrl.IsNullOrWhiteSpace()) - { - context.Result = new RedirectResult(_redirectUrl); - } - else - { - context.Result = new ForbidResult(); - } - } - } - - private bool IsAuthorized() - { - if (Enable == false) - return true; - - try - { - // if not configured (install or upgrade) then we can continue - // otherwise we need to ensure that a user is logged in - return _runtimeState.Level == RuntimeLevel.Install - || _runtimeState.Level == RuntimeLevel.Upgrade - || _backOfficeSecurityAccessor?.BackOfficeSecurity.ValidateCurrentUser(false, _requireApproval) == ValidateRequestAttempt.Success; - } - catch (Exception) - { - return false; - } - } - } -} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs index 4019f462eb..fc247af55c 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs @@ -9,10 +9,11 @@ namespace Umbraco.Web.Common.Filters { /// - /// Ensures authorization is successful for a back office user. + /// Ensures authorization is successful for a front-end member /// public class UmbracoMemberAuthorizeFilter : IAuthorizationFilter { + // TODO: Lets revisit this when we get members done and the front-end working and whether it can be replaced or moved to an authz policy private readonly IUmbracoWebsiteSecurity _websiteSecurity; public UmbracoMemberAuthorizeFilter(IUmbracoWebsiteSecurity websiteSecurity) diff --git a/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs index 45806b9d18..bbd3aa981e 100644 --- a/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs @@ -22,6 +22,8 @@ namespace Umbraco.Web.Common.Filters public class ValidateUmbracoFormRouteStringAttribute : TypeFilterAttribute { + // TODO: Lets revisit this when we get members done and the front-end working and whether it can moved to an authz policy + public ValidateUmbracoFormRouteStringAttribute() : base(typeof(ValidateUmbracoFormRouteStringFilter)) { Arguments = new object[] { }; diff --git a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs index bddb4fd015..04f743144e 100644 --- a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs +++ b/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs @@ -13,6 +13,8 @@ namespace Umbraco.Web.Common.Install /// public class InstallAuthorizeAttribute : TypeFilterAttribute { + // NOTE: This doesn't need to be an authz policy, it's only used for the installer + public InstallAuthorizeAttribute() : base(typeof(InstallAuthorizeFilter)) { } diff --git a/src/Umbraco.Web.Common/Install/InstallController.cs b/src/Umbraco.Web.Common/Install/InstallController.cs index 96f8ddf35e..1854d8dfbc 100644 --- a/src/Umbraco.Web.Common/Install/InstallController.cs +++ b/src/Umbraco.Web.Common/Install/InstallController.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Common.Install } } - // gen the install base url + // gen the install base URL ViewData.SetInstallApiBaseUrl(_linkGenerator.GetInstallerApiUrl()); // get the base umbraco folder diff --git a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs index 43425d7bb8..c79c5229fc 100644 --- a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs @@ -10,13 +10,13 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Events; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Macros; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Common.Macros; +using Umbraco.Core.Hosting; namespace Umbraco.Web.Macros { @@ -30,7 +30,7 @@ namespace Umbraco.Web.Macros private readonly ILocalizedTextService _textService; private readonly AppCaches _appCaches; private readonly IMacroService _macroService; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly ICookieManager _cookieManager; private readonly IMemberUserKeyProvider _memberUserKeyProvider; private readonly ISessionManager _sessionManager; @@ -46,7 +46,7 @@ namespace Umbraco.Web.Macros ILocalizedTextService textService, AppCaches appCaches, IMacroService macroService, - IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, ICookieManager cookieManager, IMemberUserKeyProvider memberUserKeyProvider, ISessionManager sessionManager, @@ -61,7 +61,7 @@ namespace Umbraco.Web.Macros _textService = textService; _appCaches = appCaches ?? throw new ArgumentNullException(nameof(appCaches)); _macroService = macroService ?? throw new ArgumentNullException(nameof(macroService)); - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); _cookieManager = cookieManager; _memberUserKeyProvider = memberUserKeyProvider; _sessionManager = sessionManager; @@ -186,7 +186,7 @@ namespace Umbraco.Web.Macros var filename = GetMacroFileName(model); if (filename == null) return null; - var mapped = _ioHelper.MapPath(filename); + var mapped = _hostingEnvironment.MapPathContentRoot(filename); if (mapped == null) return null; var file = new FileInfo(mapped); @@ -356,7 +356,7 @@ namespace Umbraco.Web.Macros /// The text output of the macro execution. private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) { - var engine = new PartialViewMacroEngine(_umbracoContextAccessor, _httpContextAccessor, _ioHelper); + var engine = new PartialViewMacroEngine(_umbracoContextAccessor, _httpContextAccessor, _hostingEnvironment); return engine.Execute(macro, content); } diff --git a/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs index 3566ee48ee..db1658e962 100644 --- a/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs +++ b/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs @@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; using Umbraco.Core.Models.PublishedContent; using Umbraco.Extensions; using Umbraco.Web.Macros; @@ -26,16 +26,16 @@ namespace Umbraco.Web.Common.Macros public class PartialViewMacroEngine { private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly Func _getUmbracoContext; public PartialViewMacroEngine( IUmbracoContextAccessor umbracoContextAccessor, IHttpContextAccessor httpContextAccessor, - IIOHelper ioHelper) + IHostingEnvironment hostingEnvironment) { _httpContextAccessor = httpContextAccessor; - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _getUmbracoContext = () => { @@ -122,7 +122,7 @@ namespace Umbraco.Web.Common.Macros } private string GetVirtualPathFromPhysicalPath(string physicalPath) { - var rootpath = _ioHelper.MapPath("~/"); + var rootpath = _hostingEnvironment.MapPathContentRoot("~/"); physicalPath = physicalPath.Replace(rootpath, ""); physicalPath = physicalPath.Replace("\\", "/"); return "~/" + physicalPath; diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index d5980c10b5..f20ab5ab75 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -97,8 +97,7 @@ namespace Umbraco.Web.Common.Runtime builder.Services.AddUnique(); builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(factory => new LegacyPasswordSecurity()); + builder.Services.AddUnique(); } } } diff --git a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs index 1bec3cf41c..6cd514033f 100644 --- a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs @@ -66,7 +66,7 @@ namespace Umbraco.Web.Common.Templates var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); // instantiate a request and process - // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter + // important to use CleanedUmbracoUrl - lowercase path-only version of the current URL, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. var contentRequest = _publishedRouter.CreateRequest(umbracoContext); diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js index 255fe17435..b5239d35e7 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js @@ -23,7 +23,6 @@ function runUnitTestServer() { autoWatch: true, port: 9999, singleRun: false, - browsers: ['ChromeDebugging'], keepalive: true }) .start(); diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index d272c77397..547b77cc7b 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -6,11 +6,11 @@ * =========== * This is now using Gulp 4, each child task is now a child function in its own corresponding file. * - * To add a new task, simply add a new task file to gulp/tasks folder, add a require statement below to include the one or more methods + * To add a new task, simply add a new task file to gulp/tasks folder, add a require statement below to include the one or more methods * and then add the exports command to add the new item into the task menu. */ -const { src, dest, series, parallel, lastRun } = require('gulp'); +const { series, parallel } = require('gulp'); const config = require('./gulp/config'); const { setDevelopmentMode, setTestMode } = require('./gulp/modes'); @@ -28,9 +28,9 @@ config.compile.current = config.compile.build; // These Exports are the new way of defining Tasks in Gulp 4.x // *********************************************************** exports.build = series(parallel(dependencies, js, less, views), testUnit); -exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); +exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), runUnitTestServer, watchTask); exports.watch = series(watchTask); -// +// exports.runTests = series(setTestMode, series(js, testUnit)); exports.runUnit = series(setTestMode, series(js, runUnitTestServer), watchTask); exports.testE2e = series(setTestMode, parallel(testE2e)); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js index d2fdd077f5..2863840abb 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/cy.js @@ -121,7 +121,7 @@ tinymce.addI18n('cy',{ "Date\/time": "Dyddiad\/amser", "Insert date\/time": "Mewnosod dyddiad\/amser", "Remove link": "Tynnu dolen", -"Url": "Url", +"Url": "URL", "Text to display": "Testun i'w ddangos", "Anchors": "Angorau", "Insert link": "Mewnosod dolen", @@ -227,4 +227,4 @@ tinymce.addI18n('cy',{ "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 90e4009d92..a50f2f10ec 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/da.js @@ -150,7 +150,7 @@ tinymce.addI18n('da',{ "Insert link": "Inds\u00e6t link", "Insert\/edit link": "Inds\u00e6t\/ret link", "Text to display": "Vis tekst", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "Ingen", "New window": "Nyt vindue", @@ -258,4 +258,4 @@ tinymce.addI18n('da',{ "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_AT.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js index 2af071f5ce..0073810c1a 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/de_AT.js @@ -150,7 +150,7 @@ tinymce.addI18n('de_AT',{ "Insert link": "Link einf\u00fcgen", "Insert\/edit link": "Link einf\u00fcgen\/bearbeiten", "Text to display": "Angezeigter Text", -"Url": "Url", +"Url": "URL", "Target": "Ziel", "None": "Keine", "New window": "Neues Fenster", @@ -258,4 +258,4 @@ tinymce.addI18n('de_AT',{ "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/en_CA.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js index f32de01724..cc07ffd23e 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_CA.js @@ -150,7 +150,7 @@ tinymce.addI18n('en_CA',{ "Insert link": "Insert link", "Insert\/edit link": "Insert\/edit link", "Text to display": "Text to display", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "None", "New window": "New window", @@ -258,4 +258,4 @@ tinymce.addI18n('en_CA',{ "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 index 90eae85800..0b50212fd9 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_US.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/en_US.js @@ -150,7 +150,7 @@ tinymce.addI18n('en_US',{ "Insert link": "Insert link", "Insert\/edit link": "Insert\/edit link", "Text to display": "Text to display", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "None", "New window": "New window", @@ -258,4 +258,4 @@ tinymce.addI18n('en_US',{ "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/es_MX.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js index b0f2019ffe..688f14ba51 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/es_MX.js @@ -150,7 +150,7 @@ tinymce.addI18n('es_MX',{ "Insert link": "Insertar enlace", "Insert\/edit link": "Inserta\/editar enlace", "Text to display": "Texto a mostrar", -"Url": "Url", +"Url": "URL", "Target": "Objetivo", "None": "Ninguno", "New window": "Nueva ventana", @@ -258,4 +258,4 @@ tinymce.addI18n('es_MX',{ "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 index c6beeeb510..96b763506c 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/et.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/et.js @@ -150,7 +150,7 @@ tinymce.addI18n('et',{ "Insert link": "Lisa link", "Insert\/edit link": "Lisa\/muuda link", "Text to display": "Kuvatav tekst", -"Url": "Viide (url)", +"Url": "Viide (URL)", "Target": "Sihtm\u00e4rk", "None": "Puudub", "New window": "Uus aken", @@ -258,4 +258,4 @@ tinymce.addI18n('et',{ "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 index c4374394d4..44e62db3f8 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/eu.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/eu.js @@ -150,7 +150,7 @@ tinymce.addI18n('eu',{ "Insert link": "Esteka txertatu", "Insert\/edit link": "Esteka txertatu\/editatu", "Text to display": "Bistaratzeko testua", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "Bat ere ez", "New window": "Lehio berria", @@ -258,4 +258,4 @@ tinymce.addI18n('eu',{ "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/fr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js index 2d074f8c6e..d61015efa0 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr.js @@ -150,7 +150,7 @@ tinymce.addI18n('fr_FR',{ "Insert\/Edit Link": "Ins\u00e9rer\/Modifier lien", "Insert\/edit link": "Ins\u00e9rer\/modifier un lien", "Text to display": "Texte \u00e0 afficher", -"Url": "Url", +"Url": "URL", "Open link in...": "Ouvrir le lien dans...", "Current window": "Fen\u00eatre active", "None": "n\/a", @@ -386,4 +386,4 @@ tinymce.addI18n('fr_FR',{ "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 index 5c37164b2c..5ed177c9fc 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr_FR.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/fr_FR.js @@ -150,7 +150,7 @@ tinymce.addI18n('fr_FR',{ "Insert link": "Ins\u00e9rer un lien", "Insert\/edit link": "Ins\u00e9rer\/modifier un lien", "Text to display": "Texte \u00e0 afficher", -"Url": "Url", +"Url": "URL", "Target": "Cible", "None": "n\/a", "New window": "Nouvelle fen\u00eatre", @@ -258,4 +258,4 @@ tinymce.addI18n('fr_FR',{ "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/hr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js index d52f861ce9..617e1f4823 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hr.js @@ -144,7 +144,7 @@ tinymce.addI18n('hr',{ "Insert link": "Umetni poveznicu", "Insert\/edit link": "Umetni\/izmijeni poveznicu", "Text to display": "Tekst za prikaz", -"Url": "Url", +"Url": "URL", "Target": "Meta", "None": "Ni\u0161ta", "New window": "Novi prozor", @@ -250,4 +250,4 @@ tinymce.addI18n('hr',{ "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 index 3972dc2b3c..13bb4984a9 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hu_HU.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/hu_HU.js @@ -150,7 +150,7 @@ tinymce.addI18n('hu_HU',{ "Insert link": "Hivatkoz\u00e1s beilleszt\u00e9se", "Insert\/edit link": "Hivatkoz\u00e1s beilleszt\u00e9se\/szerkeszt\u00e9se", "Text to display": "Megjelen\u0151 sz\u00f6veg", -"Url": "Url", +"Url": "URL", "Target": "C\u00e9l", "None": "Nincs", "New window": "\u00daj ablak", @@ -258,4 +258,4 @@ tinymce.addI18n('hu_HU',{ "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/it.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js index 5ffc0c0f80..a97118da21 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/it.js @@ -150,7 +150,7 @@ tinymce.addI18n('it',{ "Insert link": "Inserisci il Link", "Insert\/edit link": "Inserisci\/Modifica Link", "Text to display": "Testo da Visualizzare", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "No", "New window": "Nuova Finestra", @@ -258,4 +258,4 @@ tinymce.addI18n('it',{ "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/ka_GE.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js index 9bffb8040d..805a966489 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ka_GE.js @@ -121,7 +121,7 @@ tinymce.addI18n('ka_GE',{ "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", +"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", @@ -227,4 +227,4 @@ tinymce.addI18n('ka_GE',{ "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 index b9f9bccf40..48f7d3bf55 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kab.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/kab.js @@ -150,7 +150,7 @@ tinymce.addI18n('kab',{ "Insert link": "Ger azday", "Insert\/edit link": "Ger\/\u1e93reg azday", "Text to display": "A\u1e0dris ara yettwabeqq\u1e0den", -"Url": "Url", +"Url": "URL", "Target": "Target", "None": "Ulac", "New window": "Asfaylu amaynut", @@ -258,4 +258,4 @@ tinymce.addI18n('kab',{ "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/km_KH.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js index 5c4b055126..381d4c4a46 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/km_KH.js @@ -144,7 +144,7 @@ tinymce.addI18n('km_KH',{ "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", +"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", @@ -250,4 +250,4 @@ tinymce.addI18n('km_KH',{ "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/nb_NO.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js index 59233450b0..e6f1df8971 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nb_NO.js @@ -150,7 +150,7 @@ tinymce.addI18n('nb_NO',{ "Insert link": "Sett inn lenke", "Insert\/edit link": "Sett inn\/endre lenke", "Text to display": "Tekst som skal vises", -"Url": "Url", +"Url": "URL", "Target": "M\u00e5l", "None": "Ingen", "New window": "Nytt vindu", @@ -258,4 +258,4 @@ tinymce.addI18n('nb_NO',{ "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 c80590b297..d8631c9ab6 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/nl.js @@ -150,7 +150,7 @@ tinymce.addI18n('nl',{ "Insert link": "Hyperlink invoegen", "Insert\/edit link": "Hyperlink invoegen\/bewerken", "Text to display": "Linktekst", -"Url": "Url", +"Url": "URL", "Target": "Doel", "None": "Geen", "New window": "Nieuw venster", @@ -258,4 +258,4 @@ tinymce.addI18n('nl',{ "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/pt_BR.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js index 497043a9d4..2beccd413b 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/pt_BR.js @@ -150,7 +150,7 @@ tinymce.addI18n('pt_BR',{ "Insert link": "Inserir link", "Insert\/edit link": "Inserir\/editar link", "Text to display": "Texto para mostrar", -"Url": "Url", +"Url": "URL", "Target": "Alvo", "None": "Nenhum", "New window": "Nova janela", @@ -258,4 +258,4 @@ tinymce.addI18n('pt_BR',{ "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/ro.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js index 0a6a2eadf0..a2f3caec80 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/ro.js @@ -121,7 +121,7 @@ tinymce.addI18n('ro',{ "Date\/time": "Data\/ora", "Insert date\/time": "Insereaz\u0103 data\/ora", "Remove link": "\u0218terge link-ul", -"Url": "Url", +"Url": "URL", "Text to display": "Text de afi\u0219at", "Anchors": "Ancor\u0103", "Insert link": "Inserare link", @@ -227,4 +227,4 @@ tinymce.addI18n('ro',{ "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/sk.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js index 2362a6dbc6..5cc085abdc 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sk.js @@ -144,7 +144,7 @@ tinymce.addI18n('sk',{ "Insert link": "Vlo\u017ei\u0165 odkaz", "Insert\/edit link": "Vlo\u017ei\u0165\/upravi\u0165 odkaz", "Text to display": "Zobrazen\u00fd text", -"Url": "Url", +"Url": "URL", "Target": "Cie\u013e", "None": "\u017diadne", "New window": "Nov\u00e9 okno", @@ -250,4 +250,4 @@ tinymce.addI18n('sk',{ "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/sr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js index 9150c2ed82..01bfad7303 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sr.js @@ -150,7 +150,7 @@ tinymce.addI18n('sr',{ "Insert link": "Ubaci vezu", "Insert\/edit link": "Ubaci\/promeni vezu", "Text to display": "Tekst za prikaz", -"Url": "Url", +"Url": "URL", "Target": "Meta", "None": "Ni\u0161ta", "New window": "Novi prozor", @@ -258,4 +258,4 @@ tinymce.addI18n('sr',{ "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_SE.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js index 83aaaef59d..b97ea68a18 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/sv_SE.js @@ -150,7 +150,7 @@ tinymce.addI18n('sv_SE',{ "Insert link": "Infoga l\u00e4nk", "Insert\/edit link": "Infoga\/redigera l\u00e4nk", "Text to display": "Text att visa", -"Url": "Url", +"Url": "URL", "Target": "M\u00e5l", "None": "Ingen", "New window": "Nytt f\u00f6nster", @@ -158,8 +158,8 @@ tinymce.addI18n('sv_SE',{ "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?", +"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", @@ -258,4 +258,4 @@ tinymce.addI18n('sv_SE',{ "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/tr.js b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js index 7b69596402..3dd22ca25f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr.js @@ -150,7 +150,7 @@ tinymce.addI18n('tr',{ "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", +"Url": "URL", "Target": "Hedef", "None": "Hi\u00e7biri", "New window": "Yeni pencere", @@ -258,4 +258,4 @@ tinymce.addI18n('tr',{ "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 index ea89bc44c3..496fe3dc21 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr_TR.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/langs/tr_TR.js @@ -150,7 +150,7 @@ tinymce.addI18n('tr_TR',{ "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", +"Url": "URL", "Target": "Hedef", "None": "Hi\u00e7biri", "New window": "Yeni pencere", @@ -258,4 +258,4 @@ tinymce.addI18n('tr_TR',{ "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/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json new file mode 100644 index 0000000000..2288b7a421 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -0,0 +1,16749 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@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==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.4", + "@babel/helpers": "^7.6.2", + "@babel/parser": "^7.6.4", + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.3", + "@babel/types": "^7.6.3", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", + "dev": true, + "requires": { + "@babel/types": "^7.6.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/parser": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", + "dev": true + }, + "@babel/traverse": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.3", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", + "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "dev": true, + "requires": { + "@babel/types": "^7.6.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-define-map": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz", + "integrity": "sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/types": "^7.5.5", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz", + "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", + "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.5.5", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", + "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz", + "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.5.5", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.2.tgz", + "integrity": "sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==", + "dev": true, + "requires": { + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.2", + "@babel/types": "^7.6.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz", + "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.2.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz", + "integrity": "sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.6.2.tgz", + "integrity": "sha512-NxHETdmpeSCtiatMRYWVJo7266rrvAC3DTeG5exQBIH/fMIUK7ejDNznBbn3HQl/o9peymRRg7Yqkx6PdUXmMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.6.0" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", + "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", + "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz", + "integrity": "sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.5.5", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.5.5", + "@babel/helper-split-export-declaration": "^7.4.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", + "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.6.2.tgz", + "integrity": "sha512-KGKT9aqKV+9YMZSkowzYoYEiHqgaDhGmPNZlZxX6UeHC4z30nC1J9IrZuGqbYFB1jaIGdv91ujpze0exiVK8bA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.6.0" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", + "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", + "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", + "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", + "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", + "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz", + "integrity": "sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.3.tgz", + "integrity": "sha512-jTkk7/uE6H2s5w6VlMHeWuH+Pcy2lmdwFoeWCVnvIrDUnB5gQqTVI8WfmEAhF2CDEarGrknZcmSFg1+bkfCoSw==", + "dev": true, + "requires": { + "regexpu-core": "^4.6.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz", + "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.5.5" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", + "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", + "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz", + "integrity": "sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.6.2.tgz", + "integrity": "sha512-orZI6cWlR3nk2YmYdb0gImrgCUwb5cBUwjf6Ks6dvNVvXERkwtJWOQaEOjPiu0Gu1Tq6Yq/hruCZZOOi9F34Dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.6.0" + } + }, + "@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==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-dynamic-import": "^7.5.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.6.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.6.2", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.5.0", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.6.3", + "@babel/plugin-transform-classes": "^7.5.5", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.6.0", + "@babel/plugin-transform-dotall-regex": "^7.6.2", + "@babel/plugin-transform-duplicate-keys": "^7.5.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.4.4", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.5.0", + "@babel/plugin-transform-modules-commonjs": "^7.6.0", + "@babel/plugin-transform-modules-systemjs": "^7.5.0", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.6.3", + "@babel/plugin-transform-new-target": "^7.4.4", + "@babel/plugin-transform-object-super": "^7.5.5", + "@babel/plugin-transform-parameters": "^7.4.4", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.4.5", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.6.2", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.6.2", + "@babel/types": "^7.6.3", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/template": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", + "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0" + } + }, + "@babel/traverse": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", + "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.6.2", + "@babel/types": "^7.6.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", + "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "@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==", + "dev": true, + "requires": { + "acorn": "^5.0.3", + "css": "^2.2.1", + "normalize-path": "^2.1.1", + "source-map": "^0.6.0", + "through2": "^2.0.3" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + } + }, + "@microsoft/signalr": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-3.1.10.tgz", + "integrity": "sha512-MSPauolAV+pMMwWE3hIJbuDv03v4o3CNGmDjkWCKuL5qhw28Oa9X099oTpTulrqmDX6755cvIWv7EwbyIij4wA==", + "requires": { + "eventsource": "^1.0.7", + "request": "^2.88.0", + "ws": "^6.0.0" + }, + "dependencies": { + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "@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==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@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==", + "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==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true, + "optional": true + }, + "@types/angular": { + "version": "1.6.56", + "resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.56.tgz", + "integrity": "sha512-HxtqilvklZ7i6XOaiP7uIJIrFXEVEhfbSY45nfv2DeBRngncI58Y4ZOUMiUkcT8sqgLL1ablmbfylChUg7A3GA==" + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "14.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", + "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==", + "dev": true + }, + "@types/q": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", + "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "accord": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", + "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "glob": "^7.0.5", + "indx": "^0.2.3", + "lodash.clone": "^4.3.2", + "lodash.defaults": "^4.0.1", + "lodash.flatten": "^4.2.0", + "lodash.merge": "^4.4.0", + "lodash.partialright": "^4.1.4", + "lodash.pick": "^4.2.1", + "lodash.uniq": "^4.3.0", + "resolve": "^1.5.0", + "semver": "^5.3.0", + "uglify-js": "^2.8.22", + "when": "^3.7.8" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.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==" + }, + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "dev": true + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.0.tgz", + "integrity": "sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg==" + }, + "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==" + }, + "angular-aria": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.9.tgz", + "integrity": "sha512-luI3Jemd1AbOQW0krdzfEG3fM0IFtLY0bSSqIDEx3POE0XjKIC1MkrO8Csyq9PPgueLphyAPofzUwZ8YeZ88SA==" + }, + "angular-chart.js": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/angular-chart.js/-/angular-chart.js-1.1.1.tgz", + "integrity": "sha1-SfDhjQgXYrbUyXkeSHr/L7sw9a4=", + "requires": { + "angular": "1.x", + "chart.js": "2.3.x" + }, + "dependencies": { + "chart.js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.3.0.tgz", + "integrity": "sha1-QEYOSOLEF8BfwzJc2E97AA3H19Y=", + "requires": { + "chartjs-color": "^2.0.0", + "moment": "^2.10.6" + } + } + } + }, + "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==" + }, + "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==", + "requires": { + "@types/angular": "^1.6.25" + } + }, + "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==" + }, + "angular-local-storage": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/angular-local-storage/-/angular-local-storage-0.7.1.tgz", + "integrity": "sha1-+9JzB2PCn6mvVyXgGGx4BiHozdI=" + }, + "angular-messages": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.5.tgz", + "integrity": "sha512-YDpJpFLyrIgZjE/sIAjgww1y6r3QqXBJbNDI0QjftD37vHXLkwvAOo3A4bxPw8BikyGLcJrFrgf6hRAzntJIWA==" + }, + "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==" + }, + "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==" + }, + "angular-sanitize": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.5.tgz", + "integrity": "sha512-wjKCJOIwrkEvfD0keTnKGi6We13gtoCAQIHcdoqyoo3gwvcgNfYymVQIS3+iCGVcjfWz0jHuS3KgB4ysRWsTTA==" + }, + "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==" + }, + "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==", + "requires": { + "angular": ">=1.2.x", + "jquery": ">=3.1.x", + "jquery-ui-dist": ">=1.12.x" + } + }, + "animejs": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/animejs/-/animejs-2.2.0.tgz", + "integrity": "sha1-Ne79/FNbgZScnLBvCz5gwC5v3IA=" + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + } + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "optional": true + }, + "archive-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz", + "integrity": "sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA=", + "dev": true, + "optional": true, + "requires": { + "file-type": "^4.2.0" + }, + "dependencies": { + "file-type": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", + "integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU=", + "dev": true, + "optional": true + } + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argh": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/argh/-/argh-0.1.4.tgz", + "integrity": "sha1-PrTWEpc/xrbcbvM49W91nyrFw6Y=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "optional": true + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "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==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "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==", + "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==" + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.6.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.5.tgz", + "integrity": "sha512-rGd50YV8LgwFQ2WQp4XzOTG69u1qQsXn0amww7tjqV5jJuNazgFKYEVItEBngyyvVITKOg20zr2V+9VsrXJQ2g==", + "dev": true, + "requires": { + "browserslist": "^4.7.0", + "caniuse-lite": "^1.0.30000999", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.18", + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", + "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "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==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "optional": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "decompress": "^4.0.0", + "download": "^6.2.2", + "execa": "^0.7.0", + "p-map-series": "^1.0.0", + "tempfile": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "optional": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + } + } + }, + "bin-check": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", + "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "dev": true, + "optional": true, + "requires": { + "execa": "^0.7.0", + "executable": "^4.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "optional": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + } + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "execa": "^1.0.0", + "find-versions": "^3.0.0" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "bin-version": "^3.0.0", + "semver": "^5.6.0", + "semver-truncate": "^1.1.2" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "bin-check": "^4.1.0", + "bin-version-check": "^4.0.0", + "download": "^7.1.0", + "import-lazy": "^3.1.0", + "os-filter-obj": "^2.0.0", + "pify": "^4.0.1" + }, + "dependencies": { + "download": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz", + "integrity": "sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==", + "dev": true, + "optional": true, + "requires": { + "archive-type": "^4.0.0", + "caw": "^2.0.1", + "content-disposition": "^0.5.2", + "decompress": "^4.2.0", + "ext-name": "^5.0.0", + "file-type": "^8.1.0", + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^8.3.1", + "make-dir": "^1.2.0", + "p-event": "^2.1.0", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "file-type": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", + "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "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==", + "dev": true, + "optional": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "optional": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "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==", + "dev": true, + "optional": true, + "requires": { + "p-timeout": "^2.0.1" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, + "optional": true + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "optional": true, + "requires": { + "prepend-http": "^2.0.0" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" + }, + "bootstrap-social": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bootstrap-social/-/bootstrap-social-5.1.1.tgz", + "integrity": "sha1-dTDGeK31bPj60/qCwp1NPl0CdQE=", + "requires": { + "bootstrap": "~3", + "font-awesome": "~4.7" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", + "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000989", + "electron-to-chromium": "^1.3.247", + "node-releases": "^1.1.29" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "optional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "optional": true + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "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==", + "dev": true + }, + "bufferstreams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.0.1.tgz", + "integrity": "sha1-z7GtlWjTujz+k1upq92VLeiKqyo=", + "dev": true, + "requires": { + "readable-stream": "^1.0.33" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "dev": true, + "optional": true, + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + }, + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, + "optional": true + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "optional": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + } + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "optional": true + } + } + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001162", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001162.tgz", + "integrity": "sha512-E9FktFxaNnp4ky3ucIGzEXLM+Knzlpuq1oN1sFAU0KeayygabGTmOsndpo8QrL4D9pcThlf4D2pUKaDxPCUmVw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "caw": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", + "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "dev": true, + "optional": true, + "requires": { + "get-proxy": "^2.0.0", + "isurl": "^1.0.0-alpha5", + "tunnel-agent": "^0.6.0", + "url-to-options": "^1.0.1" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chart.js": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", + "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.3.0.tgz", + "integrity": "sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^0.5.3" + } + }, + "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==", + "requires": { + "color-name": "^1.0.0" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "clipboard": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", + "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "optional": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "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==" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + }, + "dependencies": { + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "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==", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "console-stream": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz", + "integrity": "sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ=", + "dev": true, + "optional": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-js-compat": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.3.3.tgz", + "integrity": "sha512-GNZkENsx5pMnS7Inwv7ZO/s3B68a9WU5kIjxqrD/tkNR8mtfXJRk8fAKRlbvWZSGPc59/TkiOBDYl5Cb65pTVA==", + "dev": true, + "requires": { + "browserslist": "^4.7.1", + "semver": "^6.3.0" + }, + "dependencies": { + "browserslist": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.1.tgz", + "integrity": "sha512-QtULFqKIAtiyNx7NhZ/p4rB8m3xDozVo/pi5VgTlADLF2tNigz/QH+v0m5qhn7XfHT7u+607NcCNOnC0HZAlMg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000999", + "electron-to-chromium": "^1.3.284", + "node-releases": "^1.1.36" + } + }, + "electron-to-chromium": { + "version": "1.3.292", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.292.tgz", + "integrity": "sha512-hqkem5ANpt6mxVXmhAmlbdG8iicuyM/jEYgmP1tiHPeOLyZoTyGUzrDmJS/xyrrZy9frkW1uQcubicu7f6DS5g==", + "dev": true + }, + "node-releases": { + "version": "1.1.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.37.tgz", + "integrity": "sha512-0EOsAEdn6S2vQdDGBWBpmClm5BCkXVkVOURdnhfg7//rxI2XbleRdKig87WuBrk+0PHZ4OhO58fRm9mzWW4jNw==", + "dev": true, + "requires": { + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-select": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", + "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^2.1.2", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "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==", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha.33", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.33.tgz", + "integrity": "sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.5.3" + } + }, + "css-unit-converter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", + "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=", + "dev": true + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true + }, + "csso": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "dev": true, + "requires": { + "css-tree": "1.0.0-alpha.29" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.29", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "dev": true, + "requires": { + "mdn-data": "~1.1.0", + "source-map": "^0.5.3" + } + }, + "mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", + "dev": true + } + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "optional": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "requires": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "optional": true, + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "optional": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "optional": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "optional": true, + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "dependencies": { + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true, + "optional": true + } + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "optional": true, + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true, + "optional": true + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "optional": true, + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "dependencies": { + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true, + "optional": true + } + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "dev": true, + "optional": true, + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "dev": true, + "optional": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + } + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "download": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", + "integrity": "sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==", + "dev": true, + "optional": true, + "requires": { + "caw": "^2.0.0", + "content-disposition": "^0.5.2", + "decompress": "^4.0.0", + "ext-name": "^5.0.0", + "file-type": "5.2.0", + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^7.0.0", + "make-dir": "^1.0.0", + "p-event": "^1.0.0", + "pify": "^3.0.0" + }, + "dependencies": { + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "optional": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true, + "optional": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.266", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.266.tgz", + "integrity": "sha512-UTuTZ4v8T0gLPHI7U75PXLQePWI65MTS3mckRrnLCkNljHvsutbYs+hn2Ua/RFul3Jt/L3Ht2rLP+dU/AlBfrQ==", + "dev": true + }, + "emits": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emits/-/emits-3.0.0.tgz", + "integrity": "sha1-MnUrupXhcHshlWI4Srm7ix/WL3A=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "dev": true, + "requires": { + "env-variable": "0.0.x" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "once": "^1.4.0" + }, + "dependencies": { + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + } + } + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz", + "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.51", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz", + "integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "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==", + "dev": true + }, + "es6-symbol": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz", + "integrity": "sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.51" + } + }, + "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==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.5.1.tgz", + "integrity": "sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "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==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "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==", + "dev": true + }, + "espree": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "dev": true, + "requires": { + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estemplate": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/estemplate/-/estemplate-0.5.1.tgz", + "integrity": "sha1-FxSp1GGQc4rJWLyv1J4CnNpWo54=", + "dev": true, + "requires": { + "esprima": "^2.7.2", + "estraverse": "^4.1.1" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "exec-buffer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", + "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "dev": true, + "optional": true, + "requires": { + "execa": "^0.7.0", + "p-finally": "^1.0.0", + "pify": "^3.0.0", + "rimraf": "^2.5.4", + "tempfile": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "optional": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "optional": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "optional": true, + "requires": { + "pify": "^2.2.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "mime-db": "^1.28.0" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "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==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "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==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "optional": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "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==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "dev": true, + "optional": true + }, + "filenamify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", + "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", + "dev": true, + "optional": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.0", + "trim-repeated": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "optional": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "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==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatpickr": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.2.tgz", + "integrity": "sha512-jDy4QYGpmiy7+Qk8QvKJ4spjDdxcx9cxMydmq1x427HkKWBw0qizLYeYM2F6tMcvvqGjU5VpJS55j4LnsaBblA==" + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "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==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "follow-redirects": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", + "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "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==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "optional": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz", + "integrity": "sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg=", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "optional": true + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "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==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": 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" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": 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" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "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==", + "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==", + "dev": true, + "optional": true, + "requires": { + "npm-conf": "^1.1.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "optional": true, + "requires": { + "pump": "^3.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "gifsicle": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gifsicle/-/gifsicle-5.1.0.tgz", + "integrity": "sha512-hQsOH7yjC7fMokntysN6f2QuxrnX+zmKKKVy0sC3Vhtnk8WrOxLdfH/Z2PNn7lVVx+1+drzIeAe8ufcmdSC/8g==", + "dev": true, + "optional": true, + "requires": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "execa": "^4.0.0", + "logalot": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "optional": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "optional": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "optional": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "optional": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "optional": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "optional": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "optional": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "optional": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "optional": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "optional": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "optional": true, + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "optional": true + } + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + } + }, + "gulp-angular-embed-templates": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-angular-embed-templates/-/gulp-angular-embed-templates-2.3.0.tgz", + "integrity": "sha1-wBDv3VlN7pRRMoNFN9eSOt6EDXk=", + "dev": true, + "requires": { + "gulp-util": "^3.0.6", + "htmlparser2": "~3.9.1", + "minimize": "^2.0.0", + "object-assign": "4.1.0", + "through2": "^2.0.1" + }, + "dependencies": { + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "clean-css": "4.2.1", + "plugin-error": "1.0.1", + "through2": "3.0.1", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "gulp-eslint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-6.0.0.tgz", + "integrity": "sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig==", + "dev": true, + "requires": { + "eslint": "^6.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.1" + } + }, + "gulp-imagemin": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-7.1.0.tgz", + "integrity": "sha512-6xBTNybmPY2YrvrhhlS8Mxi0zn0ypusLon63p9XXxDtIf7U7c6KcViz94K7Skosucr3378A6IY2kJSjJyuwylQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "fancy-log": "^1.3.2", + "imagemin": "^7.0.0", + "imagemin-gifsicle": "^7.0.0", + "imagemin-mozjpeg": "^8.0.0", + "imagemin-optipng": "^7.0.0", + "imagemin-svgo": "^7.0.0", + "plugin-error": "^1.0.1", + "plur": "^3.0.1", + "pretty-bytes": "^5.3.0", + "through2-concurrent": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "gulp-less": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", + "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", + "dev": true, + "requires": { + "accord": "^0.29.0", + "less": "2.6.x || ^3.7.1", + "object-assign": "^4.0.1", + "plugin-error": "^0.1.2", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + } + } + }, + "gulp-notify": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", + "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "fancy-log": "^1.3.2", + "lodash.template": "^4.4.0", + "node-notifier": "^5.2.1", + "node.extend": "^2.0.0", + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.1", + "postcss": "^7.0.2", + "postcss-load-config": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "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==", + "dev": true + }, + "gulp-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-sort/-/gulp-sort-2.0.0.tgz", + "integrity": "sha1-xnYqLx8N4KP8WVohWZ0/rI26Gso=", + "dev": true, + "requires": { + "through2": "^2.0.1" + } + }, + "gulp-sourcemaps": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "1.X", + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "5.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "1.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom-string": "1.X", + "through2": "2.X" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "gulp-watch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", + "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", + "dev": true, + "requires": { + "ansi-colors": "1.1.0", + "anymatch": "^1.3.0", + "chokidar": "^2.0.0", + "fancy-log": "1.3.2", + "glob-parent": "^3.0.1", + "object-assign": "^4.1.0", + "path-is-absolute": "^1.0.1", + "plugin-error": "1.0.1", + "readable-stream": "^2.2.2", + "slash": "^1.0.0", + "vinyl": "^2.1.0", + "vinyl-file": "^2.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "fancy-log": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "time-stamp": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "es6-promise": "^4.2.6", + "fs-readfile-promise": "^3.0.1", + "js-yaml": "^3.13.0", + "lodash": "^4.17.11", + "node.extend": "2.0.2", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tryit": "^1.0.1", + "vinyl-bufferstream": "^1.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "gulp-wrap-js": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/gulp-wrap-js/-/gulp-wrap-js-0.4.1.tgz", + "integrity": "sha1-3uYqpISqupVHqT0f9c0MPQvtwDE=", + "dev": true, + "requires": { + "escodegen": "^1.6.1", + "esprima": "^2.3.0", + "estemplate": "*", + "gulp-util": "~3.0.5", + "through2": "*", + "vinyl-sourcemaps-apply": "^0.1.4" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.1.4.tgz", + "integrity": "sha1-xfy9Q+LyOEI8LcmL3db3m3K8NFs=", + "dev": true, + "requires": { + "source-map": "^0.1.39" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "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==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "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==", + "dev": true, + "optional": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "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==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "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==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "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==", + "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==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "optional": 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==", + "dev": true + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "imagemin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", + "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==", + "dev": true, + "requires": { + "file-type": "^12.0.0", + "globby": "^10.0.0", + "graceful-fs": "^4.2.2", + "junk": "^3.1.0", + "make-dir": "^3.0.0", + "p-pipe": "^3.0.0", + "replace-ext": "^1.0.0" + }, + "dependencies": { + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true + } + } + }, + "imagemin-gifsicle": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz", + "integrity": "sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA==", + "dev": true, + "optional": true, + "requires": { + "execa": "^1.0.0", + "gifsicle": "^5.0.0", + "is-gif": "^3.0.0" + } + }, + "imagemin-mozjpeg": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.0.tgz", + "integrity": "sha512-+EciPiIjCb8JWjQNr1q8sYWYf7GDCNDxPYnkD11TNIjjWNzaV+oTg4DpOPQjl5ZX/KRCPMEgS79zLYAQzLitIA==", + "dev": true, + "optional": true, + "requires": { + "execa": "^1.0.0", + "is-jpg": "^2.0.0", + "mozjpeg": "^6.0.0" + } + }, + "imagemin-optipng": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz", + "integrity": "sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ==", + "dev": true, + "optional": true, + "requires": { + "exec-buffer": "^3.0.0", + "is-png": "^2.0.0", + "optipng-bin": "^6.0.0" + } + }, + "imagemin-svgo": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-7.1.0.tgz", + "integrity": "sha512-0JlIZNWP0Luasn1HT82uB9nU9aa+vUj6kpT+MjPW11LbprXC+iC4HDwn1r4Q2/91qj4iy9tRZNsFySMlEpLdpg==", + "dev": true, + "optional": true, + "requires": { + "is-svg": "^4.2.1", + "svgo": "^1.3.2" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "optional": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "optional": true, + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", + "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", + "dev": true, + "optional": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "optional": true + } + } + }, + "is-svg": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-4.2.1.tgz", + "integrity": "sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A==", + "dev": true, + "optional": true, + "requires": { + "html-comment-regex": "^1.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + } + } + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "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==", + "dev": true, + "optional": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "optional": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "indx": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/indx/-/indx-0.2.3.tgz", + "integrity": "sha1-Fdz1bunPZcAjTFE8J/vVgOcPvFA=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + } + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "dev": true, + "optional": true, + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "irregular-plurals": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", + "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "dev": true + }, + "is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-gif": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz", + "integrity": "sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw==", + "dev": true, + "optional": true, + "requires": { + "file-type": "^10.4.0" + }, + "dependencies": { + "file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "dev": true, + "optional": true + } + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-jpg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz", + "integrity": "sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc=", + "dev": true, + "optional": true + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", + "dev": true, + "optional": true + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true, + "optional": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "optional": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-png": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz", + "integrity": "sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==", + "dev": true, + "optional": true + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "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==", + "dev": true, + "optional": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "optional": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "optional": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" + }, + "jquery-ui-dist": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.12.1.tgz", + "integrity": "sha1-XAgV08xvkP9fqvWyaKbiO0ypBPo=" + }, + "jquery-ui-touch-punch": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/jquery-ui-touch-punch/-/jquery-ui-touch-punch-0.2.3.tgz", + "integrity": "sha1-7tgiQnM7okP0az6HwYQbMIGR2mg=" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "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==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true, + "optional": true + }, + "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==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, + "karma": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", + "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.1.0", + "connect": "^3.6.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.14", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "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==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.2.2.tgz", + "integrity": "sha512-bw3pm7kZ2Wa6+jQWYP/c7bAZy3i4GwiIiMO2EeRjrE48l8vBqC/WvFhSF0xyM8fQiPEGvwMY/5bqDG7sSEOuhg==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, + "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==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.1.tgz", + "integrity": "sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "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==", + "dev": true + }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "jasmine-core": "^3.3" + } + }, + "karma-jsdom-launcher": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/karma-jsdom-launcher/-/karma-jsdom-launcher-8.0.2.tgz", + "integrity": "sha512-jxO+Nf9U/XNSnHXrNpxBbbMyeYuvJH1V++bRdZv20vJ9pvaLuQ6LFNIgn4hA1WAVmzMsvW9j0P2Q2hTLMWcSvw==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "path-is-absolute": "^1.0.0", + "xmlbuilder": "12.0.0" + } + }, + "karma-spec-reporter": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", + "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", + "dev": true, + "requires": { + "colors": "^1.1.2" + } + }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "optional": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "dev": true + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "dev": true, + "requires": { + "colornames": "^1.1.1" + } + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "dev": true, + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lazyload-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazyload-js/-/lazyload-js-1.0.0.tgz", + "integrity": "sha1-jBA5sbaRec1J/cMkICOvSM4IOSU=" + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "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==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "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==", + "dev": true + }, + "lodash.partialright": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.partialright/-/lodash.partialright-4.2.1.tgz", + "integrity": "sha1-ATDYDoM2MmTUAHTzKbij56ihzEs=", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log4js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", + "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "dev": true, + "requires": { + "date-format": "^2.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.0", + "rfdc": "^1.1.4", + "streamroller": "^1.0.6" + } + }, + "logalot": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz", + "integrity": "sha1-X46MkNME7fElMJUaVVSruMXj9VI=", + "dev": true, + "optional": true, + "requires": { + "figures": "^1.3.5", + "squeak": "^1.0.0" + }, + "dependencies": { + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + } + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "optional": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "optional": true + }, + "lpad-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz", + "integrity": "sha1-IfYArBwwlcPG5JfuZyce4ISB/p4=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1", + "indent-string": "^2.1.0", + "longest": "^1.0.0", + "meow": "^3.3.0" + } + }, + "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==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "dev": true, + "requires": { + "es5-ext": "~0.10.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "optional": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + } + } + }, + "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==", + "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==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memoizee": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.45", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.5" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "optional": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + } + } + }, + "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==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "minimize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/minimize/-/minimize-2.2.0.tgz", + "integrity": "sha512-IxR2XMbw9pXCxApkdD9BTcH2U4XlXhbeySUrv71rmMS9XDA8BVXEsIuFu24LtwCfBgfbL7Fuh8/ZzkO5DaTLlQ==", + "dev": true, + "requires": { + "argh": "^0.1.4", + "async": "^2.1.5", + "cli-color": "^1.2.0", + "diagnostics": "^1.1.0", + "emits": "^3.0.0", + "htmlparser2": "^3.9.2", + "uuid": "^3.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, + "mozjpeg": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mozjpeg/-/mozjpeg-6.0.1.tgz", + "integrity": "sha512-9Z59pJMi8ni+IUvSH5xQwK5tNLw7p3dwDNCZ3o1xE+of3G5Hc/yOz6Ue/YuLiBXU3ZB5oaHPURyPdqfBX/QYJA==", + "dev": true, + "optional": true, + "requires": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "logalot": "^2.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "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==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "ng-file-upload": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/ng-file-upload/-/ng-file-upload-12.2.13.tgz", + "integrity": "sha1-AYAPOHLlJvlTEPhHfpnk8S0NjRQ=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "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==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.32.tgz", + "integrity": "sha512-VhVknkitq8dqtWoluagsGPn3dxTvN9fwgR59fV3D7sLBHe0JfDramsMI8n8mY//ccq/Kkrf8ZRHRpsyVZ3qw1A==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "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==", + "dev": true, + "requires": { + "has": "^1.0.3", + "is": "^3.2.1" + } + }, + "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==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "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==", + "dev": true + }, + "nouislider": { + "version": "14.6.2", + "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.6.2.tgz", + "integrity": "sha512-/lJeqJBghNAZS3P2VYrHzm1RM6YJPvvC/1wNpGaHBRX+05wpzUDafrW/ohAYp4kjKhRH8+BJ0vkorCHiMmgTMQ==" + }, + "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==", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "npm": { + "version": "6.14.9", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.9.tgz", + "integrity": "sha512-yHi1+i9LyAZF1gAmgyYtVk+HdABlLy94PMIDoK1TRKWvmFQAt5z3bodqVwKvzY0s6dLqQPVsRLiwhJfNtiHeCg==", + "requires": { + "JSONStream": "^1.3.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "^2.0.0", + "archy": "~1.0.0", + "bin-links": "^1.1.8", + "bluebird": "^3.5.5", + "byte-size": "^5.0.1", + "cacache": "^12.0.3", + "call-limit": "^1.1.1", + "chownr": "^1.1.4", + "ci-info": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.1", + "cmd-shim": "^3.0.3", + "columnify": "~1.5.4", + "config-chain": "^1.1.12", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.3.1", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.8.8", + "iferr": "^1.0.2", + "imurmurhash": "*", + "infer-owner": "^1.0.4", + "inflight": "~1.0.6", + "inherits": "^2.0.4", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^3.0.0", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^4.0.8", + "libnpm": "^3.0.1", + "libnpmaccess": "^3.0.2", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "libnpx": "^10.2.4", + "lock-verify": "^2.1.0", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^5.1.1", + "meant": "^1.0.2", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.5", + "move-concurrently": "^1.0.1", + "node-gyp": "^5.1.0", + "nopt": "^4.0.3", + "normalize-package-data": "^2.5.0", + "npm-audit-report": "^1.3.3", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "^3.0.2", + "npm-lifecycle": "^3.1.5", + "npm-package-arg": "^6.1.1", + "npm-packlist": "^1.4.8", + "npm-pick-manifest": "^3.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.7", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "^1.5.1", + "osenv": "^0.1.5", + "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.5", + "read-installed": "~4.0.3", + "read-package-json": "^2.1.1", + "read-package-tree": "^5.3.1", + "readable-stream": "^3.6.0", + "readdir-scoped-modules": "^1.1.0", + "request": "^2.88.0", + "retry": "^0.12.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.1.2", + "semver": "^5.7.1", + "sha": "^3.0.0", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.1", + "stringify-package": "^1.0.1", + "tar": "^4.4.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "^1.1.1", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.3", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.7.0", + "write-file-atomic": "^2.4.3" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.5", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.3.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "bundled": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.8.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.8", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cmd-shim": "^3.0.0", + "gentle-fs": "^2.3.0", + "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", + "write-file-atomic": "^2.3.0" + } + }, + "bluebird": { + "version": "3.5.5", + "bundled": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "5.0.1", + "bundled": true + }, + "cacache": { + "version": "12.0.3", + "bundled": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.1", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true + }, + "ci-info": { + "version": "2.0.0", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.10", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.1", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "3.0.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.3.3", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.5", + "bundled": true, + "requires": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "bundled": true + } + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "bundled": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.1", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "bundled": true + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "bundled": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "bundled": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.5.1", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "requires": { + "minipass": "^2.6.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "bundled": true + }, + "gentle-fs": { + "version": "2.3.1", + "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", + "infer-owner": "^1.0.4", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "bundled": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true + } + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-symbols": { + "version": "1.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.8.8", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "bundled": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-callable": { + "version": "1.1.4", + "bundled": true + }, + "is-ci": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ci-info": "^1.5.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "bundled": true + } + } + }, + "is-cidr": { + "version": "3.0.0", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.10" + } + }, + "is-date-object": { + "version": "1.0.1", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-regex": { + "version": "1.0.4", + "bundled": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-symbol": { + "version": "1.0.2", + "bundled": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "libcipm": { + "version": "4.0.8", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "ini": "^1.3.5", + "lock-verify": "^2.1.0", + "mkdirp": "^0.5.1", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^9.1.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpm": { + "version": "3.0.1", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.3", + "find-npm-prefix": "^1.0.2", + "libnpmaccess": "^3.0.2", + "libnpmconfig": "^1.2.1", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmpublish": "^1.1.2", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "npm-profile": "^4.0.2", + "npm-registry-fetch": "^4.0.0", + "npmlog": "^4.1.2", + "pacote": "^9.5.3", + "read-package-json": "^2.0.13", + "stringify-package": "^1.0.0" + } + }, + "libnpmaccess": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + } + } + }, + "libnpmhook": { + "version": "5.0.3", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmorg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmpublish": { + "version": "1.1.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + } + }, + "libnpmsearch": { + "version": "2.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmteam": { + "version": "1.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpx": { + "version": "10.2.4", + "bundled": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^14.2.3" + } + }, + "lock-verify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.1.0", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "5.1.1", + "bundled": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "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.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.2", + "bundled": true + }, + "mime-db": { + "version": "1.35.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "requires": { + "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "bundled": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "5.1.0", + "bundled": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", + "tar": "^4.4.12", + "which": "^1.3.1" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "bundled": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "npm-audit-report": { + "version": "1.3.3", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.2", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "3.1.5", + "bundled": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + } + }, + "npm-logical-tree": { + "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, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "4.0.4", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "figgy-pudding": "^3.4.1", + "npm-registry-fetch": "^4.0.0" + } + }, + "npm-registry-fetch": { + "version": "4.0.7", + "bundled": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "bundled": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "object-keys": { + "version": "1.0.12", + "bundled": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "bundled": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "9.5.12", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "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", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.1", + "bundled": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "psl": { + "version": "1.1.29", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.5.2", + "bundled": true + }, + "query-string": { + "version": "6.8.2", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.5", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "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", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "bundled": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "bundled": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.7.1", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "3.0.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true + }, + "socks": { + "version": "2.3.3", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "bundled": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "bundled": true + }, + "split-on-first": { + "version": "1.1.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.4.3", + "bundled": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.4.0", + "bundled": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "bundled": true + } + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "util-promisify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.1", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.4.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true + }, + "yargs": { + "version": "14.2.3", + "bundled": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "15.0.1", + "bundled": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "bundled": true + } + } + } + } + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "optional": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "optipng-bin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/optipng-bin/-/optipng-bin-6.0.0.tgz", + "integrity": "sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg==", + "dev": true, + "optional": true, + "requires": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "logalot": "^2.0.0" + } + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "arch": "^2.1.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "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==", + "dev": true, + "optional": true + }, + "p-event": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz", + "integrity": "sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU=", + "dev": true, + "optional": true, + "requires": { + "p-timeout": "^1.1.1" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "optional": true + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true, + "optional": true + }, + "p-map-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz", + "integrity": "sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco=", + "dev": true, + "optional": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true, + "optional": true + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dev": true, + "optional": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "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==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + } + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true, + "optional": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "plur": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", + "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "dev": true, + "requires": { + "irregular-plurals": "^2.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.18.tgz", + "integrity": "sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", + "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", + "dev": true, + "requires": { + "css-unit-converter": "^1.1.1", + "postcss": "^7.0.5", + "postcss-selector-parser": "^5.0.0-rc.4", + "postcss-value-parser": "^3.3.1" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "dev": true, + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "dev": true, + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true, + "optional": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-bytes": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "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==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true, + "optional": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true, + "optional": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + } + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "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==", + "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==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "optional": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-transform": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", + "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "dev": true, + "requires": { + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.1.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "optional": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "optional": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "dev": true + }, + "run-sequence": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", + "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dev": true, + "optional": true, + "requires": { + "commander": "^2.8.1" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true, + "optional": true + }, + "semver-truncate": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", + "integrity": "sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g=", + "dev": true, + "optional": true, + "requires": { + "semver": "^5.3.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "optional": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "dev": true, + "optional": true, + "requires": { + "sort-keys": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "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==", + "dev": true + }, + "spectrum-colorpicker2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/spectrum-colorpicker2/-/spectrum-colorpicker2-2.0.3.tgz", + "integrity": "sha512-uEmzdiPqSHgJ1sJvEiwsuYAt7ep/GltjWZ7yloMDFMPcr4qQmmwX4UqlFz7C2HxmmqA51jx51FfgiF65s7R3Pg==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "squeak": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz", + "integrity": "sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM=", + "dev": true, + "optional": true, + "requires": { + "chalk": "^1.0.0", + "console-stream": "^0.1.1", + "lpad-align": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true + } + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "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==", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamroller": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", + "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "date-format": "^2.0.0", + "debug": "^3.2.6", + "fs-extra": "^7.0.1", + "lodash": "^4.17.14" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "dev": true + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "optional": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "optional": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "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==", + "dev": true + }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "dev": true, + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "svgo": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz", + "integrity": "sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.33", + "csso": "^3.5.1", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "optional": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true, + "optional": true + }, + "tempfile": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", + "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", + "dev": true, + "optional": true, + "requires": { + "temp-dir": "^1.0.0", + "uuid": "^3.0.1" + } + }, + "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==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "through2": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true, + "optional": true + }, + "timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "requires": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tinymce": { + "version": "4.9.11", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.11.tgz", + "integrity": "sha512-nkSLsax+VY5DBRjMFnHFqPwTnlLEGHCco82FwJF2JNH6W+5/ClvNC1P4uhD5lXPDNiDykSHR0XJdEh7w/ICHzA==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true, + "optional": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "optional": true + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typeahead.js": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/typeahead.js/-/typeahead.js-0.11.1.tgz", + "integrity": "sha1-TmTmcbIjEKhgb0rsgFkkuoSwFbg=", + "requires": { + "jquery": ">=1.7" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "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==" + }, + "undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "dev": true + }, + "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==", + "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==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "optional": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true, + "optional": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "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==", + "dev": true, + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, + "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==", + "dev": true + }, + "v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, + "vendors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz", + "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-bufferstream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vinyl-bufferstream/-/vinyl-bufferstream-1.0.1.tgz", + "integrity": "sha1-BTeGn1gO/6TKRay0dXnkuf5jCBo=", + "dev": true, + "requires": { + "bufferstreams": "1.0.1" + } + }, + "vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" + }, + "dependencies": { + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "dev": true, + "requires": { + "source-map": "^0.5.1" + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wicg-inert": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.0.tgz", + "integrity": "sha512-P0ZiWaN9SxOkJbYtF/PIwmIRO8UTqTJtyl33QTQlHfAb6h15T0Dp5m7WTJ8N6UWIoj+KU5M0a8EtfRZLlHiP0Q==" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlbuilder": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", + "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "5.0.0-security.0" + } + }, + "yargs-parser": { + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "optional": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + } + } +} diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index eae04dc289..b7a4779e07 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -9,6 +9,9 @@ "fastdev": "gulp fastdev", "watch": "gulp watch" }, + "engines": { + "node": ">=10.00.0" + }, "dependencies": { "@microsoft/signalr": "^3.1.8", "ace-builds": "1.4.2", diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Black.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Black.woff deleted file mode 100644 index d1e2579bf8..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Black.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BlackItalic.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BlackItalic.woff deleted file mode 100644 index 142c1c9c48..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BlackItalic.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Bold.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Bold.woff deleted file mode 100644 index cdfcbe0fbc..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Bold.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BoldItalic.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BoldItalic.woff deleted file mode 100644 index 3e683fea7c..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-BoldItalic.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Italic.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Italic.woff deleted file mode 100644 index d8cf84c8b9..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Italic.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Light.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Light.woff deleted file mode 100644 index e7d4278cce..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Light.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-LightItalic.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-LightItalic.woff deleted file mode 100644 index bb72fd2200..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-LightItalic.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Regular.woff b/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Regular.woff deleted file mode 100644 index bf73a6d9f9..0000000000 Binary files a/src/Umbraco.Web.UI.Client/src/assets/fonts/lato/LatoLatin-Regular.woff and /dev/null differ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbcontextmenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbcontextmenu.directive.js index 32034818a3..f666e62587 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbcontextmenu.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbcontextmenu.directive.js @@ -1,3 +1,11 @@ +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbContextMenu +* @restrict A + * + * @description + * Handles the click events on the context menu +**/ angular.module("umbraco.directives") .directive('umbContextMenu', function (navigationService, keyboardService, backdropService) { return { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbdrawer/umbdrawer.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbdrawer/umbdrawer.directive.js index e2e94e466f..8fa1bf4a46 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbdrawer/umbdrawer.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbdrawer/umbdrawer.directive.js @@ -57,7 +57,7 @@ The drawer component is a global component and is already added to the umbraco m
  • {@link umbraco.directives.directive:umbDrawerView umbDrawerView}
  • {@link umbraco.directives.directive:umbDrawerHeader umbDrawerHeader}
  • -
  • {@link umbraco.directives.directive:umbDrawerView umbDrawerContent}
  • +
  • {@link umbraco.directives.directive:umbDrawerContent umbDrawerContent}
  • {@link umbraco.directives.directive:umbDrawerFooter umbDrawerFooter}
@@ -106,4 +106,4 @@ function Drawer($location, $routeParams, helpService, userService, localizationS }; } - angular.module('umbraco.directives').directive("umbDrawer", Drawer); \ No newline at end of file + angular.module('umbraco.directives').directive("umbDrawer", Drawer); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index a236e7f5ac..dd6fa5c5c8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -61,15 +61,15 @@ Use this directive to render an umbraco button. The directive can be used to gen @param {string=} state Set a progress state on the button ("init", "busy", "success", "error"). @param {string=} shortcut Set a keyboard shortcut for the button ("ctrl+c"). @param {string=} label Set the button label. -@param {string=} labelKey Set a localization key to make a multi lingual button ("general_buttonText"). +@param {string=} labelKey Set a localization key to make a multi-lingual button ("general_buttonText"). @param {string=} icon Set a button icon. -@param {string=} size Set a button icon ("xs", "m", "l", "xl"). +@param {string=} size Set a button size ("xs", "m", "l", "xl"). @param {boolean=} disabled Set to true to disable the button. -@param {string=} addEllipsis Adds an ellipsis character (…) to the button label which means the button will open a dialog or prompt the user for more information. -@param {string=} showCaret Shows a caret on the right side of the button label -@param {string=} autoFocus add autoFocus to the button -@param {string=} hasPopup Used to expose to the accessibility API whether the button will trigger a popup or not -@param {string=]} isExpanded Used to add an aria-expanded attribute and expose whether the button has expanded a popup or not +@param {boolean=} addEllipsis Adds an ellipsis character (…) to the button label which means the button will open a dialog or prompt the user for more information. +@param {boolean=} showCaret Shows a caret on the right side of the button label +@param {boolean=} autoFocus add autoFocus to the button +@param {boolean=} hasPopup Used to expose to the accessibility API whether the button will trigger a popup or not +@param {boolean=} isExpanded Used to add an aria-expanded attribute and expose whether the button controls collapsible content **/ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbuttonellipsis.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbuttonellipsis.directive.js index 67b85c13b9..c24c6e82d2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbuttonellipsis.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbuttonellipsis.directive.js @@ -21,15 +21,15 @@ +@param {callback} action Callback when the value of the checkbox changes through interaction. @param {string} text Set the text for the checkbox label. -@param {string} labelKey Set a dictinary/localization string for the checkbox label -@param {callback} action Callback when the value of the checkbox change by interaction. -@param {string} cssClass Set a css class modifier -@param {string} color Set a hex code like #f5c1bc -@param {boolean} showText Set to true to show the text. false by default -@param {domElement} element Highlights a DOM-element (HTML-selector) -@param {string} state Set the initial state of the component. To have it hidden use hidden -@param {string} mode Set the mode, which decides how to style the component. "small" and "tab" are currently supported +@param {string=} labelKey Set a dictionary/localization string for the checkbox label +@param {string=} cssClass Set a css class modifier +@param {string=} color Set a hex code e.g. #f5c1bc. #000000 by default +@param {boolean=} showText Set to true to show the text. false by default +@param {string=} element Highlights a DOM-element (HTML selector) e.g. "my-div-name" +@param {string=} state Set the initial state of the component. To have it hidden use hidden +@param {string=} mode Set the mode, which decides how to style the component. "small" and "tab" are currently supported **/ (function () { @@ -44,7 +44,6 @@ function onInit() { setText(); - setColor(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js index 709a3aaae4..092908cd8d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js @@ -71,7 +71,9 @@ Use this directive to render a group of toggle buttons. function ToggleGroupDirective() { function link(scope, el, attr, ctrl) { - + for(let i = 0; i < scope.items.length; i++) { + scope.items[i].inputId = "umb-toggle-group-item_" + String.CreateGuid(); + } scope.change = function(item) { if (item.disabled) { return; 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 826d6b87fc..f2dc0622c7 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 @@ -2,7 +2,7 @@ 'use strict'; function ContentEditController($rootScope, $scope, $routeParams, $q, $window, - appState, contentResource, entityResource, navigationService, notificationsService, + appState, contentResource, entityResource, navigationService, notificationsService, contentAppHelper, serverValidationManager, contentEditingHelper, localizationService, formHelper, umbRequestHelper, editorState, $http, eventsService, overlayService, $location, localStorageService, treeService, $exceptionHandler) { @@ -282,7 +282,7 @@ $scope.page.saveButtonStyle = content.trashed || content.isElement || isBlueprint ? "primary" : "info"; // only create the save/publish/preview buttons if the // content app is "Conent" - if ($scope.activeApp && $scope.activeApp.alias !== "umbContent" && $scope.activeApp.alias !== "umbInfo" && $scope.activeApp.alias !== "umbListView") { + if ($scope.activeApp && !contentAppHelper.isContentBasedApp($scope.activeApp)) { $scope.defaultButton = null; $scope.subButtons = null; $scope.page.showSaveButton = false; 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 1e929af6e9..60f877d0b6 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 @@ -18,6 +18,8 @@ scope.allowChangeTemplate = false; scope.allTemplates = []; + scope.historyLabelKey = scope.node.variants && scope.node.variants.length === 1 ? "general_history" : "auditTrails_historyIncludingVariants"; + function onInit() { entityResource.getAll("Template").then(function (templates) { scope.allTemplates = templates; @@ -46,8 +48,6 @@ "content_notCreated", "prompt_unsavedChanges", "prompt_doctypeChangeWarning", - "general_history", - "auditTrails_historyIncludingVariants", "content_itemNotPublished", "general_choose" ]; @@ -61,10 +61,8 @@ labels.notCreated = data[4]; labels.unsavedChanges = data[5]; labels.doctypeChangeWarning = data[6]; - labels.notPublished = data[9]; - - scope.historyLabel = scope.node.variants && scope.node.variants.length === 1 ? data[7] : data[8]; - scope.chooseLabel = data[10]; + labels.notPublished = data[7]; + scope.chooseLabel = data[8]; setNodePublishStatus(); @@ -233,7 +231,7 @@ } function loadRedirectUrls() { scope.loadingRedirectUrls = true; - //check if Redirect Url Management is enabled + //check if Redirect URL Management is enabled redirectUrlsResource.getEnableState().then(function (response) { scope.urlTrackerDisabled = response.enabled !== true; if (scope.urlTrackerDisabled === false) { @@ -314,13 +312,13 @@ } function updateCurrentUrls() { - // never show urls for element types (if they happen to have been created in the content tree) + // never show URLs for element types (if they happen to have been created in the content tree) if (scope.node.isElement) { scope.currentUrls = null; return; } - // find the urls for the currently selected language + // find the URLs for the currently selected language if (scope.node.variants.length > 1) { // nodes with variants scope.currentUrls = _.filter(scope.node.urls, (url) => (scope.currentVariant.language && scope.currentVariant.language.culture === url.culture)); @@ -329,7 +327,7 @@ scope.currentUrls = scope.node.urls; } - // figure out if multiple cultures apply across the content urls + // figure out if multiple cultures apply across the content URLs scope.currentUrlsHaveMultipleCultures = _.keys(_.groupBy(scope.currentUrls, url => url.culture)).length > 1; } 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 2d3a8e2238..55e66c5706 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 @@ -23,8 +23,8 @@ controllerAs: 'vm', controller: umbVariantContentController }; - - function umbVariantContentController($scope) { + + function umbVariantContentController($scope, contentAppHelper) { var unsubscribe = []; @@ -41,20 +41,19 @@ vm.showBackButton = showBackButton; function onInit() { - + // Make copy of apps, so we can have a variant specific model for the App. (needed for validation etc.) vm.editor.variantApps = Utilities.copy(vm.content.apps); var activeApp = vm.content.apps.find((app) => app.active); onAppChanged(activeApp); - } - + function showBackButton() { return vm.page.listViewPath !== null && vm.showBack; } - + /** 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 @@ -65,7 +64,7 @@ } })); } - + function onDestroy() { for (var i = 0; i < unsubscribe.length; i++) { unsubscribe[i](); @@ -88,12 +87,12 @@ */ function selectApp(item) { // call the callback if any is registered - if(vm.onSelectApp) { - vm.onSelectApp({"app": item}); + if (vm.onSelectApp) { + vm.onSelectApp({ "app": item }); } } - - $scope.$on("editors.apps.appChanged", function($event, $args) { + + $scope.$on("editors.apps.appChanged", function ($event, $args) { var activeApp = $args.app; // sync varaintApps active with new active. @@ -104,11 +103,14 @@ onAppChanged(activeApp); }); + $scope.$on("listView.itemsChanged", function ($event, $args) { + vm.disableActionsMenu = $args.items.length > 0; + }); + function onAppChanged(activeApp) { // disable the name field if the active content app is not "Content" or "Info" - vm.nameDisabled = (activeApp && activeApp.alias !== "umbContent" && activeApp.alias !== "umbInfo" && activeApp.alias !== "umbListView"); - + vm.nameDisabled = (activeApp && !contentAppHelper.isContentBasedApp(activeApp)); } /** @@ -117,8 +119,8 @@ */ function selectAppAnchor(item, anchor) { // call the callback if any is registered - if(vm.onSelectAppAnchor) { - vm.onSelectAppAnchor({"app": item, "anchor": anchor}); + if (vm.onSelectAppAnchor) { + vm.onSelectAppAnchor({ "app": item, "anchor": anchor }); } } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js index f80b3ceb3e..1be2bb9739 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js @@ -13,7 +13,8 @@ Use this directive to generate a list of breadcrumbs. + entity-type="content" + on-open="clickBreadcrumb(ancestor)"> @@ -32,6 +33,9 @@ Use this directive to generate a list of breadcrumbs. vm.ancestors = ancestors; }); + $scope.clickBreadcrumb = function(ancestor) { + // manipulate breadcrumb display + } } angular.module("umbraco").controller("My.Controller", Controller); @@ -40,7 +44,7 @@ Use this directive to generate a list of breadcrumbs. @param {array} ancestors Array of ancestors @param {string} entityType The content entity type (member, media, content). -@param {callback} Callback when an ancestor is clicked. It will override the default link behaviour. +@param {callback=} onOpen Function callback when an ancestor is clicked. This will override the default link behaviour. **/ (function () { 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 fdec4c98d0..846d5c85fe 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 @@ -220,6 +220,7 @@ nameDisabled: "true to lock the alias. @param {boolean=} hideAlias Set to true to hide alias. @param {string=} description Add a description to the content. +@param {boolean=} descriptionLocked Set to true to lock the description. @param {boolean=} hideDescription Set to true to hide description. @param {boolean=} setpagetitle If true the page title will be set to reflect the type of data the header is working with @param {string=} editorfor The localization to use to aid accessibility on the edit and create screen diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js index 665f315bee..281a52e4e5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js @@ -52,7 +52,8 @@ link: link, scope: { currentNode: "=", - currentSection: "@" + currentSection: "@", + isDisabled: " 1 && !vm.expanded; vm.onOpen({item:vm.item}); }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js index eb3503f799..47ef460c53 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js @@ -1,18 +1,27 @@ angular.module("umbraco.directives") .directive('umbAutoFocus', function($timeout) { - return function(scope, element, attr){ + return function (scope, element, attrs) { + var update = function() { //if it uses its default naming - if(element.val() === "" || attr.focusOnFilled){ + if (element.val() === "" || attrs.focusOnFilled) { element.trigger("focus"); } }; - if (attr.umbAutoFocus !== "false") { - $timeout(function() { - update(); + //check if there's a value for the attribute, if there is and it's false then we conditionally don't + //use auto focus. + if (attrs.umbAutoFocus) { + attrs.$observe("umbAutoFocus", function (newVal) { + var enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true; + if (enabled) { + $timeout(function() { + update(); + }); + } }); } - }; + + }; }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbsearchfilter.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbsearchfilter.directive.js index 7929316275..efbc384cb4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbsearchfilter.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbsearchfilter.directive.js @@ -75,7 +75,8 @@ labelKey: "@?", onChange: "&?", autoFocus: "true to hide the label. +@param {string=} alias The alias of the field within the control group. +@param {string=} labelFor The alias of the field that the label is for, used for validation. +@param {boolean=} required Set to true to mark the field as required. + **/ + angular.module("umbraco.directives.html") .directive('umbControlGroup', function (localizationService) { return { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorpicker.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorpicker.directive.js new file mode 100644 index 0000000000..e45fb174b8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorpicker.directive.js @@ -0,0 +1,243 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbColorPicker +@restrict E +@scope + +@description +Added in Umbraco v. 8.8: Use this directive to render a color picker. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+    
+        function Controller() {
+    
+            var vm = this;
+
+            vm.options = {
+                type: "color",
+                color: defaultColor,
+                showAlpha: false,
+                showPalette: true,
+                showPaletteOnly: false,
+                preferredFormat: "hex",
+            };
+            
+            vm.show = show;
+            vm.hide = hide;
+            vm.change = change;
+
+            function show(color) {
+                color.toHexString().trimStart("#");
+            }
+
+            function hide(color) {
+                color.toHexString().trimStart("#");
+            }
+
+            function change(color) {
+                color.toHexString().trimStart("#");
+            }
+        }
+    
+        angular.module("umbraco").controller("My.ColorController", Controller);
+    
+    })();
+
+ +@param {string} ngModel (binding): Value for the color picker. +@param {object} options (binding): Config object for the color picker. +@param {function} onBeforeShow (expression): Callback function before color picker is shown. +@param {function} onChange (expression): Callback function when the color is changed. +@param {function} onShow (expression): Callback function when color picker is shown. +@param {function} onHide (expression): Callback function when color picker is hidden. +@param {function} onMove (expression): Callback function when the color is moved in color picker. + +**/ + +(function () { + 'use strict'; + + function ColorPickerController($scope, $element, $timeout, assetsService, localizationService) { + + const ctrl = this; + + let colorPickerInstance = null; + let labels = {}; + + ctrl.$onInit = function () { + + // load the separate css for the editor to avoid it blocking our js loading + assetsService.loadCss("lib/spectrum/spectrum.css", $scope); + + // load the js file for the color picker + assetsService.load([ + //"lib/spectrum/tinycolor.js", + "lib/spectrum/spectrum.js" + ], $scope).then(function () { + + // init color picker + grabElementAndRun(); + }); + } + + ctrl.$onChanges = function (changes) { + if (colorPickerInstance && changes.ngModel) { + colorPickerInstance.spectrum("set", changes.ngModel.currentValue); + } + } + + function grabElementAndRun() { + + var labelKeys = [ + "general_cancel", + "general_choose", + "general_clear" + ]; + + localizationService.localizeMany(labelKeys).then(values => { + labels.cancel = values[0]; + labels.choose = values[1]; + labels.clear = values[2]; + }); + + $timeout(function () { + const element = $element.find('.umb-color-picker > input')[0]; + setColorPicker(element, labels); + }, 0, true); + + } + + function setColorPicker(element, labels) { + + // Spectrum options: https://seballot.github.io/spectrum/#options + + const defaultOptions = { + type: "color", + color: null, + showAlpha: false, + showInitial: false, + showInput: true, + cancelText: labels.cancel, + clearText: labels.clear, + chooseText: labels.choose, + preferredFormat: "hex", + clickoutFiresChange: true + }; + + // If has ngModel set the color + if (ctrl.ngModel) { + defaultOptions.color = ctrl.ngModel; + } + + //const options = ctrl.options ? ctrl.options : defaultOptions; + const options = Utilities.extend(defaultOptions, ctrl.options); + + var elem = angular.element(element); + + // Create new color pickr instance + const colorPicker = elem.spectrum(options); + + colorPickerInstance = colorPicker; + + if (colorPickerInstance) { + // destroy the color picker instance when the dom element is removed + elem.on('$destroy', function () { + colorPickerInstance.spectrum('destroy'); + }); + } + + setUpCallbacks(); + + // Refresh the scope + $scope.$applyAsync(); + } + + // Spectrum events: https://seballot.github.io/spectrum/#events + + function setUpCallbacks() { + + if (colorPickerInstance) { + + // bind hook for beforeShow + if (ctrl.onBeforeShow) { + colorPickerInstance.on('beforeShow.spectrum', (e, tinycolor) => { + $timeout(function () { + ctrl.onBeforeShow({ color: tinycolor }); + }); + }); + } + + // bind hook for show + if (ctrl.onShow) { + colorPickerInstance.on('show.spectrum', (e, tinycolor) => { + $timeout(function () { + ctrl.onShow({ color: tinycolor }); + }); + }); + } + + // bind hook for hide + if (ctrl.onHide) { + colorPickerInstance.on('hide.spectrum', (e, tinycolor) => { + $timeout(function () { + ctrl.onHide({ color: tinycolor }); + }); + }); + } + + // bind hook for change + if (ctrl.onChange) { + colorPickerInstance.on('change.spectrum', (e, tinycolor) => { + $timeout(function () { + ctrl.onChange({ color: tinycolor }); + }); + }); + } + + // bind hook for move + if (ctrl.onMove) { + colorPickerInstance.on('move.spectrum', (e, tinycolor) => { + $timeout(function () { + ctrl.onMove({ color: tinycolor }); + }); + }); + } + + } + } + } + + angular + .module('umbraco.directives') + .component('umbColorPicker', { + template: '
', + controller: ColorPickerController, + bindings: { + ngModel: '<', + options: '<', + onBeforeShow: '&', + onShow: '&', + onHide: '&', + onChange: '&', + onMove: '&' + } + }); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js index e467522c84..ed74f94f26 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js @@ -46,7 +46,7 @@ For extra details about options and events take a look here: https://refreshless @param {object} ngModel (binding): Value for the slider. -@param {object} options (binding): Config object for the date picker. +@param {object} options (binding): Config object for the slider. @param {callback} onSetup (callback): onSetup gets triggered when the slider is initialized @param {callback} onUpdate (callback): onUpdate fires every time the slider values are changed. @param {callback} onSlide (callback): onSlide gets triggered when the handle is being dragged. diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js index 3f1929e97d..1554c136b6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js @@ -98,9 +98,9 @@ })(); -@param {string} icon (binding): The node icon. -@param {string} name (binding): The node name. -@param {string} published (binding): The node published state. +@param {array} items (binding): The items for the table. +@param {array} itemProperties (binding): The properties for the items to use in table. +@param {boolean} allowSelectAll (binding): Specify whether to allow select all. @param {function} onSelect (expression): Callback function when the row is selected. @param {function} onClick (expression): Callback function when the "Name" column link is clicked. @param {function} onSelectAll (expression): Callback function when selecting all items. diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js index 8a2b230a27..2ee7d513ca 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js @@ -104,8 +104,8 @@ */ function getBlockLabel(blockObject) { if (blockObject.labelInterpolator !== undefined) { - // We are just using the data model, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) - return blockObject.labelInterpolator(blockObject.data); + var labelVars = Object.assign({"$settings": blockObject.settingsData || {}, "$layout": blockObject.layout || {}, "$index": (blockObject.index || 0)+1 }, blockObject.data); + return blockObject.labelInterpolator(labelVars); } return blockObject.content.contentTypeName; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contentapphelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contentapphelper.service.js new file mode 100644 index 0000000000..0b3dc2c6e0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/contentapphelper.service.js @@ -0,0 +1,35 @@ + +/** +* @ngdoc service +* @name umbraco.services.contentAppHelper +* @description A helper service for content app related functions. +**/ +function contentAppHelper() { + + var service = {}; + + /** + * Default known content based apps. + */ + service.CONTENT_BASED_APPS = [ "umbContent", "umbInfo", "umbListView" ]; + + /** + * @ngdoc method + * @name umbraco.services.contentAppHelper#isContentBasedApp + * @methodOf umbraco.services.contentAppHelper + * + * @param {object} app A content app to check + * + * @description + * Determines whether the supplied content app is a known content based app + * + */ + service.isContentBasedApp = function (app) { + return service.CONTENT_BASED_APPS.indexOf(app.alias) !== -1; + } + + return service; + +} + +angular.module('umbraco.services').factory('contentAppHelper', contentAppHelper); 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 381d09f62d..e512e52643 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 @@ -248,11 +248,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Method to open a new editor in infinite editing + * Method to open a new editor in infinite editing. * - * @param {Object} editor rendering options - * @param {String} editor.view Path to view - * @param {String} editor.size Sets the size of the editor ("small" || "medium"). If nothing is set it will use full width. + * @param {object} editor rendering options. + * @param {string} editor.view Path to view. + * @param {string} editor.size Sets the size of the editor ("small" || "medium"). If nothing is set it will use full width. */ function open(editor) { @@ -342,18 +342,18 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a content editor in infinite editing, the submit callback returns the updated content item - * @param {Object} editor rendering options - * @param {String} editor.id The id of the content item - * @param {Boolean} editor.create Create new content item - * @param {Function} editor.submit Callback function when the publish and close button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. - * @param {String} editor.parentId If editor.create is true, provide parentId for the creation of the content item - * @param {String} editor.documentTypeAlias If editor.create is true, provide document type alias for the creation of the content item - * @param {Boolean} editor.allowSaveAndClose If editor is being used in infinite editing allows the editor to close when the save action is performed - * @param {Boolean} editor.allowPublishAndClose If editor is being used in infinite editing allows the editor to close when the publish action is performed + * Opens a content editor in infinite editing, the submit callback returns the updated content item. + * @param {object} editor rendering options. + * @param {string} editor.id The id of the content item. + * @param {boolean} editor.create Create new content item. + * @param {function} editor.submit Callback function when the publish and close button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. + * @param {string} editor.parentId If editor.create is true, provide parentId for the creation of the content item. + * @param {string} editor.documentTypeAlias If editor.create is true, provide document type alias for the creation of the content item. + * @param {boolean} editor.allowSaveAndClose If editor is being used in infinite editing allows the editor to close when the save action is performed. + * @param {boolean} editor.allowPublishAndClose If editor is being used in infinite editing allows the editor to close when the publish action is performed. * - * @returns {Object} editor object + * @returns {object} editor object */ function contentEditor(editor) { editor.view = "views/content/edit.html"; @@ -366,15 +366,15 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a content picker in infinite editing, the submit callback returns an array of selected items + * Opens a content picker in infinite editing, the submit callback returns an array of selected items. * - * @param {Object} editor rendering options - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Int} editor.startNodeId Set the startnode of the picker (optional) - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. + * @param {object} editor rendering options. + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {number} editor.startNodeId Set the startnode of the picker (optional). + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. * - * @returns {Object} editor object + * @returns {object} editor object. */ function contentPicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -392,12 +392,12 @@ When building a custom infinite editor view you can use the same components as a * @description * Opens a content type picker in infinite editing, the submit callback returns an array of selected items * - * @param {Object} editor rendering options - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. + * @param {object} editor rendering options. + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. * - * @returns {Object} editor object + * @returns {object} editor object */ function contentTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -413,14 +413,14 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a media type picker in infinite editing, the submit callback returns an array of selected items + * Opens a media type picker in infinite editing, the submit callback returns an array of selected items. * - * @param {Object} editor rendering options - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. + * @param {object} editor rendering options. + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. * - * @returns {Object} editor object + * @returns {object} editor object. */ function mediaTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -436,14 +436,14 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a member type picker in infinite editing, the submit callback returns an array of selected items + * Opens a member type picker in infinite editing, the submit callback returns an array of selected items. * - * @param {Object} editor rendering options - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. + * @param {object} editor rendering options. + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. * - * @returns {Object} editor object + * @returns {object} editor object. */ function memberTypePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -458,12 +458,12 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a copy editor in infinite editing, the submit callback returns an array of selected items - * @param {String} editor.section The node entity type - * @param {String} editor.currentNode The current node id - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens a copy editor in infinite editing, the submit callback returns an array of selected items. + * @param {string} editor.section The node entity type. + * @param {string} editor.currentNode The current node id. + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function copy(editor) { @@ -479,11 +479,11 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens a move editor in infinite editing. - * @param {String} editor.section The node entity type - * @param {String} editor.currentNode The current node id - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {string} editor.section The node entity type. + * @param {string} editor.currentNode The current node id. + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function move(editor) { @@ -499,9 +499,9 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens an embed editor in infinite editing. - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function embed(editor) { @@ -517,10 +517,10 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens a rollback editor in infinite editing. - * @param {String} editor.node The node to rollback - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {string} editor.node The node to rollback. + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function rollback(editor) { @@ -536,12 +536,12 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens an embed editor in infinite editing. - * @param {Object} editor rendering options - * @param {String} editor.icon The icon class - * @param {String} editor.color The color class - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {string} editor.icon The icon class. + * @param {string} editor.color The color class. + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function linkPicker(editor) { editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html"; @@ -555,13 +555,13 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a media editor in infinite editing, the submit callback returns the updated media item - * @param {Object} editor rendering options - * @param {String} editor.id The id of the media item - * @param {Boolean} editor.create Create new media item - * @param {Callback} editor.submit Saves, submits, and closes the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens a media editor in infinite editing, the submit callback returns the updated media item. + * @param {object} editor rendering options. + * @param {string} editor.id The id of the media item. + * @param {boolean} editor.create Create new media item. + * @param {function} editor.submit Saves, submits, and closes the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function mediaEditor(editor) { editor.view = "views/media/edit.html"; @@ -574,17 +574,17 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a media picker in infinite editing, the submit callback returns an array of selected media items - * @param {Object} editor rendering options - * @param {Int} editor.startNodeId Set the startnode of the picker (optional) - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Boolean} editor.onlyImages Only display files that have an image file-extension - * @param {Boolean} editor.disableFolderSelect Disable folder selection - * @param {Boolean} editor.disableFocalPoint Disable focal point editor for selected media - * @param {Array} editor.updatedMediaNodes A list of ids for media items that have been updated through the media picker - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens a media picker in infinite editing, the submit callback returns an array of selected media items. + * @param {object} editor rendering options. + * @param {number} editor.startNodeId Set the startnode of the picker (optional). + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {boolean} editor.onlyImages Only display files that have an image file-extension. + * @param {boolean} editor.disableFolderSelect Disable folder selection. + * @param {boolean} editor.disableFocalPoint Disable focal point editor for selected media. + * @param {array} editor.updatedMediaNodes A list of ids for media items that have been updated through the media picker. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function mediaPicker(editor) { editor.view = "views/common/infiniteeditors/mediapicker/mediapicker.html"; @@ -599,13 +599,13 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens an icon picker in infinite editing, the submit callback returns the selected icon - * @param {Object} editor rendering options - * @param {String} editor.icon The CSS class representing the icon - eg. "icon-autofill". - * @param {String} editor.color The CSS class representing the color - eg. "color-red". - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens an icon picker in infinite editing, the submit callback returns the selected icon. + * @param {object} editor rendering options. + * @param {string} editor.icon The CSS class representing the icon - eg. "icon-autofill". + * @param {string} editor.color The CSS class representing the color - eg. "color-red". + * @param {callback} editor.submit Submits the editor. + * @param {callback} editor.close Closes the editor. + * @returns {object} editor object. */ function iconPicker(editor) { editor.view = "views/common/infiniteeditors/iconpicker/iconpicker.html"; @@ -620,15 +620,15 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens the document type editor in infinite editing, the submit callback returns the alias of the saved document type. - * @param {Object} editor rendering options - * @param {Number} editor.id Indicates the ID of the document type to be edited. Alternatively the ID may be set to `-1` in combination with `create` being set to `true` to open the document type editor for creating a new document type. - * @param {Boolean} editor.create Set to `true` to open the document type editor for creating a new document type. - * @param {Boolean} editor.noTemplate If `true` and in combination with `create` being set to `true`, the document type editor will not create a corresponding template by default. This is similar to selecting the "Document Type without a template" in the Create dialog. - * @param {Boolean} editor.isElement If `true` and in combination with `create` being set to `true`, the "Is an Element type" option will be selected by default in the document type editor. - * @param {Boolean} editor.allowVaryByCulture If `true` and in combination with `create`, the "Allow varying by culture" option will be selected by default in the document type editor. - * @param {Callback} editor.submit Submits the editor. - * @param {Callback} editor.close Closes the editor. - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {number} editor.id Indicates the ID of the document type to be edited. Alternatively the ID may be set to `-1` in combination with `create` being set to `true` to open the document type editor for creating a new document type. + * @param {boolean} editor.create Set to `true` to open the document type editor for creating a new document type. + * @param {boolean} editor.noTemplate If `true` and in combination with `create` being set to `true`, the document type editor will not create a corresponding template by default. This is similar to selecting the "Document Type without a template" in the Create dialog. + * @param {boolean} editor.isElement If `true` and in combination with `create` being set to `true`, the "Is an Element type" option will be selected by default in the document type editor. + * @param {boolean} editor.allowVaryByCulture If `true` and in combination with `create`, the "Allow varying by culture" option will be selected by default in the document type editor. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function documentTypeEditor(editor) { editor.view = "views/documenttypes/edit.html"; @@ -641,11 +641,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the media type editor in infinite editing, the submit callback returns the saved media type - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the media type editor in infinite editing, the submit callback returns the saved media type. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object */ function mediaTypeEditor(editor) { editor.view = "views/mediatypes/edit.html"; @@ -658,11 +658,11 @@ When building a custom infinite editor view you can use the same components as a * @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 + * Opens the member type editor in infinite editing, the submit callback returns the saved member type. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function memberTypeEditor(editor) { editor.view = "views/membertypes/edit.html"; @@ -675,11 +675,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the query builder in infinite editing, the submit callback returns the generted query - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the query builder in infinite editing, the submit callback returns the generated query. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function queryBuilder(editor) { editor.view = "views/common/infiniteeditors/querybuilder/querybuilder.html"; @@ -692,14 +692,14 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the query builder in infinite editing, the submit callback returns the generted query - * @param {Object} editor rendering options - * @param {String} options.section tree section to display - * @param {String} options.treeAlias specific tree to display - * @param {Boolean} options.multiPicker should the tree pick one or multiple items before returning - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the query builder in infinite editing, the submit callback returns the generted query. + * @param {object} editor rendering options. + * @param {string} options.section tree section to display. + * @param {string} options.treeAlias specific tree to display. + * @param {boolean} options.multiPicker should the tree pick one or multiple items before returning. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function treePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -714,10 +714,10 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens the an editor to set node permissions. - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function nodePermissions(editor) { editor.view = "views/common/infiniteeditors/nodepermissions/nodepermissions.html"; @@ -731,11 +731,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Open an editor to insert code snippets into the code editor - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Open an editor to insert code snippets into the code editor. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function insertCodeSnippet(editor) { editor.view = "views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html"; @@ -749,11 +749,11 @@ 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 an array of the selected user groups - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the user group picker in infinite editing, the submit callback returns an array of the selected user groups. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function userGroupPicker(editor) { editor.view = "views/common/infiniteeditors/usergrouppicker/usergrouppicker.html"; @@ -768,10 +768,10 @@ When building a custom infinite editor view you can use the same components as a * * @description * Opens the user group picker in infinite editing, the submit callback returns the saved user group - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function userGroupEditor(editor) { editor.view = "views/users/group.html"; @@ -784,12 +784,12 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * 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 - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * 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 {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function templateEditor(editor) { editor.view = "views/templates/edit.html"; @@ -802,11 +802,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the section picker in infinite editing, the submit callback returns an array of the selected sections¨ - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the section picker in infinite editing, the submit callback returns an array of the selected sections. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function sectionPicker(editor) { editor.view = "views/common/infiniteeditors/sectionpicker/sectionpicker.html"; @@ -820,11 +820,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the insert field editor in infinite editing, the submit callback returns the code snippet - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the insert field editor in infinite editing, the submit callback returns the code snippet. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function insertField(editor) { editor.view = "views/common/infiniteeditors/insertfield/insertfield.html"; @@ -838,11 +838,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the template sections editor in infinite editing, the submit callback returns the type to insert - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the template sections editor in infinite editing, the submit callback returns the type to insert. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function templateSections(editor) { editor.view = "views/common/infiniteeditors/templatesections/templatesections.html"; @@ -856,11 +856,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the section picker in infinite editing, the submit callback returns an array of the selected users - * @param {Object} editor rendering options - * @param {Callback} editor.submit Submits the editor - * @param {Callback} editor.close Closes the editor - * @returns {Object} editor object + * Opens the section picker in infinite editing, the submit callback returns an array of the selected users. + * @param {object} editor rendering options. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function userPicker(editor) { editor.view = "views/common/infiniteeditors/userpicker/userpicker.html"; @@ -874,15 +874,15 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the section picker in infinite editing, the submit callback returns an array of the selected items + * Opens the section picker in infinite editing, the submit callback returns an array of the selected items. * - * @param {Object} editor rendering options - * @param {Array} editor.availableItems Array of available items. - * @param {Array} editor.selectedItems Array of selected items. When passed in the selected items will be filtered from the available items. - * @param {Boolean} editor.filter Set to false to hide the filter. - * @param {Callback} editor.submit Submits the editor. - * @param {Callback} editor.close Closes the editor. - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {array} editor.availableItems Array of available items. + * @param {array} editor.selectedItems Array of selected items. When passed in the selected items will be filtered from the available items. + * @param {boolean} editor.filter Set to false to hide the filter. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function itemPicker(editor) { editor.view = "views/common/infiniteeditors/itempicker/itempicker.html"; @@ -896,11 +896,11 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a macro picker in infinite editing, the submit callback returns an array of the selected items + * Opens a macro picker in infinite editing, the submit callback returns an array of the selected items. * - * @param {Callback} editor.submit Submits the editor. - * @param {Callback} editor.close Closes the editor. - * @returns {Object} editor object + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function macroPicker(editor) { editor.view = "views/common/infiniteeditors/macropicker/macropicker.html"; @@ -916,11 +916,11 @@ When building a custom infinite editor view you can use the same components as a * @description * Opens a member group picker in infinite editing. * - * @param {Object} editor rendering options - * @param {Object} editor.multiPicker Pick one or multiple items. - * @param {Callback} editor.submit Submits the editor. - * @param {Callback} editor.close Closes the editor. - * @returns {Object} editor object + * @param {object} editor rendering options. + * @param {object} editor.multiPicker Pick one or multiple items. + * @param {function} editor.submit Submits the editor. + * @param {function} editor.close Closes the editor. + * @returns {object} editor object. */ function memberGroupPicker(editor) { editor.view = "views/common/infiniteeditors/membergrouppicker/membergrouppicker.html"; @@ -934,14 +934,14 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a member picker in infinite editing, the submit callback returns an array of selected items + * Opens a member picker in infinite editing, the submit callback returns an array of selected items. * - * @param {Object} editor rendering options - * @param {Boolean} editor.multiPicker Pick one or multiple items - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. + * @param {object} editor rendering options. + * @param {boolean} editor.multiPicker Pick one or multiple items. + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. * - * @returns {Object} editor object + * @returns {object} editor object. */ function memberPicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; @@ -957,15 +957,15 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens a member editor in infinite editing, the submit callback returns the updated member - * @param {Object} editor rendering options - * @param {String} editor.id The id (GUID) of the member - * @param {Boolean} editor.create Create new member - * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object - * @param {Function} editor.close Callback function when the close button is clicked. - * @param {String} editor.doctype If editor.create is true, provide member type for the creation of the member + * Opens a member editor in infinite editing, the submit callback returns the updated member. + * @param {object} editor rendering options. + * @param {string} editor.id The id (GUID) of the member. + * @param {boolean} editor.create Create new member. + * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object. + * @param {function} editor.close Callback function when the close button is clicked. + * @param {string} editor.doctype If editor.create is true, provide member type for the creation of the member. * - * @returns {Object} editor object + * @returns {object} editor object. */ function memberEditor(editor) { editor.view = "views/member/edit.html"; @@ -981,7 +981,7 @@ When building a custom infinite editor view you can use the same components as a * * @description * Internal method to keep track of keyboard shortcuts registered - * to each editor so they can be rebound when an editor closes + * to each editor so they can be rebound when an editor closes. * */ function unbindKeyboardShortcuts() { @@ -1001,7 +1001,7 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Internal method to rebind keyboard shortcuts for the editor in focus + * Internal method to rebind keyboard shortcuts for the editor in focus. * */ function rebindKeyboardShortcuts() { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js b/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js index 9ce2f41691..54acaf6426 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js @@ -1,8 +1,8 @@ (function () { "use strict"; - function focusLockService() { - var elementToInert = document.querySelector('#mainwrapper'); + function focusLockService($document) { + var elementToInert = $document[0].querySelector('#mainwrapper'); function addInertAttribute() { if (elementToInert) { @@ -24,7 +24,7 @@ return service; } - + angular.module("umbraco.services").factory("focusLockService", focusLockService); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js index ee1e2a2311..79c3880e60 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js @@ -45,7 +45,7 @@ (function () { 'use strict'; - function listViewHelper($location, localStorageService, urlHelper) { + function listViewHelper($location, $rootScope, localStorageService, urlHelper) { var firstSelectedIndex = 0; var localStorageKey = "umblistViewLayout"; @@ -286,6 +286,7 @@ selection.push(obj); item.selected = true; + $rootScope.$broadcast("listView.itemsChanged", { items: selection }); } } @@ -308,6 +309,7 @@ if ((item.id !== 2147483647 && item.id === selectedItem.id) || (item.key && item.key === selectedItem.key)) { selection.splice(i, 1); item.selected = false; + $rootScope.$broadcast("listView.itemsChanged", { items: selection }); } } } @@ -339,12 +341,13 @@ } } - if(Utilities.isArray(folders)) { + if (Utilities.isArray(folders)) { for (i = 0; folders.length > i; i++) { var folder = folders[i]; folder.selected = false; } } + $rootScope.$broadcast("listView.itemsChanged", { items: selection }); } /** @@ -395,10 +398,11 @@ if (clearSelection) { selection.length = 0; } + $rootScope.$broadcast("listView.itemsChanged", { items: selection }); } - - + + /** * @ngdoc method * @name umbraco.services.listViewHelper#selectAllItemsToggle @@ -410,27 +414,27 @@ * @param {Array} items Items to toggle selection on, should be $scope.items * @param {Array} selection Listview selection, available as $scope.selection */ - + function selectAllItemsToggle(items, selection) { - + if (!Utilities.isArray(items)) { return; } - + if (isSelectedAll(items, selection)) { // unselect all items items.forEach(function (item) { item.selected = false; }); - + // reset selection without loosing reference. selection.length = 0; - + } else { - + // reset selection without loosing reference. selection.length = 0; - + // select all items items.forEach(function (item) { var obj = { @@ -443,6 +447,7 @@ selection.push(obj); }); } + $rootScope.$broadcast("listView.itemsChanged", { items: selection }); } @@ -558,7 +563,7 @@ }; } - + /** * @ngdoc method * @name umbraco.services.listViewHelper#editItem @@ -584,7 +589,7 @@ $location.search(p, params[p]); } } - + function isMatchingLayout(id, layout) { // legacy format uses "nodeId", be sure to look for both return layout.id === id || layout.nodeId === id; @@ -592,21 +597,21 @@ var service = { - getLayout: getLayout, - getFirstAllowedLayout: getFirstAllowedLayout, - setLayout: setLayout, - saveLayoutInLocalStorage: saveLayoutInLocalStorage, - selectHandler: selectHandler, - selectItem: selectItem, - deselectItem: deselectItem, - clearSelection: clearSelection, - selectAllItems: selectAllItems, - selectAllItemsToggle: selectAllItemsToggle, - isSelectedAll: isSelectedAll, - setSortingDirection: setSortingDirection, - setSorting: setSorting, - getButtonPermissions: getButtonPermissions, - editItem: editItem + getLayout: getLayout, + getFirstAllowedLayout: getFirstAllowedLayout, + setLayout: setLayout, + saveLayoutInLocalStorage: saveLayoutInLocalStorage, + selectHandler: selectHandler, + selectItem: selectItem, + deselectItem: deselectItem, + clearSelection: clearSelection, + selectAllItems: selectAllItems, + selectAllItemsToggle: selectAllItemsToggle, + isSelectedAll: isSelectedAll, + setSortingDirection: setSortingDirection, + setSorting: setSorting, + getButtonPermissions: getButtonPermissions, + editItem: editItem }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index 37485ea7eb..9ba4d2964b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -135,7 +135,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS if (treeNode.iconIsClass === undefined || treeNode.iconIsClass) { var converted = iconHelper.convertFromLegacyTreeNodeIcon(treeNode); treeNode.cssClass = standardCssClass + " " + converted; - if (converted.startsWith('.')) { + if (converted && converted.startsWith('.')) { //its legacy so add some width/height treeNode.style = "height:16px;width:16px;"; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index 78c8b5fa88..0a4009264d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -18,7 +18,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown */ - convertVirtualToAbsolutePath: function(virtualPath) { + convertVirtualToAbsolutePath: function (virtualPath) { if (virtualPath.startsWith("/")) { return virtualPath; } @@ -136,7 +136,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe }; // if "opts" is a promise, we set "err.errorMsg" to be that promise - if (typeof(opts) == "object" && typeof(opts.then) == "function") { + if (typeof (opts) == "object" && typeof (opts.then) == "function") { err.errorMsg = opts; } @@ -179,7 +179,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe //show a ysod dialog if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { - const error = { errorMsg: 'An error occured', data: response.data }; + const error = { errorMsg: 'An error occurred', data: response.data }; // TODO: All YSOD handling should be done with an interceptor overlayService.ysod(error); } @@ -294,7 +294,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe } else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { //show a ysod dialog - const error = { errorMsg: 'An error occured', data: response.data }; + const error = { errorMsg: 'An error occurred', data: response.data }; // TODO: All YSOD handling should be done with an interceptor overlayService.ysod(error); } @@ -339,7 +339,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe // and setting the Content-type manually will not set this boundary parameter. For whatever reason, setting the Content-type to 'undefined' // will force the request to automatically populate the headers properly including the boundary parameter. headers: { 'Content-Type': undefined }, - transformRequest: function(data) { + transformRequest: function (data) { var formData = new FormData(); //add the json data if (Utilities.isArray(data)) { @@ -359,9 +359,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe return formData; }, data: jsonData - }).then(function(response) { + }).then(function (response) { return $q.resolve(response); - }, function(response) { + }, function (response) { return $q.reject(response); }); }, @@ -377,7 +377,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * @param {string} httpPath the path (url) to the resource being downloaded * @returns {Promise} http promise object. */ - downloadFile : function (httpPath) { + downloadFile: function (httpPath) { /** * Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html @@ -471,7 +471,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe window.open(httpPath, '_blank', ''); } - return $q.resolve(); + return $q.resolve(response); }, function (response) { @@ -484,4 +484,5 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe } }; } + angular.module('umbraco.services').factory('umbRequestHelper', umbRequestHelper); diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html index 92f5cc8d9d..d7c21dc4a9 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html @@ -10,13 +10,13 @@ diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index e5ce8d572c..359c3dd427 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -143,6 +143,7 @@ @import "components/umb-property-editor.less"; @import "components/umb-property-actions.less"; @import "components/umb-code-snippet.less"; +@import "components/umb-color-picker.less"; @import "components/umb-color-swatches.less"; @import "components/check-circle.less"; @import "components/umb-file-icon.less"; @@ -176,6 +177,7 @@ @import "components/umb-range-slider.less"; @import "components/umb-number.less"; @import "components/umb-tags-editor.less"; +@import "components/umb-search-filter.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index 2caa223647..c446a02424 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -342,7 +342,7 @@ input[type="submit"].btn { cursor: pointer; color: @ui-icon; margin: 0; - padding: 5px 10px; + padding: 3px 5px; width: auto; overflow: visible; background: transparent; diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index 7135692ae8..24d03d01a1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -192,6 +192,7 @@ body { #demo-iframe-wrapper { transition: all 240ms cubic-bezier(0.165, 0.84, 0.44, 1); + flex-shrink:0; } .fullsize { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less index df60c8aed1..83dcc258a4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less @@ -20,6 +20,12 @@ justify-content: center; flex: 1 1 auto; cursor: pointer; + + label { + padding: unset; + margin: unset; + pointer-events: none; + } } .umb-toggle-group-item--disabled .umb-toggle-group-item__toggle, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less index e81df77772..19d6a1306e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less @@ -12,6 +12,7 @@ .absolute(); background: @brownGrayLight; z-index: @zIndexEditor; + max-width: 100%; &--infiniteMode { transform: none; 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 f967994c0f..ffbe2224d9 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 @@ -155,6 +155,14 @@ body.touch .umb-tree { &-link { display: block; } + + &-name { + display: flex; + + &__text { + margin: 1px 0 0; + } + } } &-link { @@ -249,6 +257,7 @@ body.touch .umb-tree { > .umb-tree-item__inner > i.icon, > .umb-tree-item__inner > a { cursor: not-allowed; + opacity: 0.4; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less index d79bbbde20..18294f9e59 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less @@ -8,45 +8,88 @@ border-radius: 100px; } +.umb-badge__count { + display: flex; + width: 1rem; + height: 1rem; + line-height: 1; + justify-content: center; + align-items: center; + border-radius: 50%; + font-size: 12px; +} + // Colors .umb-badge--primary { background-color: @blueDark; color: @white; + + .umb-badge__count { + background-color: darken(@blueDark, 5%); + } } .umb-badge--secondary { background-color: @blueExtraDark; color: @white; + + .umb-badge__count { + background-color: darken(@blueExtraDark, 8%); + } } .umb-badge--gray { background-color: @sand-2; color: @blueDark; + + .umb-badge__count { + background-color: darken(@sand-2, 8%); + } } .umb-badge--danger { background-color: @red; color: @white; + + .umb-badge__count { + background-color: darken(@red, 8%); + } } .umb-badge--info { background-color: @blue; color: @white; + + .umb-badge__count { + background-color: darken(@blue, 8%); + } } .umb-badge--warning { background-color: @orange; color: @white; + + .umb-badge__count { + background-color: darken(@orange, 8%); + } } .umb-badge--success { background-color: @green; color: @white; + + .umb-badge__count { + background-color: darken(@green, 8%); + } } .umb-badge--dark { background-color: @grayDark; color: @white; + + .umb-badge__count { + background-color: darken(@grayDark, 8%); + } } // Size @@ -56,7 +99,8 @@ } .umb-badge--xs { - font-size: 13px; + font-size: 12px; + font-weight: 600; padding: 1px 10px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less new file mode 100644 index 0000000000..21439efeca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less @@ -0,0 +1,21 @@ +.umb-color-picker { + + .sp-replacer { + display: inline-flex; + margin-right: 18px; + height: 32px; + + &.sp-light { + background-color: @white; + } + + .sp-preview { + margin: 5px; + height: auto; + } + + .sp-dd { + line-height: 2rem; + } + } +} 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 641e0dc7a7..5e9772fb26 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 @@ -93,7 +93,7 @@ .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); } - &:focus-within > &__anchor_dropdown, + & > &__anchor_dropdown.is-expanded, &:hover > &__anchor_dropdown { visibility: visible; opacity: 1; 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 0915f5f64f..784598e84a 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 @@ -829,7 +829,6 @@ position: absolute; width: 100%; left: 0; - bottom: -25px; padding-top: 15px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less index 2590e0610a..87e46f5d85 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less @@ -355,7 +355,7 @@ input.umb-group-builder__group-title-input:disabled:hover { position: relative; margin: 5px 0; - > button { + button.btn-icon { border: none; font-size: 18px; position: relative; 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 d05139a06f..5f79d65de1 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 @@ -19,11 +19,19 @@ align-items: center; align-self: stretch; + border-radius: @baseBorderRadius; + margin: 10px; position: relative; user-select: none; box-shadow: 0 1px 1px 0 rgba(0,0,0,.2); transition: box-shadow 150ms ease-in-out; + + > div { + overflow: hidden; + border-radius: @baseBorderRadius; + } + } .umb-media-grid__item.-unselectable { @@ -35,7 +43,8 @@ left: 0; right: 0; bottom: 0; - background-color: rgba(230, 230, 230, .5); + border-radius: @baseBorderRadius; + background-color: rgba(230, 230, 230, .8); pointer-events: none; } } 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 5f0a6a3c00..05824ba425 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 @@ -91,9 +91,10 @@ color:@ui-option-type-hover; } - i { + .umb-nested-content__item-icon { position: absolute; - margin-top: -1px; + margin-top: -3px; + font-size: 22px; } .umb-nested-content__item-name { @@ -116,7 +117,7 @@ transition: opacity 120ms ease-in-out; position: absolute; right: 0; - top: 3px; + top: 5px; padding: 5px; background-color: @white; } @@ -167,7 +168,7 @@ .umb-nested-content__icon .icon { display: block; - font-size: 16px !important; + font-size: 18px !important; } .umb-nested-content__icon--disabled { 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 939fd79826..ac7277109a 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 @@ -28,8 +28,6 @@ .umb-node-preview__icon { display: flex; - width: 25px; - min-height: 25px; height: 100%; justify-content: center; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-search-filter.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-search-filter.less new file mode 100644 index 0000000000..bda9fa7a7e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-search-filter.less @@ -0,0 +1,29 @@ +html .umb-search-filter { + position: relative; + height: 30px; + width: 190px; + + &.w-100 { + width: 100%; + } + + &.mb-15 { + margin-bottom: 15px; + } + + &__input { + padding-left: 30px; + padding-right: 6px; + width: inherit; + margin: 0; + } + + .icon-search { + color: #d8d7d9; + position: absolute; + top: 0; + bottom: 0; + left: 10px; + margin: auto 0; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-tags-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-tags-editor.less index 9a80aa4ff3..213807e685 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-tags-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-tags-editor.less @@ -36,18 +36,17 @@ } } - input { - border: none; - background: @white; - } - .twitter-typeahead { margin: 10px; margin-top: 16px; vertical-align: top; + max-width: calc(100% - 20px); input { + border: none; + background: @white; padding-left: 0; + max-width: 100%; } } } diff --git a/src/Umbraco.Web.UI.Client/src/less/fonts.less b/src/Umbraco.Web.UI.Client/src/less/fonts.less index 73bc36037f..e9a84656f9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/fonts.less +++ b/src/Umbraco.Web.UI.Client/src/less/fonts.less @@ -15,8 +15,7 @@ font-family: 'Lato'; src: local('LatoLatin Black'), local('LatoLatin-Black'), - url('@{latoPath}/LatoLatin-Black.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-Black.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-Black.woff2') format('woff2'); font-style: normal; font-display: swap; font-weight: 900; @@ -28,8 +27,7 @@ font-family: 'Lato'; src: local('LatoLatin BlackItalic'), local('LatoLatin-BlackItalic'), - url('@{latoPath}/LatoLatin-BlackItalic.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-BlackItalic.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-BlackItalic.woff2') format('woff2'); font-style: italic; font-weight: 900; font-display: swap; @@ -41,8 +39,7 @@ font-family: 'Lato'; src: local('LatoLatin Bold'), local('LatoLatin-Bold'), - url('@{latoPath}/LatoLatin-Bold.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-Bold.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-Bold.woff2') format('woff2'); font-style: normal; font-weight: 700; font-display: swap; @@ -54,8 +51,7 @@ font-family: 'Lato'; src: local('LatoLatin BoldItalic'), local('LatoLatin-BoldItalic'), - url('@{latoPath}/LatoLatin-BoldItalic.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-BoldItalic.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-BoldItalic.woff2') format('woff2'); font-style: italic; font-weight: 700; font-display: swap; @@ -67,8 +63,7 @@ font-family: 'Lato'; src: local('LatoLatin Italic'), local('LatoLatin-Italic'), - url('@{latoPath}/LatoLatin-Italic.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-Italic.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-Italic.woff2') format('woff2'); font-style: italic; font-weight: 400; font-display: swap; @@ -80,8 +75,7 @@ font-family: 'Lato'; src: local('LatoLatin Regular'), local('LatoLatin-Regular'), - url('@{latoPath}/LatoLatin-Regular.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-Regular.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-Regular.woff2') format('woff2'); font-style: normal; font-weight: 400; font-display: swap; @@ -93,8 +87,7 @@ font-family: 'Lato'; src: local('LatoLatin Light'), local('LatoLatin-Light'), - url('@{latoPath}/LatoLatin-Light.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-Light.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-Light.woff2') format('woff2'); font-style: normal; font-weight: 300; font-display: swap; @@ -106,8 +99,7 @@ font-family: 'Lato'; src: local('LatoLatin LightItalic'), local('LatoLatin-LightItalic'), - url('@{latoPath}/LatoLatin-LightItalic.woff2') format('woff2'), /* Super Modern Browsers */ - url('@{latoPath}/LatoLatin-LightItalic.woff') format('woff'); /* Modern Browsers */ + url('@{latoPath}/LatoLatin-LightItalic.woff2') format('woff2'); font-style: italic; font-weight: 300; font-display: swap; diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 76c5af8819..90b2dbe37e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -818,6 +818,7 @@ legend + .control-group { // Labels on own row .form-horizontal .control-label { float:none; + text-align: inherit; width: 100%; } .form-horizontal .controls { diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less index 4e24161e59..9ce519186a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/installer.less +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -57,7 +57,8 @@ body { #installer { margin: auto; background: @white; - width: 750px; + width: 80%; + max-width: 750px; height: 600px; text-align: left; padding: 30px; diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index c347404619..e45a4d46bb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -168,8 +168,7 @@ // Active state .nav-pills > .active > a, -.nav-pills > .active > a:hover, -.nav-pills > .active > a:focus { +.nav-pills > .active > a:hover { color: @white; background-color: @linkColor; } @@ -184,7 +183,14 @@ float: none; } .nav-stacked > li > a { + position: relative; margin-right: 0; // no need for the gap between nav items + color: @ui-action-discreet-type; + border-radius: 3px; +} +.nav-stacked > li > a:hover { + color: @ui-action-discreet-type-hover; + background-color: @ui-action-discreet-hover; } // Tabs diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/login.less b/src/Umbraco.Web.UI.Client/src/less/pages/login.less index 818b1d84d1..cf49af526b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/login.less @@ -125,9 +125,8 @@ user-select: none; margin-left: auto; - a { + button { opacity: .5; - cursor: pointer; display: inline-block; z-index: 1; -webkit-tap-highlight-color: transparent; @@ -135,7 +134,7 @@ .password-text { background-repeat: no-repeat; background-size: 18px; - background-position: 0px 1px; + background-position: 0 1px; padding-left: 24px; &.show { diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 81326826f6..a036267c85 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -263,9 +263,14 @@ .umb-dialog a.muted:hover, .umb-dialog a.muted:focus, +.umb-dialog button.muted:hover, +.umb-dialog button.muted:focus, .umb-panel a.muted:hover, -.umb-panel a.muted:focus { +.umb-panel a.muted:focus, +.umb-panel button.muted:hover, +.umb-panel button.muted:focus { color: darken(@gray-5, 10%); + text-decoration: underline; } .umb-dialog .text-warning, diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less index dc6d2ff6cd..7960aae2c9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/properties.less +++ b/src/Umbraco.Web.UI.Client/src/less/properties.less @@ -119,7 +119,9 @@ } .history-item__date { - font-size: 13px; + font-size: 12px; + margin-top: -4px; + display: block; color: @gray-5; } @@ -131,7 +133,10 @@ } .history-item__badge { - margin-right: 5px; + margin-right: 10px; +} +.history-item__description { + color: @gray-5; } /* RESPONSIVE */ 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 3912d11161..0d8f270f1b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -104,6 +104,20 @@ // -------------------------------------------------- /* pre-value editor */ +.umb-prevalues-multivalues.umb-colors { + max-width: 600px; + width: 100%; + min-width: 66.6%; + + @media (min-width: 1101px) and (max-width: 1300px), (max-width: 930px) { + max-width: none; + } + + .umb-overlay__form & { + width: 100%; + } +} + .control-group.color-picker-preval { .thumbnail { width: 34px; @@ -160,21 +174,6 @@ width: auto; } - .sp-replacer { - display: inline-flex; - margin-right: 18px; - height: auto; - - .sp-preview { - margin: 5px; - height: auto; - } - - .sp-dd { - line-height: 2rem; - } - } - label { border: 1px solid @white; padding: 6px 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 0e43f428ae..0eab4b346b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -35,6 +35,11 @@ box-sizing: border-box; } +.umb-rte .mce-top-part { + position: sticky; + top: 0; +} + /* make sure the menu wraps */ .umb-rte .mce-top-part.mce-container div { white-space: normal; diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 9a552826dd..d87ca43452 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -29,7 +29,28 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi window.addEventListener("keydown", handleFirstTab); } + var iframeWrapper = angular.element("#demo-iframe-wrapper"); + var canvasDesignerPanel = angular.element("#canvasdesignerPanel"); + window.addEventListener("keydown", handleFirstTab); + window.addEventListener("resize", scaleIframeWrapper); + iframeWrapper.on("transitionend", scaleIframeWrapper); + + function scaleIframeWrapper() { + if ($scope.previewDevice.name == "fullsize") { // dont scale fullsize preview + iframeWrapper.css({"transform": ""}); + } + else { + var wrapWidth = canvasDesignerPanel.width(); // width of the wrapper + var wrapHeight = canvasDesignerPanel.height(); + var childWidth = iframeWrapper.width() + 30; // width of child iframe plus some space + var childHeight = iframeWrapper.height() + 30; // child height plus some space + var wScale = wrapWidth / childWidth; + var hScale = wrapHeight / childHeight; + var scale = Math.min(wScale,hScale,1); // get the lowest ratio, but not higher than 1 + iframeWrapper.css({"transform": "scale("+scale+")" }); // set scale + } + } //gets a real query string value 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 6b924d0aef..04b5501846 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 @@ -2,8 +2,8 @@ + title="{{vm.title}}" + description="{{vm.subtitle}}"> @@ -14,7 +14,6 @@
-
@@ -22,7 +21,7 @@ {{ tour.name }}
- +
@@ -98,7 +97,7 @@ -
+ + + + - -
-
- Videos -
- -
+ +
+
+ Videos +
+ +
- -
@@ -159,6 +158,7 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html index 436c4ea30a..8060605b87 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html @@ -17,17 +17,16 @@
- + + + +
@@ -113,7 +112,7 @@ - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html index 14b82892e7..3d8e1d4d0b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html @@ -21,6 +21,7 @@ label-key="placeholders_search" text="Type to search" on-change="vm.searchTermChanged()" + css-class="w-100" auto-focus="true" > diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html index e364b29b3f..7368d2f39b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html @@ -18,17 +18,14 @@
- + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html index 65148d5cc6..093e69b5ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html @@ -107,9 +107,9 @@
Link to media
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html index 151a8167e8..760c5331b7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html @@ -22,6 +22,7 @@ model="vm.searchTerm" label-key="placeholders_filter" text="Type to filter..." + css-class="w-100" on-change="vm.filterItems()" auto-focus="true" > diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html index c23aaa7cb9..33d7a471a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html @@ -17,22 +17,18 @@
- + +
    -
  • -
-
+
+ + + - + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html index eb08b5646d..4e1c6b8e6e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html @@ -18,18 +18,16 @@ - + + +
  • @@ -22,7 +22,7 @@ Open/Close backoffice help... - +
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 6f1a9b1aa3..143e8af1fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -158,10 +158,10 @@
  • @@ -173,7 +173,10 @@ type="submit">
    - Forgotten password? +
    @@ -205,7 +208,9 @@
    - Return to login form +
    @@ -241,7 +246,9 @@
    - Return to login form +
    @@ -253,7 +260,9 @@
    - Return to login form +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index 65e42fd5c4..d75a9a5379 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -23,7 +23,7 @@ class="umb-language-picker__dropdown-item" ng-class="{'umb-language-picker__dropdown-item--current': language.active}" ng-click="selectLanguage(language)" - ng-repeat="language in languages" + ng-repeat="language in languages | orderBy:'name'" > Switch language to diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle-group.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle-group.html index 1a9026c7ab..4576a30418 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle-group.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle-group.html @@ -3,10 +3,11 @@
    -
    {{ item.name }}
    +
    {{ item.description }}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 0fcec4c559..6a0744bf5c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -7,13 +7,12 @@