diff --git a/.github/README.md b/.github/README.md index 5316ad0604..83de27c859 100644 --- a/.github/README.md +++ b/.github/README.md @@ -48,4 +48,5 @@ Umbraco is contribution focused and community driven. If you want to contribute Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](CONTRIBUTING_DETAILED.md#reporting-bugs). -To view existing issues, please visit [http://issues.umbraco.org](http://issues.umbraco.org). +You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues). +Since [September 2018](https://umbraco.com/blog/a-second-take-on-umbraco-issue-tracker-hello-github-issues/) the old issue tracker is in read only mode, but can still be found at [http://issues.umbraco.org](http://issues.umbraco.org). diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index eeb23b461e..e9bd8ca6ea 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -22,12 +22,12 @@ --> - + - + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 7a7f672ea6..434fe812ef 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -22,7 +22,7 @@ not want this to happen as the alpha of the next major is, really, the next major already. --> - + @@ -40,7 +40,6 @@ - diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt index dd8874cce7..53915436ac 100644 --- a/build/NuSpecs/tools/Readme.txt +++ b/build/NuSpecs/tools/Readme.txt @@ -21,6 +21,6 @@ The following items will now be automatically included when creating a deploy pa system: umbraco, config\splashes and global.asax. Please read the release notes on our.umbraco.com: -http://our.umbraco.com/contribute/releases +https://our.umbraco.com/download/releases - Umbraco diff --git a/build/NuSpecs/tools/applications.config.install.xdt b/build/NuSpecs/tools/applications.config.install.xdt index f4a0060150..5c05b2b4e9 100644 --- a/build/NuSpecs/tools/applications.config.install.xdt +++ b/build/NuSpecs/tools/applications.config.install.xdt @@ -1,11 +1,11 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + + diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 01803741d0..d7f81c1bb1 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -19,4 +19,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.0.0")] -[assembly: AssemblyInformationalVersion("8.0.0-alpha.50")] +[assembly: AssemblyInformationalVersion("8.0.0-alpha.52")] diff --git a/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs b/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs index 25399af5c7..f44afe394f 100644 --- a/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs @@ -38,8 +38,6 @@ namespace Umbraco.Core.Composing.Composers container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index fc898dd3b6..16b824e481 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -27,7 +27,6 @@ namespace Umbraco.Core.Composing.Composers // register the services container.RegisterSingleton(); container.RegisterSingleton(); - container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index 49bb44db47..b3c71e3d81 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -158,6 +158,9 @@ namespace Umbraco.Core.Composing public static IPublishedValueFallback PublishedValueFallback => _publishedValueFallback ?? Container.GetInstance() ?? new NoopPublishedValueFallback(); + public static IVariationContextAccessor VariationContextAccessor + => Container.GetInstance(); + #endregion } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 332de45734..39861ac4e9 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -81,6 +81,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("loginBackgroundImage")] internal InnerTextConfigurationElement LoginBackgroundImage => GetOptionalTextElement("loginBackgroundImage", string.Empty); + [ConfigurationProperty("StripUdiAttributes")] + internal InnerTextConfigurationElement StripUdiAttributes + { + get { return GetOptionalTextElement("StripUdiAttributes", true); } + } + string IContentSection.NotificationEmailAddress => Notifications.NotificationEmailAddress; @@ -136,6 +142,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings bool IContentSection.EnableInheritedMediaTypes => EnableInheritedMediaTypes; + bool IContentSection.StripUdiAttributes => StripUdiAttributes; + string IContentSection.LoginBackgroundImage => LoginBackgroundImage; } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 7f6f57f4cf..ef9ffeb014 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IEnumerable ImageTagAllowedAttributes { get; } IEnumerable ImageAutoFillProperties { get; } - + string ScriptFolderPath { get; } IEnumerable ScriptFileTypes { get; } @@ -66,5 +66,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings bool EnableInheritedMediaTypes { get; } string LoginBackgroundImage { get; } + bool StripUdiAttributes { get; } + } } diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 10ca0c122f..46ad221837 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Configuration /// /// Gets the version comment of the executing code (eg "beta"). /// - public static string CurrentComment => "alpha.50"; + public static string CurrentComment => "alpha.52"; /// /// Gets the assembly version of Umbraco.Code.dll. diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index 9a1883a065..ac42274a71 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -13,9 +13,9 @@ public const string Content = "content"; /// - /// Application alias for the developer section. + /// Application alias for the packages section. /// - public const string Developer = "developer"; + public const string Packages = "packages"; /// /// Application alias for the media section. diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 7f6f6e875f..eb1a6e2925 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -23,9 +23,6 @@ namespace Umbraco.Core { public const string MemberUsernameRuleType = "MemberUsername"; public const string MemberRoleRuleType = "MemberRole"; - - [Obsolete("No longer supported, this is here for backwards compatibility only")] - public const string MemberIdRuleType = "MemberId"; } @@ -94,6 +91,11 @@ namespace Umbraco.Core /// Property alias for the Media's file extension. /// public const string Extension = "umbracoExtension"; + + /// + /// The default height/width of an image file if the size can't be determined from the metadata + /// + public const int DefaultSize = 200; } /// diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 790c143bbf..4bf944e1e1 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -71,9 +71,6 @@ namespace Umbraco.Core [Obsolete("This no longer exists in the database")] internal const string Stylesheet = "9F68DA4F-A3A8-44C2-8226-DCBD125E4840"; - [Obsolete("This no longer exists in the database")] - internal const string StylesheetProperty = "5555da4f-a123-42b2-4488-dcdfb25e4111"; - // ReSharper restore MemberHidesStaticFromOuterClass } diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index eaceff275e..abb92298f4 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -58,6 +58,7 @@ /// public const string RecycleBinMediaPathPrefix = "-1,-21,"; + public const int DefaultLabelDataTypeId = -92; public const string UmbracoConnectionName = "umbracoDbDSN"; public const string UmbracoUpgradePlanName = "Umbraco.Core"; } diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index 0d199d1d0d..c455fadad7 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -92,18 +92,7 @@ namespace Umbraco.Core } } - /// The flatten list. - /// The items. - /// The select child. - /// Item type - /// list of TItem - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")] - public static IEnumerable FlattenList(this IEnumerable e, Func> f) - { - return e.SelectMany(c => f(c).FlattenList(f)).Concat(e); - } - + /// /// Returns true if all items in the other collection exist in this collection /// diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs b/src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs index 0283ac372e..4c38c0b2ec 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/QueuingEventDispatcherBase.cs @@ -307,7 +307,7 @@ namespace Umbraco.Core.Events // fixme see notes above // delete event args does NOT superceedes 'unpublished' event - if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs<>) && infos.EventDefinition.EventName == "UnPublished") + if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs<>) && infos.EventDefinition.EventName == "Unpublished") return false; // found occurences, need to determine if this event args is superceded diff --git a/src/Umbraco.Core/HttpContextExtensions.cs b/src/Umbraco.Core/HttpContextExtensions.cs index e370b055a4..22eb4d1917 100644 --- a/src/Umbraco.Core/HttpContextExtensions.cs +++ b/src/Umbraco.Core/HttpContextExtensions.cs @@ -24,11 +24,19 @@ namespace Umbraco.Core { return "Unknown, httpContext is null"; } - if (httpContext.Request == null) + + HttpRequestBase request; + try + { + // is not null - throws + request = httpContext.Request; + } + catch { return "Unknown, httpContext.Request is null"; } - if (httpContext.Request.ServerVariables == null) + + if (request.ServerVariables == null) { return "Unknown, httpContext.Request.ServerVariables is null"; } @@ -37,16 +45,16 @@ namespace Umbraco.Core try { - var ipAddress = httpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; + var ipAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (string.IsNullOrEmpty(ipAddress)) - return httpContext.Request.UserHostAddress; + return request.UserHostAddress; var addresses = ipAddress.Split(','); if (addresses.Length != 0) return addresses[0]; - return httpContext.Request.UserHostAddress; + return request.UserHostAddress; } catch (System.Exception ex) { diff --git a/src/Umbraco.Core/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs index db530e5339..ed98e5cfab 100644 --- a/src/Umbraco.Core/Logging/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -30,17 +30,17 @@ namespace Umbraco.Core.Logging _endMessage = endMessage; _failMessage = failMessage; _thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds; - _timingId = Guid.NewGuid().ToString("N"); + _timingId = Guid.NewGuid().ToString("N").Substring(0, 7); // keep it short-ish if (thresholdMilliseconds == 0) { switch (_level) { case LogLevel.Debug: - logger.Debug(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); + logger.Debug(loggerType, "{StartMessage} [Timing {TimingId}]", startMessage, _timingId); break; case LogLevel.Information: - logger.Info(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); + logger.Info(loggerType, "{StartMessage} [Timing {TimingId}]", startMessage, _timingId); break; default: throw new ArgumentOutOfRangeException(nameof(level)); @@ -84,15 +84,15 @@ namespace Umbraco.Core.Logging { if (_failed) { - _logger.Error(_loggerType, _failException, "[Timing {TimingId}] {FailMessage} ({TimingDuration}ms)", _timingId, _failMessage, Stopwatch.ElapsedMilliseconds); + _logger.Error(_loggerType, _failException, "{FailMessage} ({Duration}ms) [Timing {TimingId}]", _failMessage, Stopwatch.ElapsedMilliseconds, _timingId); } else switch (_level) { case LogLevel.Debug: - _logger.Debug(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); + _logger.Debug(_loggerType, "{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); break; case LogLevel.Information: - _logger.Info(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); + _logger.Info(_loggerType, "{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); break; // filtered in the ctor //default: diff --git a/src/Umbraco.Core/Logging/LogHttpRequest.cs b/src/Umbraco.Core/Logging/LogHttpRequest.cs new file mode 100644 index 0000000000..34c1918b76 --- /dev/null +++ b/src/Umbraco.Core/Logging/LogHttpRequest.cs @@ -0,0 +1,32 @@ +using System; +using System.Web; + +namespace Umbraco.Core.Logging +{ + public static class LogHttpRequest + { + static readonly string RequestIdItemName = typeof(LogHttpRequest).Name + "+RequestId"; + + /// + /// Retrieve the id assigned to the currently-executing HTTP request, if any. + /// + /// The request id. + /// true if there is a request in progress; false otherwise. + public static bool TryGetCurrentHttpRequestId(out Guid requestId) + { + if (HttpContext.Current == null) + { + requestId = default(Guid); + return false; + } + + var requestIdItem = HttpContext.Current.Items[RequestIdItemName]; + if (requestIdItem == null) + HttpContext.Current.Items[RequestIdItemName] = requestId = Guid.NewGuid(); + else + requestId = (Guid)requestIdItem; + + return true; + } + } +} diff --git a/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs new file mode 100644 index 0000000000..2099698b6f --- /dev/null +++ b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs @@ -0,0 +1,36 @@ +using System; +using Serilog.Core; +using Serilog.Events; + +namespace Umbraco.Core.Logging.Serilog.Enrichers +{ + /// + /// Enrich log events with a HttpRequestId GUID. + /// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpRequestIdEnricher.cs + /// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want + /// + internal class HttpRequestIdEnricher : ILogEventEnricher + { + /// + /// The property name added to enriched log events. + /// + public const string HttpRequestIdPropertyName = "HttpRequestId"; + + /// + /// Enrich the log event with an id assigned to the currently-executing HTTP request, if any. + /// + /// The log event to enrich. + /// Factory for creating new properties to add to the event. + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + if (logEvent == null) throw new ArgumentNullException("logEvent"); + + Guid requestId; + if (!LogHttpRequest.TryGetCurrentHttpRequestId(out requestId)) + return; + + var requestIdProperty = new LogEventProperty(HttpRequestIdPropertyName, new ScalarValue(requestId)); + logEvent.AddPropertyIfAbsent(requestIdProperty); + } + } +} diff --git a/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs new file mode 100644 index 0000000000..48415cccbc --- /dev/null +++ b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading; +using System.Web; +using Serilog.Core; +using Serilog.Events; + +namespace Umbraco.Core.Logging.Serilog.Enrichers +{ + /// + /// Enrich log events with a HttpRequestNumber unique within the current + /// logging session. + /// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpRequestNumberEnricher.cs + /// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want + /// + internal class HttpRequestNumberEnricher : ILogEventEnricher + { + /// + /// The property name added to enriched log events. + /// + public const string HttpRequestNumberPropertyName = "HttpRequestNumber"; + + static int _lastRequestNumber; + static readonly string RequestNumberItemName = typeof(HttpRequestNumberEnricher).Name + "+RequestNumber"; + + /// + /// Enrich the log event with the number assigned to the currently-executing HTTP request, if any. + /// + /// The log event to enrich. + /// Factory for creating new properties to add to the event. + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + if (logEvent == null) throw new ArgumentNullException("logEvent"); + + if (HttpContext.Current == null) + return; + + int requestNumber; + var requestNumberItem = HttpContext.Current.Items[RequestNumberItemName]; + if (requestNumberItem == null) + HttpContext.Current.Items[RequestNumberItemName] = requestNumber = Interlocked.Increment(ref _lastRequestNumber); + else + requestNumber = (int)requestNumberItem; + + var requestNumberProperty = new LogEventProperty(HttpRequestNumberPropertyName, new ScalarValue(requestNumber)); + logEvent.AddPropertyIfAbsent(requestNumberProperty); + } + } +} diff --git a/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs new file mode 100644 index 0000000000..d2fbfd4627 --- /dev/null +++ b/src/Umbraco.Core/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs @@ -0,0 +1,39 @@ +using Serilog.Core; +using Serilog.Events; +using System; +using System.Web; + +namespace Umbraco.Core.Logging.Serilog.Enrichers +{ + /// + /// Enrich log events with the HttpSessionId property. + /// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpSessionIdEnricher.cs + /// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want + /// + internal class HttpSessionIdEnricher : ILogEventEnricher + { + /// + /// The property name added to enriched log events. + /// + public const string HttpSessionIdPropertyName = "HttpSessionId"; + + /// + /// Enrich the log event with the current ASP.NET session id, if sessions are enabled. + /// The log event to enrich. + /// Factory for creating new properties to add to the event. + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + if (logEvent == null) throw new ArgumentNullException("logEvent"); + + if (HttpContext.Current == null) + return; + + if (HttpContext.Current.Session == null) + return; + + var sessionId = HttpContext.Current.Session.SessionID; + var sessionIdProperty = new LogEventProperty(HttpSessionIdPropertyName, new ScalarValue(sessionId)); + logEvent.AddPropertyIfAbsent(sessionIdProperty); + } + } +} diff --git a/src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs b/src/Umbraco.Core/Logging/Serilog/Enrichers/Log4NetLevelMapperEnricher.cs similarity index 96% rename from src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs rename to src/Umbraco.Core/Logging/Serilog/Enrichers/Log4NetLevelMapperEnricher.cs index 1424fa0b55..0c255fa8b4 100644 --- a/src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs +++ b/src/Umbraco.Core/Logging/Serilog/Enrichers/Log4NetLevelMapperEnricher.cs @@ -1,7 +1,7 @@ using Serilog.Core; using Serilog.Events; -namespace Umbraco.Core.Logging.Serilog +namespace Umbraco.Core.Logging.Serilog.Enrichers { /// /// This is used to create a new property in Logs called 'Log4NetLevel' diff --git a/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs b/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs index 8861c808df..2d333ed916 100644 --- a/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs +++ b/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs @@ -3,6 +3,7 @@ using System.Web; using Serilog; using Serilog.Events; using Serilog.Formatting.Compact; +using Umbraco.Core.Logging.Serilog.Enrichers; namespace Umbraco.Core.Logging.Serilog { @@ -21,7 +22,7 @@ namespace Umbraco.Core.Logging.Serilog //Set this environment variable - so that it can be used in external config file //add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" /> Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process); - + logConfig.MinimumLevel.Verbose() //Set to highest level of logging (as any sinks may want to restrict it to Errors only) .Enrich.WithProcessId() .Enrich.WithProcessName() @@ -29,8 +30,11 @@ namespace Umbraco.Core.Logging.Serilog .Enrich.WithProperty("AppDomainId", AppDomain.CurrentDomain.Id) .Enrich.WithProperty("AppDomainAppId", HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty)) .Enrich.WithProperty("MachineName", Environment.MachineName) - .Enrich.With(); - + .Enrich.With() + .Enrich.With() + .Enrich.With() + .Enrich.With(); + return logConfig; } diff --git a/src/Umbraco.Core/Manifest/ContentAppDefinitionConverter.cs b/src/Umbraco.Core/Manifest/ContentAppDefinitionConverter.cs new file mode 100644 index 0000000000..87f104d90e --- /dev/null +++ b/src/Umbraco.Core/Manifest/ContentAppDefinitionConverter.cs @@ -0,0 +1,16 @@ +using System; +using Newtonsoft.Json.Linq; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Serialization; + +namespace Umbraco.Core.Manifest +{ + /// + /// Implements a json read converter for . + /// + internal class ContentAppDefinitionConverter : JsonReadConverter + { + protected override IContentAppDefinition Create(Type objectType, string path, JObject jObject) + => new ManifestContentAppDefinition(); + } +} diff --git a/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs b/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs new file mode 100644 index 0000000000..6b8534a88f --- /dev/null +++ b/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; + +namespace Umbraco.Core.Manifest +{ + // contentApps: [ + // { + // name: 'App Name', // required + // alias: 'appAlias', // required + // weight: 0, // optional, default is 0, use values between -99 and +99 + // icon: 'icon.app', // required + // view: 'path/view.htm', // required + // show: [ // optional, default is always show + // '-content/foo', // hide for content type 'foo' + // '+content/*', // show for all other content types + // '+media/*' // show for all media types + // ] + // }, + // ... + // ] + + /// + /// Represents a content app definition, parsed from a manifest. + /// + [DataContract(Name = "appdef", Namespace = "")] + public class ManifestContentAppDefinition : IContentAppDefinition + { + private string _view; + private ContentApp _app; + private ShowRule[] _showRules; + + /// + /// Gets or sets the name of the content app. + /// + [DataMember(Name = "name")] + public string Name { get; set; } + + /// + /// Gets or sets the unique alias of the content app. + /// + /// + /// Must be a valid javascript identifier, ie no spaces etc. + /// + [DataMember(Name = "alias")] + public string Alias { get; set; } + + /// + /// Gets or sets the weight of the content app. + /// + [DataMember(Name = "weight")] + public int Weight { get; set; } + + /// + /// Gets or sets the icon of the content app. + /// + /// + /// Must be a valid helveticons class name (see http://hlvticons.ch/). + /// + [DataMember(Name = "icon")] + public string Icon { get; set; } + + /// + /// Gets or sets the view for rendering the content app. + /// + [DataMember(Name = "view")] + public string View + { + get => _view; + set => _view = IOHelper.ResolveVirtualUrl(value); + } + + /// + /// Gets or sets the list of 'show' conditions for the content app. + /// + [DataMember(Name = "show")] + public string[] Show { get; set; } = Array.Empty(); + + /// + public ContentApp GetContentAppFor(object o) + { + string partA, partB; + + switch (o) + { + case IContent content: + partA = "content"; + partB = content.ContentType.Alias; + break; + + case IMedia media: + partA = "media"; + partB = media.ContentType.Alias; + break; + + default: + return null; + } + + var rules = _showRules ?? (_showRules = ShowRule.Parse(Show).ToArray()); + + // if no 'show' is specified, then always display the content app + if (rules.Length > 0) + { + var ok = false; + + // else iterate over each entry + foreach (var rule in rules) + { + // if the entry does not apply, skip it + if (!rule.Matches(partA, partB)) + continue; + + // if the entry applies, + // if it's an exclude entry, exit, do not display the content app + if (!rule.Show) + return null; + + // else break - ok to display + ok = true; + break; + } + + // when 'show' is specified, default is to *not* show the content app + if (!ok) + return null; + } + + // content app can be displayed + return _app ?? (_app = new ContentApp + { + Alias = Alias, + Name = Name, + Icon = Icon, + View = View, + Weight = Weight + }); + } + + private class ShowRule + { + private static readonly Regex ShowRegex = new Regex("^([+-])?([a-z]+)/([a-z0-9_]+|\\*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public bool Show { get; private set; } + public string PartA { get; private set; } + public string PartB { get; private set; } + + public bool Matches(string partA, string partB) + { + return (PartA == "*" || PartA.InvariantEquals(partA)) && (PartB == "*" || PartB.InvariantEquals(partB)); + } + + public static IEnumerable Parse(string[] rules) + { + foreach (var rule in rules) + { + var match = ShowRegex.Match(rule); + if (!match.Success) + throw new FormatException($"Illegal 'show' entry \"{rule}\" in manifest."); + + yield return new ShowRule + { + Show = match.Groups[1].Value != "-", + PartA = match.Groups[2].Value, + PartB = match.Groups[3].Value + }; + } + } + } + } +} diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs index e2363e314f..125dee5c05 100644 --- a/src/Umbraco.Core/Manifest/ManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ManifestParser.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Manifest @@ -98,6 +99,7 @@ namespace Umbraco.Core.Manifest var propertyEditors = new List(); var parameterEditors = new List(); var gridEditors = new List(); + var contentApps = new List(); foreach (var manifest in manifests) { @@ -106,6 +108,7 @@ namespace Umbraco.Core.Manifest if (manifest.PropertyEditors != null) propertyEditors.AddRange(manifest.PropertyEditors); if (manifest.ParameterEditors != null) parameterEditors.AddRange(manifest.ParameterEditors); if (manifest.GridEditors != null) gridEditors.AddRange(manifest.GridEditors); + if (manifest.ContentApps != null) contentApps.AddRange(manifest.ContentApps); } return new PackageManifest @@ -114,7 +117,8 @@ namespace Umbraco.Core.Manifest Stylesheets = stylesheets.ToArray(), PropertyEditors = propertyEditors.ToArray(), ParameterEditors = parameterEditors.ToArray(), - GridEditors = gridEditors.ToArray() + GridEditors = gridEditors.ToArray(), + ContentApps = contentApps.ToArray() }; } @@ -146,7 +150,8 @@ namespace Umbraco.Core.Manifest var manifest = JsonConvert.DeserializeObject(text, new DataEditorConverter(_logger), - new ValueValidatorConverter(_validators)); + new ValueValidatorConverter(_validators), + new ContentAppDefinitionConverter()); // scripts and stylesheets are raw string, must process here for (var i = 0; i < manifest.Scripts.Length; i++) diff --git a/src/Umbraco.Core/Manifest/PackageManifest.cs b/src/Umbraco.Core/Manifest/PackageManifest.cs index a1702cc58b..32dae46a9a 100644 --- a/src/Umbraco.Core/Manifest/PackageManifest.cs +++ b/src/Umbraco.Core/Manifest/PackageManifest.cs @@ -1,5 +1,6 @@ using System; using Newtonsoft.Json; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Manifest @@ -23,5 +24,8 @@ namespace Umbraco.Core.Manifest [JsonProperty("gridEditors")] public GridEditor[] GridEditors { get; set; } = Array.Empty(); + + [JsonProperty("contentApps")] + public IContentAppDefinition[] ContentApps { get; set; } = Array.Empty(); } } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index ac74e6ee66..ba491fb5e1 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -69,10 +69,7 @@ namespace Umbraco.Core.Migrations.Install if (tableName.Equals(Constants.DatabaseSchema.Tables.RelationType)) CreateRelationTypeData(); - - if (tableName.Equals(Constants.DatabaseSchema.Tables.TaskType)) - CreateTaskTypeData(); - + if (tableName.Equals(Constants.DatabaseSchema.Tables.KeyValue)) CreateKeyValueData(); @@ -152,6 +149,7 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MemberTypes, Name = "MemberTypes" }); _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MemberTree, Name = "MemberTree" }); _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Domains, Name = "Domains" }); + _database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Languages, Name = "Languages" }); } private void CreateContentTypeData() @@ -185,7 +183,7 @@ namespace Umbraco.Core.Migrations.Install private void CreateUserGroup2AppData() { _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Content }); - _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer }); + _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Packages }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Media }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Members }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Settings }); @@ -232,7 +230,7 @@ namespace Umbraco.Core.Migrations.Install private void CreateLanguageData() { - _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)" }); + _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefault = true }); } private void CreateContentChildTypeData() @@ -318,11 +316,6 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); } - private void CreateTaskTypeData() - { - _database.Insert(Constants.DatabaseSchema.Tables.TaskType, "id", false, new TaskTypeDto { Id = 1, Alias = "toTranslate" }); - } - private void CreateKeyValueData() { // on install, initialize the umbraco migration plan with the final state diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index ae3f95ec4d..d45126f07f 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -59,8 +59,6 @@ namespace Umbraco.Core.Migrations.Install typeof (RelationDto), typeof (TagDto), typeof (TagRelationshipDto), - typeof (TaskTypeDto), - typeof (TaskDto), typeof (ContentType2ContentTypeDto), typeof (ContentTypeAllowedContentTypeDto), typeof (User2NodeNotifyDto), diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 48ac39a630..eeaf7533a9 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -107,16 +107,37 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{3E44F712-E2E3-473A-AE49-5D7F8E67CE3F}"); // shannon added that one - let's keep it as the default path //Chain("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}"); // stephan added that one = merge conflict, remove, - Chain("{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); // but it after shannon's, with a new target state, + Chain("{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); // but add it after shannon's, with a new target state, Add("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}", "{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); // and provide a path out of the conflict state // resume at {4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4} ... Chain("{1350617A-4930-4D61-852F-E3AA9E692173}"); Chain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}"); // from 7.12.0 + //Chain("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); // andy added that one = merge conflict, remove Chain("{0541A62B-EF87-4CA2-8225-F0EB98ECCC9F}"); // from 7.12.0 Chain("{EB34B5DC-BB87-4005-985E-D983EA496C38}"); // from 7.12.0 Chain("{517CE9EA-36D7-472A-BF4B-A0D6FB1B8F89}"); // from 7.12.0 Chain("{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}"); // from 7.12.0 + //Chain("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}"); // stephan added that one = merge conflict, remove + + Chain("{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // add andy's after others, with a new target state + From("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}") // and provide a path out of andy's + .CopyChain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}", "{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // to next + // resume at {8B14CEBD-EE47-4AAD-A841-93551D917F11} ... + + Chain("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // add stephan's after others, with a new target state + From("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}") // and provide a path out of stephan's + .Chain("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // to next + // resume at {5F4597F4-A4E0-4AFE-90B5-6D2F896830EB} ... + + //Chain("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}"); + Chain("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); + From("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}") + .Chain("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); + // resume at {290C18EE-B3DE-4769-84F1-1F467F3F76DA}... + + Chain("{6A2C7C1B-A9DB-4EA9-B6AB-78E7D5B722A7}"); + //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs index d595c70fa0..7c0b26dd53 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { @@ -23,11 +24,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 private void EnsureLockObject(int id, string name) { - var db = Database; + EnsureLockObject(Database, id, name); + } + + internal static void EnsureLockObject(IUmbracoDatabase db, int id, string name) + { + // not if it already exists var exists = db.Exists(id); if (exists) return; + // be safe: delete old umbracoNode lock objects if any db.Execute($"DELETE FROM umbracoNode WHERE id={id};"); + // then create umbracoLock object db.Execute($"INSERT umbracoLock (id, name, value) VALUES ({id}, '{name}', 1);"); } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs new file mode 100644 index 0000000000..f706671022 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class DropTaskTables : MigrationBase + { + public DropTaskTables(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + Delete.Table("cmsTaskType"); + Delete.Table("cmsTask"); + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs new file mode 100644 index 0000000000..f0d7c02b82 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs @@ -0,0 +1,24 @@ +using System.Linq; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + /// + /// Adds a new, self-joined field to umbracoLanguages to hold the fall-back language for + /// a given language. + /// + public class FallbackLanguage : MigrationBase + { + public FallbackLanguage(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + + if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.Language) && x.ColumnName.InvariantEquals("fallbackLanguageId")) == false) + AddColumn("fallbackLanguageId"); + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs new file mode 100644 index 0000000000..aa498583ff --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs @@ -0,0 +1,79 @@ +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class RefactorVariantsModel : MigrationBase + { + public RefactorVariantsModel(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + Delete.Column("edited").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + + + // add available column + AddColumn("available", out var sqls); + + // so far, only those cultures that were available had records in the table + Update.Table(DocumentCultureVariationDto.TableName).Set(new { available = true }).AllRows().Do(); + + foreach (var sql in sqls) Execute.Sql(sql).Do(); + + + // add published column + AddColumn("published", out sqls); + + // make it false by default + Update.Table(DocumentCultureVariationDto.TableName).Set(new { published = false }).AllRows().Do(); + + // now figure out whether these available cultures are published, too + var getPublished = Sql() + .Select(x => x.NodeId) + .AndSelect(x => x.LanguageId) + .From() + .InnerJoin().On((node, cv) => node.NodeId == cv.NodeId) + .InnerJoin().On((cv, dv) => cv.Id == dv.Id && dv.Published) + .InnerJoin().On((cv, ccv) => cv.Id == ccv.VersionId); + + foreach (var dto in Database.Fetch(getPublished)) + Database.Execute(Sql() + .Update(u => u.Set(x => x.Published, true)) + .Where(x => x.NodeId == dto.NodeId && x.LanguageId == dto.LanguageId)); + + foreach (var sql in sqls) Execute.Sql(sql).Do(); + + // so far, it was kinda impossible to make a culture unavailable again, + // so we *should* not have anything published but not available - ignore + + + // add name column + AddColumn("name"); + + // so far, every record in the table mapped to an available culture + var getNames = Sql() + .Select(x => x.NodeId) + .AndSelect(x => x.LanguageId, x => x.Name) + .From() + .InnerJoin().On((node, cv) => node.NodeId == cv.NodeId && cv.Current) + .InnerJoin().On((cv, ccv) => cv.Id == ccv.VersionId); + + foreach (var dto in Database.Fetch(getNames)) + Database.Execute(Sql() + .Update(u => u.Set(x => x.Name, dto.Name)) + .Where(x => x.NodeId == dto.NodeId && x.LanguageId == dto.LanguageId)); + } + + // ReSharper disable once ClassNeverInstantiated.Local + // ReSharper disable UnusedAutoPropertyAccessor.Local + private class TempDto + { + public int NodeId { get; set; } + public int LanguageId { get; set; } + public string Name { get; set; } + } + // ReSharper restore UnusedAutoPropertyAccessor.Local + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs new file mode 100644 index 0000000000..b965bc71d2 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs @@ -0,0 +1,48 @@ +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class UpdateDefaultMandatoryLanguage : MigrationBase + { + public UpdateDefaultMandatoryLanguage(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + // add the new languages lock object + AddLockObjects.EnsureLockObject(Database, Constants.Locks.Languages, "Languages"); + + // get all existing languages + var selectDtos = Sql() + .Select() + .From(); + + var dtos = Database.Fetch(selectDtos); + + // get the id of the language which is already the default one, if any, + // else get the lowest language id, which will become the default language + var defaultId = int.MaxValue; + foreach (var dto in dtos) + { + if (dto.IsDefault) + { + defaultId = dto.Id; + break; + } + + if (dto.Id < defaultId) defaultId = dto.Id; + } + + // update, so that language with that id is now default and mandatory + var updateDefault = Sql() + .Update(u => u + .Set(x => x.IsDefault, true) + .Set(x => x.IsMandatory, true)) + .Where(x => x.Id == defaultId); + + Database.Execute(updateDefault); + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 174404b1b9..9e223e7beb 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -324,9 +324,7 @@ WHERE v1.propertyTypeId=v2.propertyTypeId AND v1.languageId=v2.languageId AND v1 public const string Tag = "cmsTags"; public const string TagRelationship = "cmsTagRelationship"; - - public const string Task = "cmsTask"; - public const string TaskType = "cmsTaskType"; + // ReSharper restore UnusedMember.Local } } diff --git a/src/Umbraco.Core/Models/AuditType.cs b/src/Umbraco.Core/Models/AuditType.cs index da2c8e5d8e..a5ae34a89d 100644 --- a/src/Umbraco.Core/Models/AuditType.cs +++ b/src/Umbraco.Core/Models/AuditType.cs @@ -32,7 +32,7 @@ /// /// Used when nodes are unpublished /// - UnPublish, + Unpublish, /// /// Used when nodes are moved /// diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index dd379e02f8..238d87b186 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -269,7 +269,7 @@ namespace Umbraco.Core.Models if (_publishInfos == null) _publishInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); - _publishInfos[culture] = (name, date); + _publishInfos[culture.ToLowerInvariant()] = (name, date); } private void ClearPublishInfos() @@ -294,7 +294,7 @@ namespace Umbraco.Core.Models throw new ArgumentNullOrEmptyException(nameof(culture)); if (_editedCultures == null) _editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase); - _editedCultures.Add(culture); + _editedCultures.Add(culture.ToLowerInvariant()); } // sets all publish edited diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 56a31cd76d..bf2fd580d9 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -167,7 +167,7 @@ namespace Umbraco.Core.Models } /// - public DateTime? GetCultureDate(string culture) + public DateTime? GetUpdateDate(string culture) { if (culture.IsNullOrWhiteSpace()) return null; if (!ContentTypeBase.VariesByCulture()) return null; @@ -202,6 +202,12 @@ namespace Umbraco.Core.Models } } + internal void TouchCulture(string culture) + { + if (ContentTypeBase.VariesByCulture() && _cultureInfos != null && _cultureInfos.TryGetValue(culture, out var infos)) + _cultureInfos[culture] = (infos.Name, DateTime.Now); + } + protected void ClearCultureInfos() { _cultureInfos = null; diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentApp.cs b/src/Umbraco.Core/Models/ContentEditing/ContentApp.cs new file mode 100644 index 0000000000..bf28c28c9e --- /dev/null +++ b/src/Umbraco.Core/Models/ContentEditing/ContentApp.cs @@ -0,0 +1,72 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models.ContentEditing +{ + /// + /// Represents a content app. + /// + /// + /// Content apps are editor extensions. + /// + [DataContract(Name = "app", Namespace = "")] + public class ContentApp + { + /// + /// Gets the name of the content app. + /// + [DataMember(Name = "name")] + public string Name { get; set; } + + /// + /// Gets the unique alias of the content app. + /// + /// + /// Must be a valid javascript identifier, ie no spaces etc. + /// + [DataMember(Name = "alias")] + public string Alias { get; set; } + + /// + /// Gets or sets the weight of the content app. + /// + /// + /// Content apps are ordered by weight, from left (lowest values) to right (highest values). + /// Some built-in apps have special weights: listview is -666, content is -100 and infos is +100. + /// The default weight is 0, meaning somewhere in-between content and infos, but weight could + /// be used for ordering between user-level apps, or anything really. + /// + [DataMember(Name = "weight")] + public int Weight { get; set; } + + /// + /// Gets the icon of the content app. + /// + /// + /// Must be a valid helveticons class name (see http://hlvticons.ch/). + /// + [DataMember(Name = "icon")] + public string Icon { get; set; } + + /// + /// Gets the view for rendering the content app. + /// + [DataMember(Name = "view")] + public string View { get; set; } + + /// + /// The view model specific to this app + /// + [DataMember(Name = "viewModel")] + public object ViewModel { get; set; } + + /// + /// Gets a value indicating whether the app is active. + /// + /// + /// Normally reserved for Angular to deal with but in some cases this can be set on the server side. + /// + [DataMember(Name = "active")] + public bool Active { get; set; } + } +} + diff --git a/src/Umbraco.Core/Models/ContentEditing/IContentAppDefinition.cs b/src/Umbraco.Core/Models/ContentEditing/IContentAppDefinition.cs new file mode 100644 index 0000000000..5e0c421742 --- /dev/null +++ b/src/Umbraco.Core/Models/ContentEditing/IContentAppDefinition.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Core.Models.ContentEditing +{ + /// + /// Represents a content app definition. + /// + public interface IContentAppDefinition + { + /// + /// Gets the content app for an object. + /// + /// The source object. + /// The content app for the object, or null. + /// + /// The definition must determine, based on , whether + /// the content app should be displayed or not, and return either a + /// instance, or null. + /// + ContentApp GetContentAppFor(object source); + } +} diff --git a/src/Umbraco.Core/Models/DataType.cs b/src/Umbraco.Core/Models/DataType.cs index 9668864588..4f0d0d6c31 100644 --- a/src/Umbraco.Core/Models/DataType.cs +++ b/src/Umbraco.Core/Models/DataType.cs @@ -30,6 +30,9 @@ namespace Umbraco.Core.Models { _editor = editor ?? throw new ArgumentNullException(nameof(editor)); ParentId = parentId; + + // set a default configuration + Configuration = _editor.GetConfigurationEditor().DefaultConfigurationObject; } private static PropertySelectors Selectors => _selectors ?? (_selectors = new PropertySelectors()); diff --git a/src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs index 5b63ad81c5..39ece5fa10 100644 --- a/src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/DocumentEntitySlim.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.Models.Entities { @@ -8,13 +9,32 @@ namespace Umbraco.Core.Models.Entities public class DocumentEntitySlim : ContentEntitySlim, IDocumentEntitySlim { private static readonly IReadOnlyDictionary Empty = new Dictionary(); + private IReadOnlyDictionary _cultureNames; + private IEnumerable _publishedCultures; + private IEnumerable _editedCultures; + + /// public IReadOnlyDictionary CultureNames { get => _cultureNames ?? Empty; set => _cultureNames = value; } + /// + public IEnumerable PublishedCultures + { + get => _publishedCultures ?? Enumerable.Empty(); + set => _publishedCultures = value; + } + + /// + public IEnumerable EditedCultures + { + get => _editedCultures ?? Enumerable.Empty(); + set => _editedCultures = value; + } + public ContentVariation Variations { get; set; } /// @@ -22,5 +42,6 @@ namespace Umbraco.Core.Models.Entities /// public bool Edited { get; set; } + } } diff --git a/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs index dc986a4cd9..9ab557b02c 100644 --- a/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs @@ -20,4 +20,4 @@ /// string ContentTypeThumbnail { get; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs index 6b72fd4a2b..38fd9a02f1 100644 --- a/src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/IDocumentEntitySlim.cs @@ -7,26 +7,35 @@ namespace Umbraco.Core.Models.Entities /// public interface IDocumentEntitySlim : IContentEntitySlim { - //fixme we need to supply more information than this and change this property name. This will need to include Published/Editor per variation since we need this information for the tree + /// + /// Gets the variant name for each culture + /// IReadOnlyDictionary CultureNames { get; } + /// + /// Gets the published cultures. + /// + IEnumerable PublishedCultures { get; } + + /// + /// Gets the edited cultures. + /// + IEnumerable EditedCultures { get; } + + /// + /// Gets the content variation of the content type. + /// ContentVariation Variations { get; } /// - /// At least one variation is published + /// Gets a value indicating whether the content is published. /// - /// - /// If the document is invariant, this simply means there is a published version - /// - bool Published { get; set; } + bool Published { get; } /// - /// At least one variation has pending changes + /// Gets a value indicating whether the content has been edited. /// - /// - /// If the document is invariant, this simply means there is pending changes - /// - bool Edited { get; set; } + bool Edited { get; } } } diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index de1b2666d5..460bd521d4 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -80,13 +80,13 @@ namespace Umbraco.Core.Models bool IsCultureAvailable(string culture); /// - /// Gets the date a culture was created. + /// Gets the date a culture was updated. /// /// /// When is null, returns null. /// If the specified culture is not available, returns null. /// - DateTime? GetCultureDate(string culture); + DateTime? GetUpdateDate(string culture); /// /// List of properties, which make up all the data available for this Content object diff --git a/src/Umbraco.Core/Models/ILanguage.cs b/src/Umbraco.Core/Models/ILanguage.cs index 7bf9e9b32c..c0d2fed839 100644 --- a/src/Umbraco.Core/Models/ILanguage.cs +++ b/src/Umbraco.Core/Models/ILanguage.cs @@ -4,34 +4,54 @@ using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models { + /// + /// Represents a language. + /// public interface ILanguage : IEntity, IRememberBeingDirty { /// - /// Gets or sets the Iso Code for the Language + /// Gets or sets the ISO code of the language. /// [DataMember] string IsoCode { get; set; } /// - /// Gets or sets the Culture Name for the Language + /// Gets or sets the culture name of the language. /// [DataMember] string CultureName { get; set; } /// - /// Returns a object for the current Language + /// Gets the object for the language. /// [IgnoreDataMember] CultureInfo CultureInfo { get; } /// - /// Defines if this language is the default variant language when language variants are in use + /// Gets or sets a value indicating whether the language is the default language. /// - bool IsDefaultVariantLanguage { get; set; } + [DataMember] + bool IsDefault { get; set; } /// - /// If true, a variant node cannot be published unless this language variant is created + /// Gets or sets a value indicating whether the language is mandatory. /// - bool Mandatory { get; set; } + /// + /// When a language is mandatory, a multi-lingual document cannot be published + /// without that language being published, and unpublishing that language unpublishes + /// the entire document. + /// + [DataMember] + bool IsMandatory { get; set; } + + /// + /// Gets or sets the identifier of a fallback language. + /// + /// + /// The fallback language can be used in multi-lingual scenarios, to help + /// define fallback strategies when a value does not exist for a requested language. + /// + [DataMember] + int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index fa1c9dc826..940648c4b9 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -19,6 +19,7 @@ namespace Umbraco.Core.Models private string _cultureName; private bool _isDefaultVariantLanguage; private bool _mandatory; + private int? _fallbackLanguageId; public Language(string isoCode) { @@ -30,13 +31,12 @@ namespace Umbraco.Core.Models { public readonly PropertyInfo IsoCodeSelector = ExpressionHelper.GetPropertyInfo(x => x.IsoCode); public readonly PropertyInfo CultureNameSelector = ExpressionHelper.GetPropertyInfo(x => x.CultureName); - public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefaultVariantLanguage); - public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.Mandatory); + public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefault); + public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.IsMandatory); + public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.FallbackLanguageId); } - /// - /// Gets or sets the Iso Code for the Language - /// + /// [DataMember] public string IsoCode { @@ -44,9 +44,7 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _isoCode, Ps.Value.IsoCodeSelector); } - /// - /// Gets or sets the Culture Name for the Language - /// + /// [DataMember] public string CultureName { @@ -54,22 +52,29 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _cultureName, Ps.Value.CultureNameSelector); } - /// - /// Returns a object for the current Language - /// + /// [IgnoreDataMember] public CultureInfo CultureInfo => CultureInfo.GetCultureInfo(IsoCode); - public bool IsDefaultVariantLanguage + /// + public bool IsDefault { get => _isDefaultVariantLanguage; set => SetPropertyValueAndDetectChanges(value, ref _isDefaultVariantLanguage, Ps.Value.IsDefaultVariantLanguageSelector); } - public bool Mandatory + /// + public bool IsMandatory { get => _mandatory; set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector); } + + /// + public int? FallbackLanguageId + { + get => _fallbackLanguageId; + set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguageId, Ps.Value.FallbackLanguageSelector); + } } } diff --git a/src/Umbraco.Core/Models/PropertyTagChange.cs b/src/Umbraco.Core/Models/PropertyTagChange.cs deleted file mode 100644 index 5c911f4c30..0000000000 --- a/src/Umbraco.Core/Models/PropertyTagChange.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Models -{ - /// - /// A set of tag changes. - /// - internal class PropertyTagChange - { - public ChangeType Type { get; set; } - - public IEnumerable<(string Type, string Tags)> Tags { get; set; } - - public enum ChangeType - { - Replace, - Remove, - Merge - } - } -} diff --git a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs new file mode 100644 index 0000000000..0434218555 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Manages the built-in fallback policies. + /// + public struct Fallback : IEnumerable + { + private readonly int[] _values; + + /// + /// Initializes a new instance of the struct with values. + /// + private Fallback(int[] values) + { + _values = values; + } + + /// + /// Gets an ordered set of fallback policies. + /// + /// + public static Fallback To(params int[] values) => new Fallback(values); + + /// + /// Do not fallback. + /// + public const int None = 0; + + /// + /// Fallback to default value. + /// + public const int DefaultValue = 1; + + /// + /// Gets the fallback to default value policy. + /// + public static Fallback ToDefaultValue => new Fallback(new[] { DefaultValue }); + + /// + /// Fallback to other languages. + /// + public const int Language = 2; + + /// + /// Gets the fallback to language policy. + /// + public static Fallback ToLanguage => new Fallback(new[] { Language }); + + /// + /// Fallback to tree ancestors. + /// + public const int Ancestors = 3; + + /// + /// Gets the fallback to tree ancestors policy. + /// + public static Fallback ToAncestors => new Fallback(new[] { Ancestors }); + + /// + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_values ?? Array.Empty()).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index 8e1dcfd543..f30a53c8b6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,37 +1,122 @@ -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Models.PublishedContent +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a fallback strategy for getting values. /// - // fixme - IPublishedValueFallback is still WorkInProgress - // todo - properly document methods, etc - // todo - understand caching vs fallback (recurse etc) public interface IPublishedValueFallback { - // note that at property level, property.GetValue() does NOT implement fallback, and one has - // to get property.Value() or property.Value() to trigger fallback + /// + /// Tries to get a fallback value for a property. + /// + /// The property. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever property.Value(culture, segment, defaultValue) is called, and + /// property.HasValue(culture, segment) is false. + /// It can only fallback at property level (no recurse). + /// At property level, property.GetValue() does *not* implement fallback, and one has to + /// get property.Value() or property.Value{T}() to trigger fallback. + /// Note that and may not be contextualized, + /// so the variant context should be used to contextualize them (see our default implementation in + /// the web project. + /// + bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value); - // this method is called whenever property.Value(culture, segment, defaultValue) is called, and - // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + /// + /// Tries to get a fallback value for a property. + /// + /// The type of the value. + /// The property. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever property.Value{T}(culture, segment, defaultValue) is called, and + /// property.HasValue(culture, segment) is false. + /// It can only fallback at property level (no recurse). + /// At property level, property.GetValue() does *not* implement fallback, and one has to + /// get property.Value() or property.Value{T}() to trigger fallback. + /// + bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value); - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + /// + /// Tries to get a fallback value for a published element property. + /// + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// It can only fallback at element level (no recurse). + /// + bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value); - // this method is called whenever property.Value(culture, segment, defaultValue) is called, and - // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + /// + /// Tries to get a fallback value for a published element property. + /// + /// The type of the value. + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// It can only fallback at element level (no recurse). + /// + bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value); - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + /// + /// Tries to get a fallback value for a published content property. + /// + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// + bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value); - // these methods to be called whenever getting the property value for the specified alias, culture and segment, - // either returned no property at all, or a property that does not HasValue for the specified culture and segment. - - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); - - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); - - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse); - - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse); + /// + /// Tries to get a fallback value for a published content property. + /// + /// The type of the value. + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// A fallback strategy. + /// An optional default value. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// + bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/ModelType.cs b/src/Umbraco.Core/Models/PublishedContent/ModelType.cs index 962148e138..a366742cc5 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ModelType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ModelType.cs @@ -49,17 +49,24 @@ namespace Umbraco.Core.Models.PublishedContent /// The model types map. /// The actual Clr type. public static Type Map(Type type, Dictionary modelTypes) + => Map(type, modelTypes, false); + + public static Type Map(Type type, Dictionary modelTypes, bool dictionaryIsInvariant) { + // it may be that senders forgot to send an invariant dictionary (garbage-in) + if (!dictionaryIsInvariant) + modelTypes = new Dictionary(modelTypes, StringComparer.InvariantCultureIgnoreCase); + if (type is ModelType modelType) { - if (modelTypes.TryGetValue(modelType.ContentTypeAlias, out Type actualType)) + if (modelTypes.TryGetValue(modelType.ContentTypeAlias, out var actualType)) return actualType; throw new InvalidOperationException($"Don't know how to map ModelType with content type alias \"{modelType.ContentTypeAlias}\"."); } if (type is ModelTypeArrayType arrayType) { - if (modelTypes.TryGetValue(arrayType.ContentTypeAlias, out Type actualType)) + if (modelTypes.TryGetValue(arrayType.ContentTypeAlias, out var actualType)) return actualType.MakeArrayType(); throw new InvalidOperationException($"Don't know how to map ModelType with content type alias \"{arrayType.ContentTypeAlias}\"."); } @@ -70,7 +77,7 @@ namespace Umbraco.Core.Models.PublishedContent if (def == null) throw new InvalidOperationException("panic"); - var args = type.GetGenericArguments().Select(x => Map(x, modelTypes)).ToArray(); + var args = type.GetGenericArguments().Select(x => Map(x, modelTypes, true)).ToArray(); return def.MakeGenericType(args); } @@ -81,7 +88,14 @@ namespace Umbraco.Core.Models.PublishedContent /// The model types map. /// The actual Clr type name. public static string MapToName(Type type, Dictionary map) + => MapToName(type, map, false); + + private static string MapToName(Type type, Dictionary map, bool dictionaryIsInvariant) { + // it may be that senders forgot to send an invariant dictionary (garbage-in) + if (!dictionaryIsInvariant) + map = new Dictionary(map, StringComparer.InvariantCultureIgnoreCase); + if (type is ModelType modelType) { if (map.TryGetValue(modelType.ContentTypeAlias, out var actualTypeName)) @@ -102,7 +116,7 @@ namespace Umbraco.Core.Models.PublishedContent if (def == null) throw new InvalidOperationException("panic"); - var args = type.GetGenericArguments().Select(x => MapToName(x, map)).ToArray(); + var args = type.GetGenericArguments().Select(x => MapToName(x, map, true)).ToArray(); var defFullName = def.FullName.Substring(0, def.FullName.IndexOf('`')); return defFullName + "<" + string.Join(", ", args) + ">"; } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index b99b4ad415..cd7b063d44 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -9,21 +9,45 @@ public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) => defaultValue; + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) => defaultValue; + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs index e0514b38d7..882109f908 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs @@ -6,7 +6,7 @@ /// /// Every strongly-typed property set class should inherit from PublishedElementModel /// (or inherit from a class that inherits from... etc.) so they are picked by the factory. - public class PublishedElementModel : PublishedElementWrapped + public abstract class PublishedElementModel : PublishedElementWrapped { /// /// diff --git a/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs new file mode 100644 index 0000000000..6710d79cc6 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + public static class VariationContextAccessorExtensions + { + public static void ContextualizeVariation(this IVariationContextAccessor variationContextAccessor, ContentVariation variations, ref string culture, ref string segment) + { + if (culture != null && segment != null) return; + + // use context values + var publishedVariationContext = variationContextAccessor?.VariationContext; + if (culture == null) culture = variations.VariesByCulture() ? publishedVariationContext?.Culture : ""; + if (segment == null) segment = variations.VariesBySegment() ? publishedVariationContext?.Segment : ""; + } + } +} diff --git a/src/Umbraco.Core/Models/Section.cs b/src/Umbraco.Core/Models/Section.cs index cbd9dfe7dc..b9eda285b0 100644 --- a/src/Umbraco.Core/Models/Section.cs +++ b/src/Umbraco.Core/Models/Section.cs @@ -5,11 +5,10 @@ /// public class Section { - public Section(string name, string @alias, string icon, int sortOrder) + public Section(string name, string @alias, int sortOrder) { Name = name; Alias = alias; - Icon = icon; SortOrder = sortOrder; } @@ -20,7 +19,6 @@ public string Name { get; set; } public string Alias { get; set; } - public string Icon { get; set; } public int SortOrder { get; set; } } } diff --git a/src/Umbraco.Core/Models/Task.cs b/src/Umbraco.Core/Models/Task.cs deleted file mode 100644 index cb35813619..0000000000 --- a/src/Umbraco.Core/Models/Task.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.Entities; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a Task - /// - [Serializable] - [DataContract(IsReference = true)] - public class Task : EntityBase - { - private bool _closed; - private TaskType _taskType; - private int _entityId; - private int _ownerUserId; - private int _assigneeUserId; - private string _comment; - - public Task(TaskType taskType) - { - _taskType = taskType; - } - - private static readonly Lazy Ps = new Lazy(); - - private class PropertySelectors - { - public readonly PropertyInfo ClosedSelector = ExpressionHelper.GetPropertyInfo(x => x.Closed); - public readonly PropertyInfo TaskTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.TaskType); - public readonly PropertyInfo EntityIdSelector = ExpressionHelper.GetPropertyInfo(x => x.EntityId); - public readonly PropertyInfo OwnerUserIdSelector = ExpressionHelper.GetPropertyInfo(x => x.OwnerUserId); - public readonly PropertyInfo AssigneeUserIdSelector = ExpressionHelper.GetPropertyInfo(x => x.AssigneeUserId); - public readonly PropertyInfo CommentSelector = ExpressionHelper.GetPropertyInfo(x => x.Comment); - } - - /// - /// Gets or sets a boolean indicating whether the task is closed - /// - [DataMember] - public bool Closed - { - get { return _closed; } - set { SetPropertyValueAndDetectChanges(value, ref _closed, Ps.Value.ClosedSelector); } - } - - /// - /// Gets or sets the TaskType of the Task - /// - [DataMember] - public TaskType TaskType - { - get { return _taskType; } - set { SetPropertyValueAndDetectChanges(value, ref _taskType, Ps.Value.TaskTypeSelector); } - } - - /// - /// Gets or sets the Id of the entity, which this task is associated to - /// - [DataMember] - public int EntityId - { - get { return _entityId; } - set { SetPropertyValueAndDetectChanges(value, ref _entityId, Ps.Value.EntityIdSelector); } - } - - /// - /// Gets or sets the Id of the user, who owns this task - /// - [DataMember] - public int OwnerUserId - { - get { return _ownerUserId; } - set { SetPropertyValueAndDetectChanges(value, ref _ownerUserId, Ps.Value.OwnerUserIdSelector); } - } - - /// - /// Gets or sets the Id of the user, who is assigned to this task - /// - [DataMember] - public int AssigneeUserId - { - get { return _assigneeUserId; } - set { SetPropertyValueAndDetectChanges(value, ref _assigneeUserId, Ps.Value.AssigneeUserIdSelector); } - } - - /// - /// Gets or sets the Comment for the Task - /// - [DataMember] - public string Comment - { - get { return _comment; } - set { SetPropertyValueAndDetectChanges(value, ref _comment, Ps.Value.CommentSelector); } - } - - } -} diff --git a/src/Umbraco.Core/Models/TaskType.cs b/src/Umbraco.Core/Models/TaskType.cs deleted file mode 100644 index f5e6621239..0000000000 --- a/src/Umbraco.Core/Models/TaskType.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.Entities; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a Task Type - /// - [Serializable] - [DataContract(IsReference = true)] - public class TaskType : EntityBase - { - private string _alias; - - public TaskType(string @alias) - { - _alias = alias; - } - - private static readonly Lazy Ps = new Lazy(); - - private class PropertySelectors - { - public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); - } - - /// - /// Gets or sets the Alias of the TaskType - /// - [DataMember] - public string Alias - { - get { return _alias; } - set { SetPropertyValueAndDetectChanges(value, ref _alias, Ps.Value.AliasSelector); } - } - } -} diff --git a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs index 79dd45b73b..523d09c1f7 100644 --- a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs +++ b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs @@ -69,10 +69,7 @@ namespace Umbraco.Core public const string Tag = /*TableNamePrefix*/ "cms" + "Tags"; public const string TagRelationship = /*TableNamePrefix*/ "cms" + "TagRelationship"; - - public const string Task = /*TableNamePrefix*/ "cms" + "Task"; - public const string TaskType = /*TableNamePrefix*/ "cms" + "TaskType"; - + public const string KeyValue = TableNamePrefix + "KeyValue"; public const string AuditEntry = /*TableNamePrefix*/ "umbraco" + "Audit"; diff --git a/src/Umbraco.Core/Persistence/Constants-Locks.cs b/src/Umbraco.Core/Persistence/Constants-Locks.cs index 6f5d4bb0dc..1dcd2408e7 100644 --- a/src/Umbraco.Core/Persistence/Constants-Locks.cs +++ b/src/Umbraco.Core/Persistence/Constants-Locks.cs @@ -3,17 +3,60 @@ namespace Umbraco.Core { static partial class Constants { + /// + /// Defines lock objects. + /// public static class Locks { + /// + /// All servers. + /// public const int Servers = -331; + + /// + /// All content and media types. + /// public const int ContentTypes = -332; + + /// + /// The entire content tree, i.e. all content items. + /// public const int ContentTree = -333; + + /// + /// The entire media tree, i.e. all media items. + /// public const int MediaTree = -334; + + /// + /// The entire member tree, i.e. all members. + /// public const int MemberTree = -335; + + /// + /// All media types. + /// public const int MediaTypes = -336; + + /// + /// All member types. + /// public const int MemberTypes = -337; + + /// + /// All domains. + /// public const int Domains = -338; + + /// + /// All key-values. + /// public const int KeyValues = -339; + + /// + /// All languages. + /// + public const int Languages = -340; } } } diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs index a4e51b913e..1dca820a6c 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Dtos internal class ContentVersionCultureVariationDto { public const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCultureVariation; - private int? _publishedUserId; + private int? _updateUserId; [Column("id")] [PrimaryKeyColumn] @@ -33,16 +33,12 @@ namespace Umbraco.Core.Persistence.Dtos [Column("name")] public string Name { get; set; } - [Column("date")] - public DateTime Date { get; set; } + [Column("date")] // fixme: db rename to 'updateDate' + public DateTime UpdateDate { get; set; } - // fixme want? - [Column("availableUserId")] + [Column("availableUserId")] // fixme: db rename to 'updateDate' [ForeignKey(typeof(UserDto))] [NullSetting(NullSetting = NullSettings.Null)] - public int? PublishedUserId { get => _publishedUserId == 0 ? null : _publishedUserId; set => _publishedUserId = value; } //return null if zero - - [Column("edited")] - public bool Edited { get; set; } + public int? UpdateUserId { get => _updateUserId == 0 ? null : _updateUserId; set => _updateUserId = value; } //return null if zero } } diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs index 3ec40c74b3..a13bf921d9 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs @@ -21,11 +21,11 @@ namespace Umbraco.Core.Persistence.Dtos [ForeignKey(typeof(ContentDto))] public int NodeId { get; set; } - [Column("versionDate")] + [Column("versionDate")] // fixme: db rename to 'updateDate' [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime VersionDate { get; set; } - [Column("userId")] + [Column("userId")] // fixme: db rename to 'updateUserId' [ForeignKey(typeof(UserDto))] [NullSetting(NullSetting = NullSettings.Null)] public int? UserId { get => _userId == 0 ? null : _userId; set => _userId = value; } //return null if zero diff --git a/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs index 78e819e714..ed61ea5622 100644 --- a/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs @@ -28,7 +28,25 @@ namespace Umbraco.Core.Persistence.Dtos [Ignore] public string Culture { get; set; } + // authority on whether a culture has been edited [Column("edited")] public bool Edited { get; set; } + + // de-normalized for perfs + // (means there is a current content version culture variation for the language) + [Column("available")] + public bool Available { get; set; } + + // de-normalized for perfs + // (means there is a published content version culture variation for the language) + [Column("published")] + public bool Published { get; set; } + + // de-normalized for perfs + // (when available, copies name from current content version culture variation for the language) + // (otherwise, it's the published one, 'cos we need to have one) + [Column("name")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Name { get; set; } } } diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index 08bd2a0582..488390f985 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -10,33 +10,51 @@ namespace Umbraco.Core.Persistence.Dtos { public const string TableName = Constants.DatabaseSchema.Tables.Language; + /// + /// Gets or sets the identifier of the language. + /// [Column("id")] [PrimaryKeyColumn(IdentitySeed = 2)] public short Id { get; set; } + /// + /// Gets or sets the ISO code of the language. + /// [Column("languageISOCode")] [Index(IndexTypes.UniqueNonClustered)] [NullSetting(NullSetting = NullSettings.Null)] [Length(14)] public string IsoCode { get; set; } + /// + /// Gets or sets the culture name of the language. + /// [Column("languageCultureName")] [NullSetting(NullSetting = NullSettings.Null)] [Length(100)] public string CultureName { get; set; } /// - /// Defines if this language is the default variant language when language variants are in use + /// Gets or sets a value indicating whether the language is the default language. /// [Column("isDefaultVariantLang")] [Constraint(Default = "0")] - public bool IsDefaultVariantLanguage { get; set; } + public bool IsDefault { get; set; } /// - /// If true, a variant node cannot be published unless this language variant is created + /// Gets or sets a value indicating whether the language is mandatory. /// [Column("mandatory")] [Constraint(Default = "0")] - public bool Mandatory { get; set; } + public bool IsMandatory { get; set; } + + /// + /// Gets or sets the identifier of a fallback language. + /// + [Column("fallbackLanguageId")] + [ForeignKey(typeof(LanguageDto), Column = "id")] + [Index(IndexTypes.NonClustered)] + [NullSetting(NullSetting = NullSettings.Null)] + public int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Core/Persistence/Dtos/NodeDto.cs b/src/Umbraco.Core/Persistence/Dtos/NodeDto.cs index 10450f2bf4..56da821360 100644 --- a/src/Umbraco.Core/Persistence/Dtos/NodeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/NodeDto.cs @@ -45,7 +45,7 @@ namespace Umbraco.Core.Persistence.Dtos [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Trashed")] public bool Trashed { get; set; } - [Column("nodeUser")] // fixme dbfix rename userId + [Column("nodeUser")] // fixme: db rename to 'createUserId' [ForeignKey(typeof(UserDto))] [NullSetting(NullSetting = NullSettings.Null)] public int? UserId { get => _userId == 0 ? null : _userId; set => _userId = value; } //return null if zero @@ -54,10 +54,10 @@ namespace Umbraco.Core.Persistence.Dtos [NullSetting(NullSetting = NullSettings.Null)] public string Text { get; set; } - [Column("nodeObjectType")] // fixme dbfix rename objectType + [Column("nodeObjectType")] // fixme: db rename to 'objectType' [NullSetting(NullSetting = NullSettings.Null)] [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ObjectType")] - public Guid? NodeObjectType { get; set; } // fixme dbfix rename ObjectType + public Guid? NodeObjectType { get; set; } [Column("createDate")] [Constraint(Default = SystemMethods.CurrentDateTime)] diff --git a/src/Umbraco.Core/Persistence/Dtos/TaskDto.cs b/src/Umbraco.Core/Persistence/Dtos/TaskDto.cs deleted file mode 100644 index 9531ffb7b9..0000000000 --- a/src/Umbraco.Core/Persistence/Dtos/TaskDto.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using NPoco; -using Umbraco.Core.Persistence.DatabaseAnnotations; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; - -namespace Umbraco.Core.Persistence.Dtos -{ - [TableName(Constants.DatabaseSchema.Tables.Task)] - [PrimaryKey("id")] - [ExplicitColumns] - internal class TaskDto - { - [Column("closed")] - [Constraint(Default = "0")] - public bool Closed { get; set; } - - [Column("id")] - [PrimaryKeyColumn] - public int Id { get; set; } - - [Column("taskTypeId")] - [ForeignKey(typeof(TaskTypeDto))] - public byte TaskTypeId { get; set; } - - [Column("nodeId")] - [ForeignKey(typeof(NodeDto))] - public int NodeId { get; set; } - - [Column("parentUserId")] - [ForeignKey(typeof(UserDto), Name = "FK_cmsTask_umbracoUser")] - public int ParentUserId { get; set; } - - [Column("userId")] - [ForeignKey(typeof(UserDto), Name = "FK_cmsTask_umbracoUser1")] - public int UserId { get; set; } - - [Column("DateTime")] - [Constraint(Default = SystemMethods.CurrentDateTime)] - public DateTime DateTime { get; set; } - - [Column("Comment")] - [NullSetting(NullSetting = NullSettings.Null)] - [Length(500)] - public string Comment { get; set; } - - [ResultColumn] - [Reference(ReferenceType.OneToOne, ColumnName = "TaskTypeId")] - public TaskTypeDto TaskTypeDto { get; set; } - } -} diff --git a/src/Umbraco.Core/Persistence/Dtos/TaskTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/TaskTypeDto.cs deleted file mode 100644 index ca731c29c9..0000000000 --- a/src/Umbraco.Core/Persistence/Dtos/TaskTypeDto.cs +++ /dev/null @@ -1,19 +0,0 @@ -using NPoco; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.Dtos -{ - [TableName(Constants.DatabaseSchema.Tables.TaskType)] - [PrimaryKey("id")] - [ExplicitColumns] - internal class TaskTypeDto - { - [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 2)] - public byte Id { get; set; } - - [Column("alias")] - [Index(IndexTypes.NonClustered)] - public string Alias { get; set; } - } -} diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index 7b24411498..ad58c5b570 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -8,7 +8,15 @@ namespace Umbraco.Core.Persistence.Factories { public static ILanguage BuildEntity(LanguageDto dto) { - var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id, IsDefaultVariantLanguage = dto.IsDefaultVariantLanguage, Mandatory = dto.Mandatory }; + var lang = new Language(dto.IsoCode) + { + CultureName = dto.CultureName, + Id = dto.Id, + IsDefault = dto.IsDefault, + IsMandatory = dto.IsMandatory, + FallbackLanguageId = dto.FallbackLanguageId + }; + // reset dirty initial properties (U4-1946) lang.ResetDirtyProperties(false); return lang; @@ -16,9 +24,19 @@ namespace Umbraco.Core.Persistence.Factories public static LanguageDto BuildDto(ILanguage entity) { - var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefaultVariantLanguage, Mandatory = entity.Mandatory }; + var dto = new LanguageDto + { + CultureName = entity.CultureName, + IsoCode = entity.IsoCode, + IsDefault = entity.IsDefault, + IsMandatory = entity.IsMandatory, + FallbackLanguageId = entity.FallbackLanguageId + }; + if (entity.HasIdentity) + { dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); + } return dto; } diff --git a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs b/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs deleted file mode 100644 index 49bcbd4262..0000000000 --- a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Dtos; - -namespace Umbraco.Core.Persistence.Factories -{ - /// - /// Creates the model mappings for Tasks - /// - internal static class TaskFactory - { - public static Task BuildEntity(TaskDto dto) - { - var entity = new Task(new TaskType(dto.TaskTypeDto.Alias) { Id = dto.TaskTypeDto.Id }); - - try - { - entity.DisableChangeTracking(); - - entity.Closed = dto.Closed; - entity.AssigneeUserId = dto.UserId; - entity.Comment = dto.Comment; - entity.CreateDate = dto.DateTime; - entity.EntityId = dto.NodeId; - entity.Id = dto.Id; - entity.OwnerUserId = dto.ParentUserId; - - // reset dirty initial properties (U4-1946) - entity.ResetDirtyProperties(false); - return entity; - } - finally - { - entity.EnableChangeTracking(); - } - } - - public static TaskDto BuildDto(Task entity) - { - var dto = new TaskDto - { - Closed = entity.Closed, - Comment = entity.Comment, - DateTime = entity.CreateDate, - Id = entity.Id, - NodeId = entity.EntityId, - ParentUserId = entity.OwnerUserId, - TaskTypeId = (byte)entity.TaskType.Id, - UserId = entity.AssigneeUserId - }; - - return dto; - } - } -} diff --git a/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs deleted file mode 100644 index 01a25ff852..0000000000 --- a/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Dtos; - -namespace Umbraco.Core.Persistence.Factories -{ - internal static class TaskTypeFactory - { - public static TaskType BuildEntity(TaskTypeDto dto) - { - var entity = new TaskType(dto.Alias) {Id = dto.Id}; - // reset dirty initial properties (U4-1946) - entity.ResetDirtyProperties(false); - return entity; - } - - public static TaskTypeDto BuildDto(TaskType entity) - { - var dto = new TaskTypeDto - { - Id = (byte)entity.Id, - Alias = entity.Alias - }; - return dto; - } - } -} diff --git a/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs b/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs index f861c450aa..65bfbcdbaf 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs @@ -47,7 +47,6 @@ namespace Umbraco.Core.Persistence.Mappers Add(); Add(); Add(); - Add(); Add(); Add(); Add(); diff --git a/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs deleted file mode 100644 index f503fc1684..0000000000 --- a/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Concurrent; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Dtos; - -namespace Umbraco.Core.Persistence.Mappers -{ - /// - /// Represents a to DTO mapper used to translate the properties of the public api - /// implementation to that of the database's DTO as sql: [tableName].[columnName]. - /// - [MapperFor(typeof(TaskType))] - public sealed class TaskTypeMapper : BaseMapper - { - private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); - - internal override ConcurrentDictionary PropertyInfoCache => PropertyInfoCacheInstance; - - protected override void BuildMap() - { - CacheMap(src => src.Id, dto => dto.Id); - CacheMap(src => src.Alias, dto => dto.Alias); - } - } -} diff --git a/src/Umbraco.Core/Persistence/NPocoDatabaseTypeExtensions.cs b/src/Umbraco.Core/Persistence/NPocoDatabaseTypeExtensions.cs index e40e6dedd3..bd44c095fa 100644 --- a/src/Umbraco.Core/Persistence/NPocoDatabaseTypeExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoDatabaseTypeExtensions.cs @@ -22,6 +22,11 @@ namespace Umbraco.Core.Persistence return databaseType is NPoco.DatabaseTypes.SqlServer2008DatabaseType; } + public static bool IsSqlServer2012OrLater(this DatabaseType databaseType) + { + return databaseType is NPoco.DatabaseTypes.SqlServer2012DatabaseType; + } + public static bool IsSqlCe(this DatabaseType databaseType) { return databaseType is NPoco.DatabaseTypes.SqlServerCEDatabaseType; diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index 9c1f0d9a07..d97c748b6f 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Text; using NPoco; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence { @@ -74,12 +73,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql Where(this Sql sql, Expression> predicate, string alias = null) { - var expresionist = new PocoToSqlExpressionVisitor(sql.SqlContext, alias); - var whereExpression = expresionist.Visit(predicate); - sql.Where(whereExpression, expresionist.GetSqlParameters()); - return sql; + var (s, a) = sql.SqlContext.Visit(predicate, alias); + return sql.Where(s, a); } - + /// /// Appends a WHERE clause to the Sql statement. /// @@ -92,10 +89,8 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql Where(this Sql sql, Expression> predicate, string alias1 = null, string alias2 = null) { - var expresionist = new PocoToSqlExpressionVisitor(sql.SqlContext, alias1, alias2); - var whereExpression = expresionist.Visit(predicate); - sql.Where(whereExpression, expresionist.GetSqlParameters()); - return sql; + var (s, a) = sql.SqlContext.Visit(predicate, alias1, alias2); + return sql.Where(s, a); } /// @@ -108,7 +103,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql WhereIn(this Sql sql, Expression> field, IEnumerable values) { - var fieldName = GetFieldName(field, sql.SqlContext.SqlSyntax); + var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(field); sql.Where(fieldName + " IN (@values)", new { values }); return sql; } @@ -136,7 +131,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql WhereNotIn(this Sql sql, Expression> field, IEnumerable values) { - var fieldName = GetFieldName(field, sql.SqlContext.SqlSyntax); + var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(field); sql.Where(fieldName + " NOT IN (@values)", new { values }); return sql; } @@ -164,7 +159,8 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql WhereAnyIn(this Sql sql, Expression>[] fields, IEnumerable values) { - var fieldNames = fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + var sqlSyntax = sql.SqlContext.SqlSyntax; + var fieldNames = fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); var sb = new StringBuilder(); sb.Append("("); for (var i = 0; i < fieldNames.Length; i++) @@ -180,7 +176,7 @@ namespace Umbraco.Core.Persistence private static Sql WhereIn(this Sql sql, Expression> fieldSelector, Sql valuesSql, bool not) { - var fieldName = GetFieldName(fieldSelector, sql.SqlContext.SqlSyntax); + var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector); sql.Where(fieldName + (not ? " NOT" : "") +" IN (" + valuesSql.SQL + ")", valuesSql.Arguments); return sql; } @@ -274,7 +270,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql OrderBy(this Sql sql, Expression> field) { - return sql.OrderBy("(" + GetFieldName(field, sql.SqlContext.SqlSyntax) + ")"); + return sql.OrderBy("(" + sql.SqlContext.SqlSyntax.GetFieldName(field) + ")"); } /// @@ -286,9 +282,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql OrderBy(this Sql sql, params Expression>[] fields) { + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.OrderBy(columns); } @@ -301,7 +298,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql OrderByDescending(this Sql sql, Expression> field) { - return sql.OrderBy("(" + GetFieldName(field, sql.SqlContext.SqlSyntax) + ") DESC"); + return sql.OrderBy("(" + sql.SqlContext.SqlSyntax.GetFieldName(field) + ") DESC"); } /// @@ -313,9 +310,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql OrderByDescending(this Sql sql, params Expression>[] fields) { + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.OrderBy(columns.Select(x => x + " DESC")); } @@ -339,7 +337,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql GroupBy(this Sql sql, Expression> field) { - return sql.GroupBy(GetFieldName(field, sql.SqlContext.SqlSyntax)); + return sql.GroupBy(sql.SqlContext.SqlSyntax.GetFieldName(field)); } /// @@ -351,9 +349,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql GroupBy(this Sql sql, params Expression>[] fields) { + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.GroupBy(columns); } @@ -366,9 +365,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql AndBy(this Sql sql, params Expression>[] fields) { + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.Append(", " + string.Join(", ", columns)); } @@ -381,9 +381,10 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql AndByDescending(this Sql sql, params Expression>[] fields) { + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.Append(", " + string.Join(", ", columns.Select(x => x + " DESC"))); } @@ -391,6 +392,23 @@ namespace Umbraco.Core.Persistence #region Joins + /// + /// Appends a CROSS JOIN clause to the Sql statement. + /// + /// The type of the Dto. + /// The Sql statement. + /// An optional alias for the joined table. + /// The Sql statement. + public static Sql CrossJoin(this Sql sql, string alias = null) + { + var type = typeof(TDto); + var tableName = type.GetTableName(); + var join = sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName); + if (alias != null) join += " " + sql.SqlContext.SqlSyntax.GetQuotedTableName(alias); + + return sql.Append("CROSS JOIN " + join); + } + /// /// Appends an INNER JOIN clause to the Sql statement. /// @@ -532,6 +550,25 @@ namespace Umbraco.Core.Persistence return sqlJoin.On(onExpression, expresionist.GetSqlParameters()); } + /// + /// Appends an ON clause to a SqlJoin statement. + /// + /// The type of Dto 1. + /// The type of Dto 2. + /// The type of Dto 3. + /// The SqlJoin statement. + /// A predicate to transform and use as the ON clause body. + /// An optional alias for Dto 1 table. + /// An optional alias for Dto 2 table. + /// An optional alias for Dto 3 table. + /// The Sql statement. + public static Sql On(this Sql.SqlJoinClause sqlJoin, Expression> predicate, string aliasLeft = null, string aliasRight = null, string aliasOther = null) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlJoin.SqlContext, aliasLeft, aliasRight, aliasOther); + var onExpression = expresionist.Visit(predicate); + return sqlJoin.On(onExpression, expresionist.GetSqlParameters()); + } + #endregion #region Select @@ -572,9 +609,10 @@ namespace Umbraco.Core.Persistence public static Sql SelectCount(this Sql sql, params Expression>[] fields) { if (sql == null) throw new ArgumentNullException(nameof(sql)); + var sqlSyntax = sql.SqlContext.SqlSyntax; var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) - : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.Select("COUNT (" + string.Join(", ", columns) + ")"); } @@ -906,7 +944,7 @@ namespace Umbraco.Core.Persistence public SqlUpd Set(Expression> fieldSelector, object value) { - var fieldName = GetFieldName(fieldSelector, _sqlContext.SqlSyntax); + var fieldName = _sqlContext.SqlSyntax.GetFieldName(fieldSelector); _setExpressions.Add(new Tuple(fieldName, value)); return this; } @@ -1062,17 +1100,6 @@ namespace Umbraco.Core.Persistence return string.IsNullOrWhiteSpace(attr?.Name) ? column.Name : attr.Name; } - private static string GetFieldName(Expression> fieldSelector, ISqlSyntaxProvider sqlSyntax) - { - var field = ExpressionHelper.FindProperty(fieldSelector).Item1 as PropertyInfo; - var fieldName = field.GetColumnName(); - - var type = typeof (TDto); - var tableName = type.GetTableName(); - - return sqlSyntax.GetQuotedTableName(tableName) + "." + sqlSyntax.GetQuotedColumnName(fieldName); - } - internal static void WriteToConsole(this Sql sql) { Console.WriteLine(sql.SQL); diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs index 4d33977c72..971b65c220 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs @@ -167,4 +167,93 @@ namespace Umbraco.Core.Persistence.Querying } } + /// + /// Represents an expression tree parser used to turn strongly typed expressions into SQL statements. + /// + /// The type of DTO 1. + /// The type of DTO 2. + /// The type of DTO 3. + /// This visitor is stateful and cannot be reused. + internal class PocoToSqlExpressionVisitor : ExpressionVisitorBase + { + private readonly PocoData _pocoData1, _pocoData2, _pocoData3; + private readonly string _alias1, _alias2, _alias3; + private string _parameterName1, _parameterName2, _parameterName3; + + public PocoToSqlExpressionVisitor(ISqlContext sqlContext, string alias1, string alias2, string alias3) + : base(sqlContext.SqlSyntax) + { + _pocoData1 = sqlContext.PocoDataFactory.ForType(typeof(TDto1)); + _pocoData2 = sqlContext.PocoDataFactory.ForType(typeof(TDto2)); + _pocoData3 = sqlContext.PocoDataFactory.ForType(typeof(TDto3)); + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + } + + protected override string VisitLambda(LambdaExpression lambda) + { + if (lambda.Parameters.Count == 3) + { + _parameterName1 = lambda.Parameters[0].Name; + _parameterName2 = lambda.Parameters[1].Name; + _parameterName3 = lambda.Parameters[2].Name; + } + else if (lambda.Parameters.Count == 2) + { + _parameterName1 = lambda.Parameters[0].Name; + _parameterName2 = lambda.Parameters[1].Name; + } + else + { + _parameterName1 = _parameterName2 = null; + } + return base.VisitLambda(lambda); + } + + protected override string VisitMemberAccess(MemberExpression m) + { + if (m.Expression != null) + { + if (m.Expression.NodeType == ExpressionType.Parameter) + { + var pex = (ParameterExpression)m.Expression; + + if (pex.Name == _parameterName1) + return Visited ? string.Empty : GetFieldName(_pocoData1, m.Member.Name, _alias1); + + if (pex.Name == _parameterName2) + return Visited ? string.Empty : GetFieldName(_pocoData2, m.Member.Name, _alias2); + + if (pex.Name == _parameterName3) + return Visited ? string.Empty : GetFieldName(_pocoData3, m.Member.Name, _alias3); + } + else if (m.Expression.NodeType == ExpressionType.Convert) + { + // here: which _pd should we use?! + throw new NotSupportedException(); + //return Visited ? string.Empty : GetFieldName(_pd, m.Member.Name); + } + } + + var member = Expression.Convert(m, typeof(object)); + var lambda = Expression.Lambda>(member); + var getter = lambda.Compile(); + var o = getter(); + + SqlParameters.Add(o); + + // execute if not already compiled + return Visited ? string.Empty : "@" + (SqlParameters.Count - 1); + } + + protected virtual string GetFieldName(PocoData pocoData, string name, string alias) + { + var column = pocoData.Columns.FirstOrDefault(x => x.Value.MemberInfoData.Name == name); + var tableName = SqlSyntax.GetQuotedTableName(alias ?? pocoData.TableInfo.TableName); + var columnName = SqlSyntax.GetQuotedColumnName(column.Value.ColumnName); + + return tableName + "." + columnName; + } + } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IContentRepository.cs index fea0a61589..f7341d112b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IContentRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories { @@ -67,7 +68,8 @@ namespace Umbraco.Core.Persistence.Repositories /// /// Gets paged content items. /// + /// Here, can be null but cannot. IEnumerable GetPage(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null); + IQuery filter, Ordering ordering); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ITaskRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITaskRepository.cs deleted file mode 100644 index 10c517fa72..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/ITaskRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - public interface ITaskRepository : IReadWriteQueryRepository - { - IEnumerable GetTasks(int? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false); - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/ITaskTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITaskTypeRepository.cs deleted file mode 100644 index 9fe483b462..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/ITaskTypeRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - public interface ITaskTypeRepository : IReadWriteQueryRepository - { } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs index 83d4148689..4bab445bee 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs @@ -18,25 +18,6 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetDescendants(int masterTemplateId); IEnumerable GetDescendants(string alias); - /// - /// Returns a template as a template node which can be traversed (parent, children) - /// - /// - /// - [Obsolete("Use GetDescendants instead")] - [EditorBrowsable(EditorBrowsableState.Never)] - TemplateNode GetTemplateNode(string alias); - - /// - /// Given a template node in a tree, this will find the template node with the given alias if it is found in the hierarchy, otherwise null - /// - /// - /// - /// - [Obsolete("Use GetDescendants instead")] - [EditorBrowsable(EditorBrowsableState.Never)] - TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias); - /// /// This checks what the default rendering engine is set in config but then also ensures that there isn't already /// a template that exists in the opposite rendering engine's template folder, then returns the appropriate diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 2e0139aa30..6ace73fbc3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -232,16 +232,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement #endregion - public abstract IEnumerable GetPage(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null); - - // sql: the main sql - // filterSql: a filtering ? fixme different from v7? - // orderBy: the name of an ordering field - // orderDirection: direction for orderBy - // orderBySystemField: whether orderBy is a system field or a custom field (property value) - private Sql PrepareSqlForPage(Sql sql, Sql filterSql, string orderBy, Direction orderDirection, bool orderBySystemField) + private Sql PreparePageSql(Sql sql, Sql filterSql, Ordering ordering) { - if (filterSql == null && string.IsNullOrEmpty(orderBy)) return sql; + // non-filtering, non-ordering = nothing to do + if (filterSql == null && ordering.IsEmpty) return sql; // preserve original var psql = new Sql(sql.SqlContext, sql.SQL, sql.Arguments); @@ -251,74 +245,131 @@ namespace Umbraco.Core.Persistence.Repositories.Implement psql.Append(filterSql); // non-sorting, we're done - if (string.IsNullOrEmpty(orderBy)) + if (ordering.IsEmpty) return psql; - // else apply sort - var dbfield = orderBySystemField - ? GetOrderBySystemField(ref psql, orderBy) - : GetOrderByNonSystemField(ref psql, orderBy); - - if (orderDirection == Direction.Ascending) - psql.OrderBy(dbfield); - else - psql.OrderByDescending(dbfield); + // else apply ordering + ApplyOrdering(ref psql, ordering); // no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique. // if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column // is empty for many nodes) - see: http://issues.umbraco.org/issue/U4-8831 - dbfield = GetDatabaseFieldNameForOrderBy("umbracoNode", "id"); - if (orderBySystemField == false || orderBy.InvariantEquals(dbfield) == false) + var dbfield = GetQuotedFieldName("umbracoNode", "id"); + (dbfield, _) = SqlContext.Visit(x => x.NodeId); // fixme?! + if (ordering.IsCustomField || !ordering.OrderBy.InvariantEquals("id")) { - // get alias, if aliased - var matches = SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL); - var match = matches.Cast().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield)); - if (match != null) dbfield = match.Groups[2].Value; - - // add field - psql.OrderBy(dbfield); + psql.OrderBy(GetAliasedField(dbfield, sql)); // fixme why aliased? } // create prepared sql // ensure it's single-line as NPoco PagingHelper has issues with multi-lines - psql = new Sql(psql.SqlContext, psql.SQL.ToSingleLine(), psql.Arguments); + psql = Sql(psql.SQL.ToSingleLine(), psql.Arguments); return psql; } - private string GetOrderBySystemField(ref Sql sql, string orderBy) + private void ApplyOrdering(ref Sql sql, Ordering ordering) { - // get the database field eg "[table].[column]" - var dbfield = GetDatabaseFieldNameForOrderBy(orderBy); + if (sql == null) throw new ArgumentNullException(nameof(sql)); + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); - // for SqlServer pagination to work, the "order by" field needs to be the alias eg if - // the select statement has "umbracoNode.text AS NodeDto__Text" then the order field needs - // to be "NodeDto__Text" and NOT "umbracoNode.text". - // not sure about SqlCE nor MySql, so better do it too. initially thought about patching - // NPoco but that would be expensive and not 100% possible, so better give NPoco proper - // queries to begin with. - // thought about maintaining a map of columns-to-aliases in the sql context but that would - // be expensive and most of the time, useless. so instead we parse the SQL looking for the - // alias. somewhat expensive too but nothing's free. + var orderBy = ordering.IsCustomField + ? ApplyCustomOrdering(ref sql, ordering) + : ApplySystemOrdering(ref sql, ordering); - // note: ContentTypeAlias is not properly managed because it's not part of the query to begin with! + // beware! NPoco paging code parses the query to isolate the ORDER BY fragment, + // using a regex that wants "([\w\.\[\]\(\)\s""`,]+)" - meaning that anything + // else in orderBy is going to break NPoco / not be detected - // get alias, if aliased - var matches = SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL); - var match = matches.Cast().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield)); - if (match != null) dbfield = match.Groups[2].Value; + // beware! NPoco paging code (in PagingHelper) collapses everything [foo].[bar] + // to [bar] only, so we MUST use aliases, cannot use [table].[field] - return dbfield; + // beware! pre-2012 SqlServer is using a convoluted syntax for paging, which + // includes "SELECT ROW_NUMBER() OVER (ORDER BY ...) poco_rn FROM SELECT (...", + // so anything added here MUST also be part of the inner SELECT statement, ie + // the original statement, AND must be using the proper alias, as the inner SELECT + // will hide the original table.field names entirely + + if (ordering.Direction == Direction.Ascending) + sql.OrderBy(orderBy); + else + sql.OrderByDescending(orderBy); } - private string GetOrderByNonSystemField(ref Sql sql, string orderBy) + protected virtual string ApplySystemOrdering(ref Sql sql, Ordering ordering) + { + // id is invariant + if (ordering.OrderBy.InvariantEquals("id")) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.NodeId), sql); + + // sort order is invariant + if (ordering.OrderBy.InvariantEquals("sortOrder")) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.SortOrder), sql); + + // path is invariant + if (ordering.OrderBy.InvariantEquals("path")) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.Path), sql); + + // note: 'owner' is the user who created the item as a whole, + // we don't have an 'owner' per culture (should we?) + if (ordering.OrderBy.InvariantEquals("owner")) + { + var joins = Sql() + .InnerJoin("ownerUser").On((node, user) => node.UserId == user.Id, aliasRight: "ownerUser"); + + // see notes in ApplyOrdering: the field MUST be selected + aliased + sql = Sql(InsertBefore(sql, "FROM", ", " + SqlSyntax.GetFieldName(x => x.UserName, "ownerUser") + " AS ordering "), sql.Arguments); + + sql = InsertJoins(sql, joins); + + return "ordering"; + } + + // note: each version culture variation has a date too, + // maybe we would want to use it instead? + if (ordering.OrderBy.InvariantEquals("versionDate") || ordering.OrderBy.InvariantEquals("updateDate")) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.VersionDate), sql); + + // create date is invariant (we don't keep each culture's creation date) + if (ordering.OrderBy.InvariantEquals("createDate")) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.CreateDate), sql); + + // name is variant + if (ordering.OrderBy.InvariantEquals("name")) + { + // no culture = can only work on the invariant name + // see notes in ApplyOrdering: the field MUST be aliased + if (ordering.Culture.IsNullOrWhiteSpace()) + return GetAliasedField(SqlSyntax.GetFieldName(x => x.Text), sql); + + // culture = must work on variant name ?? invariant name + // insert proper join and return coalesced ordering field + + var joins = Sql() + .LeftJoin(nested => + nested.InnerJoin("lang").On((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == ordering.Culture, "ccv", "lang"), "ccv") + .On((version, ccv) => version.Id == ccv.VersionId, aliasRight: "ccv"); + + // see notes in ApplyOrdering: the field MUST be selected + aliased + sql = Sql(InsertBefore(sql, "FROM", ", " + SqlContext.Visit((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql + " AS ordering "), sql.Arguments); + + sql = InsertJoins(sql, joins); + + return "ordering"; + } + + // previously, we'd accept anything and just sanitize it - not anymore + throw new NotSupportedException($"Ordering by {ordering.OrderBy} not supported."); + } + + private string ApplyCustomOrdering(ref Sql sql, Ordering ordering) { // sorting by a custom field, so set-up sub-query for ORDER BY clause to pull through value // from 'current' content version for the given order by field var sortedInt = string.Format(SqlContext.SqlSyntax.ConvertIntegerToOrderableString, "intValue"); + var sortedDecimal = string.Format(SqlContext.SqlSyntax.ConvertDecimalToOrderableString, "decimalValue"); var sortedDate = string.Format(SqlContext.SqlSyntax.ConvertDateToOrderableString, "dateValue"); var sortedString = "COALESCE(varcharValue,'')"; // assuming COALESCE is ok for all syntaxes - var sortedDecimal = string.Format(SqlContext.SqlSyntax.ConvertDecimalToOrderableString, "decimalValue"); // needs to be an outer join since there's no guarantee that any of the nodes have values for this property var innerSql = Sql().Select($@"CASE @@ -329,49 +380,60 @@ namespace Umbraco.Core.Persistence.Repositories.Implement END AS customPropVal, cver.nodeId AS customPropNodeId") .From("cver") - .InnerJoin("opdata").On((left, right) => left.Id == right.Id, "cver", "opdata") - .InnerJoin("optype").On((left, right) => left.PropertyTypeId == right.Id, "opdata", "optype") + .InnerJoin("opdata") + .On((version, pdata) => version.Id == pdata.VersionId, "cver", "opdata") + .InnerJoin("optype").On((pdata, ptype) => pdata.PropertyTypeId == ptype.Id, "opdata", "optype") + .LeftJoin().On((pdata, lang) => pdata.LanguageId == lang.Id, "opdata") .Where(x => x.Current, "cver") // always query on current (edit) values - .Where(x => x.Alias == "", "optype"); + .Where(x => x.Alias == ordering.OrderBy, "optype") + .Where((opdata, lang) => opdata.LanguageId == null || lang.IsoCode == ordering.Culture, "opdata"); - // @0 is for x.Current ie 'true' = 1 - // @1 is for x.Alias - var innerSqlString = innerSql.SQL.Replace("@0", "1").Replace("@1", "@" + sql.Arguments.Length); + // merge arguments + var argsList = sql.Arguments.ToList(); + var innerSqlString = ParameterHelper.ProcessParams(innerSql.SQL, innerSql.Arguments, argsList); + + // create the outer join complete sql fragment var outerJoinTempTable = $@"LEFT OUTER JOIN ({innerSqlString}) AS customPropData ON customPropData.customPropNodeId = {Constants.DatabaseSchema.Tables.Node}.id "; // trailing space is important! - // insert this just above the last WHERE - var pos = sql.SQL.InvariantIndexOf("WHERE"); - if (pos < 0) throw new Exception("Oops, WHERE not found."); - var newSql = sql.SQL.Insert(pos, outerJoinTempTable); + // insert this just above the first WHERE + var newSql = InsertBefore(sql.SQL, "WHERE", outerJoinTempTable); - var newArgs = sql.Arguments.ToList(); - newArgs.Add(orderBy); + // see notes in ApplyOrdering: the field MUST be selected + aliased + newSql = InsertBefore(newSql, "FROM", ", customPropData.customPropVal AS ordering "); // trailing space is important! - // insert the SQL selected field, too, else ordering cannot work - if (sql.SQL.StartsWith("SELECT ") == false) throw new Exception("Oops: SELECT not found."); - newSql = newSql.Insert("SELECT ".Length, "customPropData.customPropVal, "); - - sql = new Sql(sql.SqlContext, newSql, newArgs.ToArray()); + // create the new sql + sql = Sql(newSql, argsList.ToArray()); // and order by the custom field - return "customPropData.customPropVal"; + // this original code means that an ascending sort would first expose all NULL values, ie items without a value + return "ordering"; + + // note: adding an extra sorting criteria on + // "(CASE WHEN customPropData.customPropVal IS NULL THEN 1 ELSE 0 END") + // would ensure that items without a value always come last, both in ASC and DESC-ending sorts } + public abstract IEnumerable GetPage(IQuery query, + long pageIndex, int pageSize, out long totalRecords, + IQuery filter, + Ordering ordering); + + // here, filter can be null and ordering cannot protected IEnumerable GetPage(IQuery query, long pageIndex, int pageSize, out long totalRecords, Func, IEnumerable> mapDtos, - string orderBy, Direction orderDirection, bool orderBySystemField, - Sql filterSql = null) // fixme filter is different on v7? + Sql filter, + Ordering ordering) { - if (orderBy == null) throw new ArgumentNullException(nameof(orderBy)); + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); // start with base query, and apply the supplied IQuery - if (query == null) query = AmbientScope.SqlContext.Query(); + if (query == null) query = Query(); var sql = new SqlTranslator(GetBaseQuery(QueryType.Many), query).Translate(); // sort and filter - sql = PrepareSqlForPage(sql, filterSql, orderBy, orderDirection, orderBySystemField); + sql = PreparePageSql(sql, filter, ordering); // get a page of DTOs and the total count var pagedResult = Database.Page(pageIndex + 1, pageSize, sql); @@ -498,36 +560,48 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return result; } - protected virtual string GetDatabaseFieldNameForOrderBy(string orderBy) - { - // translate the supplied "order by" field, which were originally defined for in-memory - // object sorting of ContentItemBasic instance, to the actual database field names. + protected string InsertBefore(Sql s, string atToken, string insert) + => InsertBefore(s.SQL, atToken, insert); - switch (orderBy.ToUpperInvariant()) - { - case "VERSIONDATE": - case "UPDATEDATE": - return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.ContentVersion, "versionDate"); - case "CREATEDATE": - return GetDatabaseFieldNameForOrderBy("umbracoNode", "createDate"); - case "NAME": - return GetDatabaseFieldNameForOrderBy("umbracoNode", "text"); - case "PUBLISHED": - return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.Document, "published"); - case "OWNER": - //TODO: This isn't going to work very nicely because it's going to order by ID, not by letter - return GetDatabaseFieldNameForOrderBy("umbracoNode", "nodeUser"); - case "PATH": - return GetDatabaseFieldNameForOrderBy("umbracoNode", "path"); - case "SORTORDER": - return GetDatabaseFieldNameForOrderBy("umbracoNode", "sortOrder"); - default: - //ensure invalid SQL cannot be submitted - return Regex.Replace(orderBy, @"[^\w\.,`\[\]@-]", ""); - } + protected string InsertBefore(string s, string atToken, string insert) + { + var pos = s.InvariantIndexOf(atToken); + if (pos < 0) throw new Exception($"Could not find token \"{atToken}\"."); + return s.Insert(pos, insert); } - protected string GetDatabaseFieldNameForOrderBy(string tableName, string fieldName) + protected Sql InsertJoins(Sql sql, Sql joins) + { + var joinsSql = joins.SQL; + var args = sql.Arguments; + + // merge args if any + if (joins.Arguments.Length > 0) + { + var argsList = args.ToList(); + joinsSql = ParameterHelper.ProcessParams(joinsSql, joins.Arguments, argsList); + args = argsList.ToArray(); + } + + return Sql(InsertBefore(sql.SQL, "WHERE", joinsSql), args); + } + + private string GetAliasedField(string field, Sql sql) + { + // get alias, if aliased + // + // regex looks for pattern "([\w+].[\w+]) AS ([\w+])" ie "(field) AS (alias)" + // and, if found & a group's field matches the field name, returns the alias + // + // so... if query contains "[umbracoNode].[nodeId] AS [umbracoNode__nodeId]" + // then GetAliased for "[umbracoNode].[nodeId]" returns "[umbracoNode__nodeId]" + + var matches = SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL); + var match = matches.Cast().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(field)); + return match == null ? field : match.Groups[2].Value; + } + + protected string GetQuotedFieldName(string tableName, string fieldName) { return SqlContext.SqlSyntax.GetQuotedTableName(tableName) + "." + SqlContext.SqlSyntax.GetQuotedColumnName(fieldName); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 28c0b0dec6..bf41cd1ad1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -8,13 +8,12 @@ using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Scoping; -using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; +using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -168,7 +167,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var list = new List { "DELETE FROM " + Constants.DatabaseSchema.Tables.RedirectUrl + " WHERE contentKey IN (SELECT uniqueId FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Task + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2NodePermission + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", @@ -504,8 +502,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // names also impact 'edited' foreach (var (culture, name) in content.CultureNames) if (name != content.GetPublishName(culture)) + { + edited = true; (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); + // fixme - change tracking + // at the moment, we don't do any dirty tracking on property values, so we don't know whether the + // culture has just been edited or not, so we don't update its update date - that date only changes + // when the name is set, and it all works because the controller does it - but, if someone uses a + // service to change a property value and save (without setting name), the update date does not change. + } + // replace the content version variations (rather than updating) // only need to delete for the version that existed, the new version (if any) has no property data yet var deleteContentVariations = Sql().Delete().Where(x => x.VersionId == versionToDelete); @@ -673,13 +680,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement PermissionRepository.Save(permission); } - /// - /// Gets paged content results. - /// + /// public override IEnumerable GetPage(IQuery query, - long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, - IQuery filter = null) + long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering) { Sql filterSql = null; @@ -692,8 +696,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return GetPage(query, pageIndex, pageSize, out totalRecords, x => MapDtosToContent(x), - orderBy, orderDirection, orderBySystemField, - filterSql); + filterSql, + ordering); } public bool IsPathPublished(IContent content) @@ -814,25 +818,59 @@ namespace Umbraco.Core.Persistence.Repositories.Implement #endregion - protected override string GetDatabaseFieldNameForOrderBy(string orderBy) + protected override string ApplySystemOrdering(ref Sql sql, Ordering ordering) { - // NOTE see sortby.prevalues.controller.js for possible values - // that need to be handled here or in VersionableRepositoryBase - - //Some custom ones - switch (orderBy.ToUpperInvariant()) + // note: 'updater' is the user who created the latest draft version, + // we don't have an 'updater' per culture (should we?) + if (ordering.OrderBy.InvariantEquals("updater")) { - case "UPDATER": - // fixme orders by id not letter = bad - return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.ContentVersion, "userId"); - case "PUBLISHED": - // fixme kill - return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.Document, "published"); - case "CONTENTTYPEALIAS": - throw new NotSupportedException("Don't know how to support ContentTypeAlias."); + var joins = Sql() + .InnerJoin("updaterUser").On((version, user) => version.UserId == user.Id, aliasRight: "updaterUser"); + + // see notes in ApplyOrdering: the field MUST be selected + aliased + sql = Sql(InsertBefore(sql, "FROM", SqlSyntax.GetFieldName(x => x.UserName, "updaterUser") + " AS ordering"), sql.Arguments); + + sql = InsertJoins(sql, joins); + + return "ordering"; } - return base.GetDatabaseFieldNameForOrderBy(orderBy); + if (ordering.OrderBy.InvariantEquals("published")) + { + // no culture = can only work on the global 'published' flag + if (ordering.Culture.IsNullOrWhiteSpace()) + { + // see notes in ApplyOrdering: the field MUST be selected + aliased, and we cannot have + // the whole CASE fragment in ORDER BY due to it not being detected by NPoco + sql = Sql(InsertBefore(sql, "FROM", ", (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) AS ordering "), sql.Arguments); + return "ordering"; + } + + // invariant: left join will yield NULL and we must use pcv to determine published + // variant: left join may yield NULL or something, and that determines published + + var joins = Sql() + .InnerJoin("ctype").On((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype") + .LeftJoin(nested => + nested.InnerJoin("lang").On((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == ordering.Culture, "ccv", "lang"), "ccv") + .On((pcv, ccv) => pcv.Id == ccv.VersionId, "pcv", "ccv"); // join on *published* content version + + sql = InsertJoins(sql, joins); + + // see notes in ApplyOrdering: the field MUST be selected + aliased, and we cannot have + // the whole CASE fragment in ORDER BY due to it not being detected by NPoco + var sqlText = InsertBefore(sql.SQL, "FROM", + + // when invariant, ie 'variations' does not have the culture flag (value 1), use the global 'published' flag on pcv.id, + // otherwise check if there's a version culture variation for the lang, via ccv.id + ", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccv.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important! + + sql = Sql(sqlText, sql.Arguments); + + return "ordering"; + } + + return base.ApplySystemOrdering(ref sql, ordering); } private IEnumerable MapDtosToContent(List dtos, bool withCache = false) @@ -1001,7 +1039,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), Name = dto.Name, - Date = dto.Date + Date = dto.UpdateDate }); } @@ -1046,7 +1084,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, Name = name, - Date = content.GetCultureDate(culture) ?? DateTime.MinValue // we *know* there is a value + UpdateDate = content.GetUpdateDate(culture) ?? DateTime.MinValue // we *know* there is a value }; // if not publishing, we're just updating the 'current' (non-published) version, @@ -1061,22 +1099,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, Name = name, - Date = content.GetPublishDate(culture) ?? DateTime.MinValue // we *know* there is a value + UpdateDate = content.GetPublishDate(culture) ?? DateTime.MinValue // we *know* there is a value }; } private IEnumerable GetDocumentVariationDtos(IContent content, bool publishing, HashSet editedCultures) { - foreach (var (culture, name) in content.CultureNames) + var allCultures = content.AvailableCultures.Union(content.PublishedCultures); // union = distinct + foreach (var culture in allCultures) yield return new DocumentCultureVariationDto { NodeId = content.Id, LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, - // if not published, always edited - // no need to check for availability: it *is* available since it is in content.CultureNames - Edited = !content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)) + Name = content.GetCultureName(culture) ?? content.GetPublishName(culture), + + // note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem + + Available = content.IsCultureAvailable(culture), + Published = content.IsCulturePublished(culture), + Edited = content.IsCultureAvailable(culture) && + (!content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture))) }; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index 340eecb538..fb8c2732e6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Scoping; using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -34,6 +35,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected IUmbracoDatabase Database => _scopeAccessor.AmbientScope.Database; protected Sql Sql() => _scopeAccessor.AmbientScope.SqlContext.Sql(); + protected ISqlSyntaxProvider SqlSyntax => _scopeAccessor.AmbientScope.SqlContext.SqlSyntax; #region Repository @@ -57,83 +59,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //fixme - we should be able to do sql = sql.OrderBy(x => Alias(x.NodeId, "NodeId")); but we can't because the OrderBy extension don't support Alias currently sql = sql.OrderBy("NodeId"); - //IEnumerable result; - // - //if (isMedia) - //{ - // //Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag! - // var pagedResult = UnitOfWork.Database.Page(pageIndex + 1, pageSize, pagedSql); - - // var ids = pagedResult.Items.Select(x => (int)x.id).InGroupsOf(2000); - // var entities = pagedResult.Items.Select(BuildEntityFromDynamic).Cast().ToList(); - - // //Now we need to merge in the property data since we need paging and we can't do this the way that the big media query was working before - // foreach (var idGroup in ids) - // { - // var propSql = GetPropertySql(Constants.ObjectTypes.Media) - // .WhereIn(x => x.NodeId, idGroup) - // .OrderBy(x => x.NodeId); - - // //This does NOT fetch all data into memory in a list, this will read - // // over the records as a data reader, this is much better for performance and memory, - // // but it means that during the reading of this data set, nothing else can be read - // // from SQL server otherwise we'll get an exception. - // var allPropertyData = UnitOfWork.Database.Query(propSql); - - // //keep track of the current property data item being enumerated - // var propertyDataSetEnumerator = allPropertyData.GetEnumerator(); - // var hasCurrent = false; // initially there is no enumerator.Current - - // try - // { - // //This must be sorted by node id (which is done by SQL) because this is how we are sorting the query to lookup property types above, - // // which allows us to more efficiently iterate over the large data set of property values. - // foreach (var entity in entities) - // { - // // assemble the dtos for this def - // // use the available enumerator.Current if any else move to next - // while (hasCurrent || propertyDataSetEnumerator.MoveNext()) - // { - // if (propertyDataSetEnumerator.Current.nodeId == entity.Id) - // { - // hasCurrent = false; // enumerator.Current is not available - - // //the property data goes into the additional data - // entity.AdditionalData[propertyDataSetEnumerator.Current.propertyTypeAlias] = new UmbracoEntity.EntityProperty - // { - // PropertyEditorAlias = propertyDataSetEnumerator.Current.propertyEditorAlias, - // Value = StringExtensions.IsNullOrWhiteSpace(propertyDataSetEnumerator.Current.textValue) - // ? propertyDataSetEnumerator.Current.varcharValue - // : StringExtensions.ConvertToJsonIfPossible(propertyDataSetEnumerator.Current.textValue) - // }; - // } - // else - // { - // hasCurrent = true; // enumerator.Current is available for another def - // break; // no more propertyDataDto for this def - // } - // } - // } - // } - // finally - // { - // propertyDataSetEnumerator.Dispose(); - // } - // } - - // result = entities; - //} - //else - //{ - // var pagedResult = UnitOfWork.Database.Page(pageIndex + 1, pageSize, pagedSql); - // result = pagedResult.Items.Select(BuildEntityFromDynamic).Cast().ToList(); - //} - var page = Database.Page(pageIndex + 1, pageSize, sql); var dtos = page.Items; var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - - //TODO: For isContent will we need to build up the variation info? + + if (isContent) + BuildVariants(entities.Cast()); if (isMedia) BuildProperties(entities, dtos); @@ -154,11 +85,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //isContent is going to return a 1:M result now with the variants so we need to do different things if (isContent) { - var dtos = Database.FetchOneToMany( - ddto => ddto.VariationInfo, - ddto => ddto.VersionId, - sql); - return dtos.Count == 0 ? null : BuildDocumentEntity(dtos[0]); + var cdtos = Database.Fetch(sql); + + return cdtos.Count == 0 ? null : BuildVariants(BuildDocumentEntity(cdtos[0])); } var dto = Database.FirstOrDefault(sql); @@ -216,13 +145,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //isContent is going to return a 1:M result now with the variants so we need to do different things if (isContent) { - var cdtos = Database.FetchOneToMany( - dto => dto.VariationInfo, - dto => dto.VersionId, - sql); + var cdtos = Database.Fetch(sql); + return cdtos.Count == 0 ? Enumerable.Empty() - : cdtos.Select(BuildDocumentEntity).ToArray(); + : BuildVariants(cdtos.Select(BuildDocumentEntity)).ToList(); } var dtos = Database.Fetch(sql); @@ -323,7 +250,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private void BuildProperties(EntitySlim[] entities, List dtos) { - var versionIds = dtos.Select(x => x.VersionId).Distinct().ToArray(); + var versionIds = dtos.Select(x => x.VersionId).Distinct().ToList(); var pdtos = Database.FetchByGroups(versionIds, 2000, GetPropertyData); var xentity = entities.ToDictionary(x => x.Id, x => x); // nodeId -> entity @@ -346,10 +273,70 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.AdditionalData[pdto.PropertyTypeDto.Alias] = new EntitySlim.PropertySlim(pdto.PropertyTypeDto.DataTypeDto.EditorAlias, value); } + private DocumentEntitySlim BuildVariants(DocumentEntitySlim entity) + => BuildVariants(new[] { entity }).First(); + + private IEnumerable BuildVariants(IEnumerable entities) + { + List v = null; + var entitiesList = entities.ToList(); + foreach (var e in entitiesList) + { + if (e.Variations.VariesByCulture()) + (v ?? (v = new List())).Add(e); + } + + if (v == null) return entitiesList; + + // fetch all variant info dtos + var dtos = Database.FetchByGroups(v.Select(x => x.Id), 2000, GetVariantInfos); + + // group by node id (each group contains all languages) + var xdtos = dtos.GroupBy(x => x.NodeId).ToDictionary(x => x.Key, x => x); + + foreach (var e in v) + { + // since we're only iterating on entities that vary, we must have something + var edtos = xdtos[e.Id]; + + e.CultureNames = edtos.Where(x => x.CultureAvailable).ToDictionary(x => x.IsoCode, x => x.Name); + e.PublishedCultures = edtos.Where(x => x.CulturePublished).Select(x => x.IsoCode); + e.EditedCultures = edtos.Where(x => x.CultureAvailable && x.CultureEdited).Select(x => x.IsoCode); + } + + return entitiesList; + } + #endregion #region Sql + protected Sql GetVariantInfos(IEnumerable ids) + { + return Sql() + .Select(x => x.NodeId) + .AndSelect(x => x.IsoCode) + .AndSelect("doc", x => Alias(x.Published, "DocumentPublished"), x => Alias(x.Edited, "DocumentEdited")) + .AndSelect("dcv", + x => Alias(x.Available, "CultureAvailable"), x => Alias(x.Published, "CulturePublished"), x => Alias(x.Edited, "CultureEdited"), + x => Alias(x.Name, "Name")) + + // from node x language + .From() + .CrossJoin() + + // join to document - always exists - indicates global document published/edited status + .InnerJoin("doc") + .On((node, doc) => node.NodeId == doc.NodeId, aliasRight: "doc") + + // left-join do document variation - matches cultures that are *available* + indicates when *edited* + .LeftJoin("dcv") + .On((node, dcv, lang) => node.NodeId == dcv.NodeId && lang.Id == dcv.LanguageId, aliasRight: "dcv") + + // for selected nodes + .WhereIn(x => x.NodeId, ids); + } + // gets the full sql for a given object type and a given unique id protected Sql GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, Guid uniqueId) { @@ -371,24 +358,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return AddGroupBy(isContent, isMedia, sql); } - // fixme kill this nonsense - //// gets the SELECT + FROM + WHERE sql - //// to get all property data for all items of the specified object type - //private Sql GetPropertySql(Guid objectType) - //{ - // return Sql() - // .Select(x => x.VersionId, x => x.TextValue, x => x.VarcharValue) - // .AndSelect(x => x.NodeId) - // .AndSelect(x => x.PropertyEditorAlias) - // .AndSelect(x => Alias(x.Alias, "propertyTypeAlias")) - // .From() - // .InnerJoin().On((left, right) => left.VersionId == right.Id) - // .InnerJoin().On((left, right) => left.NodeId == right.NodeId) - // .InnerJoin().On(dto => dto.PropertyTypeId, dto => dto.Id) - // .InnerJoin().On(dto => dto.DataTypeId, dto => dto.DataTypeId) - // .Where(x => x.NodeObjectType == objectType); - //} - private Sql GetPropertyData(int versionId) { return Sql() @@ -408,89 +377,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .InnerJoin().On((left, right) => left.DataTypeId == right.NodeId) .WhereIn(x => x.VersionId, versionIds) .OrderBy(x => x.VersionId); - } - - // fixme - wtf is this? - //private Sql GetFullSqlForMedia(Sql entitySql, Action> filter = null) - //{ - // //this will add any varcharValue property to the output which can be added to the additional properties - - // var sql = GetPropertySql(Constants.ObjectTypes.Media); - - // filter?.Invoke(sql); - - // // We're going to create a query to query against the entity SQL - // // because we cannot group by nText columns and we have a COUNT in the entitySql we cannot simply left join - // // the entitySql query, we have to join the wrapped query to get the ntext in the result - - // var wrappedSql = Sql() - // .Append("SELECT * FROM (") - // .Append(entitySql) - // .Append(") tmpTbl LEFT JOIN (") - // .Append(sql) - // .Append(") as property ON id = property.nodeId") - // .OrderBy("sortOrder, id"); - - // return wrappedSql; - //} - - - /// - /// The DTO used to fetch results for a content item with its variation info - /// - private class ContentEntityDto : BaseDto - { - public ContentVariation Variations { get; set; } - - [ResultColumn, Reference(ReferenceType.Many)] - public List VariationInfo { get; set; } - - public bool Published { get; set; } - public bool Edited { get; set; } - } - - /// - /// The DTO used in the 1:M result for content variation info - /// - private class ContentEntityVariationInfoDto - { - [Column("versionCultureId")] - public int VersionCultureId { get; set; } - [Column("versionCultureLangId")] - public int LanguageId { get; set; } - [Column("versionCultureName")] - public string Name { get; set; } - } - - // ReSharper disable once ClassNeverInstantiated.Local - /// - /// the DTO corresponding to fields selected by GetBase - /// - private class BaseDto - { - // ReSharper disable UnusedAutoPropertyAccessor.Local - // ReSharper disable UnusedMember.Local - public int NodeId { get; set; } - public bool Trashed { get; set; } - public int ParentId { get; set; } - public int? UserId { get; set; } - public int Level { get; set; } - public string Path { get; set; } - public int SortOrder { get; set; } - public Guid UniqueId { get; set; } - public string Text { get; set; } - public Guid NodeObjectType { get; set; } - public DateTime CreateDate { get; set; } - public int Children { get; set; } - public int VersionId { get; set; } - public string Alias { get; set; } - public string Icon { get; set; } - public string Thumbnail { get; set; } - public bool IsContainer { get; set; } - - // ReSharper restore UnusedAutoPropertyAccessor.Local - // ReSharper restore UnusedMember.Local - } + } // gets the base SELECT + FROM [+ filter] sql // always from the 'current' content version @@ -517,12 +404,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (isContent) { sql - .AndSelect(x => x.Published, x => x.Edited) - //This MUST come last in the select statements since we will end up with a 1:M query - .AndSelect( - x => Alias(x.Id, "versionCultureId"), - x => Alias(x.LanguageId, "versionCultureLangId"), - x => Alias(x.Name, "versionCultureName")); + .AndSelect(x => x.Published, x => x.Edited); } } @@ -534,7 +416,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement sql .InnerJoin().On((left, right) => left.NodeId == right.NodeId && right.Current) .InnerJoin().On((left, right) => left.NodeId == right.NodeId) - .LeftJoin().On((left, right) => left.ContentTypeId == right.NodeId); + .InnerJoin().On((left, right) => left.ContentTypeId == right.NodeId); } if (isContent) @@ -548,10 +430,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { sql .LeftJoin("child").On((left, right) => left.NodeId == right.ParentId, aliasRight: "child"); - - if (isContent) - sql - .LeftJoin().On((left, right) => left.Id == right.VersionId); } @@ -613,8 +491,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (isContent) { sql - .AndBy(x => x.Published, x => x.Edited) - .AndBy(x => x.Id, x => x.LanguageId, x => x.Name); + .AndBy(x => x.Published, x => x.Edited); } @@ -649,184 +526,60 @@ namespace Umbraco.Core.Persistence.Repositories.Implement public string TextValue { get; set; } } - // fixme kill + /// - /// This is a special relator in that it is not returning a DTO but a real resolved entity and that it accepts - /// a dynamic instance. + /// The DTO used to fetch results for a content item with its variation info /// - /// - /// We're doing this because when we query the db, we want to use dynamic so that it returns all available fields not just the ones - /// defined on the entity so we can them to additional data - /// - //internal class UmbracoEntityRelator - //{ - // internal UmbracoEntity Current; + private class ContentEntityDto : BaseDto + { + public ContentVariation Variations { get; set; } - // public IEnumerable MapAll(IEnumerable input) - // { - // UmbracoEntity entity; + public bool Published { get; set; } + public bool Edited { get; set; } + } - // foreach (var x in input) - // { - // entity = Map(x); - // if (entity != null) yield return entity; - // } + public class VariantInfoDto + { + public int NodeId { get; set; } + public string IsoCode { get; set; } + public string Name { get; set; } + public bool DocumentPublished { get; set; } + public bool DocumentEdited { get; set; } - // entity = Map((dynamic) null); - // if (entity != null) yield return entity; - // } + public bool CultureAvailable { get; set; } + public bool CulturePublished { get; set; } + public bool CultureEdited { get; set; } + } - // // must be called one last time with null in order to return the last one! - // public UmbracoEntity Map(dynamic a) - // { - // // Terminating call. Since we can return null from this function - // // we need to be ready for NPoco to callback later with null - // // parameters - // if (a == null) - // return Current; - - // string pPropertyEditorAlias = a.propertyEditorAlias; - // var pExists = pPropertyEditorAlias != null; - // string pPropertyAlias = a.propertyTypeAlias; - // string pTextValue = a.textValue; - // string pNVarcharValue = a.varcharValue; - - // // Is this the same UmbracoEntity as the current one we're processing - // if (Current != null && Current.Key == a.uniqueID) - // { - // if (pExists && pPropertyAlias.IsNullOrWhiteSpace() == false) - // { - // // Add this UmbracoProperty to the current additional data - // Current.AdditionalData[pPropertyAlias] = new UmbracoEntity.EntityProperty - // { - // PropertyEditorAlias = pPropertyEditorAlias, - // Value = pTextValue.IsNullOrWhiteSpace() - // ? pNVarcharValue - // : pTextValue.ConvertToJsonIfPossible() - // }; - // } - - // // Return null to indicate we're not done with this UmbracoEntity yet - // return null; - // } - - // // This is a different UmbracoEntity to the current one, or this is the - // // first time through and we don't have a Tab yet - - // // Save the current UmbracoEntityDto - // var prev = Current; - - // // Setup the new current UmbracoEntity - - // Current = BuildEntityFromDynamic(a); - - // if (pExists && pPropertyAlias.IsNullOrWhiteSpace() == false) - // { - // //add the property/create the prop list if null - // Current.AdditionalData[pPropertyAlias] = new UmbracoEntity.EntityProperty - // { - // PropertyEditorAlias = pPropertyEditorAlias, - // Value = pTextValue.IsNullOrWhiteSpace() - // ? pNVarcharValue - // : pTextValue.ConvertToJsonIfPossible() - // }; - // } - - // // Return the now populated previous UmbracoEntity (or null if first time through) - // return prev; - // } - //} - - // fixme need to review what's below - // comes from 7.6, similar to what's in VersionableRepositoryBase - // not sure it really makes sense... - - //private class EntityDefinitionCollection : KeyedCollection - //{ - // protected override int GetKeyForItem(EntityDefinition item) - // { - // return item.Id; - // } - - // /// - // /// if this key already exists if it does then we need to check - // /// if the existing item is 'older' than the new item and if that is the case we'll replace the older one - // /// - // /// - // /// - // public bool AddOrUpdate(EntityDefinition item) - // { - // if (Dictionary == null) - // { - // Add(item); - // return true; - // } - - // var key = GetKeyForItem(item); - // if (TryGetValue(key, out EntityDefinition found)) - // { - // //it already exists and it's older so we need to replace it - // if (item.VersionId > found.VersionId) - // { - // var currIndex = Items.IndexOf(found); - // if (currIndex == -1) - // throw new IndexOutOfRangeException("Could not find the item in the list: " + found.Id); - - // //replace the current one with the newer one - // SetItem(currIndex, item); - // return true; - // } - // //could not add or update - // return false; - // } - - // Add(item); - // return true; - // } - - // private bool TryGetValue(int key, out EntityDefinition val) - // { - // if (Dictionary != null) return Dictionary.TryGetValue(key, out val); - - // val = null; - // return false; - // } - //} - - // fixme wtf is this, why dynamics here, this is horrible !! - //private class EntityDefinition - //{ - // private readonly dynamic _entity; - // private readonly bool _isContent; - // private readonly bool _isMedia; - - // public EntityDefinition(dynamic entity, bool isContent, bool isMedia) - // { - // _entity = entity; - // _isContent = isContent; - // _isMedia = isMedia; - // } - - // public IUmbracoEntity BuildFromDynamic() - // { - // return BuildEntityFromDynamic(_entity); - // } - - // public int Id => _entity.id; - - // public int VersionId - // { - // get - // { - // if (_isContent || _isMedia) - // { - // return _entity.versionId; - // } - // return _entity.id; - // } - // } - //} + // ReSharper disable once ClassNeverInstantiated.Local + /// + /// the DTO corresponding to fields selected by GetBase + /// + private class BaseDto + { + // ReSharper disable UnusedAutoPropertyAccessor.Local + // ReSharper disable UnusedMember.Local + public int NodeId { get; set; } + public bool Trashed { get; set; } + public int ParentId { get; set; } + public int? UserId { get; set; } + public int Level { get; set; } + public string Path { get; set; } + public int SortOrder { get; set; } + public Guid UniqueId { get; set; } + public string Text { get; set; } + public Guid NodeObjectType { get; set; } + public DateTime CreateDate { get; set; } + public int Children { get; set; } + public int VersionId { get; set; } + public string Alias { get; set; } + public string Icon { get; set; } + public string Thumbnail { get; set; } + public bool IsContainer { get; set; } + // ReSharper restore UnusedAutoPropertyAccessor.Local + // ReSharper restore UnusedMember.Local + } #endregion #region Factory @@ -879,44 +632,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private DocumentEntitySlim BuildDocumentEntity(BaseDto dto) { + // EntitySlim does not track changes + var entity = new DocumentEntitySlim(); + BuildContentEntity(entity, dto); + if (dto is ContentEntityDto contentDto) { - return BuildDocumentEntity(contentDto); + // fill in the invariant info + entity.Edited = contentDto.Edited; + entity.Published = contentDto.Published; + entity.Variations = contentDto.Variations; } - // EntitySlim does not track changes - var entity = new DocumentEntitySlim(); - BuildContentEntity(entity, dto); - return entity; - } - - /// - /// Builds the from a and ensures the AdditionalData is populated with variant info - /// - /// - /// - private DocumentEntitySlim BuildDocumentEntity(ContentEntityDto dto) - { - // EntitySlim does not track changes - var entity = new DocumentEntitySlim(); - BuildContentEntity(entity, dto); - - //fixme we need to set these statuses for each variant, see notes in IDocumentEntitySlim - entity.Edited = dto.Edited; - entity.Published = dto.Published; - - if (dto.Variations.VariesByCulture() && dto.VariationInfo != null && dto.VariationInfo.Count > 0) - { - var variantInfo = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var info in dto.VariationInfo) - { - var isoCode = _langRepository.GetIsoCodeById(info.LanguageId); - if (isoCode != null) - variantInfo[isoCode] = info.Name; - } - entity.CultureNames = variantInfo; - entity.Variations = dto.Variations; - } return entity; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 740015683a..2b3674700b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -54,11 +54,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // get languages var languages = Database.Fetch(sql).Select(ConvertFromDto).OrderBy(x => x.Id).ToList(); - // fix inconsistencies: there has to be a default language, and it has to be mandatory - var defaultLanguage = languages.FirstOrDefault(x => x.IsDefaultVariantLanguage) ?? languages.First(); - defaultLanguage.IsDefaultVariantLanguage = true; - defaultLanguage.Mandatory = true; - // initialize the code-id map lock (_codeIdMap) { @@ -79,7 +74,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - return Database.Fetch(sql).Select(ConvertFromDto); + var dtos = Database.Fetch(sql); + return dtos.Select(ConvertFromDto).ToList(); } #endregion @@ -120,10 +116,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return list; } - protected override Guid NodeObjectTypeId - { - get { throw new NotImplementedException(); } - } + protected override Guid NodeObjectTypeId => throw new NotImplementedException(); #endregion @@ -131,70 +124,95 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistNewItem(ILanguage entity) { - if (entity.IsoCode.IsNullOrWhiteSpace() || entity.CultureInfo == null || entity.CultureName.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The required language data is missing"); + // validate iso code and culture name + if (entity.IsoCode.IsNullOrWhiteSpace() || entity.CultureName.IsNullOrWhiteSpace()) + throw new InvalidOperationException("Cannot save a language without an ISO code and a culture name."); - ((EntityBase)entity).AddingEntity(); + ((EntityBase) entity).AddingEntity(); - if (entity.IsDefaultVariantLanguage) + // deal with entity becoming the new default entity + if (entity.IsDefault) { - //if this entity is flagged as the default, we need to set all others to false - Database.Execute(Sql().Update(u => u.Set(x => x.IsDefaultVariantLanguage, false))); - //We need to clear the whole cache since all languages will be updated - IsolatedCache.ClearAllCache(); + // set all other entities to non-default + // safe (no race cond) because the service locks languages + var setAllDefaultToFalse = Sql() + .Update(u => u.Set(x => x.IsDefault, false)); + Database.Execute(setAllDefaultToFalse); } -; - var dto = LanguageFactory.BuildDto(entity); + // fallback cycles are detected at service level + // insert + var dto = LanguageFactory.BuildDto(entity); var id = Convert.ToInt32(Database.Insert(dto)); entity.Id = id; - entity.ResetDirtyProperties(); - } protected override void PersistUpdatedItem(ILanguage entity) { - if (entity.IsoCode.IsNullOrWhiteSpace() || entity.CultureInfo == null || entity.CultureName.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The required language data is missing"); + // validate iso code and culture name + if (entity.IsoCode.IsNullOrWhiteSpace() || entity.CultureName.IsNullOrWhiteSpace()) + throw new InvalidOperationException("Cannot save a language without an ISO code and a culture name."); - ((EntityBase)entity).UpdatingEntity(); + ((EntityBase) entity).UpdatingEntity(); - if (entity.IsDefaultVariantLanguage) + if (entity.IsDefault) { - //if this entity is flagged as the default, we need to set all others to false - Database.Execute(Sql().Update(u => u.Set(x => x.IsDefaultVariantLanguage, false))); - //We need to clear the whole cache since all languages will be updated - IsolatedCache.ClearAllCache(); + // deal with entity becoming the new default entity + + // set all other entities to non-default + // safe (no race cond) because the service locks languages + var setAllDefaultToFalse = Sql() + .Update(u => u.Set(x => x.IsDefault, false)); + Database.Execute(setAllDefaultToFalse); } - + else + { + // deal with the entity not being default anymore + // which is illegal - another entity has to become default + var selectDefaultId = Sql() + .Select(x => x.Id) + .From() + .Where(x => x.IsDefault); + + var defaultId = Database.ExecuteScalar(selectDefaultId); + if (entity.Id == defaultId) + throw new InvalidOperationException($"Cannot save the default language ({entity.IsoCode}) as non-default. Make another language the default language instead."); + } + + // fallback cycles are detected at service level + + // update var dto = LanguageFactory.BuildDto(entity); - Database.Update(dto); - entity.ResetDirtyProperties(); - - //Clear the cache entries that exist by key/iso - IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.IsoCode)); - IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.CultureName)); } protected override void PersistDeletedItem(ILanguage entity) { - //we need to validate that we can delete this language - if (entity.IsDefaultVariantLanguage) - throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); + // validate that the entity is not the default language. + // safe (no race cond) because the service locks languages - var count = Database.ExecuteScalar(Sql().SelectCount().From()); - if (count == 1) - throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); + var selectDefaultId = Sql() + .Select(x => x.Id) + .From() + .Where(x => x.IsDefault); + var defaultId = Database.ExecuteScalar(selectDefaultId); + if (entity.Id == defaultId) + throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})."); + + // We need to remove any references to the language if it's being used as a fall-back from other ones + var clearFallbackLanguage = Sql() + .Update(u => u + .Set(x => x.FallbackLanguageId, null)) + .Where(x => x.FallbackLanguageId == entity.Id); + + Database.Execute(clearFallbackLanguage); + + // delete base.PersistDeletedItem(entity); - - //Clear the cache entries that exist by key/iso - IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.IsoCode)); - IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.CultureName)); } #endregion @@ -204,7 +222,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var entity = LanguageFactory.BuildEntity(dto); return entity; } - + public ILanguage GetByIsoCode(string isoCode) { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way @@ -262,17 +280,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private ILanguage GetDefault() { // get all cached, non-cloned - var all = TypedCachePolicy.GetAllCached(PerformGetAll); + var languages = TypedCachePolicy.GetAllCached(PerformGetAll).ToList(); + var language = languages.FirstOrDefault(x => x.IsDefault); + if (language != null) return language; + + // this is an anomaly, the service/repo should ensure it cannot happen + Logger.Warn("There is no default language. Fix this anomaly by editing the language table in database and setting one language as the default language."); + + // still, don't kill the site, and return "something" ILanguage first = null; - foreach (var language in all) + foreach (var l in languages) { - // if one language is default, return - if (language.IsDefaultVariantLanguage) - return language; - // keep track of language with lowest id - if (first == null || language.Id < first.Id) - first = language; + if (first == null || l.Id < first.Id) + first = l; } return first; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index 982a5bb885..2390ce9a7b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Scoping; +using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -136,7 +137,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { var list = new List { - "DELETE FROM " + Constants.DatabaseSchema.Tables.Task + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2NodePermission + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", @@ -455,11 +455,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement #endregion - /// - /// Gets paged media results. - /// - public override IEnumerable GetPage(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null) + /// + public override IEnumerable GetPage(IQuery query, + long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering) { Sql filterSql = null; @@ -471,8 +470,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } return GetPage(query, pageIndex, pageSize, out totalRecords, - x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, - filterSql); + x => MapDtosToContent(x), + filterSql, + ordering); } private IEnumerable MapDtosToContent(List dtos, bool withCache = false) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 98c38603b1..ee6727a32e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Scoping; +using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -492,8 +493,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// /// Gets paged member results. /// - public override IEnumerable GetPage(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null) + public override IEnumerable GetPage(IQuery query, + long pageIndex, int pageSize, out long totalRecords, + IQuery filter, + Ordering ordering) { Sql filterSql = null; @@ -505,8 +508,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } return GetPage(query, pageIndex, pageSize, out totalRecords, - x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, - filterSql); + x => MapDtosToContent(x), + filterSql, + ordering); } private string _pagedResultsByQueryWhere; @@ -523,20 +527,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return _pagedResultsByQueryWhere; } - protected override string GetDatabaseFieldNameForOrderBy(string orderBy) + protected override string ApplySystemOrdering(ref Sql sql, Ordering ordering) { - //Some custom ones - switch (orderBy.ToUpperInvariant()) - { - case "EMAIL": - return GetDatabaseFieldNameForOrderBy("cmsMember", "email"); - case "LOGINNAME": - return GetDatabaseFieldNameForOrderBy("cmsMember", "loginName"); - case "USERNAME": - return GetDatabaseFieldNameForOrderBy("cmsMember", "loginName"); - } + if (ordering.OrderBy.InvariantEquals("email")) + return SqlSyntax.GetFieldName(x => x.Email); - return base.GetDatabaseFieldNameForOrderBy(orderBy); + if (ordering.OrderBy.InvariantEquals("loginName")) + return SqlSyntax.GetFieldName(x => x.LoginName); + + if (ordering.OrderBy.InvariantEquals("userName")) + return SqlSyntax.GetFieldName(x => x.LoginName); + + return base.ApplySystemOrdering(ref sql, ordering); } private IEnumerable MapDtosToContent(List dtos, bool withCache = false) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TaskRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TaskRepository.cs deleted file mode 100644 index 1b44fd8cc8..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TaskRepository.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NPoco; -using Umbraco.Core.Cache; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Dtos; -using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Persistence.Repositories.Implement -{ - internal class TaskRepository : NPocoRepositoryBase, ITaskRepository - { - public TaskRepository(IScopeAccessor scopeAccessor, ILogger logger) - : base(scopeAccessor, CacheHelper.NoCache, logger) - { } - - protected override Task PerformGet(int id) - { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { id = id }); - - var taskDto = Database.Fetch(SqlContext.SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); - if (taskDto == null) - return null; - - var entity = TaskFactory.BuildEntity(taskDto); - return entity; - } - - protected override IEnumerable PerformGetAll(params int[] ids) - { - var sql = GetBaseQuery(false); - - if (ids.Any()) - { - sql.Where("cmsTask.id IN (@ids)", new { ids = ids }); - } - - var dtos = Database.Fetch(sql); - return dtos.Select(TaskFactory.BuildEntity); - } - - protected override IEnumerable PerformGetByQuery(IQuery query) - { - var sqlClause = GetBaseQuery(false); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); - - var dtos = Database.Fetch(sql); - return dtos.Select(TaskFactory.BuildEntity); - } - - protected override Sql GetBaseQuery(bool isCount) - { - return isCount ? SqlContext.Sql().SelectCount().From() : GetBaseQuery(); - } - - private Sql GetBaseQuery() - { - return SqlContext.Sql() - .Select("cmsTask.closed,cmsTask.id,cmsTask.taskTypeId,cmsTask.nodeId,cmsTask.parentUserId,cmsTask.userId,cmsTask." + SqlContext.SqlSyntax.GetQuotedColumnName("DateTime") + ",cmsTask.Comment,cmsTaskType.id, cmsTaskType.alias") - .From() - .InnerJoin() - .On(left => left.TaskTypeId, right => right.Id) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId); - } - - protected override string GetBaseWhereClause() - { - return "cmsTask.id = @id"; - } - - protected override IEnumerable GetDeleteClauses() - { - var list = new List - { - "DELETE FROM cmsTask WHERE id = @id" - }; - return list; - } - - protected override Guid NodeObjectTypeId => throw new WontImplementException(); - - protected override void PersistNewItem(Task entity) - { - entity.AddingEntity(); - - //ensure the task type exists - var taskType = Database.SingleOrDefault("Where alias = @alias", new { alias = entity.TaskType.Alias }); - if (taskType == null) - { - var taskTypeId = Convert.ToInt32(Database.Insert(new TaskTypeDto { Alias = entity.TaskType.Alias })); - entity.TaskType.Id = taskTypeId; - } - else - { - entity.TaskType.Id = taskType.Id; - } - - var dto = TaskFactory.BuildDto(entity); - - var id = Convert.ToInt32(Database.Insert(dto)); - entity.Id = id; - - entity.ResetDirtyProperties(); - } - - protected override void PersistUpdatedItem(Task entity) - { - entity.UpdatingEntity(); - - var dto = TaskFactory.BuildDto(entity); - - Database.Update(dto); - - entity.ResetDirtyProperties(); - } - - public IEnumerable GetTasks(int? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false) - { - var sql = GetGetTasksQuery(assignedUser, ownerUser, taskTypeAlias, includeClosed); - if (itemId.HasValue) - { - sql.Where(dto => dto.NodeId == itemId.Value); - } - - var dtos = Database.Fetch(sql); - - return dtos.Select(TaskFactory.BuildEntity); - } - - private Sql GetGetTasksQuery(int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false) - { - var sql = GetBaseQuery(false); - - if (includeClosed == false) - { - sql.Where(dto => dto.Closed == false); - } - if (taskTypeAlias.IsNullOrWhiteSpace() == false) - { - sql.Where("cmsTaskType.alias = @alias", new { alias = taskTypeAlias }); - } - if (ownerUser.HasValue) - { - sql.Where(dto => dto.ParentUserId == ownerUser.Value); - } - if (assignedUser.HasValue) - { - sql.Where(dto => dto.UserId == assignedUser.Value); - } - return sql; - } - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TaskTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TaskTypeRepository.cs deleted file mode 100644 index ada49b4ec7..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TaskTypeRepository.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NPoco; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Dtos; -using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Persistence.Repositories.Implement -{ - internal class TaskTypeRepository : NPocoRepositoryBase, ITaskTypeRepository - { - public TaskTypeRepository(IScopeAccessor scopeAccessor, ILogger logger) - : base(scopeAccessor, CacheHelper.NoCache, logger) - { } - - protected override TaskType PerformGet(int id) - { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - - var taskDto = Database.Fetch(SqlContext.SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); - if (taskDto == null) - return null; - - var entity = TaskTypeFactory.BuildEntity(taskDto); - return entity; - } - - protected override IEnumerable PerformGetAll(params int[] ids) - { - var sql = GetBaseQuery(false); - - if (ids.Any()) - { - sql.Where("cmsTaskType.id IN (@ids)", new { ids }); - } - - var dtos = Database.Fetch(sql); - return dtos.Select(TaskTypeFactory.BuildEntity); - } - - protected override IEnumerable PerformGetByQuery(IQuery query) - { - var sqlClause = GetBaseQuery(false); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); - - var dtos = Database.Fetch(sql); - return dtos.Select(TaskTypeFactory.BuildEntity); - } - - protected override Sql GetBaseQuery(bool isCount) - { - return SqlContext.Sql().SelectAll().From(); - } - - protected override string GetBaseWhereClause() - { - return "cmsTaskType.id = @id"; - } - - protected override IEnumerable GetDeleteClauses() - { - var list = new List - { - "DELETE FROM cmsTask WHERE taskTypeId = @id", - "DELETE FROM cmsTaskType WHERE id = @id" - }; - return list; - } - - protected override Guid NodeObjectTypeId => throw new NotImplementedException(); - - protected override void PersistNewItem(TaskType entity) - { - entity.AddingEntity(); - - //TODO: Just remove the task type db table or add a unique index to the alias - - //ensure the task type exists - var taskType = Database.SingleOrDefault("Where alias = @alias", new { alias = entity.Alias }); - if (taskType != null) - { - throw new InvalidOperationException("A task type already exists with the given alias " + entity.Alias); - } - - var dto = TaskTypeFactory.BuildDto(entity); - - var id = Convert.ToInt32(Database.Insert(dto)); - entity.Id = id; - - entity.ResetDirtyProperties(); - } - - protected override void PersistUpdatedItem(TaskType entity) - { - entity.UpdatingEntity(); - - var dto = TaskTypeFactory.BuildDto(entity); - - Database.Update(dto); - - entity.ResetDirtyProperties(); - } - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs index ba4e22de41..6a2c81c6b5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs @@ -629,72 +629,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } } - /// - /// Returns a template as a template node which can be traversed (parent, children) - /// - /// - /// - [Obsolete("Use GetDescendants instead")] - public TemplateNode GetTemplateNode(string alias) - { - //first get all template objects - var allTemplates = base.GetMany().ToArray(); - - var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias.InvariantEquals(alias)); - if (selfTemplate == null) - { - return null; - } - - var top = selfTemplate; - while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false) - { - top = allTemplates.Single(x => x.Alias.InvariantEquals(top.MasterTemplateAlias)); - } - - var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.Id)); - var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(top.Alias)); - //This now creates the hierarchy recursively - topNode.Children = CreateChildren(topNode, childTemplates, allTemplates); - - //now we'll return the TemplateNode requested - return FindTemplateInTree(topNode, alias); - } - - [Obsolete("Only used by obsolete code")] - private static TemplateNode WalkTree(TemplateNode current, string alias) - { - //now walk the tree to find the node - if (current.Template.Alias.InvariantEquals(alias)) - { - return current; - } - foreach (var c in current.Children) - { - var found = WalkTree(c, alias); - if (found != null) return found; - } - return null; - } - - /// - /// Given a template node in a tree, this will find the template node with the given alias if it is found in the hierarchy, otherwise null - /// - /// - /// - /// - [Obsolete("Use GetDescendants instead")] - public TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias) - { - //first get the root - var top = anyNode; - while (top.Parent != null) - { - top = top.Parent; - } - return WalkTree(top, alias); - } - /// /// This checks what the default rendering engine is set in config but then also ensures that there isn't already /// a template that exists in the opposite rendering engine's template folder, then returns the appropriate diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs index e15560264a..853b716242 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs @@ -748,7 +748,7 @@ ORDER BY colName"; if (excludeUserGroups != null && excludeUserGroups.Length > 0) { - var subQuery = @"AND (umbracoUser.id NOT IN (SELECT DISTINCT umbracoUser.id + const string subQuery = @"AND (umbracoUser.id NOT IN (SELECT DISTINCT umbracoUser.id FROM umbracoUser INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId @@ -809,7 +809,7 @@ ORDER BY colName"; sql = new SqlTranslator(sql, query).Translate(); // get sorted and filtered sql - var sqlNodeIdsWithSort = ApplySort(ApplyFilter(sql, filterSql), orderDirection, orderBy); + var sqlNodeIdsWithSort = ApplySort(ApplyFilter(sql, filterSql, query != null), orderDirection, orderBy); // get a page of results and total count var pagedResult = Database.Page(pageIndex + 1, pageSize, sqlNodeIdsWithSort); @@ -820,11 +820,17 @@ ORDER BY colName"; return pagedResult.Items.Select(UserFactory.BuildEntity); } - private Sql ApplyFilter(Sql sql, Sql filterSql) + private Sql ApplyFilter(Sql sql, Sql filterSql, bool hasWhereClause) { if (filterSql == null) return sql; - sql.Append(SqlContext.Sql(" WHERE " + filterSql.SQL.TrimStart("AND "), filterSql.Arguments)); + //ensure we don't append a WHERE if there is already one + var args = filterSql.Arguments; + var sqlFilter = hasWhereClause + ? filterSql.SQL + : " WHERE " + filterSql.SQL.TrimStart("AND "); + + sql.Append(SqlContext.Sql(sqlFilter, args)); return sql; } diff --git a/src/Umbraco.Core/Persistence/SqlContextExtensions.cs b/src/Umbraco.Core/Persistence/SqlContextExtensions.cs new file mode 100644 index 0000000000..e28816b6a4 --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlContextExtensions.cs @@ -0,0 +1,78 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.Persistence.Querying; + +namespace Umbraco.Core.Persistence +{ + /// + /// Provides extension methods to . + /// + public static class SqlContextExtensions + { + /// + /// Visit an expression. + /// + /// The type of the DTO. + /// An . + /// An expression to visit. + /// An optional table alias. + /// A SQL statement, and arguments, corresponding to the expression. + public static (string Sql, object[] Args) Visit(this ISqlContext sqlContext, Expression> expression, string alias = null) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlContext, alias); + var visited = expresionist.Visit(expression); + return (visited, expresionist.GetSqlParameters()); + } + + /// + /// Visit an expression. + /// + /// The type of the DTO. + /// The type returned by the expression. + /// An . + /// An expression to visit. + /// An optional table alias. + /// A SQL statement, and arguments, corresponding to the expression. + public static (string Sql, object[] Args) Visit(this ISqlContext sqlContext, Expression> expression, string alias = null) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlContext, alias); + var visited = expresionist.Visit(expression); + return (visited, expresionist.GetSqlParameters()); + } + + /// + /// Visit an expression. + /// + /// The type of the first DTO. + /// The type of the second DTO. + /// An . + /// An expression to visit. + /// An optional table alias for the first DTO. + /// An optional table alias for the second DTO. + /// A SQL statement, and arguments, corresponding to the expression. + public static (string Sql, object[] Args) Visit(this ISqlContext sqlContext, Expression> expression, string alias1 = null, string alias2 = null) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlContext, alias1, alias2); + var visited = expresionist.Visit(expression); + return (visited, expresionist.GetSqlParameters()); + } + + /// + /// Visit an expression. + /// + /// The type of the first DTO. + /// The type of the second DTO. + /// The type returned by the expression. + /// An . + /// An expression to visit. + /// An optional table alias for the first DTO. + /// An optional table alias for the second DTO. + /// A SQL statement, and arguments, corresponding to the expression. + public static (string Sql, object[] Args) Visit(this ISqlContext sqlContext, Expression> expression, string alias1 = null, string alias2 = null) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlContext, alias1, alias2); + var visited = expresionist.Visit(expression); + return (visited, expresionist.GetSqlParameters()); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index af58604bf0..98c57644aa 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -44,9 +44,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax string TruncateTable { get; } string CreateConstraint { get; } string DeleteConstraint { get; } - - [Obsolete("This is never used, use the Format(ForeignKeyDefinition) instead")] - string CreateForeignKeyConstraint { get; } + string DeleteDefaultConstraint { get; } string FormatDateTime(DateTime date, bool includeTime = true); string Format(TableDefinition table); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 4a94ff3ba5..90a2215e3d 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using NPoco; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -45,6 +46,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax V2012 = 5, V2014 = 6, V2016 = 7, + V2017 = 8, Other = 99 } @@ -71,37 +73,35 @@ namespace Umbraco.Core.Persistence.SqlSyntax public void Initialize() { - var firstPart = string.IsNullOrWhiteSpace(ProductVersion) ? "??" : ProductVersion.Split('.')[0]; - switch (firstPart) - { - case "??": - ProductVersionName = VersionName.Invalid; - break; - case "13": - ProductVersionName = VersionName.V2016; - break; - case "12": - ProductVersionName = VersionName.V2014; - break; - case "11": - ProductVersionName = VersionName.V2012; - break; - case "10": - ProductVersionName = VersionName.V2008; - break; - case "9": - ProductVersionName = VersionName.V2005; - break; - case "8": - ProductVersionName = VersionName.V2000; - break; - case "7": - ProductVersionName = VersionName.V7; - break; - default: - ProductVersionName = VersionName.Other; - break; - } + ProductVersionName = MapProductVersion(ProductVersion); + } + } + + private static VersionName MapProductVersion(string productVersion) + { + var firstPart = string.IsNullOrWhiteSpace(productVersion) ? "??" : productVersion.Split('.')[0]; + switch (firstPart) + { + case "??": + return VersionName.Invalid; + case "14": + return VersionName.V2017; + case "13": + return VersionName.V2016; + case "12": + return VersionName.V2014; + case "11": + return VersionName.V2012; + case "10": + return VersionName.V2008; + case "9": + return VersionName.V2005; + case "8": + return VersionName.V2000; + case "7": + return VersionName.V7; + default: + return VersionName.Other; } } @@ -136,6 +136,33 @@ namespace Umbraco.Core.Persistence.SqlSyntax } } + internal static VersionName GetVersionName(string connectionString, string providerName) + { + var factory = DbProviderFactories.GetFactory(providerName); + var connection = factory.CreateConnection(); + + if (connection == null) + throw new InvalidOperationException($"Could not create a connection for provider \"{providerName}\"."); + + connection.ConnectionString = connectionString; + using (connection) + { + try + { + connection.Open(); + var command = connection.CreateCommand(); + command.CommandText = "SELECT SERVERPROPERTY('ProductVersion');"; + var productVersion = command.ExecuteScalar().ToString(); + connection.Close(); + return MapProductVersion(productVersion); + } + catch + { + return VersionName.Unknown; + } + } + } + /// /// SQL Server stores default values assigned to columns as constraints, it also stores them with named values, this is the only /// server type that does this, therefore this method doesn't exist on any other syntax provider diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index f8c937ce8d..afab5b19f9 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -47,7 +47,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax var col = Regex.Escape(GetQuotedColumnName("column")).Replace("column", @"\w+"); var fld = Regex.Escape(GetQuotedTableName("table") + ".").Replace("table", @"\w+") + col; // ReSharper restore VirtualMemberCallInConstructor - AliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase); + AliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); } public Regex AliasRegex { get; } diff --git a/src/Umbraco.Core/Persistence/SqlSyntaxExtensions.cs b/src/Umbraco.Core/Persistence/SqlSyntaxExtensions.cs new file mode 100644 index 0000000000..43ef03327b --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntaxExtensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; +using NPoco; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence +{ + /// + /// Provides extension methods to . + /// + public static class SqlSyntaxExtensions + { + private static string GetTableName(this Type type) + { + // todo: returning string.Empty for now + // BUT the code bits that calls this method cannot deal with string.Empty so we + // should either throw, or fix these code bits... + var attr = type.FirstAttribute(); + return string.IsNullOrWhiteSpace(attr?.Value) ? string.Empty : attr.Value; + } + + private static string GetColumnName(this PropertyInfo column) + { + var attr = column.FirstAttribute(); + return string.IsNullOrWhiteSpace(attr?.Name) ? column.Name : attr.Name; + } + + /// + /// Gets a quoted table and field name. + /// + /// The type of the DTO. + /// An . + /// An expression specifying the field. + /// An optional table alias. + /// + public static string GetFieldName(this ISqlSyntaxProvider sqlSyntax, Expression> fieldSelector, string tableAlias = null) + { + var field = ExpressionHelper.FindProperty(fieldSelector).Item1 as PropertyInfo; + var fieldName = field.GetColumnName(); + + var type = typeof(TDto); + var tableName = tableAlias ?? type.GetTableName(); + + return sqlSyntax.GetQuotedTableName(tableName) + "." + sqlSyntax.GetQuotedColumnName(fieldName); + } + } +} diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 8d2a230fec..ea597007b7 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -39,6 +39,7 @@ namespace Umbraco.Core.Persistence private string _providerName; private DbProviderFactory _dbProviderFactory; private DatabaseType _databaseType; + private bool _serverVersionDetected; private ISqlSyntaxProvider _sqlSyntax; private RetryPolicy _connectionRetryPolicy; private RetryPolicy _commandRetryPolicy; @@ -112,7 +113,56 @@ namespace Umbraco.Core.Persistence public bool Configured { get; private set; } /// - public bool CanConnect => Configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName); + public bool CanConnect + { + get + { + if (!Configured || !DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName)) return false; + + if (_serverVersionDetected) return true; + + if (_databaseType.IsSqlServer()) + DetectSqlServerVersion(); + _serverVersionDetected = true; + + return true; + } + } + + private void DetectSqlServerVersion() + { + // replace NPoco database type by a more efficient one + + var setting = ConfigurationManager.AppSettings["Umbraco.DatabaseFactory.ServerVersion"]; + var fromSettings = false; + + if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.") + || !Enum.TryParse(setting.Substring("SqlServer.".Length), out var versionName, true)) + { + versionName = SqlServerSyntaxProvider.GetVersionName(_connectionString, _providerName); + } + else + { + fromSettings = true; + } + + switch (versionName) + { + case SqlServerSyntaxProvider.VersionName.V2008: + _databaseType = DatabaseType.SqlServer2008; + break; + case SqlServerSyntaxProvider.VersionName.V2012: + case SqlServerSyntaxProvider.VersionName.V2014: + case SqlServerSyntaxProvider.VersionName.V2016: + case SqlServerSyntaxProvider.VersionName.V2017: + _databaseType = DatabaseType.SqlServer2012; + break; + // else leave unchanged + } + + _logger.Debug("SqlServer {SqlServerVersion}, DatabaseType is {DatabaseType} ({Source}).", + versionName, _databaseType, fromSettings ? "settings" : "detected"); + } /// public ISqlContext SqlContext => _sqlContext; diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs index e13419f0d6..e311c3bbfc 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs @@ -57,6 +57,9 @@ namespace Umbraco.Core.PropertyEditors [JsonProperty("defaultConfig")] public virtual IDictionary DefaultConfiguration => new Dictionary(); + /// + public virtual object DefaultConfigurationObject => DefaultConfiguration; + /// public virtual bool IsConfiguration(object obj) => obj is IDictionary; @@ -97,7 +100,7 @@ namespace Umbraco.Core.PropertyEditors // clone the default configuration, and apply the current configuration values var d = new Dictionary(DefaultConfiguration); - foreach ((var key, var value) in c) + foreach (var (key, value) in c) d[key] = value; return d; } diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs index 2145a580e4..cf007e681d 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs @@ -94,7 +94,10 @@ namespace Umbraco.Core.PropertyEditors } /// - public override IDictionary DefaultConfiguration => ToConfigurationEditor(new TConfiguration()); + public override IDictionary DefaultConfiguration => ToConfigurationEditor(DefaultConfigurationObject); + + /// + public override object DefaultConfigurationObject => new TConfiguration(); /// public override bool IsConfiguration(object obj) diff --git a/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs index cd8c8be030..003fe9a80e 100644 --- a/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs @@ -15,8 +15,24 @@ namespace Umbraco.Core.PropertyEditors /// /// Gets the default configuration. /// + /// + /// For basic configuration editors, this will be a dictionary of key/values. For advanced editors + /// which inherit from , this will be the dictionary + /// equivalent of an actual configuration object (ie an instance of TConfiguration, obtained + /// via . + /// IDictionary DefaultConfiguration { get; } + /// + /// Gets the default configuration object. + /// + /// + /// For basic configuration editors, this will be , ie a + /// dictionary of key/values. For advanced editors which inherit from , + /// this will be an actual configuration object (ie an instance of TConfiguration. + /// + object DefaultConfigurationObject { get; } + /// /// Determines whether a configuration object is of the type expected by the configuration editor. /// @@ -48,7 +64,7 @@ namespace Umbraco.Core.PropertyEditors IDictionary ToConfigurationEditor(object configuration); /// - /// Converts the configuration object to values for the value editror. + /// Converts the configuration object to values for the value editor. /// /// The configuration. IDictionary ToValueEditor(object configuration); diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 3e86003b9d..0ad621b97c 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -361,7 +361,7 @@ namespace Umbraco.Core.Runtime _state.CurrentMigrationState = state; _state.FinalMigrationState = umbracoPlan.FinalState; - logger.Debug("Final upgrade state is '{FinalMigrationState}', database contains {DatabaseState}", _state.FinalMigrationState, state ?? ""); + logger.Debug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", _state.FinalMigrationState, state ?? ""); return state == _state.FinalMigrationState; } diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index d09bd95081..0177619a68 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core var change = url != null && !_applicationUrls.Contains(url); if (change) { - _logger.Info(typeof(ApplicationUrlHelper), "New url '{Url}' detected, re-discovering application url.", url); + _logger.Info(typeof(ApplicationUrlHelper), "New url {Url} detected, re-discovering application url.", url); _applicationUrls.Add(url); } diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 3b497bff86..b65ab83439 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Globalization; using System.Linq; using System.Security.Claims; using System.Security.Principal; @@ -63,5 +65,6 @@ namespace Umbraco.Core.Security /// Used so that we aren't creating a new CultureInfo object for every single request /// private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); + } } diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index 63970aca8f..2dc14c727f 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -635,7 +635,9 @@ namespace Umbraco.Core.Security || identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value) { anythingChanged = true; - user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime(); + //if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime + var dt = identityUser.LastLoginDateUtc == DateTime.MinValue ? DateTime.MinValue : identityUser.LastLoginDateUtc.Value.ToLocalTime(); + user.LastLoginDate = dt; } if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc") || (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 2e788382fe..022bee8b41 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -166,31 +166,28 @@ namespace Umbraco.Core.Services IEnumerable GetContentInRecycleBin(); /// - /// Gets child documents of a given parent. + /// Gets child documents of a parent. /// /// The parent identifier. /// The page number. /// The page size. /// Total number of documents. - /// A field to order by. - /// The ordering direction. /// Search text filter. + /// Ordering infos. IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// - /// Gets child documents of a given parent. + /// Gets child documents of a parent. /// /// The parent identifier. /// The page number. /// The page size. /// Total number of documents. - /// A field to order by. - /// The ordering direction. - /// A flag indicating whether the ordering field is a system field. /// Query filter. + /// Ordering infos. IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); + IQuery filter, Ordering ordering = null); /// /// Gets descendant documents of a given parent. diff --git a/src/Umbraco.Core/Services/ITaskService.cs b/src/Umbraco.Core/Services/ITaskService.cs deleted file mode 100644 index 2fd37777ad..0000000000 --- a/src/Umbraco.Core/Services/ITaskService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - public interface ITaskService : IService - { - TaskType GetTaskTypeByAlias(string taskTypeAlias); - TaskType GetTaskTypeById(int id); - void Save(TaskType taskType); - void Delete(TaskType taskTypeEntity); - - //IEnumerable GetTasks(Guid? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false); - IEnumerable GetTasks(int? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false); - - /// - /// Saves a task - /// - /// - void Save(Task task); - void Delete(Task task); - - IEnumerable GetAllTaskTypes(); - Task GetTaskById(int id); - } -} diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 0bb00ef5a4..a849813b13 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -553,97 +553,47 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string orderBy, Direction orderDirection, string filter = "") + string filter = null, Ordering ordering = null) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); + var filterQuery = filter.IsNullOrWhiteSpace() + ? null + : Query().Where(x => x.Name.Contains(filter)); - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); - } + return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects + /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + IQuery filter, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (ordering == null) + ordering = Ordering.By("sortOrder"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); - var query = Query(); - //if the id is System Root, then just get all - NO! does not make sense! - //if (id != Constants.System.Root) - query.Where(x => x.ParentId == id); - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + var query = Query().Where(x => x.ParentId == id); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); + var filterQuery = filter.IsNullOrWhiteSpace() + ? null + : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); - } + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// Search filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); @@ -654,6 +604,7 @@ namespace Umbraco.Core.Services.Implement scope.ReadLock(Constants.Locks.ContentTree); var query = Query(); + //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -665,7 +616,8 @@ namespace Umbraco.Core.Services.Implement } query.Where(x => x.Path.SqlStartsWith($"{contentPath[0].Path},", TextColumnType.NVarchar)); } - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } } @@ -1036,14 +988,14 @@ namespace Umbraco.Core.Services.Implement UnpublishResultType result; if (culture == "*" || culture == null) { - Audit(AuditType.UnPublish, "Unpublished by user", userId, content.Id); + Audit(AuditType.Unpublish, "Unpublished by user", userId, content.Id); result = UnpublishResultType.Success; } else { - Audit(AuditType.UnPublish, $"Culture \"{culture}\" unpublished by user", userId, content.Id); + Audit(AuditType.Unpublish, $"Culture \"{culture}\" unpublished by user", userId, content.Id); if (!content.Published) - Audit(AuditType.UnPublish, $"Unpublished (culture \"{culture}\" is mandatory) by user", userId, content.Id); + Audit(AuditType.Unpublish, $"Unpublished (culture \"{culture}\" is mandatory) by user", userId, content.Id); result = content.Published ? UnpublishResultType.SuccessCulture : UnpublishResultType.SuccessMandatoryCulture; } scope.Complete(); @@ -1082,7 +1034,7 @@ namespace Umbraco.Core.Services.Implement var cannotBePublished = publishedCultures.Count == 0; // no published cultures = cannot be published if (!cannotBePublished) { - var mandatoryCultures = _languageRepository.GetMany().Where(x => x.Mandatory).Select(x => x.IsoCode); + var mandatoryCultures = _languageRepository.GetMany().Where(x => x.IsMandatory).Select(x => x.IsoCode); cannotBePublished = mandatoryCultures.Any(x => !publishedCultures.Contains(x, StringComparer.OrdinalIgnoreCase)); // missing mandatory culture = cannot be published } @@ -1168,9 +1120,9 @@ namespace Umbraco.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - scope.Events.Dispatch(UnPublished, this, new PublishEventArgs(content, false, false), "UnPublished"); + scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), "Unpublished"); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); - Audit(AuditType.UnPublish, "Unpublished by user", userId, content.Id); + Audit(AuditType.Unpublish, "Unpublished by user", userId, content.Id); scope.Complete(); return new PublishResult(PublishResultType.Success, evtMsgs, content); } @@ -1396,10 +1348,10 @@ namespace Umbraco.Core.Services.Implement scope.WriteLock(Constants.Locks.ContentTree); // if it's not trashed yet, and published, we should unpublish - // but... UnPublishing event makes no sense (not going to cancel?) and no need to save + // but... Unpublishing event makes no sense (not going to cancel?) and no need to save // just raise the event if (content.Trashed == false && content.Published) - scope.Events.Dispatch(UnPublished, this, new PublishEventArgs(content, false, false), nameof(UnPublished)); + scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), nameof(Unpublished)); DeleteLocked(scope, content); @@ -2146,12 +2098,12 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs before unpublish /// - public static event TypedEventHandler> UnPublishing; + public static event TypedEventHandler> Unpublishing; /// /// Occurs after unpublish /// - public static event TypedEventHandler> UnPublished; + public static event TypedEventHandler> Unpublished; /// /// Occurs after change. @@ -2178,7 +2130,7 @@ namespace Umbraco.Core.Services.Implement // raise Publishing event if (scope.Events.DispatchCancelable(Publishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); } @@ -2186,7 +2138,7 @@ namespace Umbraco.Core.Services.Implement // either because it is 'publishing' or because it already has a published version if (((Content) content).PublishedState != PublishedState.Publishing && content.PublishedVersionId == 0) { - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document does not have published values"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document does not have published values"); return new PublishResult(PublishResultType.FailedNoPublishedValues, evtMsgs, content); } @@ -2194,15 +2146,15 @@ namespace Umbraco.Core.Services.Implement switch (content.Status) { case ContentStatus.Expired: - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document has expired"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document has expired"); return new PublishResult(PublishResultType.FailedHasExpired, evtMsgs, content); case ContentStatus.AwaitingRelease: - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is awaiting release"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is awaiting release"); return new PublishResult(PublishResultType.FailedAwaitingRelease, evtMsgs, content); case ContentStatus.Trashed: - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is trashed"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is trashed"); return new PublishResult(PublishResultType.FailedIsTrashed, evtMsgs, content); } @@ -2214,7 +2166,7 @@ namespace Umbraco.Core.Services.Implement var pathIsOk = content.ParentId == Constants.System.Root || IsPathPublished(GetParent(content)); if (pathIsOk == false) { - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "parent is not published"); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "parent is not published"); return new PublishResult(PublishResultType.FailedPathNotPublished, evtMsgs, content); } @@ -2238,17 +2190,17 @@ namespace Umbraco.Core.Services.Implement // change state to publishing ((Content) content).PublishedState = PublishedState.Publishing; - Logger.Info("Document '{ContentName}' (id={ContentId}) has been published.", content.Name, content.Id); + Logger.Info("Document {ContentName} (id={ContentId}) has been published.", content.Name, content.Id); return result; } // ensures that a document can be unpublished internal UnpublishResult StrategyCanUnpublish(IScope scope, IContent content, int userId, EventMessages evtMsgs) { - // raise UnPublishing event - if (scope.Events.DispatchCancelable(UnPublishing, this, new PublishEventArgs(content, evtMsgs))) + // raise Unpublishing event + if (scope.Events.DispatchCancelable(Unpublishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); + Logger.Info("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new UnpublishResult(UnpublishResultType.FailedCancelledByEvent, evtMsgs, content); } @@ -2271,13 +2223,13 @@ namespace Umbraco.Core.Services.Implement if (content.ReleaseDate.HasValue && content.ReleaseDate.Value <= DateTime.Now) { content.ReleaseDate = null; - Logger.Info("Document '{ContentName}' (id={ContentId}) had its release date removed, because it was unpublished.", content.Name, content.Id); + Logger.Info("Document {ContentName} (id={ContentId}) had its release date removed, because it was unpublished.", content.Name, content.Id); } // change state to unpublishing ((Content) content).PublishedState = PublishedState.Unpublishing; - Logger.Info("Document '{ContentName}' (id={ContentId}) has been unpublished.", content.Name, content.Id); + Logger.Info("Document {ContentName} (id={ContentId}) has been unpublished.", content.Name, content.Id); return attempt; } @@ -2330,10 +2282,10 @@ namespace Umbraco.Core.Services.Implement foreach (var content in contents.OrderByDescending(x => x.ParentId)) { // if it's not trashed yet, and published, we should unpublish - // but... UnPublishing event makes no sense (not going to cancel?) and no need to save + // but... Unpublishing event makes no sense (not going to cancel?) and no need to save // just raise the event if (content.Trashed == false && content.Published) - scope.Events.Dispatch(UnPublished, this, new PublishEventArgs(content, false, false), nameof(UnPublished)); + scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), nameof(Unpublished)); // if current content has children, move them to trash var c = content; diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index 663ecf586c..49a764b533 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -360,6 +360,19 @@ namespace Umbraco.Core.Services.Implement { using (var scope = ScopeProvider.CreateScope()) { + // write-lock languages to guard against race conds when dealing with default language + scope.WriteLock(Constants.Locks.Languages); + + // look for cycles - within write-lock + if (language.FallbackLanguageId.HasValue) + { + var languages = _languageRepository.GetMany().ToDictionary(x => x.Id, x => x); + if (!languages.ContainsKey(language.FallbackLanguageId.Value)) + throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback id={language.FallbackLanguageId.Value} which is not a valid language id."); + if (CreatesCycle(language, languages)) + throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback {languages[language.FallbackLanguageId.Value].IsoCode} as it would create a fallback cycle."); + } + var saveEventArgs = new SaveEventArgs(language); if (scope.Events.DispatchCancelable(SavingLanguage, this, saveEventArgs)) { @@ -377,6 +390,20 @@ namespace Umbraco.Core.Services.Implement } } + private bool CreatesCycle(ILanguage language, IDictionary languages) + { + // a new language is not referenced yet, so cannot be part of a cycle + if (!language.HasIdentity) return false; + + var id = language.FallbackLanguageId; + while (true) // assuming languages does not already contains a cycle, this must end + { + if (!id.HasValue) return false; // no fallback means no cycle + if (id.Value == language.Id) return true; // back to language = cycle! + id = languages[id.Value].FallbackLanguageId; // else keep chaining + } + } + /// /// Deletes a by removing it (but not its usages) from the db /// @@ -386,6 +413,9 @@ namespace Umbraco.Core.Services.Implement { using (var scope = ScopeProvider.CreateScope()) { + // write-lock languages to guard against race conds when dealing with default language + scope.WriteLock(Constants.Locks.Languages); + var deleteEventArgs = new DeleteEventArgs(language); if (scope.Events.DispatchCancelable(DeletingLanguage, this, deleteEventArgs)) { @@ -393,8 +423,7 @@ namespace Umbraco.Core.Services.Implement return; } - //NOTE: There isn't any constraints in the db, so possible references aren't deleted - + // NOTE: Other than the fall-back language, there aren't any other constraints in the db, so possible references aren't deleted _languageRepository.Delete(language); deleteEventArgs.CanCancel = false; diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 8f6a1c6000..431e20044c 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -515,14 +515,9 @@ namespace Umbraco.Core.Services.Implement using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); - var query = Query(); - //if the id is System Root, then just get all - NO! does not make sense! - //if (id != Constants.System.Root) - - query.Where(x => x.ParentId == id); - - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + var query = Query().Where(x => x.ParentId == id); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } } @@ -561,7 +556,7 @@ namespace Umbraco.Core.Services.Implement var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filterQuery); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filterQuery, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } } @@ -607,6 +602,7 @@ namespace Umbraco.Core.Services.Implement scope.ReadLock(Constants.Locks.MediaTree); var query = Query(); + //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -618,7 +614,8 @@ namespace Umbraco.Core.Services.Implement } query.Where(x => x.Path.SqlStartsWith(mediaPath[0].Path + ",", TextColumnType.NVarchar)); } - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } } diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 288809bf33..211e30d01c 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -387,7 +387,7 @@ namespace Umbraco.Core.Services.Implement using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MemberTree); - return _memberRepository.GetPage(null, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true); + return _memberRepository.GetPage(null, pageIndex, pageSize, out totalRecords, null, Ordering.By("LoginName")); } } @@ -405,7 +405,7 @@ namespace Umbraco.Core.Services.Implement scope.ReadLock(Constants.Locks.MemberTree); var query1 = memberTypeAlias == null ? null : Query().Where(x => x.ContentTypeAlias == memberTypeAlias); var query2 = filter == null ? null : Query().Where(x => x.Name.Contains(filter) || x.Username.Contains(filter) || x.Email.Contains(filter)); - return _memberRepository.GetPage(query1, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, query2); + return _memberRepository.GetPage(query1, pageIndex, pageSize, out totalRecords, query2, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } } @@ -557,7 +557,7 @@ namespace Umbraco.Core.Services.Implement throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback // causes rollback } - return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, "Name", Direction.Ascending, true); + return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, null, Ordering.By("Name")); } } @@ -598,7 +598,7 @@ namespace Umbraco.Core.Services.Implement throw new ArgumentOutOfRangeException(nameof(matchType)); } - return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, "Email", Direction.Ascending, true); + return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, null, Ordering.By("Email")); } } @@ -639,7 +639,7 @@ namespace Umbraco.Core.Services.Implement throw new ArgumentOutOfRangeException(nameof(matchType)); } - return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true); + return _memberRepository.GetPage(query, pageIndex, pageSize, out totalRecords, null, Ordering.By("LoginName")); } } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index ef22861947..67e07e63b6 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -52,6 +52,7 @@ namespace Umbraco.Core.Services.Implement private readonly IAuditRepository _auditRepository; private readonly IContentTypeRepository _contentTypeRepository; private readonly PropertyEditorCollection _propertyEditors; + private static HttpClient _httpClient; public PackagingService( ILogger logger, @@ -1441,7 +1442,6 @@ namespace Umbraco.Core.Services.Implement /// public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) { - using (var httpClient = new HttpClient()) using (var scope = _scopeProvider.CreateScope()) { //includeHidden = true because we don't care if it's hidden we want to get the file regardless @@ -1449,7 +1449,11 @@ namespace Umbraco.Core.Services.Implement byte[] bytes; try { - bytes = httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); + if (_httpClient == null) + { + _httpClient = new HttpClient(); + } + bytes = _httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); } catch (HttpRequestException ex) { diff --git a/src/Umbraco.Core/Services/Implement/ServerRegistrationService.cs b/src/Umbraco.Core/Services/Implement/ServerRegistrationService.cs index 1c6c4b353d..d9ce978274 100644 --- a/src/Umbraco.Core/Services/Implement/ServerRegistrationService.cs +++ b/src/Umbraco.Core/Services/Implement/ServerRegistrationService.cs @@ -76,11 +76,11 @@ namespace Umbraco.Core.Services.Implement // reload - cheap, cached // default role is single server, but if registrations contain more - // than one active server, then role is master or slave + // than one active server, then role is master or replica regs = _serverRegistrationRepository.GetMany().ToArray(); // default role is single server, but if registrations contain more - // than one active server, then role is master or slave + // than one active server, then role is master or replica _currentServerRole = regs.Count(x => x.IsActive) > 1 ? (server.IsMaster ? ServerRole.Master : ServerRole.Replica) : ServerRole.Single; diff --git a/src/Umbraco.Core/Services/Implement/TaskService.cs b/src/Umbraco.Core/Services/Implement/TaskService.cs deleted file mode 100644 index 74fdddedfa..0000000000 --- a/src/Umbraco.Core/Services/Implement/TaskService.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Events; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Services.Implement -{ - public class TaskService : RepositoryService, ITaskService - { - private readonly ITaskTypeRepository _taskTypeRepository; - private readonly ITaskRepository _taskRepository; - - public TaskService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, - ITaskTypeRepository taskTypeRepository, ITaskRepository taskRepository) - : base(provider, logger, eventMessagesFactory) - { - _taskTypeRepository = taskTypeRepository; - _taskRepository = taskRepository; - } - - public TaskType GetTaskTypeByAlias(string taskTypeAlias) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _taskTypeRepository.Get(Query().Where(x => x.Alias == taskTypeAlias)).FirstOrDefault(); - } - } - - public TaskType GetTaskTypeById(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _taskTypeRepository.Get(id); - } - } - - public void Save(TaskType taskType) - { - using (var scope = ScopeProvider.CreateScope()) - { - _taskTypeRepository.Save(taskType); - scope.Complete(); - } - } - - public void Delete(TaskType taskTypeEntity) - { - using (var scope = ScopeProvider.CreateScope()) - { - _taskTypeRepository.Delete(taskTypeEntity); - scope.Complete(); - } - } - - public IEnumerable GetAllTaskTypes() - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _taskTypeRepository.GetMany(); - } - } - - - public IEnumerable GetTasks(int? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _taskRepository.GetTasks(itemId, assignedUser, ownerUser, taskTypeAlias); - } - } - - /// - /// Saves a task - /// - /// - public void Save(Task task) - { - using (var scope = ScopeProvider.CreateScope()) - { - _taskRepository.Save(task); - scope.Complete(); - } - } - - public void Delete(Task task) - { - using (var scope = ScopeProvider.CreateScope()) - { - _taskRepository.Delete(task); - scope.Complete(); - } - } - - public Task GetTaskById(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _taskRepository.Get(id); - } - } - } -} diff --git a/src/Umbraco.Core/Services/Ordering.cs b/src/Umbraco.Core/Services/Ordering.cs new file mode 100644 index 0000000000..9713aa6bf5 --- /dev/null +++ b/src/Umbraco.Core/Services/Ordering.cs @@ -0,0 +1,81 @@ +using Umbraco.Core.Persistence.DatabaseModelDefinitions; + +namespace Umbraco.Core.Services +{ + /// + /// Represents ordering information. + /// + public class Ordering + { + private static readonly Ordering DefaultOrdering = new Ordering(null); + + /// + /// Initializes a new instance of the class. + /// + /// The name of the ordering field. + /// The ordering direction. + /// The (ISO) culture to consider when sorting multi-lingual fields. + /// A value indicating whether the ordering field is a custom user property. + /// + /// The can be null, meaning: not sorting. If it is the empty string, it becomes null. + /// The can be the empty string, meaning: invariant. If it is null, it becomes the empty string. + /// + public Ordering(string orderBy, Direction direction = Direction.Ascending, string culture = null, bool isCustomField = false) + { + OrderBy = orderBy.IfNullOrWhiteSpace(null); // empty is null and means, not sorting + Direction = direction; + Culture = culture.IfNullOrWhiteSpace(string.Empty); // empty is "" and means, invariant + IsCustomField = isCustomField; + } + + /// + /// Creates a new instance of the class. + /// + /// The name of the ordering field. + /// The ordering direction. + /// The (ISO) culture to consider when sorting multi-lingual fields. + /// A value indicating whether the ordering field is a custom user property. + /// + /// The can be null, meaning: not sorting. If it is the empty string, it becomes null. + /// The can be the empty string, meaning: invariant. If it is null, it becomes the empty string. + /// + public static Ordering By(string orderBy, Direction direction = Direction.Ascending, string culture = null, bool isCustomField = false) + => new Ordering(orderBy, direction, culture, isCustomField); + + /// + /// Gets the default instance. + /// + public static Ordering ByDefault() + => DefaultOrdering; + + /// + /// Gets the name of the ordering field. + /// + public string OrderBy { get; } + + /// + /// Gets the ordering direction. + /// + public Direction Direction { get; } + + /// + /// Gets (ISO) culture to consider when sorting multi-lingual fields. + /// + public string Culture { get; } + + /// + /// Gets a value indicating whether the ordering field is a custom user property. + /// + public bool IsCustomField { get; } + + /// + /// Gets a value indicating whether this ordering is the default ordering. + /// + public bool IsEmpty => this == DefaultOrdering || OrderBy == null; + + /// + /// Gets a value indicating whether the culture of this ordering is invariant. + /// + public bool IsInvariant => this == DefaultOrdering || Culture == string.Empty; + } +} diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index b14158efd5..e6df6dec81 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -8,7 +8,6 @@ namespace Umbraco.Core.Services public class ServiceContext { private readonly Lazy _publicAccessService; - private readonly Lazy _taskService; private readonly Lazy _domainService; private readonly Lazy _auditService; private readonly Lazy _localizedTextService; @@ -40,10 +39,9 @@ namespace Umbraco.Core.Services /// Initializes a new instance of the class with lazy services. /// /// Used by IoC. Note that LightInject will favor lazy args when picking a constructor. - public ServiceContext(Lazy publicAccessService, Lazy taskService, Lazy domainService, Lazy auditService, Lazy localizedTextService, Lazy tagService, Lazy contentService, Lazy userService, Lazy memberService, Lazy mediaService, Lazy contentTypeService, Lazy mediaTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy treeService, Lazy sectionService, Lazy macroService, Lazy memberTypeService, Lazy memberGroupService, Lazy notificationService, Lazy externalLoginService, Lazy redirectUrlService, Lazy consentService) + public ServiceContext(Lazy publicAccessService, Lazy domainService, Lazy auditService, Lazy localizedTextService, Lazy tagService, Lazy contentService, Lazy userService, Lazy memberService, Lazy mediaService, Lazy contentTypeService, Lazy mediaTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy treeService, Lazy sectionService, Lazy macroService, Lazy memberTypeService, Lazy memberGroupService, Lazy notificationService, Lazy externalLoginService, Lazy redirectUrlService, Lazy consentService) { _publicAccessService = publicAccessService; - _taskService = taskService; _domainService = domainService; _auditService = auditService; _localizedTextService = localizedTextService; @@ -97,7 +95,6 @@ namespace Umbraco.Core.Services ILocalizedTextService localizedTextService = null, IAuditService auditService = null, IDomainService domainService = null, - ITaskService taskService = null, IMacroService macroService = null, IPublicAccessService publicAccessService = null, IExternalLoginService externalLoginService = null, @@ -128,7 +125,6 @@ namespace Umbraco.Core.Services if (userService != null) _userService = new Lazy(() => userService); if (notificationService != null) _notificationService = new Lazy(() => notificationService); if (domainService != null) _domainService = new Lazy(() => domainService); - if (taskService != null) _taskService = new Lazy(() => taskService); if (macroService != null) _macroService = new Lazy(() => macroService); if (publicAccessService != null) _publicAccessService = new Lazy(() => publicAccessService); if (redirectUrlService != null) _redirectUrlService = new Lazy(() => redirectUrlService); @@ -139,12 +135,7 @@ namespace Umbraco.Core.Services /// Gets the /// public IPublicAccessService PublicAccessService => _publicAccessService.Value; - - /// - /// Gets the - /// - public ITaskService TaskService => _taskService.Value; - + /// /// Gets the /// diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index aa7075f418..9c686c4353 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1384,6 +1384,30 @@ namespace Umbraco.Core return idCheckList.Contains(value); } + /// + /// Converts a file name to a friendly name for a content item + /// + /// + /// + public static string ToFriendlyName(this string fileName) + { + // strip the file extension + fileName = fileName.StripFileExtension(); + + // underscores and dashes to spaces + fileName = fileName.ReplaceMany(new[] { '_', '-' }, ' '); + + // any other conversions ? + + // Pascalcase (to be done last) + fileName = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(fileName); + + // Replace multiple consecutive spaces with a single space + fileName = string.Join(" ", fileName.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); + + return fileName; + } + // From: http://stackoverflow.com/a/961504/5018 // filters control characters but allows only properly-formed surrogate sequences private static readonly Lazy InvalidXmlChars = new Lazy(() => diff --git a/src/Umbraco.Core/Sync/ServerRole.cs b/src/Umbraco.Core/Sync/ServerRole.cs index fb8dd784ca..21091e41c5 100644 --- a/src/Umbraco.Core/Sync/ServerRole.cs +++ b/src/Umbraco.Core/Sync/ServerRole.cs @@ -16,7 +16,7 @@ Single = 1, /// - /// In a multi-servers environment, the server is a slave server. + /// In a multi-servers environment, the server is a replica server. /// Replica = 2, diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6ce08cacae..dcf6832b6c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -333,10 +333,16 @@ + + + + - + + + @@ -357,16 +363,22 @@ + + + + + + @@ -388,12 +400,14 @@ + + @@ -410,6 +424,8 @@ + + @@ -722,7 +738,6 @@ - @@ -796,8 +811,6 @@ - - @@ -819,8 +832,6 @@ - - @@ -895,8 +906,6 @@ - - @@ -946,7 +955,6 @@ - @@ -1155,8 +1163,6 @@ - - @@ -1187,8 +1193,6 @@ - - @@ -1374,7 +1378,6 @@ - @@ -1393,6 +1396,7 @@ + @@ -1403,7 +1407,6 @@ - diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index d018dddfbf..b99fc2695e 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -30,6 +30,7 @@ namespace Umbraco.Core /// These are def back office: /// /Umbraco/RestServices = back office /// /Umbraco/BackOffice = back office + /// /Umbraco/Preview = back office /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. @@ -77,7 +78,8 @@ namespace Umbraco.Core //check for special back office paths if (urlPath.InvariantStartsWith("/" + globalSettings.GetUmbracoMvcArea() + "/BackOffice/") - || urlPath.InvariantStartsWith("/" + globalSettings.GetUmbracoMvcArea() + "/RestServices/")) + || urlPath.InvariantStartsWith("/" + globalSettings.GetUmbracoMvcArea() + "/RestServices/") + || urlPath.InvariantStartsWith("/" + globalSettings.GetUmbracoMvcArea() + "/Preview/")) { return true; } diff --git a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs index e79b26cd1d..0616b4098f 100644 --- a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs +++ b/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.Cache new EventDefinition>(null, serviceContext.ContentService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), new EventDefinition(null, serviceContext.ContentService, new RecycleBinEventArgs(Guid.NewGuid())), new EventDefinition>(null, serviceContext.ContentService, new PublishEventArgs(Enumerable.Empty()), "Published"), - new EventDefinition>(null, serviceContext.ContentService, new PublishEventArgs(Enumerable.Empty()), "UnPublished"), + new EventDefinition>(null, serviceContext.ContentService, new PublishEventArgs(Enumerable.Empty()), "Unpublished"), new EventDefinition>(null, serviceContext.PublicAccessService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, serviceContext.PublicAccessService, new DeleteEventArgs(Enumerable.Empty())), diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 955f6f94c8..a8624e8871 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = TypeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); - Assert.AreEqual(21, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static ProfilingLogger GetTestProfilingLogger() diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config index 37f2723c81..4040412603 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config @@ -1,114 +1,114 @@  -
- - settings - - - - views/dashboard/settings/settingsdashboardintro.html - - - views/dashboard/settings/settingsdashboardvideos.html - - -
+
+ + settings + + + + views/dashboard/settings/settingsdashboardintro.html + + + views/dashboard/settings/settingsdashboardvideos.html + + + + dashboard/ExamineManagement.ascx + +
-
- - developer - - - - views/dashboard/developer/developerdashboardintro.html - - - views/dashboard/developer/developerdashboardvideos.html - - - - dashboard/ExamineManagement.ascx - -
+
+ + developer + + + + views/dashboard/developer/developerdashboardintro.html + + + views/dashboard/developer/developerdashboardvideos.html + + +
-
- - media - - - - views/dashboard/media/mediafolderbrowser.html - - - - - admin - - - views/dashboard/media/mediadashboardintro.html - - - views/dashboard/media/desktopmediauploader.html - - - views/dashboard/media/mediadashboardvideos.html - - -
+
+ + media + + + + views/dashboard/media/mediafolderbrowser.html + + + + + admin + + + views/dashboard/media/mediadashboardintro.html + + + views/dashboard/media/desktopmediauploader.html + + + views/dashboard/media/mediadashboardvideos.html + + +
-
- - translator - hello - world - - - content - - - - admin - - - views/dashboard/default/startupdashboardintro.html - - - views/dashboard/default/startupdashboardkits.html +
- editor - writer + translator + hello + world - - - views/dashboard/default/startupdashboardvideos.html - - - - dashboard/latestEdits.ascx - - - - views/dashboard/changepassword.html - - -
+ + content + + + + admin + + + views/dashboard/default/startupdashboardintro.html + + + views/dashboard/default/startupdashboardkits.html + + editor + writer + + + + views/dashboard/default/startupdashboardvideos.html + + + + dashboard/latestEdits.ascx + + + + views/dashboard/changepassword.html + + +
-
- - default - member - - - - views/dashboard/members/membersdashboardintro.html - - - members/membersearch.ascx - - - views/dashboard/members/membersdashboardvideos.html - - -
+
+ + default + member + + + + views/dashboard/members/membersdashboardintro.html + + + members/membersearch.ascx + + + views/dashboard/members/membersdashboardvideos.html + + +
diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs index ed3b36c91f..862dfb3dc2 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs @@ -66,10 +66,21 @@ namespace Umbraco.Tests.Configurations.DashboardSettings [Test] public void Test_Section_Tabs() { - Assert.AreEqual(1, SettingsSection.Sections.ElementAt(0).Tabs.Count()); - Assert.AreEqual(2, SettingsSection.Sections.ElementAt(1).Tabs.Count()); + //Element 0 Alias "StartupSettingsDashboardSection" + Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.Count()); + + //Element 1 Alias "StartupDeveloperDashboardSection" + Assert.AreEqual(1, SettingsSection.Sections.ElementAt(1).Tabs.Count()); + + //Element 2 Alias "StartupMediaDashboardSection" + Assert.AreEqual(2, SettingsSection.Sections.ElementAt(2).Tabs.Count()); + + //Element 3 Alias "StartupDashboardSection" Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).Tabs.Count()); + //Element 4 Alias "StartupMemberDashboardSection" + Assert.AreEqual(1, SettingsSection.Sections.ElementAt(4).Tabs.Count()); + } [Test] diff --git a/src/Umbraco.Tests/CoreThings/EnumerableExtensionsTests.cs b/src/Umbraco.Tests/CoreThings/EnumerableExtensionsTests.cs index 7d7dcaea71..e734713c76 100644 --- a/src/Umbraco.Tests/CoreThings/EnumerableExtensionsTests.cs +++ b/src/Umbraco.Tests/CoreThings/EnumerableExtensionsTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core; @@ -38,7 +39,7 @@ namespace Umbraco.Tests.CoreThings } [Test] - public void Flatten_List_2() + public void SelectRecursive_2() { var hierarchy = new TestItem("1") { @@ -50,19 +51,13 @@ namespace Umbraco.Tests.CoreThings } }; -#pragma warning disable CS0618 // Type or member is obsolete - var flattened = hierarchy.Children.FlattenList(x => x.Children); -#pragma warning restore CS0618 // Type or member is obsolete var selectRecursive = hierarchy.Children.SelectRecursive(x => x.Children); - Assert.AreEqual(3, flattened.Count()); Assert.AreEqual(3, selectRecursive.Count()); - - Assert.IsTrue(flattened.SequenceEqual(selectRecursive)); } [Test] - public void Flatten_List() + public void SelectRecursive() { var hierarchy = new TestItem("1") { @@ -117,17 +112,8 @@ namespace Umbraco.Tests.CoreThings } }; -#pragma warning disable CS0618 // Type or member is obsolete - var flattened = hierarchy.Children.FlattenList(x => x.Children); -#pragma warning restore CS0618 // Type or member is obsolete var selectRecursive = hierarchy.Children.SelectRecursive(x => x.Children); - - Assert.AreEqual(10, flattened.Count()); Assert.AreEqual(10, selectRecursive.Count()); - - // both methods return the same elements, but not in the same order - Assert.IsFalse(flattened.SequenceEqual(selectRecursive)); - foreach (var x in flattened) Assert.IsTrue(selectRecursive.Contains(x)); } private class TestItem diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 6075ab3371..c3d3ca3f4b 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -460,7 +460,7 @@ namespace Umbraco.Tests.Integration #endregion - #region Save, Publish & UnPublish single content + #region Save, Publish & Unpublish single content [Test] public void SaveUnpublishedContent() @@ -718,7 +718,7 @@ namespace Umbraco.Tests.Integration #endregion - #region Publish & UnPublish branch + #region Publish & Unpublish branch [Test] public void UnpublishContentBranch() diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs index a399e75cff..98ebeb9315 100644 --- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; using Umbraco.Core.Services; +using Umbraco.Web.ContentApps; namespace Umbraco.Tests.Manifest { @@ -343,5 +344,42 @@ javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2 // fixme - should we resolveUrl in configs? } + + [Test] + public void CanParseManifest_ContentApps() + { + const string json = @"{'contentApps': [ + { + alias: 'myPackageApp1', + name: 'My App1', + icon: 'icon-foo', + view: '~/App_Plugins/MyPackage/ContentApps/MyApp1.html' + }, + { + alias: 'myPackageApp2', + name: 'My App2', + config: { key1: 'some config val' }, + icon: 'icon-bar', + view: '~/App_Plugins/MyPackage/ContentApps/MyApp2.html' + } +]}"; + + var manifest = _parser.ParseManifest(json); + Assert.AreEqual(2, manifest.ContentApps.Length); + + Assert.IsInstanceOf(manifest.ContentApps[0]); + var app0 = (ManifestContentAppDefinition) manifest.ContentApps[0]; + Assert.AreEqual("myPackageApp1", app0.Alias); + Assert.AreEqual("My App1", app0.Name); + Assert.AreEqual("icon-foo", app0.Icon); + Assert.AreEqual("/App_Plugins/MyPackage/ContentApps/MyApp1.html", app0.View); + + Assert.IsInstanceOf(manifest.ContentApps[1]); + var app1 = (ManifestContentAppDefinition)manifest.ContentApps[1]; + Assert.AreEqual("myPackageApp2", app1.Alias); + Assert.AreEqual("My App2", app1.Name); + Assert.AreEqual("icon-bar", app1.Icon); + Assert.AreEqual("/App_Plugins/MyPackage/ContentApps/MyApp2.html", app1.View); + } } } diff --git a/src/Umbraco.Tests/Models/TaskTests.cs b/src/Umbraco.Tests/Models/TaskTests.cs deleted file mode 100644 index 808c2884b7..0000000000 --- a/src/Umbraco.Tests/Models/TaskTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Diagnostics; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Serialization; - -namespace Umbraco.Tests.Models -{ - [TestFixture] - public class TaskTests - { - [Test] - public void Can_Deep_Clone() - { - var item = new Task(new TaskType("test") {Id = 3}) - { - AssigneeUserId = 4, - Closed = true, - Comment = "blah", - CreateDate = DateTime.Now, - EntityId = 99, - Id = 2, - Key = Guid.NewGuid(), - OwnerUserId = 89, - TaskType = new TaskType("asdf") {Id = 99}, - UpdateDate = DateTime.Now - }; - - var clone = (Task) item.DeepClone(); - - Assert.AreNotSame(clone, item); - Assert.AreEqual(clone, item); - Assert.AreEqual(clone.CreateDate, item.CreateDate); - Assert.AreEqual(clone.UpdateDate, item.UpdateDate); - Assert.AreEqual(clone.AssigneeUserId, item.AssigneeUserId); - Assert.AreEqual(clone.Closed, item.Closed); - Assert.AreEqual(clone.Comment, item.Comment); - Assert.AreEqual(clone.EntityId, item.EntityId); - Assert.AreEqual(clone.Id, item.Id); - Assert.AreEqual(clone.Key, item.Key); - Assert.AreEqual(clone.OwnerUserId, item.OwnerUserId); - Assert.AreNotSame(clone.TaskType, item.TaskType); - Assert.AreEqual(clone.TaskType, item.TaskType); - - //This double verifies by reflection - var allProps = clone.GetType().GetProperties(); - foreach (var propertyInfo in allProps) - { - Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); - } - } - - [Test] - public void Can_Serialize_Without_Error() - { - var ss = new SerializationService(new JsonNetSerializer()); - - var item = new Task(new TaskType("test") { Id = 3 }) - { - AssigneeUserId = 4, - Closed = true, - Comment = "blah", - CreateDate = DateTime.Now, - EntityId = 99, - Id = 2, - Key = Guid.NewGuid(), - OwnerUserId = 89, - TaskType = new TaskType("asdf") { Id = 99 }, - UpdateDate = DateTime.Now - }; - - var result = ss.ToStream(item); - var json = result.ResultStream.ToJsonString(); - Debug.Print(json); - } - - } -} diff --git a/src/Umbraco.Tests/Models/TaskTypeTests.cs b/src/Umbraco.Tests/Models/TaskTypeTests.cs deleted file mode 100644 index 5e31e8a1d8..0000000000 --- a/src/Umbraco.Tests/Models/TaskTypeTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Diagnostics; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Serialization; - -namespace Umbraco.Tests.Models -{ - [TestFixture] - public class TaskTypeTests - { - [Test] - public void Can_Deep_Clone() - { - var item = new TaskType("test") - { - Id = 3, - Alias = "test", - CreateDate = DateTime.Now, - Key = Guid.NewGuid(), - UpdateDate = DateTime.Now - }; - - var clone = (TaskType)item.DeepClone(); - - Assert.AreNotSame(clone, item); - Assert.AreEqual(clone, item); - Assert.AreEqual(clone.CreateDate, item.CreateDate); - Assert.AreEqual(clone.Id, item.Id); - Assert.AreEqual(clone.Key, item.Key); - Assert.AreEqual(clone.Alias, item.Alias); - Assert.AreEqual(clone.UpdateDate, item.UpdateDate); - - //This double verifies by reflection - var allProps = clone.GetType().GetProperties(); - foreach (var propertyInfo in allProps) - { - Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); - } - - } - - [Test] - public void Can_Serialize_Without_Error() - { - var ss = new SerializationService(new JsonNetSerializer()); - - var item = new TaskType("test") - { - Id = 3, - Alias = "test", - CreateDate = DateTime.Now, - Key = Guid.NewGuid(), - UpdateDate = DateTime.Now - }; - - var result = ss.ToStream(item); - var json = result.ResultStream.ToJsonString(); - Debug.Print(json); - } - } -} diff --git a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs index a8cdbfcdac..e57a1ebbbc 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs @@ -1,22 +1,24 @@ using System.Linq; using NUnit.Framework; -using Umbraco.Core.Cache; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Persistence.Repositories { [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] public class AuditRepositoryTest : TestWithDatabaseBase { + [Test] public void Can_Add_Audit_Entry() { @@ -24,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = sp.CreateScope()) { var repo = new AuditRepository((IScopeAccessor) sp, Logger); - repo.Save(new AuditItem(-1, "This is a System audit trail", AuditType.System, 0)); + repo.Save(new AuditItem(-1, "This is a System audit trail", AuditType.System, -1)); var dtos = scope.Database.Fetch("WHERE id > -1"); @@ -43,8 +45,8 @@ namespace Umbraco.Tests.Persistence.Repositories for (var i = 0; i < 100; i++) { - repo.Save(new AuditItem(i, $"Content {i} created", AuditType.New, 0)); - repo.Save(new AuditItem(i, $"Content {i} published", AuditType.Publish, 0)); + repo.Save(new AuditItem(i, $"Content {i} created", AuditType.New, -1)); + repo.Save(new AuditItem(i, $"Content {i} published", AuditType.Publish, -1)); } scope.Complete(); @@ -61,6 +63,49 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Get_Paged_Items_By_User_Id_With_Query_And_Filter() + { + var sp = TestObjects.GetScopeProvider(Logger); + using (var scope = sp.CreateScope()) + { + var repo = new AuditRepository((IScopeAccessor)sp, CacheHelper, Logger); + + for (var i = 0; i < 100; i++) + { + repo.Save(new AuditItem(i, $"Content {i} created", AuditType.New, -1)); + repo.Save(new AuditItem(i, $"Content {i} published", AuditType.Publish, -1)); + } + + scope.Complete(); + } + + using (var scope = sp.CreateScope()) + { + var repo = new AuditRepository((IScopeAccessor)sp, CacheHelper, Logger); + + var query = sp.SqlContext.Query().Where(x => x.UserId == -1); + + try + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; + scope.Database.AsUmbracoDatabase().EnableSqlCount = true; + + var page = repo.GetPagedResultsByQuery(query, 0, 10, out var total, Direction.Descending, + new[] { AuditType.Publish }, + sp.SqlContext.Query().Where(x => x.UserId > -2)); + + Assert.AreEqual(10, page.Count()); + Assert.AreEqual(100, total); + } + finally + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = false; + scope.Database.AsUmbracoDatabase().EnableSqlCount = false; + } + } + } + [Test] public void Get_Paged_Items_With_AuditType_Filter() { @@ -71,8 +116,8 @@ namespace Umbraco.Tests.Persistence.Repositories for (var i = 0; i < 100; i++) { - repo.Save(new AuditItem(i, $"Content {i} created", AuditType.New, 0)); - repo.Save(new AuditItem(i, $"Content {i} published", AuditType.Publish, 0)); + repo.Save(new AuditItem(i, $"Content {i} created", AuditType.New, -1)); + repo.Save(new AuditItem(i, $"Content {i} published", AuditType.Publish, -1)); } scope.Complete(); @@ -102,8 +147,8 @@ namespace Umbraco.Tests.Persistence.Repositories for (var i = 0; i < 100; i++) { - repo.Save(new AuditItem(i, "Content created", AuditType.New, 0)); - repo.Save(new AuditItem(i, "Content published", AuditType.Publish, 0)); + repo.Save(new AuditItem(i, "Content created", AuditType.New, -1)); + repo.Save(new AuditItem(i, "Content published", AuditType.Publish, -1)); } scope.Complete(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 2a3b197218..c4105ba97e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; +using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; @@ -760,7 +761,7 @@ namespace Umbraco.Tests.Persistence.Repositories scope.Database.AsUmbracoDatabase().EnableSqlCount = true; var query = scope.SqlContext.Query().Where(x => x.ParentId == root.Id); - var result = repository.GetPage(query, 0, 20, out var totalRecords, "UpdateDate", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 20, out var totalRecords, null, Ordering.By("UpdateDate")); Assert.AreEqual(25, totalRecords); foreach (var r in result) @@ -803,12 +804,12 @@ namespace Umbraco.Tests.Persistence.Repositories scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; scope.Database.AsUmbracoDatabase().EnableSqlCount = true; - var result = repository.GetPage(query, 0, 2, out var totalRecords, "title", Direction.Ascending, false); + var result = repository.GetPage(query, 0, 2, out var totalRecords, null, Ordering.By("title", isCustomField: true)); Assert.AreEqual(3, totalRecords); Assert.AreEqual(2, result.Count()); - result = repository.GetPage(query, 1, 2, out totalRecords, "title", Direction.Ascending, false); + result = repository.GetPage(query, 1, 2, out totalRecords, null, Ordering.By("title", isCustomField: true)); Assert.AreEqual(1, result.Count()); } @@ -835,7 +836,7 @@ namespace Umbraco.Tests.Persistence.Repositories scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; scope.Database.AsUmbracoDatabase().EnableSqlCount = true; - var result = repository.GetPage(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name")); Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); @@ -858,7 +859,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); - var result = repository.GetPage(query, 1, 1, out var totalRecords, "Name", Direction.Ascending, true); + var result = repository.GetPage(query, 1, 1, out var totalRecords, null, Ordering.By("Name")); Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); @@ -875,7 +876,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); - var result = repository.GetPage(query, 0, 2, out var totalRecords, "Name", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 2, out var totalRecords, null, Ordering.By("Name")); Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(2)); @@ -892,7 +893,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); - var result = repository.GetPage(query, 0, 1, out var totalRecords, "Name", Direction.Descending, true); + var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name", Direction.Descending)); Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); @@ -911,7 +912,7 @@ namespace Umbraco.Tests.Persistence.Repositories var query = scope.SqlContext.Query().Where(x => x.Level == 2); var filterQuery = scope.SqlContext.Query().Where(x => x.Name.Contains("Page 2")); - var result = repository.GetPage(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true, filterQuery); + var result = repository.GetPage(query, 0, 1, out var totalRecords, filterQuery, Ordering.By("Name")); Assert.That(totalRecords, Is.EqualTo(1)); Assert.That(result.Count(), Is.EqualTo(1)); @@ -930,7 +931,7 @@ namespace Umbraco.Tests.Persistence.Repositories var query = scope.SqlContext.Query().Where(x => x.Level == 2); var filterQuery = scope.SqlContext.Query().Where(x => x.Name.Contains("text")); - var result = repository.GetPage(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true, filterQuery); + var result = repository.GetPage(query, 0, 1, out var totalRecords, filterQuery, Ordering.By("Name")); Assert.That(totalRecords, Is.EqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index 7be52c7212..2f91c602af 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -47,6 +47,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo("English (United States)")); Assert.That(language.IsoCode, Is.EqualTo("en-US")); + Assert.That(language.FallbackLanguageId, Is.Null); } } @@ -61,7 +62,8 @@ namespace Umbraco.Tests.Persistence.Repositories var au = CultureInfo.GetCultureInfo("en-AU"); var language = (ILanguage)new Language(au.Name) { - CultureName = au.DisplayName + CultureName = au.DisplayName, + FallbackLanguageId = 1 }; repository.Save(language); @@ -73,6 +75,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo(au.DisplayName)); Assert.That(language.IsoCode, Is.EqualTo(au.Name)); + Assert.That(language.FallbackLanguageId, Is.EqualTo(1)); } } @@ -182,14 +185,15 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var languageBR = new Language("pt-BR") {CultureName = "pt-BR"}; + var languageBR = new Language("pt-BR") { CultureName = "pt-BR" }; repository.Save(languageBR); // Assert Assert.That(languageBR.HasIdentity, Is.True); Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 - Assert.IsFalse(languageBR.IsDefaultVariantLanguage); - Assert.IsFalse(languageBR.Mandatory); + Assert.IsFalse(languageBR.IsDefault); + Assert.IsFalse(languageBR.IsMandatory); + Assert.IsNull(languageBR.FallbackLanguageId); } } @@ -203,14 +207,39 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var languageBR = new Language("pt-BR") { CultureName = "pt-BR", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageBR = new Language("pt-BR") { CultureName = "pt-BR", IsDefault = true, IsMandatory = true }; repository.Save(languageBR); // Assert Assert.That(languageBR.HasIdentity, Is.True); Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 - Assert.IsTrue(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageBR.Mandatory); + Assert.IsTrue(languageBR.IsDefault); + Assert.IsTrue(languageBR.IsMandatory); + Assert.IsNull(languageBR.FallbackLanguageId); + } + } + + [Test] + public void Can_Perform_Add_On_LanguageRepository_With_Fallback_Language() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + + // Act + var languageBR = new Language("pt-BR") + { + CultureName = "pt-BR", + FallbackLanguageId = 1 + }; + repository.Save(languageBR); + + // Assert + Assert.That(languageBR.HasIdentity, Is.True); + Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 + Assert.That(languageBR.FallbackLanguageId, Is.EqualTo(1)); } } @@ -223,24 +252,22 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(provider); - var languageBR = (ILanguage)new Language("pt-BR") { CultureName = "pt-BR", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageBR = (ILanguage)new Language("pt-BR") { CultureName = "pt-BR", IsDefault = true, IsMandatory = true }; repository.Save(languageBR); var languageEN = new Language("en-AU") { CultureName = "en-AU" }; repository.Save(languageEN); - Assert.IsTrue(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageBR.Mandatory); + Assert.IsTrue(languageBR.IsDefault); + Assert.IsTrue(languageBR.IsMandatory); // Act - - var languageNZ = new Language("en-NZ") { CultureName = "en-NZ", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageNZ = new Language("en-NZ") { CultureName = "en-NZ", IsDefault = true, IsMandatory = true }; repository.Save(languageNZ); languageBR = repository.Get(languageBR.Id); // Assert - - Assert.IsFalse(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageNZ.IsDefaultVariantLanguage); + Assert.IsFalse(languageBR.IsDefault); + Assert.IsTrue(languageNZ.IsDefault); } } @@ -257,6 +284,7 @@ namespace Umbraco.Tests.Persistence.Repositories var language = repository.Get(5); language.IsoCode = "pt-BR"; language.CultureName = "pt-BR"; + language.FallbackLanguageId = 1; repository.Save(language); @@ -266,6 +294,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageUpdated, Is.Not.Null); Assert.That(languageUpdated.IsoCode, Is.EqualTo("pt-BR")); Assert.That(languageUpdated.CultureName, Is.EqualTo("pt-BR")); + Assert.That(languageUpdated.FallbackLanguageId, Is.EqualTo(1)); } } @@ -289,6 +318,30 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Perform_Delete_On_LanguageRepository_With_Language_Used_As_Fallback() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + // Add language to delete as a fall-back language to another one + var repository = CreateRepository(provider); + var languageToFallbackFrom = repository.Get(5); + languageToFallbackFrom.FallbackLanguageId = 2; // fall back to #2 (something we can delete) + repository.Save(languageToFallbackFrom); + + // delete #2 + var languageToDelete = repository.Get(2); + repository.Delete(languageToDelete); + + var exists = repository.Exists(2); + + // has been deleted + Assert.That(exists, Is.False); + } + } + [Test] public void Can_Perform_Exists_On_LanguageRepository() { @@ -314,8 +367,10 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - public void CreateTestData() + private void CreateTestData() { + //Id 1 is en-US - when Umbraco is installed + var languageDK = new Language("da-DK") { CultureName = "da-DK" }; ServiceContext.LocalizationService.Save(languageDK);//Id 2 diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 87759db3df..d1e7f96ff3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -326,7 +326,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); long totalRecords; - var result = repository.GetPage(query, 0, 1, out totalRecords, "SortOrder", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 1, out totalRecords, null, Ordering.By("SortOrder")); // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); @@ -348,7 +348,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); long totalRecords; - var result = repository.GetPage(query, 1, 1, out totalRecords, "SortOrder", Direction.Ascending, true); + var result = repository.GetPage(query, 1, 1, out totalRecords, null, Ordering.By("SortOrder")); // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); @@ -370,7 +370,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); long totalRecords; - var result = repository.GetPage(query, 0, 2, out totalRecords, "SortOrder", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 2, out totalRecords, null, Ordering.By("SortOrder")); // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); @@ -392,7 +392,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); long totalRecords; - var result = repository.GetPage(query, 0, 1, out totalRecords, "SortOrder", Direction.Descending, true); + var result = repository.GetPage(query, 0, 1, out totalRecords, null, Ordering.By("SortOrder", Direction.Descending)); // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); @@ -413,8 +413,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var result = repository.GetPage(query, 0, 1, out totalRecords, "Name", Direction.Ascending, true); + var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name")); // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); @@ -435,10 +434,9 @@ namespace Umbraco.Tests.Persistence.Repositories // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; var filter = scope.SqlContext.Query().Where(x => x.Name.Contains("File")); - var result = repository.GetPage(query, 0, 1, out totalRecords, "SortOrder", Direction.Ascending, true, filter); + var result = repository.GetPage(query, 0, 1, out var totalRecords, filter, Ordering.By("SortOrder")); // Assert Assert.That(totalRecords, Is.EqualTo(1)); @@ -454,15 +452,13 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - MediaTypeRepository mediaTypeRepository; - var repository = CreateRepository(provider, out mediaTypeRepository); + var repository = CreateRepository(provider, out _); // Act var query = scope.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; var filter = scope.SqlContext.Query().Where(x => x.Name.Contains("Test")); - var result = repository.GetPage(query, 0, 1, out totalRecords, "SortOrder", Direction.Ascending, true, filter); + var result = repository.GetPage(query, 0, 1, out var totalRecords, filter, Ordering.By("SortOrder")); // Assert Assert.That(totalRecords, Is.EqualTo(2)); diff --git a/src/Umbraco.Tests/Persistence/Repositories/TaskRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TaskRepositoryTest.cs deleted file mode 100644 index a4b01aef9e..0000000000 --- a/src/Umbraco.Tests/Persistence/Repositories/TaskRepositoryTest.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.Scoping; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Persistence.Repositories -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class TaskRepositoryTest : TestWithDatabaseBase - { - [Test] - public void Can_Delete() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var created = DateTime.Now; - var task = new Task(new TaskType("asdfasdf")) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = false, - Comment = "hello world", - EntityId = -1, - OwnerUserId = Constants.Security.SuperUserId - }; - repo.Save(task); - - - repo.Delete(task); - - - task = repo.Get(task.Id); - Assert.IsNull(task); - } - } - - [Test] - public void Can_Add() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var created = DateTime.Now; - repo.Save(new Task(new TaskType("asdfasdf")) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = false, - Comment = "hello world", - EntityId = -1, - OwnerUserId = Constants.Security.SuperUserId - }); - - - var found = repo.GetMany().ToArray(); - - Assert.AreEqual(1, found.Length); - Assert.AreEqual(Constants.Security.SuperUserId, found.First().AssigneeUserId); - Assert.AreEqual(false, found.First().Closed); - Assert.AreEqual("hello world", found.First().Comment); - Assert.GreaterOrEqual(found.First().CreateDate.TruncateTo(DateTimeExtensions.DateTruncate.Second), created.TruncateTo(DateTimeExtensions.DateTruncate.Second)); - Assert.AreEqual(-1, found.First().EntityId); - Assert.AreEqual(Constants.Security.SuperUserId, found.First().OwnerUserId); - Assert.AreEqual(true, found.First().HasIdentity); - Assert.AreEqual(true, found.First().TaskType.HasIdentity); - } - } - - [Test] - public void Can_Update() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var task = new Task(new TaskType("asdfasdf")) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = false, - Comment = "hello world", - EntityId = -1, - OwnerUserId = Constants.Security.SuperUserId - }; - - repo.Save(task); - - - //re-get - task = repo.Get(task.Id); - - task.Comment = "blah"; - task.Closed = true; - - repo.Save(task); - - - //re-get - task = repo.Get(task.Id); - - Assert.AreEqual(true, task.Closed); - Assert.AreEqual("blah", task.Comment); - } - } - - [Test] - public void Get_By_Id() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var task = new Task(new TaskType("asdfasdf")) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = false, - Comment = "hello world", - EntityId = -1, - OwnerUserId = Constants.Security.SuperUserId - }; - - repo.Save(task); - - - //re-get - task = repo.Get(task.Id); - - Assert.IsNotNull(task); - } - } - - [Test] - public void Get_All() - { - CreateTestData(false, 20); - - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var found = repo.GetMany().ToArray(); - Assert.AreEqual(20, found.Count()); - } - } - - [Test] - public void Get_All_With_Closed() - { - CreateTestData(false, 10); - CreateTestData(true, 5); - - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var found = repo.GetTasks(includeClosed: true).ToArray(); - Assert.AreEqual(15, found.Count()); - } - } - - [Test] - public void Get_All_With_Node_Id() - { - CreateTestData(false, 10, -20); - CreateTestData(false, 5, -21); - - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var found = repo.GetTasks(itemId:-20).ToArray(); - Assert.AreEqual(10, found.Count()); - } - } - - [Test] - public void Get_All_Without_Closed() - { - CreateTestData(false, 10); - CreateTestData(true, 5); - - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - var found = repo.GetTasks(includeClosed: false); - Assert.AreEqual(10, found.Count()); - } - } - - private void CreateTestData(bool closed, int count, int entityId = -1) - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - - for (int i = 0; i < count; i++) - { - repo.Save(new Task(new TaskType("asdfasdf")) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = closed, - Comment = "hello world " + i, - EntityId = entityId, - OwnerUserId = Constants.Security.SuperUserId - }); - } - - scope.Complete(); - } - } - } -} diff --git a/src/Umbraco.Tests/Persistence/Repositories/TaskTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TaskTypeRepositoryTest.cs deleted file mode 100644 index 7f159215b7..0000000000 --- a/src/Umbraco.Tests/Persistence/Repositories/TaskTypeRepositoryTest.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.Scoping; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Persistence.Repositories -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class TaskTypeRepositoryTest : TestWithDatabaseBase - { - [Test] - public void Can_Delete() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) - { - var taskType = new TaskType("asdfasdf"); - var repo = new TaskRepository((IScopeAccessor) provider, Logger); - var taskTypeRepo = new TaskTypeRepository((IScopeAccessor) provider, Logger); - - var created = DateTime.Now; - var task = new Task(taskType) - { - AssigneeUserId = Constants.Security.SuperUserId, - Closed = false, - Comment = "hello world", - EntityId = -1, - OwnerUserId = Constants.Security.SuperUserId - }; - repo.Save(task); - - var alltasktypes = taskTypeRepo.GetMany(); - - taskTypeRepo.Delete(taskType); - - Assert.AreEqual(alltasktypes.Count() - 1, taskTypeRepo.GetMany().Count()); - Assert.AreEqual(0, repo.GetMany().Count()); - } - - - } - } -} diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index b9bbfbb363..3ec9d572bf 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -429,32 +429,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] - public void Can_Get_Template_Tree() - { - // Arrange - using (ScopeProvider.CreateScope()) - { - var repository = CreateRepository(ScopeProvider); - - CreateHierarchy(repository); - - // Act - var rootNode = repository.GetTemplateNode("parent"); - - // Assert - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "parent")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child1")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child2")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler1")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler2")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler3")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler4")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1")); - Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2")); - } - } - [Test] public void Can_Get_All() { diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 64615c1950..847972cc50 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -8,17 +8,19 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; +using Umbraco.Core.Persistence; namespace Umbraco.Tests.Persistence.Repositories { [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true, Logger = UmbracoTestOptions.Logger.Console)] public class UserRepositoryTest : TestWithDatabaseBase { private MediaRepository CreateMediaRepository(IScopeProvider provider, out IMediaTypeRepository mediaTypeRepository) @@ -50,14 +52,14 @@ namespace Umbraco.Tests.Persistence.Repositories private UserRepository CreateRepository(IScopeProvider provider) { var accessor = (IScopeAccessor) provider; - var repository = new UserRepository(accessor, CacheHelper.Disabled, Mock.Of(), Mock.Of(), TestObjects.GetGlobalSettings()); + var repository = new UserRepository(accessor, CacheHelper.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings()); return repository; } private UserGroupRepository CreateUserGroupRepository(IScopeProvider provider) { var accessor = (IScopeAccessor) provider; - return new UserGroupRepository(accessor, CacheHelper.Disabled, Mock.Of()); + return new UserGroupRepository(accessor, CacheHelper.Disabled, Logger); } [Test] @@ -338,7 +340,72 @@ namespace Umbraco.Tests.Persistence.Repositories var result = repository.Count(query); // Assert - Assert.That(result, Is.GreaterThanOrEqualTo(2)); + Assert.AreEqual(2, result); + } + } + + [Test] + public void Can_Get_Paged_Results_By_Query_And_Filter_And_Groups() + { + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + + var users = CreateAndCommitMultipleUsers(repository); + var query = provider.SqlContext.Query().Where(x => x.Username == "TestUser1" || x.Username == "TestUser2"); + + try + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; + scope.Database.AsUmbracoDatabase().EnableSqlCount = true; + + // Act + var result = repository.GetPagedResultsByQuery(query, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending, + excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias }, + filter: provider.SqlContext.Query().Where(x => x.Id > -1)); + + // Assert + Assert.AreEqual(2, totalRecs); + } + finally + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = false; + scope.Database.AsUmbracoDatabase().EnableSqlCount = false; + } + } + + } + + [Test] + public void Can_Get_Paged_Results_With_Filter_And_Groups() + { + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + + var users = CreateAndCommitMultipleUsers(repository); + + try + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; + scope.Database.AsUmbracoDatabase().EnableSqlCount = true; + + // Act + var result = repository.GetPagedResultsByQuery(null, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending, + includeUserGroups: new[] { Constants.Security.AdminGroupAlias, Constants.Security.SensitiveDataGroupAlias }, + excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias }, + filter: provider.SqlContext.Query().Where(x => x.Id == -1)); + + // Assert + Assert.AreEqual(1, totalRecs); + } + finally + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = false; + scope.Database.AsUmbracoDatabase().EnableSqlCount = false; + } } } diff --git a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs index ba9a6b219c..38daa2c1a7 100644 --- a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs @@ -450,35 +450,6 @@ namespace Umbraco.Tests.Persistence } } - [Test] - public void Can_Create_cmsTask_Table() - { - using (var scope = ScopeProvider.CreateScope()) - { - var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of()); - - helper.CreateTable(); - helper.CreateTable(); - helper.CreateTable(); - helper.CreateTable(); - - scope.Complete(); - } - } - - [Test] - public void Can_Create_cmsTaskType_Table() - { - using (var scope = ScopeProvider.CreateScope()) - { - var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of()); - - helper.CreateTable(); - - scope.Complete(); - } - } - [Test] public void Can_Create_umbracoUser_Table() { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs new file mode 100644 index 0000000000..4e98aea000 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Tests.Testing; +using Umbraco.Web; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] + public class PublishedContentLanguageVariantTests : PublishedContentSnapshotTestBase + { + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(_ => GetServiceContext()); + } + + protected ServiceContext GetServiceContext() + { + var serviceContext = TestObjects.GetServiceContextMock(Container); + MockLocalizationService(serviceContext); + return serviceContext; + } + + private static void MockLocalizationService(ServiceContext serviceContext) + { + // Set up languages. + // Spanish falls back to English and Italian to Spanish (and then to English). + // French has no fall back. + // Danish, Swedish and Norweigan create an invalid loop. + var languages = new List + { + new Language("en-US") { Id = 1, CultureName = "English", IsDefault = true }, + new Language("fr") { Id = 2, CultureName = "French" }, + new Language("es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, + new Language("it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, + new Language("de") { Id = 5, CultureName = "German" }, + new Language("da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, + new Language("sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, + new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 }, + new Language("nl") { Id = 9, CultureName = "Dutch", FallbackLanguageId = 1 } + }; + + var localizationService = Mock.Get(serviceContext.LocalizationService); + localizationService.Setup(x => x.GetAllLanguages()).Returns(languages); + localizationService.Setup(x => x.GetLanguageById(It.IsAny())) + .Returns((int id) => languages.SingleOrDefault(y => y.Id == id)); + localizationService.Setup(x => x.GetLanguageByIsoCode(It.IsAny())) + .Returns((string c) => languages.SingleOrDefault(y => y.IsoCode == c)); + } + + internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) + { + var props = new[] + { + factory.CreatePropertyType("prop1", 1), + factory.CreatePropertyType("welcomeText", 1), + factory.CreatePropertyType("welcomeText2", 1), + }; + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + + var prop1 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText", + }; + prop1.SetSourceValue("en-US", "Welcome", true); + prop1.SetValue("en-US", "Welcome", true); + prop1.SetSourceValue("de", "Willkommen"); + prop1.SetValue("de", "Willkommen"); + prop1.SetSourceValue("nl", "Welkom"); + prop1.SetValue("nl", "Welkom"); + + var prop2 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText2", + }; + prop2.SetSourceValue("en-US", "Welcome", true); + prop2.SetValue("en-US", "Welcome", true); + + var prop3 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText", + }; + prop3.SetSourceValue("en-US", "Welcome", true); + prop3.SetValue("en-US", "Welcome", true); + + var item1 = new SolidPublishedContent(contentType1) + { + Id = 1, + SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", + Path = "/1", + Level = 1, + Url = "/content-1", + ParentId = -1, + ChildIds = new[] { 2 }, + Properties = new Collection + { + prop1, prop2 + } + }; + + var item2 = new SolidPublishedContent(contentType1) + { + Id = 2, + SortOrder = 0, + Name = "Content 2", + UrlSegment = "content-2", + Path = "/1/2", + Level = 2, + Url = "/content-1/content-2", + ParentId = 1, + ChildIds = new int[] { }, + Properties = new Collection + { + prop3 + } + }; + + item1.Children = new List { item2 }; + item2.Parent = item1; + + cache.Add(item1); + cache.Add(item2); + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Language() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "en-US"); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "de"); + Assert.AreEqual("Willkommen", value); + } + + [Test] + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "fr"); + Assert.IsNull(value); + } + + [Test] + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "es"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "es", fallback: Fallback.ToLanguage); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "no", fallback: Fallback.ToLanguage); + Assert.IsNull(value); + } + + [Test] + public void Do_Not_Get_Content_Recursively_Unless_Requested() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText2"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_Recursively() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_With_Recursive_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); + + // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. + Assert.AreEqual("Welkom", value); + } + + [Test] + public void Can_Get_Content_With_Fallback_Language_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); + + // No Dutch value is directly assigned. Check has fallen back to English value from language variant. + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Throws_For_Non_Supported_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); + } + + [Test] + public void Can_Fallback_To_Default_Value() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + + // no Dutch value is assigned, so getting null + var value = content.Value("welcomeText", "nl"); + Assert.IsNull(value); + + // even if we 'just' provide a default value + value = content.Value("welcomeText", "nl", defaultValue: "woop"); + Assert.IsNull(value); + + // but it works with proper fallback settings + value = content.Value("welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); + Assert.AreEqual("woop", value); + } + + [Test] + public void Can_Have_Custom_Default_Value() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + + // hack the value, pretend the converter would return something + var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; + Assert.IsNotNull(prop); + prop.SetValue("nl", "nope"); // HasValue false but getting value returns this + + // there is an EN value + var value = content.Value("welcomeText", "en-US"); + Assert.AreEqual("Welcome", value); + + // there is no NL value and we get the 'converted' value + value = content.Value("welcomeText", "nl"); + Assert.AreEqual("nope", value); + + // but it works with proper fallback settings + value = content.Value("welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); + Assert.AreEqual("woop", value); + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 0f7212a0b4..9751a7ae6a 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -1,90 +1,95 @@ -using System; +using System.Collections.ObjectModel; using System.Linq; -using System.Collections.ObjectModel; -using System.Web.Routing; -using Moq; using NUnit.Framework; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; -using Umbraco.Core.Composing; -using Current = Umbraco.Core.Composing.Current; using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.PublishedContent { [TestFixture] [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] - public class PublishedContentMoreTests : PublishedContentTestBase + public class PublishedContentMoreTests : PublishedContentSnapshotTestBase { - // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet - // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx - // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx - - public override void SetUp() + internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) { - base.SetUp(); + var props = new[] + { + factory.CreatePropertyType("prop1", 1), + }; + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); + var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); - var umbracoContext = GetUmbracoContext(); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } + cache.Add(new SolidPublishedContent(contentType1) + { + Id = 1, + SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", + Path = "/1", + Level = 1, + Url = "/content-1", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); - protected override void Compose() - { - base.Compose(); + cache.Add(new SolidPublishedContent(contentType2) + { + Id = 2, + SortOrder = 1, + Name = "Content 2", + UrlSegment = "content-2", + Path = "/2", + Level = 1, + Url = "/content-2", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); - Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - } - - protected override TypeLoader CreatePluginManager(IContainer f) - { - var pluginManager = base.CreatePluginManager(f); - - // this is so the model factory looks into the test assembly - pluginManager.AssembliesToScan = pluginManager.AssembliesToScan - .Union(new[] { typeof (PublishedContentMoreTests).Assembly }) - .ToList(); - - return pluginManager; - } - - private UmbracoContext GetUmbracoContext() - { - RouteData routeData = null; - - var publishedSnapshot = CreatePublishedSnapshot(); - - var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); - - var globalSettings = TestObjects.GetGlobalSettings(); - - var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; - var umbracoContext = new UmbracoContext( - httpContext, - publishedSnapshotService.Object, - new WebSecurity(httpContext, Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - globalSettings, - new TestVariationContextAccessor()); - - return umbracoContext; - } - - public override void TearDown() - { - base.TearDown(); - - Current.Reset(); + cache.Add(new SolidPublishedContent(contentType2Sub) + { + Id = 3, + SortOrder = 2, + Name = "Content 2Sub", + UrlSegment = "content-2sub", + Path = "/3", + Level = 1, + Url = "/content-2sub", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); } [Test] @@ -196,95 +201,5 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(1, result[0].Id); Assert.AreEqual(2, result[1].Id); } - - private static SolidPublishedSnapshot CreatePublishedSnapshot() - { - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var caches = new SolidPublishedSnapshot(); - var cache = caches.InnerContentCache; - - var props = new[] - { - factory.CreatePropertyType("prop1", 1), - }; - - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); - var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); - var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); - - cache.Add(new SolidPublishedContent(contentType1) - { - Id = 1, - SortOrder = 0, - Name = "Content 1", - UrlSegment = "content-1", - Path = "/1", - Level = 1, - Url = "/content-1", - ParentId = -1, - ChildIds = new int[] {}, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2) - { - Id = 2, - SortOrder = 1, - Name = "Content 2", - UrlSegment = "content-2", - Path = "/2", - Level = 1, - Url = "/content-2", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2Sub) - { - Id = 3, - SortOrder = 2, - Name = "Content 2Sub", - UrlSegment = "content-2sub", - Path = "/3", - Level = 1, - Url = "/content-2sub", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - return caches; - } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs new file mode 100644 index 0000000000..623472a023 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using System.Collections.ObjectModel; +using System.Web.Routing; +using Moq; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; +using Umbraco.Core.Composing; +using Current = Umbraco.Core.Composing.Current; +using LightInject; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects.Accessors; + +namespace Umbraco.Tests.PublishedContent +{ + public abstract class PublishedContentSnapshotTestBase : PublishedContentTestBase + { + // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet + // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx + // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx + + public override void SetUp() + { + base.SetUp(); + + var umbracoContext = GetUmbracoContext(); + Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; + } + + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); + } + + protected override TypeLoader CreatePluginManager(IServiceFactory f) + { + var pluginManager = base.CreatePluginManager(f); + + // this is so the model factory looks into the test assembly + pluginManager.AssembliesToScan = pluginManager.AssembliesToScan + .Union(new[] { typeof (PublishedContentMoreTests).Assembly }) + .ToList(); + + return pluginManager; + } + + private UmbracoContext GetUmbracoContext() + { + RouteData routeData = null; + + var publishedSnapshot = CreatePublishedSnapshot(); + + var publishedSnapshotService = new Mock(); + publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); + + var globalSettings = TestObjects.GetGlobalSettings(); + + var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; + var umbracoContext = new UmbracoContext( + httpContext, + publishedSnapshotService.Object, + new WebSecurity(httpContext, Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), + Enumerable.Empty(), + globalSettings, + new TestVariationContextAccessor()); + + return umbracoContext; + } + + public override void TearDown() + { + base.TearDown(); + + Current.Reset(); + } + + private SolidPublishedSnapshot CreatePublishedSnapshot() + { + var dataTypeService = new TestObjects.TestDataTypeService( + new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); + + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); + var caches = new SolidPublishedSnapshot(); + var cache = caches.InnerContentCache; + PopulateCache(factory, cache); + return caches; + } + + internal abstract void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache); + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2a8bc8a8b2..6eef72bcd9 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -37,7 +37,7 @@ namespace Umbraco.Tests.PublishedContent var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(logger)) { Id = 1}, + new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, new DataType(new RichTextPropertyEditor(logger)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, @@ -337,11 +337,11 @@ namespace Umbraco.Tests.PublishedContent } [Test] - public void GetPropertyValueRecursiveTest() + public void Get_Property_Value_Recursive() { var doc = GetNode(1174); - var rVal = doc.Value("testRecursive", recurse: true); - var nullVal = doc.Value("DoNotFindThis", recurse: true); + var rVal = doc.Value("testRecursive", fallback: Fallback.ToAncestors); + var nullVal = doc.Value("DoNotFindThis", fallback: Fallback.ToAncestors); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index df7e1d9d09..0e360bae58 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -40,21 +40,6 @@ namespace Umbraco.Tests.PublishedContent Assert.IsFalse(result); } - [Test] - public void ConfigureRequest_Adds_HttpContext_Items_When_Published_Content_Assigned() - { - var umbracoContext = GetUmbracoContext("/test"); - var publishedRouter = CreatePublishedRouter(); - var request = publishedRouter.CreateRequest(umbracoContext); - var content = GetPublishedContentMock(); - request.PublishedContent = content.Object; - request.Culture = new CultureInfo("en-AU"); - publishedRouter.ConfigureRequest(request); - - Assert.AreEqual(1, umbracoContext.HttpContext.Items["pageID"]); - Assert.AreEqual(request.UmbracoPage.Elements.Count, ((Hashtable) umbracoContext.HttpContext.Items["pageElements"]).Count); - } - [Test] public void ConfigureRequest_Sets_UmbracoPage_When_Published_Content_Assigned() { diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 756b775e46..efd1c6ae8b 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -248,7 +248,7 @@ namespace Umbraco.Tests.PublishedContent #endregion } - class SolidPublishedProperty : IPublishedProperty + internal class SolidPublishedProperty : IPublishedProperty { public PublishedPropertyType PropertyType { get; set; } public string Alias { get; set; } @@ -257,10 +257,86 @@ namespace Umbraco.Tests.PublishedContent public bool SolidHasValue { get; set; } public object SolidXPathValue { get; set; } - public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; - public object GetValue(string culture = null, string segment = null) => SolidValue; - public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; - public bool HasValue(string culture = null, string segment = null) => SolidHasValue; + public virtual object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; + public virtual object GetValue(string culture = null, string segment = null) => SolidValue; + public virtual object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; + public virtual bool HasValue(string culture = null, string segment = null) => SolidHasValue; + } + + internal class SolidPublishedPropertyWithLanguageVariants : SolidPublishedProperty + { + private readonly IDictionary _solidSourceValues = new Dictionary(); + private readonly IDictionary _solidValues = new Dictionary(); + private readonly IDictionary _solidXPathValues = new Dictionary(); + + public override object GetSourceValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetSourceValue(culture, segment); + } + + return _solidSourceValues.ContainsKey(culture) ? _solidSourceValues[culture] : null; + } + + public override object GetValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetValue(culture, segment); + } + + return _solidValues.ContainsKey(culture) ? _solidValues[culture] : null; + } + + public override object GetXPathValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetXPathValue(culture, segment); + } + + return _solidXPathValues.ContainsKey(culture) ? _solidXPathValues[culture] : null; + } + + public override bool HasValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.HasValue(culture, segment); + } + + return _solidSourceValues.ContainsKey(culture); + } + + public void SetSourceValue(string culture, object value, bool defaultValue = false) + { + _solidSourceValues.Add(culture, value); + if (defaultValue) + { + SolidSourceValue = value; + SolidHasValue = true; + } + } + + public void SetValue(string culture, object value, bool defaultValue = false) + { + _solidValues.Add(culture, value); + if (defaultValue) + { + SolidValue = value; + SolidHasValue = true; + } + } + + public void SetXPathValue(string culture, object value, bool defaultValue = false) + { + _solidXPathValues.Add(culture, value); + if (defaultValue) + { + SolidXPathValue = value; + } + } } [PublishedModel("ContentType2")] diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 9d08229563..f652205147 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -168,7 +168,7 @@ namespace Umbraco.Tests.Scoping [Test] public void SupersededEvents2() { - Test_UnPublished += OnDoThingFail; + Test_Unpublished += OnDoThingFail; Test_Deleted += OnDoThingFail; var contentService = Mock.Of(); @@ -177,7 +177,7 @@ namespace Umbraco.Tests.Scoping var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { - scope.Events.Dispatch(Test_UnPublished, contentService, new PublishEventArgs(new [] { content }), "UnPublished"); + scope.Events.Dispatch(Test_Unpublished, contentService, new PublishEventArgs(new [] { content }), "Unpublished"); scope.Events.Dispatch(Test_Deleted, contentService, new DeleteEventArgs(new [] { content }), "Deleted"); // see U4-10764 @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Scoping public static event TypedEventHandler> DoThing3; - public static event TypedEventHandler> Test_UnPublished; + public static event TypedEventHandler> Test_Unpublished; public static event TypedEventHandler> Test_Deleted; public class TestEventArgs : CancellableObjectEventArgs diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 2f1cb74e80..cde161132a 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1205,7 +1205,7 @@ namespace Umbraco.Tests.Services } [Test] - public void Can_UnPublish_Content() + public void Can_Unpublish_Content() { // Arrange var contentService = ServiceContext.ContentService; @@ -2486,7 +2486,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefaultVariantLanguage = true }; + var langUk = new Language("en-UK") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2521,7 +2521,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefaultVariantLanguage = true }; + var langUk = new Language("en-UK") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2559,7 +2559,7 @@ namespace Umbraco.Tests.Services var languageService = ServiceContext.LocalizationService; //var langFr = new Language("fr-FR") { IsDefaultVariantLanguage = true }; - var langXx = new Language("pt-PT") { IsDefaultVariantLanguage = true }; + var langXx = new Language("pt-PT") { IsDefault = true }; var langFr = new Language("fr-FR"); var langUk = new Language("en-UK"); var langDe = new Language("de-DE"); diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index 033fa08d4a..a61b208c4d 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -192,6 +192,20 @@ namespace Umbraco.Tests.Services Assert.Null(language); } + [Test] + public void Can_Delete_Language_Used_As_Fallback() + { + var danish = ServiceContext.LocalizationService.GetLanguageByIsoCode("da-DK"); + var norwegian = new Language("nb-NO") { CultureName = "Norwegian", FallbackLanguageId = danish.Id }; + ServiceContext.LocalizationService.Save(norwegian, 0); + var languageId = danish.Id; + + ServiceContext.LocalizationService.Delete(danish); + + var language = ServiceContext.LocalizationService.GetLanguageById(languageId); + Assert.Null(language); + } + [Test] public void Can_Create_DictionaryItem_At_Root() { @@ -362,21 +376,21 @@ namespace Umbraco.Tests.Services { var localizationService = ServiceContext.LocalizationService; var language = new Core.Models.Language("en-AU"); - language.IsDefaultVariantLanguage = true; + language.IsDefault = true; localizationService.Save(language); var result = localizationService.GetLanguageById(language.Id); - Assert.IsTrue(result.IsDefaultVariantLanguage); + Assert.IsTrue(result.IsDefault); var language2 = new Core.Models.Language("en-NZ"); - language2.IsDefaultVariantLanguage = true; + language2.IsDefault = true; localizationService.Save(language2); var result2 = localizationService.GetLanguageById(language2.Id); //re-get result = localizationService.GetLanguageById(language.Id); - Assert.IsTrue(result2.IsDefaultVariantLanguage); - Assert.IsFalse(result.IsDefaultVariantLanguage); + Assert.IsTrue(result2.IsDefault); + Assert.IsFalse(result.IsDefault); } [Test] diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index 08e91071cb..81dec809c8 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -30,6 +32,14 @@ namespace Umbraco.Tests.Strings Assert.IsInstanceOf(helper); } + [TestCase("hello-world.png", "Hello World")] + [TestCase("hello-world .png", "Hello World")] + [TestCase("_hello-world __1.png", "Hello World 1")] + public void To_Friendly_Name(string first, string second) + { + Assert.AreEqual(first.ToFriendlyName(), second); + } + [TestCase("hello", "world", false)] [TestCase("hello", "hello", true)] [TestCase("hellohellohellohellohellohellohello", "hellohellohellohellohellohellohelloo", false)] diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 19e5d12650..9af90f1d8e 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -77,7 +77,6 @@ namespace Umbraco.Tests.TestHelpers MockService(), MockService(), MockService(), - MockService(), MockService()); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 00a193b40a..37933fa1c2 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -119,7 +119,6 @@ namespace Umbraco.Tests.TestHelpers var externalLoginService = GetLazyService(container, c => new ExternalLoginService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); var publicAccessService = GetLazyService(container, c => new PublicAccessService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var taskService = GetLazyService(container, c => new TaskService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); var domainService = GetLazyService(container, c => new DomainService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); var auditService = GetLazyService(container, c => new AuditService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); @@ -189,7 +188,6 @@ namespace Umbraco.Tests.TestHelpers return new ServiceContext( publicAccessService, - taskService, domainService, auditService, localizedTextService, diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 5951a87d77..26c4c1e785 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -35,6 +35,7 @@ using Umbraco.Web; using Umbraco.Web.Services; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Composing.Composers; +using Umbraco.Web.ContentApps; using Umbraco.Web._Legacy.Actions; using Current = Umbraco.Core.Composing.Current; using Umbraco.Web.Routing; @@ -168,6 +169,11 @@ namespace Umbraco.Tests.Testing Container.RegisterSingleton(f => new SerilogLogger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config")))); Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); } + else if (option == UmbracoTestOptions.Logger.Console) + { + Container.RegisterSingleton(f => new ConsoleLogger()); + Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); + } Container.RegisterSingleton(f => new ProfilingLogger(f.GetInstance(), f.GetInstance())); } @@ -203,6 +209,9 @@ namespace Umbraco.Tests.Testing Container.RegisterSingleton(); Container.RegisterSingleton(); + + // register empty content apps collection + Container.RegisterCollectionBuilder(); } protected virtual void ComposeCacheHelper() diff --git a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs index deefd33946..5248026788 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs @@ -7,7 +7,9 @@ // pure mocks Mock, // Serilog for tests - Serilog + Serilog, + // console logger + Console } public enum Database diff --git a/src/Umbraco.Tests/UI/LegacyDialogTests.cs b/src/Umbraco.Tests/UI/LegacyDialogTests.cs index ffbca1e0e8..ba7c4f0e66 100644 --- a/src/Umbraco.Tests/UI/LegacyDialogTests.cs +++ b/src/Umbraco.Tests/UI/LegacyDialogTests.cs @@ -25,8 +25,8 @@ namespace Umbraco.Tests.UI [TestCase(typeof(MemberGroupTasks), Constants.Applications.Members)] [TestCase(typeof(dictionaryTasks), Constants.Applications.Settings)] - [TestCase(typeof(macroTasks), Constants.Applications.Developer)] - [TestCase(typeof(CreatedPackageTasks), Constants.Applications.Developer)] + [TestCase(typeof(macroTasks), Constants.Applications.Packages)] + [TestCase(typeof(CreatedPackageTasks), Constants.Applications.Packages)] public void Check_Assigned_Apps_For_Tasks(Type taskType, string app) { var task = (LegacyDialogTask)Activator.CreateInstance(taskType); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 369b3b4055..e11695bef5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -78,6 +78,9 @@ + + 1.8.9 + @@ -121,6 +124,8 @@ + + @@ -223,8 +228,6 @@ - - @@ -259,8 +262,6 @@ - - diff --git a/src/Umbraco.Tests/Web/AngularIntegration/JsInitializationTests.cs b/src/Umbraco.Tests/Web/AngularIntegration/JsInitializationTests.cs index dacba39163..f16abd578a 100644 --- a/src/Umbraco.Tests/Web/AngularIntegration/JsInitializationTests.cs +++ b/src/Umbraco.Tests/Web/AngularIntegration/JsInitializationTests.cs @@ -19,15 +19,17 @@ namespace Umbraco.Tests.Web.AngularIntegration [Test] public void Parse_Main() { - var result = JsInitialization.WriteScript(new[] {"[World]", "Hello" }); + var result = JsInitialization.WriteScript("[World]", "Hello", "Blah"); Assert.AreEqual(@"LazyLoad.js([World], function () { //we need to set the legacy UmbClientMgr path - UmbClientMgr.setUmbracoPath('Hello'); + if ((typeof UmbClientMgr) !== ""undefined"") { + UmbClientMgr.setUmbracoPath('Hello'); + } jQuery(document).ready(function () { - angular.bootstrap(document, ['umbraco']); + angular.bootstrap(document, ['Blah']); }); });".StripWhitespace(), result.StripWhitespace()); diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index e60a4d9397..db496dbfd3 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,6 +1,6 @@ using System; -using System.Linq; using System.Web; +using HtmlAgilityPack; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -10,7 +10,6 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; @@ -18,6 +17,8 @@ using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.Templates; +using System.Linq; +using Umbraco.Core.Services; namespace Umbraco.Tests.Web { @@ -59,6 +60,14 @@ namespace Umbraco.Tests.Web [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] //this one has an invalid char so won't match [TestCase("hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")] + // with a-tag with data-udi attribute, that needs to be stripped + [TestCase("hello world ", "hello world ")] + // with a-tag with data-udi attribute spelled wrong, so don't need stripping + [TestCase("hello world ", "hello world ")] + // with a img-tag with data-udi id, that needs to be strippde + [TestCase("hello world ", "hello world ")] + // with a img-tag with data-udi id spelled wrong, so don't need stripping + [TestCase("hello world ", "hello world ")] public void ParseLocalLinks(string input, string result) { var serviceCtxMock = new TestObjects(null).GetServiceContextMock(); @@ -99,7 +108,7 @@ namespace Umbraco.Tests.Web //setup a quick mock of the WebRouting section Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), //pass in the custom url provider - new[]{ testUrlProvider.Object }, + new[] { testUrlProvider.Object }, globalSettings, new TestVariationContextAccessor(), true)) @@ -109,5 +118,27 @@ namespace Umbraco.Tests.Web Assert.AreEqual(result, output); } } + + [Test] + public void StripDataUdiAttributesUsingSrtringOnLinks() + { + var input = "hello world "; + var expected = "hello world "; + + var result = TemplateUtilities.StripUdiDataAttributes(input); + + Assert.AreEqual(expected, result); + } + + [Test] + public void StripDataUdiAttributesUsingStringOnImages() + { + var input = "hello world "; + var expected = "hello world "; + + var result = TemplateUtilities.StripUdiDataAttributes(input); + + Assert.AreEqual(expected, result); + } } } diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json index 1d7096cb29..9f9c4fff01 100644 --- a/src/Umbraco.Web.UI.Client/bower.json +++ b/src/Umbraco.Web.UI.Client/bower.json @@ -1,98 +1,100 @@ { - "name": "umbraco", - "version": "7", - "homepage": "https://github.com/umbraco/Umbraco-CMS", - "authors": [ - "Shannon " - ], - "description": "Umbraco CMS", - "license": "MIT", - "private": true, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "angular": "~1.7.2", - "angular-cookies": "~1.7.2", - "angular-sanitize": "~1.7.2", - "angular-touch": "~1.7.2", - "angular-route": "~1.7.2", - "angular-animate": "~1.7.2", - "angular-i18n": "~1.7.2", - "signalr": "^2.2.1", - "typeahead.js": "~0.10.5", - "underscore": "~1.9.1", - "rgrove-lazyload": "*", - "bootstrap-social": "~4.8.0", - "jquery": "2.2.4", - "jquery-ui": "~1.12.0", - "jquery-migrate": "1.4.0", - "jquery-validate": "~1.17.0", - "jquery-validation-unobtrusive": "3.2.10", - "angular-dynamic-locale": "~0.1.36", - "ng-file-upload": "~12.2.13", - "tinymce": "~4.7.1", - "codemirror": "~5.3.0", - "angular-local-storage": "~0.7.1", - "moment": "~2.10.3", - "ace-builds": "~1.3.0", - "clipboard": "~2.0.0", - "font-awesome": "~4.2", - "animejs": "^2.2.0", - "angular-ui-sortable": "0.14.4", - "angular-messages": "^1.7.2" - }, - "install": { - "path": "lib-bower", - "ignore": [ - "font-awesome", - "bootstrap", - "codemirror", - "ace-builds" + "name": "umbraco", + "version": "7", + "homepage": "https://github.com/umbraco/Umbraco-CMS", + "authors": [ + "Shannon " ], - "sources": { - "moment": [ - "bower_components/moment/min/moment.min.js", - "bower_components/moment/min/moment-with-locales.js", - "bower_components/moment/min/moment-with-locales.min.js", - "bower_components/moment/locale/*.js" + "description": "Umbraco CMS", + "license": "MIT", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "angular": "~1.7.4", + "angular-cookies": "~1.7.4", + "angular-sanitize": "~1.7.4", + "angular-touch": "~1.7.4", + "angular-route": "~1.7.4", + "angular-animate": "~1.7.4", + "angular-i18n": "~1.7.4", + "signalr": "^2.2.1", + "typeahead.js": "~0.10.5", + "underscore": "~1.9.1", + "rgrove-lazyload": "*", + "bootstrap-social": "~4.8.0", + "jquery": "2.2.4", + "jquery-ui": "~1.12.0", + "jquery-migrate": "1.4.0", + "jquery-validate": "~1.17.0", + "jquery-validation-unobtrusive": "3.2.10", + "angular-dynamic-locale": "~0.1.36", + "ng-file-upload": "~12.2.13", + "tinymce": "~4.7.1", + "codemirror": "~5.3.0", + "angular-local-storage": "~0.7.1", + "moment": "~2.10.3", + "ace-builds": "~1.3.0", + "clipboard": "~2.0.0", + "font-awesome": "~4.2", + "animejs": "^2.2.0", + "angular-ui-sortable": "0.14.4", + "angular-messages": "^1.7.2", + "jsdiff": "^3.4.0" + }, + "install": { + "path": "lib-bower", + "ignore": [ + "font-awesome", + "bootstrap", + "codemirror", + "ace-builds" ], - "underscore": [ - "bower_components/underscore/underscore-min.js", - "bower_components/underscore/underscore-min.map" - ], - "jquery": [ - "bower_components/jquery/dist/jquery.min.js", - "bower_components/jquery/dist/jquery.min.map" - ], - "angular-dynamic-locale": [ - "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js", - "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map" - ], - "angular-local-storage": [ - "bower_components/angular-local-storage/dist/angular-local-storage.min.js", - "bower_components/angular-local-storage/dist/angular-local-storage.min.js.map" - ], - "tinymce": [ - "bower_components/tinymce/tinymce.min.js" - ], - "angular-i18n": "bower_components/angular-i18n/angular-locale_*.js", - "typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js", - "rgrove-lazyload": "bower_components/rgrove-lazyload/lazyload.js", - "ng-file-upload": "bower_components/ng-file-upload/ng-file-upload.min.js", - "jquery-ui": "bower_components/jquery-ui/jquery-ui.min.js", - "jquery-migrate": "bower_components/jquery-migrate/jquery-migrate.min.js", - "clipboard": "bower_components/clipboard/dist/clipboard.min.js", - "animejs": "bower_components/animejs/anime.min.js", - "jquery-validate": "bower_components/jquery-validate/dist/jquery.validate.min.js", - "jquery-validation-unobtrusive": "bower_components/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js" + "sources": { + "moment": [ + "bower_components/moment/min/moment.min.js", + "bower_components/moment/min/moment-with-locales.js", + "bower_components/moment/min/moment-with-locales.min.js", + "bower_components/moment/locale/*.js" + ], + "underscore": [ + "bower_components/underscore/underscore-min.js", + "bower_components/underscore/underscore-min.map" + ], + "jquery": [ + "bower_components/jquery/dist/jquery.min.js", + "bower_components/jquery/dist/jquery.min.map" + ], + "angular-dynamic-locale": [ + "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js", + "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map" + ], + "angular-local-storage": [ + "bower_components/angular-local-storage/dist/angular-local-storage.min.js", + "bower_components/angular-local-storage/dist/angular-local-storage.min.js.map" + ], + "tinymce": [ + "bower_components/tinymce/tinymce.min.js" + ], + "angular-i18n": "bower_components/angular-i18n/angular-locale_*.js", + "typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js", + "rgrove-lazyload": "bower_components/rgrove-lazyload/lazyload.js", + "ng-file-upload": "bower_components/ng-file-upload/ng-file-upload.min.js", + "jquery-ui": "bower_components/jquery-ui/jquery-ui.min.js", + "jquery-migrate": "bower_components/jquery-migrate/jquery-migrate.min.js", + "clipboard": "bower_components/clipboard/dist/clipboard.min.js", + "animejs": "bower_components/animejs/anime.min.js", + "jquery-validate": "bower_components/jquery-validate/dist/jquery.validate.min.js", + "jquery-validation-unobtrusive": "bower_components/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js", + "jsdiff": "bower_components/jsdiff/diff.min.js" + } + }, + "devDependencies": { + "angular-mocks": "~1.7.2" } - }, - "devDependencies": { - "angular-mocks": "~1.7.2" - } } diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 5afe6ecf44..2d34e8ecb3 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -80,7 +80,7 @@ var sources = { //js files for backoffie //processed in the js task js: { - preview: { files: ["src/canvasdesigner/**/*.js"], out: "umbraco.canvasdesigner.js" }, + preview: { files: ["src/preview/**/*.js"], out: "umbraco.preview.js" }, installer: { files: ["src/installer/**/*.js"], out: "umbraco.installer.js" }, controllers: { files: ["src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" }, directives: { files: ["src/common/directives/**/*.js"], out: "umbraco.directives.js" }, diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 2497bdc592..4f2ca6a4b2 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1,9 +1,64 @@ { - "name": "umbraco", - "version": "0.0.0", - "lockfileVersion": 1, "requires": true, + "lockfileVersion": 1, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "4.0.0" + }, + "dependencies": { + "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.3" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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" + } + } + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -16,7 +71,7 @@ "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", "dev": true, "requires": { - "mime-types": "~2.1.6", + "mime-types": "2.1.20", "negotiator": "0.5.3" } }, @@ -26,43 +81,43 @@ "integrity": "sha1-vsUWovci59UPX59C+Bt387lUSLo=", "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" + "convert-source-map": "1.6.0", + "glob": "7.1.3", + "indx": "0.2.3", + "lodash.clone": "4.5.0", + "lodash.defaults": "4.2.0", + "lodash.flatten": "4.4.0", + "lodash.merge": "4.6.1", + "lodash.partialright": "4.2.1", + "lodash.pick": "4.4.0", + "lodash.uniq": "4.5.0", + "resolve": "1.8.1", + "semver": "5.5.1", + "uglify-js": "2.8.29", + "when": "3.7.8" }, "dependencies": { "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true } } }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true }, "acorn-jsx": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", + "integrity": "sha1-6OQeSOov4MiWdAYQq2pP/YrdIl4=", "dev": true, "requires": { - "acorn": "^5.0.3" + "acorn": "5.7.3" } }, "addressparser": { @@ -81,20 +136,22 @@ "agent-base": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "integrity": "sha1-2J5ZmfeXh1Z0wH2H8mD8Qeg+jKk=", "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "es6-promisify": "5.0.0" } }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", "dev": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "ajv-keywords": { @@ -109,9 +166,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" }, "dependencies": { "kind-of": { @@ -120,7 +177,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -140,36 +197,36 @@ "amqplib": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz", - "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==", + "integrity": "sha1-0tcxPH/6pNELzx5iUt5FkbbMe2M=", "dev": true, "optional": true, "requires": { - "bitsyntax": "~0.0.4", - "bluebird": "^3.4.6", + "bitsyntax": "0.0.4", + "bluebird": "3.5.2", "buffer-more-ints": "0.0.2", - "readable-stream": "1.x >=1.1.9", - "safe-buffer": "^5.0.1" + "readable-stream": "1.1.14", + "safe-buffer": "5.1.2" } }, "angular": { "version": "1.3.20", - "resolved": "https://registry.npmjs.org/angular/-/angular-1.3.20.tgz", + "resolved": "http://registry.npmjs.org/angular/-/angular-1.3.20.tgz", "integrity": "sha1-sjo9fF5/mffZW5tNSbmsNVJpuu4=", "dev": true }, "angular-animate": { "version": "1.3.20", - "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.3.20.tgz", + "resolved": "http://registry.npmjs.org/angular-animate/-/angular-animate-1.3.20.tgz", "integrity": "sha1-0XB8cn+K0N8hxKLewgzX/ElFtSo=", "dev": true }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { - "ansi-wrap": "^0.1.0" + "ansi-wrap": "0.1.0" } }, "ansi-cyan": { @@ -184,7 +241,7 @@ "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "integrity": "sha1-9zIHu4EgfXX9bIPxJa8m7qN4yjA=", "dev": true }, "ansi-gray": { @@ -229,8 +286,8 @@ "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" }, "dependencies": { "arr-diff": { @@ -239,7 +296,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -254,9 +311,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" } }, "expand-brackets": { @@ -265,7 +322,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -274,7 +331,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-extglob": { @@ -289,7 +346,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "kind-of": { @@ -298,7 +355,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "micromatch": { @@ -307,19 +364,19 @@ "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" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } } } @@ -330,7 +387,7 @@ "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", "dev": true, "requires": { - "file-type": "^3.1.0" + "file-type": "3.9.0" }, "dependencies": { "file-type": { @@ -353,7 +410,7 @@ "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -404,7 +461,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -422,7 +479,7 @@ "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==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "arrify": { @@ -439,10 +496,13 @@ "optional": true }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } }, "assert-plus": { "version": "0.2.0", @@ -459,17 +519,17 @@ "ast-types": { "version": "0.11.5", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", - "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", + "integrity": "sha1-mJCCXWYMA8KDOfMV6foKNg4x7Cg=", "dev": true, "optional": true }, "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.11" } }, "async-each": { @@ -488,7 +548,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -498,9 +558,9 @@ "dev": true }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "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": { @@ -509,12 +569,12 @@ "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", "dev": true, "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000889", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" } }, "aws-sign2": { @@ -524,14 +584,14 @@ "dev": true }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha1-1NDpudv8p3vwjusKikcVUP454ok=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "axios": { "version": "0.15.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.15.3.tgz", "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", "dev": true, "optional": true, @@ -541,7 +601,7 @@ "dependencies": { "follow-redirects": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", "dev": true, "optional": true, @@ -551,17 +611,6 @@ } } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -580,13 +629,13 @@ "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "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" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -595,7 +644,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -604,7 +653,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -613,7 +662,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -622,9 +671,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -649,7 +698,7 @@ }, "basic-auth": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz", + "resolved": "http://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz", "integrity": "sha1-Awk1sB3nyblKgksp8/zLdQ06UpA=", "dev": true }, @@ -666,13 +715,13 @@ "dev": true }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "beeper": { @@ -697,13 +746,13 @@ "dev": true, "optional": true, "requires": { - "archive-type": "^3.0.1", - "decompress": "^3.0.0", - "download": "^4.1.2", - "exec-series": "^1.0.0", - "rimraf": "^2.2.6", - "tempfile": "^1.0.0", - "url-regex": "^3.0.0" + "archive-type": "3.2.0", + "decompress": "3.0.0", + "download": "4.4.3", + "exec-series": "1.0.3", + "rimraf": "2.6.2", + "tempfile": "1.1.1", + "url-regex": "3.2.0" }, "dependencies": { "tempfile": { @@ -713,8 +762,8 @@ "dev": true, "optional": true, "requires": { - "os-tmpdir": "^1.0.0", - "uuid": "^2.0.1" + "os-tmpdir": "1.0.2", + "uuid": "2.0.3" } }, "uuid": { @@ -733,7 +782,7 @@ "dev": true, "optional": true, "requires": { - "executable": "^1.0.0" + "executable": "1.1.0" } }, "bin-version": { @@ -743,7 +792,7 @@ "dev": true, "optional": true, "requires": { - "find-versions": "^1.0.0" + "find-versions": "1.2.1" } }, "bin-version-check": { @@ -753,10 +802,10 @@ "dev": true, "optional": true, "requires": { - "bin-version": "^1.0.0", - "minimist": "^1.1.0", - "semver": "^4.0.3", - "semver-truncate": "^1.0.0" + "bin-version": "1.0.4", + "minimist": "1.2.0", + "semver": "4.3.6", + "semver-truncate": "1.1.2" }, "dependencies": { "minimist": { @@ -775,18 +824,18 @@ "dev": true, "optional": true, "requires": { - "bin-check": "^2.0.0", - "bin-version-check": "^2.1.0", - "download": "^4.0.0", - "each-async": "^1.1.1", - "lazy-req": "^1.0.0", - "os-filter-obj": "^1.0.0" + "bin-check": "2.0.0", + "bin-version-check": "2.1.0", + "download": "4.4.3", + "each-async": "1.1.1", + "lazy-req": "1.1.0", + "os-filter-obj": "1.0.3" } }, "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", + "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, "bitsyntax": { @@ -805,8 +854,8 @@ "integrity": "sha1-oWCRFxcQPAdBDO9j71Gzl8Alr5w=", "dev": true, "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2" }, "dependencies": { "isarray": { @@ -817,7 +866,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -833,7 +882,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -848,9 +897,9 @@ "dev": true }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", "dev": true }, "body-parser": { @@ -860,20 +909,20 @@ "dev": true, "requires": { "bytes": "2.1.0", - "content-type": "~1.0.1", - "debug": "~2.2.0", - "depd": "~1.0.1", - "http-errors": "~1.3.1", + "content-type": "1.0.4", + "debug": "2.2.0", + "depd": "1.0.1", + "http-errors": "1.3.1", "iconv-lite": "0.4.11", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "4.0.0", - "raw-body": "~2.1.2", - "type-is": "~1.6.6" + "raw-body": "2.1.7", + "type-is": "1.6.16" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -900,7 +949,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "bower": { @@ -915,14 +964,14 @@ "integrity": "sha1-z3g5tlQh0rJwqyLHT8hYpV46E3A=", "dev": true, "requires": { - "async": "^2.1.4", - "bower": "^1.8.0", - "colors": "^1.1.2", - "glob": "^7.1.1", - "lodash": "^4.17.2", - "mkdirp": "^0.5.1", - "node-fs": "~0.1.7", - "nopt": "^3.0.6" + "async": "2.6.1", + "bower": "1.8.4", + "colors": "1.3.2", + "glob": "7.1.3", + "lodash": "4.17.11", + "mkdirp": "0.5.1", + "node-fs": "0.1.7", + "nopt": "3.0.6" } }, "brace-expansion": { @@ -931,7 +980,7 @@ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -941,16 +990,16 @@ "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "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" + "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.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -959,7 +1008,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -970,8 +1019,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-db": "1.0.30000889", + "electron-to-chromium": "1.3.73" } }, "buffer-alloc": { @@ -980,8 +1029,8 @@ "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", "dev": true, "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" } }, "buffer-alloc-unsafe": { @@ -990,6 +1039,12 @@ "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", "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 + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -997,11 +1052,10 @@ "dev": true }, "buffer-from": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", - "integrity": "sha1-FfS5vO8BIETfMRQsFDM8r24CYNA=", - "dev": true, - "optional": true + "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 }, "buffer-more-ints": { "version": "0.0.2", @@ -1015,10 +1069,10 @@ "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", "dev": true, "requires": { - "file-type": "^3.1.0", - "readable-stream": "^2.0.2", - "uuid": "^2.0.1", - "vinyl": "^1.0.0" + "file-type": "3.9.0", + "readable-stream": "2.3.6", + "uuid": "2.0.3", + "vinyl": "1.2.0" }, "dependencies": { "file-type": { @@ -1035,8 +1089,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -1054,12 +1108,12 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "uuid": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", "dev": true }, @@ -1069,8 +1123,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -1082,7 +1136,7 @@ "integrity": "sha1-z7GtlWjTujz+k1upq92VLeiKqyo=", "dev": true, "requires": { - "readable-stream": "^1.0.33" + "readable-stream": "1.1.14" } }, "buildmail": { @@ -1099,6 +1153,15 @@ "nodemailer-fetch": "1.6.0", "nodemailer-shared": "1.1.0", "punycode": "1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + } } }, "builtin-modules": { @@ -1119,15 +1182,15 @@ "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "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" + "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" } }, "caller-path": { @@ -1136,7 +1199,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsite": { @@ -1152,9 +1215,9 @@ "dev": true }, "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true }, "camelcase-keys": { @@ -1163,16 +1226,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": 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 - } + "camelcase": "2.1.1", + "map-obj": "1.0.1" } }, "caniuse-api": { @@ -1181,16 +1236,16 @@ "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "dev": true, "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000889", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" } }, "caniuse-db": { - "version": "1.0.30000836", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000836.tgz", - "integrity": "sha1-eItsj28CmRdDsYzbvVT5bQW0uVo=", + "version": "1.0.30000889", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000889.tgz", + "integrity": "sha512-Rf9Sbm2KS7s6Rk8iNeI5zJdquqctXBXAfy/bb1tCCYRds5RAaHNdyt2D4z8TSRToDkYsAwiSBV/bFHR+4IgTiw==", "dev": true }, "canonical-path": { @@ -1200,9 +1255,9 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -1217,10 +1272,10 @@ "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", "dev": true, "requires": { - "get-proxy": "^1.0.1", - "is-obj": "^1.0.0", - "object-assign": "^3.0.0", - "tunnel-agent": "^0.4.0" + "get-proxy": "1.1.0", + "is-obj": "1.0.1", + "object-assign": "3.0.0", + "tunnel-agent": "0.4.3" }, "dependencies": { "object-assign": { @@ -1228,12 +1283,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", "dev": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true } } }, @@ -1243,13 +1292,13 @@ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1269,9 +1318,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chokidar": { @@ -1280,15 +1329,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.2.4", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1" }, "dependencies": { "glob-parent": { @@ -1297,7 +1346,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "is-extglob": { @@ -1312,7 +1361,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -1329,7 +1378,7 @@ "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", "dev": true, "requires": { - "chalk": "^1.1.3" + "chalk": "1.1.3" } }, "class-utils": { @@ -1338,10 +1387,10 @@ "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -1350,7 +1399,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -1361,7 +1410,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -1376,9 +1425,17 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + } } }, "clone": { @@ -1405,9 +1462,9 @@ "integrity": "sha1-1ZHe5Kj4vBXaQ86X3O66E9Q+KmU=", "dev": true, "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -1418,8 +1475,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -1437,15 +1494,15 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } }, "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", + "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", "dev": true }, "coa": { @@ -1454,7 +1511,7 @@ "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", "dev": true, "requires": { - "q": "^1.1.2" + "q": "1.5.1" } }, "collection-visit": { @@ -1463,8 +1520,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color": { @@ -1473,18 +1530,18 @@ "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", "dev": true, "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" + "clone": "1.0.4", + "color-convert": "1.9.3", + "color-string": "0.3.0" } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=", + "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.1" + "color-name": "1.1.3" } }, "color-name": { @@ -1499,7 +1556,7 @@ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true, "requires": { - "color-name": "^1.0.0" + "color-name": "1.1.3" } }, "color-support": { @@ -1514,15 +1571,15 @@ "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", "dev": true, "requires": { - "color": "^0.11.0", + "color": "0.11.4", "css-color-names": "0.0.4", - "has": "^1.0.1" + "has": "1.0.3" } }, "colors": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.4.tgz", - "integrity": "sha1-4MtB0+SyCAazv8J/RVnwG5S8L3w=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz", + "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==", "dev": true }, "combine-lists": { @@ -1531,21 +1588,21 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "^4.5.0" + "lodash": "4.17.11" } }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { @@ -1571,12 +1628,12 @@ "dev": true }, "compressible": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", - "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", + "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", "dev": true, "requires": { - "mime-db": ">= 1.33.0 < 2" + "mime-db": "1.36.0" } }, "compression": { @@ -1585,17 +1642,17 @@ "integrity": "sha1-sDuNhub4rSloPLqN+R3cb/x3s5U=", "dev": true, "requires": { - "accepts": "~1.2.12", + "accepts": "1.2.13", "bytes": "2.1.0", - "compressible": "~2.0.5", - "debug": "~2.2.0", - "on-headers": "~1.0.0", - "vary": "~1.0.1" + "compressible": "2.0.15", + "debug": "2.2.0", + "on-headers": "1.0.1", + "vary": "1.0.1" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1617,14 +1674,15 @@ "dev": true }, "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "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": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" }, "dependencies": { "isarray": { @@ -1635,8 +1693,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -1654,7 +1712,7 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -1665,7 +1723,7 @@ "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=", "dev": true, "requires": { - "source-map": "^0.6.1" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -1683,41 +1741,41 @@ "dev": true, "requires": { "basic-auth-connect": "1.0.0", - "body-parser": "~1.13.3", + "body-parser": "1.13.3", "bytes": "2.1.0", - "compression": "~1.5.2", - "connect-timeout": "~1.6.2", - "content-type": "~1.0.1", + "compression": "1.5.2", + "connect-timeout": "1.6.2", + "content-type": "1.0.4", "cookie": "0.1.3", - "cookie-parser": "~1.3.5", + "cookie-parser": "1.3.5", "cookie-signature": "1.0.6", - "csurf": "~1.8.3", - "debug": "~2.2.0", - "depd": "~1.0.1", - "errorhandler": "~1.4.2", - "express-session": "~1.11.3", + "csurf": "1.8.3", + "debug": "2.2.0", + "depd": "1.0.1", + "errorhandler": "1.4.3", + "express-session": "1.11.3", "finalhandler": "0.4.0", "fresh": "0.3.0", - "http-errors": "~1.3.1", - "method-override": "~2.3.5", - "morgan": "~1.6.1", + "http-errors": "1.3.1", + "method-override": "2.3.10", + "morgan": "1.6.1", "multiparty": "3.3.2", - "on-headers": "~1.0.0", - "parseurl": "~1.3.0", + "on-headers": "1.0.1", + "parseurl": "1.3.2", "pause": "0.1.0", "qs": "4.0.0", - "response-time": "~2.3.1", - "serve-favicon": "~2.3.0", - "serve-index": "~1.7.2", - "serve-static": "~1.10.0", - "type-is": "~1.6.6", + "response-time": "2.3.2", + "serve-favicon": "2.3.2", + "serve-index": "1.7.3", + "serve-static": "1.10.3", + "type-is": "1.6.16", "utils-merge": "1.0.0", - "vhost": "~3.0.1" + "vhost": "3.0.2" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1744,15 +1802,15 @@ "integrity": "sha1-3ppexh4zoStu2qt7XwYumMWZuI4=", "dev": true, "requires": { - "debug": "~2.2.0", - "http-errors": "~1.3.1", + "debug": "2.2.0", + "http-errors": "1.3.1", "ms": "0.7.1", - "on-headers": "~1.0.0" + "on-headers": "1.0.1" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1780,7 +1838,7 @@ "integrity": "sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=", "dev": true, "requires": { - "bluebird": "^3.1.1" + "bluebird": "3.5.2" } }, "content-type": { @@ -1790,10 +1848,13 @@ "dev": true }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true + "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.2" + } }, "cookie": { "version": "0.1.3", @@ -1826,7 +1887,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=", "dev": true }, "core-util-is": { @@ -1841,18 +1902,18 @@ "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", "dev": true, "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" + "is-directory": "0.3.1", + "js-yaml": "3.7.0", + "minimist": "1.2.0", + "object-assign": "4.1.1", + "os-homedir": "1.0.2", + "parse-json": "2.2.0", + "require-from-string": "1.2.1" }, "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -1870,31 +1931,27 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.1" } }, "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=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, - "optional": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.5.1", + "shebang-command": "1.2.0", + "which": "1.3.1" }, "dependencies": { - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", - "dev": true, - "optional": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true } } }, @@ -1904,7 +1961,7 @@ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "2.x.x" + "boom": "2.10.1" } }, "csrf": { @@ -1925,16 +1982,16 @@ "dev": true }, "css-select": { - "version": "1.3.0-rc0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.3.0-rc0.tgz", - "integrity": "sha1-b5MZaqrnN2ZuoQNqjLFKj8t6kjE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.0.tgz", + "integrity": "sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg==", "dev": true, "optional": true, "requires": { - "boolbase": "^1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "^1.0.1" + "boolbase": "1.0.0", + "css-what": "2.1.0", + "domutils": "1.7.0", + "nth-check": "1.0.1" } }, "css-select-base-adapter": { @@ -1945,14 +2002,14 @@ "optional": true }, "css-tree": { - "version": "1.0.0-alpha25", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha25.tgz", - "integrity": "sha1-G7+r+/bu708B2RCP8u3Qvi/jVZc=", + "version": "1.0.0-alpha.28", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", + "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", "dev": true, "optional": true, "requires": { - "mdn-data": "^1.0.0", - "source-map": "^0.5.3" + "mdn-data": "1.1.4", + "source-map": "0.5.7" } }, "css-url-regex": { @@ -1971,7 +2028,7 @@ }, "cssnano": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "resolved": "http://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, "requires": { @@ -2015,8 +2072,8 @@ "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", "dev": true, "requires": { - "clap": "^1.0.9", - "source-map": "^0.5.3" + "clap": "1.2.3", + "source-map": "0.5.7" } }, "csurf": { @@ -2027,8 +2084,8 @@ "requires": { "cookie": "0.1.3", "cookie-signature": "1.0.6", - "csrf": "~3.0.0", - "http-errors": "~1.3.1" + "csrf": "3.0.6", + "http-errors": "1.3.1" } }, "currently-unhandled": { @@ -2037,7 +2094,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "custom-event": { @@ -2052,7 +2109,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -2066,7 +2123,7 @@ "data-uri-to-buffer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", + "integrity": "sha1-dxY+qcINhkG0cH6PGKvfmnjzSDU=", "dev": true, "optional": true }, @@ -2109,15 +2166,15 @@ "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", "dev": true, "requires": { - "buffer-to-vinyl": "^1.0.0", - "concat-stream": "^1.4.6", - "decompress-tar": "^3.0.0", - "decompress-tarbz2": "^3.0.0", - "decompress-targz": "^3.0.0", - "decompress-unzip": "^3.0.0", - "stream-combiner2": "^1.1.1", - "vinyl-assign": "^1.0.1", - "vinyl-fs": "^2.2.0" + "buffer-to-vinyl": "1.1.0", + "concat-stream": "1.6.2", + "decompress-tar": "3.1.0", + "decompress-tarbz2": "3.1.0", + "decompress-targz": "3.1.0", + "decompress-unzip": "3.4.0", + "stream-combiner2": "1.1.1", + "vinyl-assign": "1.2.1", + "vinyl-fs": "2.4.4" }, "dependencies": { "arr-diff": { @@ -2126,7 +2183,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -2141,9 +2198,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" } }, "expand-brackets": { @@ -2152,7 +2209,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -2161,7 +2218,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "glob": { @@ -2170,11 +2227,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-stream": { @@ -2183,14 +2240,14 @@ "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, "requires": { - "extend": "^3.0.0", - "glob": "^5.0.3", - "glob-parent": "^3.0.0", - "micromatch": "^2.3.7", - "ordered-read-streams": "^0.3.0", - "through2": "^0.6.0", - "to-absolute-glob": "^0.1.1", - "unique-stream": "^2.0.2" + "extend": "3.0.2", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" }, "dependencies": { "isarray": { @@ -2201,7 +2258,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2223,8 +2280,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -2247,7 +2304,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "isarray": { @@ -2262,7 +2319,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "micromatch": { @@ -2271,19 +2328,19 @@ "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" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "ordered-read-streams": { @@ -2292,13 +2349,13 @@ "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, "requires": { - "is-stream": "^1.0.1", - "readable-stream": "^2.0.1" + "is-stream": "1.1.0", + "readable-stream": "2.3.6" } }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -2314,7 +2371,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -2326,17 +2383,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "^1.0.0", - "strip-bom": "^2.0.0" + "is-utf8": "0.2.1" } }, "unique-stream": { @@ -2345,8 +2392,8 @@ "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", "dev": true, "requires": { - "json-stable-stringify": "^1.0.0", - "through2-filter": "^2.0.0" + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" } }, "vinyl": { @@ -2355,8 +2402,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -2366,23 +2413,23 @@ "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, "requires": { - "duplexify": "^3.2.0", - "glob-stream": "^5.3.2", - "graceful-fs": "^4.0.0", + "duplexify": "3.6.0", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", "gulp-sourcemaps": "1.6.0", - "is-valid-glob": "^0.3.0", - "lazystream": "^1.0.0", - "lodash.isequal": "^4.0.0", - "merge-stream": "^1.0.0", - "mkdirp": "^0.5.0", - "object-assign": "^4.0.0", - "readable-stream": "^2.0.4", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^1.0.0", - "through2": "^2.0.0", - "through2-filter": "^2.0.0", - "vali-date": "^1.0.0", - "vinyl": "^1.0.0" + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.6", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" } } } @@ -2393,12 +2440,12 @@ "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", "dev": true, "requires": { - "is-tar": "^1.0.0", - "object-assign": "^2.0.0", - "strip-dirs": "^1.0.0", - "tar-stream": "^1.1.1", - "through2": "^0.6.1", - "vinyl": "^0.4.3" + "is-tar": "1.0.0", + "object-assign": "2.1.1", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.2", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -2415,7 +2462,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2431,8 +2478,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -2441,8 +2488,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -2453,13 +2500,13 @@ "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", "dev": true, "requires": { - "is-bzip2": "^1.0.0", - "object-assign": "^2.0.0", - "seek-bzip": "^1.0.3", - "strip-dirs": "^1.0.0", - "tar-stream": "^1.1.1", - "through2": "^0.6.1", - "vinyl": "^0.4.3" + "is-bzip2": "1.0.0", + "object-assign": "2.1.1", + "seek-bzip": "1.0.5", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.2", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -2476,7 +2523,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2492,8 +2539,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -2502,8 +2549,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -2514,12 +2561,12 @@ "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", "dev": true, "requires": { - "is-gzip": "^1.0.0", - "object-assign": "^2.0.0", - "strip-dirs": "^1.0.0", - "tar-stream": "^1.1.1", - "through2": "^0.6.1", - "vinyl": "^0.4.3" + "is-gzip": "1.0.0", + "object-assign": "2.1.1", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.2", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -2536,7 +2583,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2552,8 +2599,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -2562,8 +2609,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -2574,13 +2621,13 @@ "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", "dev": true, "requires": { - "is-zip": "^1.0.0", - "read-all-stream": "^3.0.0", - "stat-mode": "^0.2.0", - "strip-dirs": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^1.0.0", - "yauzl": "^2.2.1" + "is-zip": "1.0.0", + "read-all-stream": "3.1.0", + "stat-mode": "0.2.2", + "strip-dirs": "1.1.1", + "through2": "2.0.3", + "vinyl": "1.2.0", + "yauzl": "2.10.0" }, "dependencies": { "vinyl": { @@ -2589,8 +2636,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -2614,17 +2661,16 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" } }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "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": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "object-keys": "1.0.12" } }, "define-property": { @@ -2633,8 +2679,8 @@ "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -2643,7 +2689,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -2652,7 +2698,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -2661,9 +2707,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -2681,9 +2727,9 @@ "dev": true, "optional": true, "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" + "ast-types": "0.11.5", + "escodegen": "1.11.0", + "esprima": "3.1.3" }, "dependencies": { "esprima": { @@ -2701,29 +2747,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" } }, "delayed-stream": { @@ -2765,10 +2795,10 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.2" } }, "dom-serialize": { @@ -2777,10 +2807,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.2", + "void-elements": "2.0.1" } }, "dom-serializer": { @@ -2790,8 +2820,8 @@ "dev": true, "optional": true, "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "domelementtype": "1.1.3", + "entities": "1.1.1" }, "dependencies": { "domelementtype": { @@ -2811,14 +2841,14 @@ "optional": true }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "optional": true, "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" } }, "double-ended-queue": { @@ -2834,21 +2864,21 @@ "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", "dev": true, "requires": { - "caw": "^1.0.1", - "concat-stream": "^1.4.7", - "each-async": "^1.0.0", - "filenamify": "^1.0.1", - "got": "^5.0.0", - "gulp-decompress": "^1.2.0", - "gulp-rename": "^1.2.0", - "is-url": "^1.2.0", - "object-assign": "^4.0.1", - "read-all-stream": "^3.0.0", - "readable-stream": "^2.0.2", - "stream-combiner2": "^1.1.1", - "vinyl": "^1.0.0", - "vinyl-fs": "^2.2.0", - "ware": "^1.2.0" + "caw": "1.2.0", + "concat-stream": "1.6.2", + "each-async": "1.1.1", + "filenamify": "1.2.1", + "got": "5.7.1", + "gulp-decompress": "1.2.0", + "gulp-rename": "1.4.0", + "is-url": "1.2.4", + "object-assign": "4.1.1", + "read-all-stream": "3.1.0", + "readable-stream": "2.3.6", + "stream-combiner2": "1.1.1", + "vinyl": "1.2.0", + "vinyl-fs": "2.4.4", + "ware": "1.3.0" }, "dependencies": { "arr-diff": { @@ -2857,7 +2887,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -2872,9 +2902,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" } }, "expand-brackets": { @@ -2883,7 +2913,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -2892,7 +2922,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "glob": { @@ -2901,11 +2931,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-stream": { @@ -2914,14 +2944,14 @@ "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, "requires": { - "extend": "^3.0.0", - "glob": "^5.0.3", - "glob-parent": "^3.0.0", - "micromatch": "^2.3.7", - "ordered-read-streams": "^0.3.0", - "through2": "^0.6.0", - "to-absolute-glob": "^0.1.1", - "unique-stream": "^2.0.2" + "extend": "3.0.2", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" }, "dependencies": { "isarray": { @@ -2932,7 +2962,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2954,8 +2984,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -2978,7 +3008,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "isarray": { @@ -2993,7 +3023,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "micromatch": { @@ -3002,19 +3032,19 @@ "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" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "ordered-read-streams": { @@ -3023,13 +3053,13 @@ "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, "requires": { - "is-stream": "^1.0.1", - "readable-stream": "^2.0.1" + "is-stream": "1.1.0", + "readable-stream": "2.3.6" } }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -3045,7 +3075,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -3057,17 +3087,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "^1.0.0", - "strip-bom": "^2.0.0" + "is-utf8": "0.2.1" } }, "unique-stream": { @@ -3076,8 +3096,8 @@ "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", "dev": true, "requires": { - "json-stable-stringify": "^1.0.0", - "through2-filter": "^2.0.0" + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" } }, "vinyl": { @@ -3086,8 +3106,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -3097,30 +3117,30 @@ "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, "requires": { - "duplexify": "^3.2.0", - "glob-stream": "^5.3.2", - "graceful-fs": "^4.0.0", + "duplexify": "3.6.0", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", "gulp-sourcemaps": "1.6.0", - "is-valid-glob": "^0.3.0", - "lazystream": "^1.0.0", - "lodash.isequal": "^4.0.0", - "merge-stream": "^1.0.0", - "mkdirp": "^0.5.0", - "object-assign": "^4.0.0", - "readable-stream": "^2.0.4", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^1.0.0", - "through2": "^2.0.0", - "through2-filter": "^2.0.0", - "vali-date": "^1.0.0", - "vinyl": "^1.0.0" + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.6", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" } } } }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -3130,7 +3150,7 @@ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "dev": true, "requires": { - "readable-stream": "~1.1.9" + "readable-stream": "1.1.14" } }, "duplexify": { @@ -3139,16 +3159,16 @@ "integrity": "sha1-WSkD9dgLONA3IgVBJk1poZj7NBA=", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" }, "dependencies": { "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, "requires": { "once": "1.4.0" @@ -3162,7 +3182,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -3178,7 +3198,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -3192,18 +3212,27 @@ "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", "dev": true, "requires": { - "onetime": "^1.0.0", - "set-immediate-shim": "^1.0.0" + "onetime": "1.1.0", + "set-immediate-shim": "1.0.1" + }, + "dependencies": { + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + } } }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "ee-first": { @@ -3213,9 +3242,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.45", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz", - "integrity": "sha1-RYrBscXHYM6IEaFtK/vZfsMLr7g=", + "version": "1.3.73", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz", + "integrity": "sha512-6PIg7v9zRoVGh6EheRF8h6Plti+3Yo/qtHobS4/Htyt53DNHmKKGFqSae1AIk0k1S4gCQvt7I2WgpbuZNcDY+g==", "dev": true }, "encodeurl": { @@ -3230,7 +3259,7 @@ "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "dev": true, "requires": { - "once": "~1.3.0" + "once": "1.3.3" }, "dependencies": { "once": { @@ -3239,7 +3268,7 @@ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } } } @@ -3247,16 +3276,16 @@ "engine.io": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz", - "integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==", + "integrity": "sha1-Dn751pDrCzVZfx1K0Comyi26OEU=", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "uws": "~9.14.0", - "ws": "~3.3.1" + "debug": "3.1.0", + "engine.io-parser": "2.1.2", + "uws": "9.14.0", + "ws": "3.3.3" }, "dependencies": { "accepts": { @@ -3265,7 +3294,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.20", "negotiator": "0.6.1" } }, @@ -3278,7 +3307,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3295,26 +3324,26 @@ "engine.io-client": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz", - "integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==", + "integrity": "sha1-W96xMPi5SlCsXL63JYPnpKBj3f0=", "dev": true, "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "3.1.0", + "engine.io-parser": "2.1.2", "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", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", "yeast": "0.1.2" }, "dependencies": { "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3325,14 +3354,14 @@ "engine.io-parser": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", - "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", + "integrity": "sha1-TA9M/3mq7su9z96maoI8YIVAkZY=", "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", + "arraybuffer.slice": "0.0.7", "base64-arraybuffer": "0.1.5", "blob": "0.0.4", - "has-binary2": "~1.0.2" + "has-binary2": "1.0.3" } }, "ent": { @@ -3355,16 +3384,16 @@ "dev": true, "optional": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "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" + "is-arrayish": "0.2.1" } }, "errorhandler": { @@ -3373,8 +3402,8 @@ "integrity": "sha1-t7cO2PNZ6duICS8tIMD4MUIK2D8=", "dev": true, "requires": { - "accepts": "~1.3.0", - "escape-html": "~1.0.3" + "accepts": "1.3.5", + "escape-html": "1.0.3" }, "dependencies": { "accepts": { @@ -3383,7 +3412,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.20", "negotiator": "0.6.1" } }, @@ -3401,27 +3430,27 @@ "integrity": "sha1-nbvdJ8aFbwABQhyhh4LXhr+KYWU=", "dev": 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": "1.2.0", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" } }, "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "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.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.2" } }, "es6-promise": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", "dev": true }, @@ -3431,13 +3460,13 @@ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "es6-promise": "^4.0.3" + "es6-promise": "4.2.5" }, "dependencies": { "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", "dev": true } } @@ -3455,16 +3484,16 @@ "dev": true }, "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha1-264X75bI5L7bE1b0UE+kzC98t+I=", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", + "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" }, "dependencies": { "esprima": { @@ -3483,64 +3512,51 @@ } }, "eslint": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", - "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz", + "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==", "dev": true, "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", - "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "string.prototype.matchall": "^2.0.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" + "@babel/code-frame": "7.0.0", + "ajv": "6.5.4", + "chalk": "2.4.1", + "cross-spawn": "6.0.5", + "debug": "4.0.1", + "doctrine": "2.1.0", + "eslint-scope": "4.0.0", + "eslint-utils": "1.3.1", + "eslint-visitor-keys": "1.0.0", + "espree": "4.0.0", + "esquery": "1.0.1", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.3", + "globals": "11.8.0", + "ignore": "4.0.6", + "imurmurhash": "0.1.4", + "inquirer": "6.2.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.12.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "regexpp": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.5.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.3", + "text-table": "0.2.0" }, "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -3553,7 +3569,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.3" } }, "chalk": { @@ -3562,31 +3578,18 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.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" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.0.1.tgz", + "integrity": "sha512-K23FHJ/Mt404FSlp6gSZCevIbTMLX0j3fmHhUEhQ3Wq0FMODW3+cUSoLdy1Gx4polAf4t/lphhmHH35BB8cLYw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "esprima": { @@ -3607,20 +3610,20 @@ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, "strip-ansi": { @@ -3629,16 +3632,16 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "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" + "has-flag": "3.0.0" } } } @@ -3646,33 +3649,33 @@ "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint-utils": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", "dev": true }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", "dev": true }, "espree": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", + "integrity": "sha1-JTmY8goPgttdhmOFeZ2RKoOjZjQ=", "dev": true, "requires": { - "acorn": "^5.6.0", - "acorn-jsx": "^4.1.1" + "acorn": "5.7.3", + "acorn-jsx": "4.1.1" } }, "esprima": { @@ -3684,19 +3687,19 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "4.2.0" } }, "estemplate": { @@ -3705,8 +3708,8 @@ "integrity": "sha1-FxSp1GGQc4rJWLyv1J4CnNpWo54=", "dev": true, "requires": { - "esprima": "^2.7.2", - "estraverse": "^4.1.1" + "esprima": "2.7.3", + "estraverse": "4.2.0" } }, "estraverse": { @@ -3728,24 +3731,25 @@ "dev": true }, "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz", + "integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "flatmap-stream": "0.1.0", + "from": "0.1.7", + "map-stream": "0.0.7", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "1.0.1", + "stream-combiner": "0.2.2", + "through": "2.3.8" } }, "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { @@ -3755,11 +3759,11 @@ "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" + "execa": "0.7.0", + "p-finally": "1.0.0", + "pify": "3.0.0", + "rimraf": "2.6.2", + "tempfile": "2.0.0" }, "dependencies": { "pify": { @@ -3778,8 +3782,8 @@ "dev": true, "optional": true, "requires": { - "async-each-series": "^1.1.0", - "object-assign": "^4.1.0" + "async-each-series": "1.1.0", + "object-assign": "4.1.1" } }, "execa": { @@ -3789,13 +3793,38 @@ "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" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.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.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "optional": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + } } }, "executable": { @@ -3805,7 +3834,7 @@ "dev": true, "optional": true, "requires": { - "meow": "^3.1.0" + "meow": "3.7.0" } }, "expand-braces": { @@ -3814,9 +3843,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" + "array-slice": "0.2.3", + "array-unique": "0.2.1", + "braces": "0.1.5" }, "dependencies": { "array-slice": { @@ -3837,7 +3866,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "^0.1.0" + "expand-range": "0.1.1" } }, "expand-range": { @@ -3846,8 +3875,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" + "is-number": "0.1.1", + "repeat-string": "0.2.2" } }, "is-number": { @@ -3870,13 +3899,13 @@ "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" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -3885,7 +3914,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -3894,7 +3923,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -3905,7 +3934,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.4" }, "dependencies": { "fill-range": { @@ -3914,11 +3943,11 @@ "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "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": "2.1.0", + "isobject": "2.1.0", + "randomatic": "3.1.0", + "repeat-element": "1.1.3", + "repeat-string": "1.6.1" } }, "is-number": { @@ -3927,7 +3956,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "isarray": { @@ -3951,7 +3980,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -3962,7 +3991,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } }, "express-session": { @@ -3974,17 +4003,17 @@ "cookie": "0.1.3", "cookie-signature": "1.0.6", "crc": "3.3.0", - "debug": "~2.2.0", - "depd": "~1.0.1", - "on-headers": "~1.0.0", - "parseurl": "~1.3.0", - "uid-safe": "~2.0.0", + "debug": "2.2.0", + "depd": "1.0.1", + "on-headers": "1.0.1", + "parseurl": "1.3.2", + "uid-safe": "2.0.0", "utils-merge": "1.0.0" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -4009,9 +4038,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extend-shallow": { @@ -4020,8 +4049,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -4030,38 +4059,29 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" + "chardet": "0.7.0", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" }, "dependencies": { "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "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" - } - }, - "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" + "safer-buffer": "2.1.2" } } } @@ -4072,14 +4092,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -4088,7 +4108,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -4097,7 +4117,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -4106,7 +4126,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -4115,7 +4135,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -4124,32 +4144,41 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } }, "extract-zip": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", - "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", "dev": true, "requires": { - "concat-stream": "1.6.0", + "concat-stream": "1.6.2", "debug": "2.6.9", - "mkdirp": "0.5.0", + "mkdirp": "0.5.1", "yauzl": "2.4.1" }, "dependencies": { - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "minimist": "0.0.8" + "pend": "1.2.0" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "1.0.1" } } } @@ -4166,9 +4195,9 @@ "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -4195,27 +4224,25 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } }, "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { - "pend": "~1.2.0" + "pend": "1.2.0" } }, "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, - "optional": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -4224,8 +4251,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.0", + "object-assign": "4.1.1" } }, "file-type": { @@ -4237,7 +4264,7 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", "dev": true, "optional": true }, @@ -4259,9 +4286,9 @@ "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", "dev": true, "requires": { - "filename-reserved-regex": "^1.0.0", - "strip-outer": "^1.0.0", - "trim-repeated": "^1.0.0" + "filename-reserved-regex": "1.0.0", + "strip-outer": "1.0.1", + "trim-repeated": "1.0.0" } }, "fill-range": { @@ -4270,10 +4297,10 @@ "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" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -4282,14 +4309,14 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "finalhandler": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", "integrity": "sha1-llpS2ejQXSuFdUhUH7ibU6JJfZs=", "dev": true, "requires": { @@ -4301,7 +4328,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -4334,8 +4361,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "find-versions": { @@ -4345,10 +4372,10 @@ "dev": true, "optional": true, "requires": { - "array-uniq": "^1.0.0", - "get-stdin": "^4.0.1", - "meow": "^3.5.0", - "semver-regex": "^1.0.0" + "array-uniq": "1.0.3", + "get-stdin": "4.0.1", + "meow": "3.7.0", + "semver-regex": "1.0.0" } }, "findup-sync": { @@ -4357,10 +4384,10 @@ "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" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" } }, "fined": { @@ -4369,11 +4396,11 @@ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "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" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" } }, "first-chunk-stream": { @@ -4394,10 +4421,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" }, "dependencies": { "graceful-fs": { @@ -4408,6 +4435,12 @@ } } }, + "flatmap-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.0.tgz", + "integrity": "sha512-Nlic4ZRYxikqnK5rj3YoxDVKGGtUjcNDUtvQ7XsdGLZmMwdUYnXf10o1zcXtzEZTBgc6GxeRpQxV/Wu3WPIIHA==", + "dev": true + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -4415,12 +4448,12 @@ "dev": true }, "follow-redirects": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.2.tgz", - "integrity": "sha512-kssLorP/9acIdpQ2udQVTiCS5LQmdEz9mvdIfDcl1gYX2tPKFADHSyFdvJS040XdFsPzemWtgI3q8mFVCxtX8A==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.8.tgz", + "integrity": "sha512-sy1mXPmv7kLAMKW/8XofG7o9T+6gAjzdZK4AJF6ryqQYUa/hnzgiypoeUecZ53x7XiqKNEpNqLtS97MshW2nxg==", "dev": true, "requires": { - "debug": "^3.1.0" + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -4446,15 +4479,9 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4467,9 +4494,9 @@ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.7", + "mime-types": "2.1.20" } }, "fragment-cache": { @@ -4478,7 +4505,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fresh": { @@ -4505,9 +4532,9 @@ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0", + "klaw": "1.3.1" }, "dependencies": { "graceful-fs": { @@ -4524,7 +4551,7 @@ "integrity": "sha1-gAI4I5gfn//+AWCei+Zo9prknnA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.1.11" }, "dependencies": { "graceful-fs": { @@ -4548,8 +4575,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -4575,8 +4602,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { @@ -4589,7 +4616,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -4653,7 +4680,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "fs.realpath": { @@ -4668,14 +4695,14 @@ "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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" } }, "glob": { @@ -4684,12 +4711,12 @@ "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -4704,7 +4731,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -4713,7 +4740,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -4722,8 +4749,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -4742,7 +4769,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -4756,7 +4783,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -4769,8 +4796,8 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "minizlib": { @@ -4779,7 +4806,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "mkdirp": { @@ -4802,9 +4829,9 @@ "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -4813,16 +4840,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" } }, "nopt": { @@ -4831,8 +4858,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -4847,8 +4874,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { @@ -4857,10 +4884,10 @@ "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" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -4879,7 +4906,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -4900,8 +4927,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -4922,10 +4949,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -4942,13 +4969,13 @@ "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" + "core-util-is": "1.0.2", + "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.2" } }, "rimraf": { @@ -4957,7 +4984,7 @@ "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-buffer": { @@ -5000,9 +5027,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -5011,7 +5038,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -5019,7 +5046,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -5034,13 +5061,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "util-deprecate": { @@ -5055,7 +5082,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -5077,7 +5104,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "1.1.x", + "readable-stream": "1.1.14", "xregexp": "2.0.0" } }, @@ -5099,15 +5126,18 @@ "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "dev": true, "requires": { - "globule": "~0.1.0" + "globule": "0.1.0" } }, "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "is-property": "1.0.2" + } }, "generate-object-property": { "version": "1.2.0", @@ -5116,7 +5146,7 @@ "dev": true, "optional": true, "requires": { - "is-property": "^1.0.0" + "is-property": "1.0.2" } }, "get-proxy": { @@ -5125,7 +5155,7 @@ "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", "dev": true, "requires": { - "rc": "^1.1.2" + "rc": "1.2.8" } }, "get-stdin": { @@ -5136,7 +5166,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true, "optional": true @@ -5144,16 +5174,16 @@ "get-uri": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", - "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", + "integrity": "sha1-XHlecTJvbKEoby/IJXXNK6sq9Xg=", "dev": true, "optional": true, "requires": { - "data-uri-to-buffer": "1", - "debug": "2", - "extend": "3", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "2" + "data-uri-to-buffer": "1.2.0", + "debug": "2.6.9", + "extend": "3.0.2", + "file-uri-to-path": "1.0.0", + "ftp": "0.3.10", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -5165,7 +5195,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -5182,11 +5212,11 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -5203,7 +5233,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -5221,23 +5251,23 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^2.0.0", - "bin-wrapper": "^3.0.0", - "logalot": "^2.0.0" + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" } }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -5246,8 +5276,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" }, "dependencies": { "glob-parent": { @@ -5256,7 +5286,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "is-extglob": { @@ -5271,7 +5301,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -5282,8 +5312,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" } }, "glob-stream": { @@ -5292,12 +5322,12 @@ "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "dev": true, "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" }, "dependencies": { "glob": { @@ -5306,10 +5336,10 @@ "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.4.0" } }, "minimatch": { @@ -5318,12 +5348,12 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "1.1.11" } }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -5339,8 +5369,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -5351,7 +5381,7 @@ "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "dev": true, "requires": { - "gaze": "^0.5.1" + "gaze": "0.5.2" } }, "glob2base": { @@ -5360,7 +5390,7 @@ "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", "dev": true, "requires": { - "find-index": "^0.1.1" + "find-index": "0.1.1" } }, "global-modules": { @@ -5369,9 +5399,9 @@ "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" } }, "global-prefix": { @@ -5380,30 +5410,31 @@ "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" + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.1" } }, "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true }, "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.3", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "globule": { @@ -5412,9 +5443,9 @@ "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "dev": true, "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" }, "dependencies": { "glob": { @@ -5423,9 +5454,9 @@ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "dev": true, "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" } }, "graceful-fs": { @@ -5442,7 +5473,7 @@ }, "lodash": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", "dev": true }, @@ -5452,8 +5483,8 @@ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "dev": true, "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "lru-cache": "2.7.3", + "sigmund": "1.0.1" } } } @@ -5464,12 +5495,12 @@ "integrity": "sha1-3PdY5EeJzD89MsHzVio2duajSBA=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "got": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true, "requires": { @@ -5507,17 +5538,17 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "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.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "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": { @@ -5526,7 +5557,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } } } @@ -5537,7 +5568,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "^1.1.0" + "natives": "1.1.5" } }, "graceful-readlink": { @@ -5552,24 +5583,24 @@ "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.1.0", + "liftoff": "2.5.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" }, "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -5581,15 +5612,15 @@ "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", "dev": true, "requires": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" + "concat-with-sourcemaps": "1.1.0", + "through2": "2.0.3", + "vinyl": "2.2.0" }, "dependencies": { "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true }, "clone-stats": { @@ -5605,24 +5636,24 @@ "dev": true }, "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "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" + "clone": "2.1.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" } } } }, "gulp-connect": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/gulp-connect/-/gulp-connect-5.0.0.tgz", "integrity": "sha1-8v3zBq6RFGg2jCKF8teC8T7dr04=", "dev": true, "requires": { @@ -5639,10 +5670,10 @@ "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", "dev": true, "requires": { - "archive-type": "^3.0.0", - "decompress": "^3.0.0", - "gulp-util": "^3.0.1", - "readable-stream": "^2.0.2" + "archive-type": "3.2.0", + "decompress": "3.0.0", + "gulp-util": "3.0.8", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -5653,7 +5684,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -5669,7 +5700,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -5680,26 +5711,12 @@ "gulp-eslint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz", - "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", + "integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=", "dev": true, "requires": { - "eslint": "^5.0.1", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.1" - }, - "dependencies": { - "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" - } - } + "eslint": "5.6.1", + "fancy-log": "1.3.2", + "plugin-error": "1.0.1" } }, "gulp-imagemin": { @@ -5708,17 +5725,17 @@ "integrity": "sha1-XONH8dFwb+08yPF3fKkJSlg7ULc=", "dev": true, "requires": { - "chalk": "^2.1.0", - "fancy-log": "^1.3.2", - "imagemin": "^5.3.1", - "imagemin-gifsicle": "^5.2.0", - "imagemin-jpegtran": "^5.0.2", - "imagemin-optipng": "^5.2.1", - "imagemin-svgo": "^6.0.0", - "plugin-error": "^0.1.2", - "plur": "^2.1.2", - "pretty-bytes": "^4.0.2", - "through2-concurrent": "^1.1.1" + "chalk": "2.4.1", + "fancy-log": "1.3.2", + "imagemin": "5.3.1", + "imagemin-gifsicle": "5.2.0", + "imagemin-jpegtran": "5.0.2", + "imagemin-optipng": "5.2.1", + "imagemin-svgo": "6.0.0", + "plugin-error": "0.1.2", + "plur": "2.1.2", + "pretty-bytes": "4.0.2", + "through2-concurrent": "1.1.1" }, "dependencies": { "ansi-styles": { @@ -5727,18 +5744,49 @@ "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.3" } }, + "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.1.0", + "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": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.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" } }, "has-flag": { @@ -5747,13 +5795,32 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", + "kind-of": { + "version": "1.1.0", + "resolved": "http://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": { - "has-flag": "^3.0.0" + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + }, + "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" } } } @@ -5764,15 +5831,65 @@ "integrity": "sha1-gBT0ad38ZUTX3aUAmN7wNxu7T3g=", "dev": true, "requires": { - "accord": "^0.28.0", - "less": "2.6.x || ^2.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" + "accord": "0.28.0", + "less": "2.7.3", + "object-assign": "4.1.1", + "plugin-error": "0.1.2", + "replace-ext": "1.0.0", + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.2.1" }, "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.1.0", + "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": "http://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.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + }, "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", @@ -5787,8 +5904,8 @@ "integrity": "sha1-J7Z6Ql6zh1ImNFq4lgTXN/Y1fCE=", "dev": true, "requires": { - "angular": "~1.3.1", - "angular-animate": "~1.3.1", + "angular": "1.3.20", + "angular-animate": "1.3.20", "canonical-path": "0.0.2", "extend": "1.3.0", "gulp-util": "3.0.0", @@ -5803,7 +5920,7 @@ "dependencies": { "ansi-regex": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", "dev": true }, @@ -5815,7 +5932,7 @@ }, "chalk": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", "dev": true, "requires": { @@ -5838,8 +5955,8 @@ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "meow": "3.7.0" } }, "extend": { @@ -5854,15 +5971,15 @@ "integrity": "sha1-b+7cR9aXKCO6zplH/F9GvYo0Zuc=", "dev": true, "requires": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash": "^2.4.1", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.0", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" }, "dependencies": { "through2": { @@ -5871,8 +5988,8 @@ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" + "readable-stream": "1.0.34", + "xtend": "3.0.0" } } } @@ -5883,12 +6000,12 @@ "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", "dev": true, "requires": { - "ansi-regex": "^0.2.0" + "ansi-regex": "0.2.1" } }, "lodash": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", "integrity": "sha1-W3cjA03aTSYuWkb7LFjXzCL3FCA=", "dev": true }, @@ -5904,8 +6021,8 @@ "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash.escape": { @@ -5914,9 +6031,9 @@ "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", "dev": true, "requires": { - "lodash._escapehtmlchar": "~2.4.1", - "lodash._reunescapedhtml": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash.keys": { @@ -5925,9 +6042,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } }, "lodash.template": { @@ -5936,13 +6053,13 @@ "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", "dev": true, "requires": { - "lodash._escapestringchar": "~2.4.1", - "lodash._reinterpolate": "~2.4.1", - "lodash.defaults": "~2.4.1", - "lodash.escape": "~2.4.1", - "lodash.keys": "~2.4.1", - "lodash.templatesettings": "~2.4.1", - "lodash.values": "~2.4.1" + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" } }, "lodash.templatesettings": { @@ -5951,8 +6068,8 @@ "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", "dev": true, "requires": { - "lodash._reinterpolate": "~2.4.1", - "lodash.escape": "~2.4.1" + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" } }, "merge-stream": { @@ -5961,7 +6078,7 @@ "integrity": "sha1-5oIPet267gA/SMpKWMfFolPV4Fw=", "dev": true, "requires": { - "through2": "^0.5.1" + "through2": "0.5.1" }, "dependencies": { "through2": { @@ -5970,21 +6087,21 @@ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" + "readable-stream": "1.0.34", + "xtend": "3.0.0" } } } }, "minimist": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", "dev": true }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -5996,7 +6113,7 @@ }, "strip-ansi": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "dev": true, "requires": { @@ -6015,8 +6132,8 @@ "integrity": "sha1-90KzKJPovSYUbnieT9LMssB6cX4=", "dev": true, "requires": { - "readable-stream": ">=1.0.27-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" }, "dependencies": { "xtend": { @@ -6033,7 +6150,7 @@ "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", "dev": true, "requires": { - "clone-stats": "~0.0.1" + "clone-stats": "0.0.1" } }, "vinyl-fs": { @@ -6042,14 +6159,14 @@ "integrity": "sha1-LiXP5t9cgIGPl/9Be/XCGkHkpJs=", "dev": true, "requires": { - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "lodash": "^2.4.1", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "lodash": "2.4.1", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.1", + "vinyl": "0.4.6" }, "dependencies": { "vinyl": { @@ -6058,8 +6175,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -6078,10 +6195,10 @@ "integrity": "sha1-rAHu6JjXenrAgS+tTz1T0IaH1Bw=", "dev": true, "requires": { - "colors": "^1.1.2", + "colors": "1.3.2", "open": "0.0.5", - "plugin-log": "^0.1.0", - "through2": "^2.0.1" + "plugin-log": "0.1.0", + "through2": "2.0.3" } }, "gulp-postcss": { @@ -6090,16 +6207,16 @@ "integrity": "sha1-eKMuPIeqbNzsWuHJBeGW1HjoxdU=", "dev": true, "requires": { - "gulp-util": "^3.0.8", - "postcss": "^5.2.12", - "postcss-load-config": "^1.2.0", - "vinyl-sourcemaps-apply": "^0.2.1" + "gulp-util": "3.0.8", + "postcss": "5.2.18", + "postcss-load-config": "1.2.0", + "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-rename": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", - "integrity": "sha1-OtRCh2PwXidk3sHGfYaNsnVoeBc=", + "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": { @@ -6108,7 +6225,7 @@ "integrity": "sha1-xnYqLx8N4KP8WVohWZ0/rI26Gso=", "dev": true, "requires": { - "through2": "^2.0.1" + "through2": "2.0.3" } }, "gulp-sourcemaps": { @@ -6117,11 +6234,11 @@ "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", "dev": true, "requires": { - "convert-source-map": "^1.1.1", - "graceful-fs": "^4.1.2", - "strip-bom": "^2.0.0", - "through2": "^2.0.0", - "vinyl": "^1.0.0" + "convert-source-map": "1.6.0", + "graceful-fs": "4.1.11", + "strip-bom": "2.0.0", + "through2": "2.0.3", + "vinyl": "1.2.0" }, "dependencies": { "graceful-fs": { @@ -6136,7 +6253,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "vinyl": { @@ -6145,8 +6262,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -6158,29 +6275,29 @@ "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", + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.2", + "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.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "through2": "2.0.3", + "vinyl": "0.5.3" }, "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -6198,16 +6315,16 @@ "integrity": "sha1-Fi/FY96fx3DpH5p845VVE6mhGMA=", "dev": true, "requires": { - "anymatch": "^1.3.0", - "chokidar": "^1.6.1", - "glob-parent": "^3.0.1", - "gulp-util": "^3.0.7", - "object-assign": "^4.1.0", - "path-is-absolute": "^1.0.1", - "readable-stream": "^2.2.2", - "slash": "^1.0.0", - "vinyl": "^1.2.0", - "vinyl-file": "^2.0.0" + "anymatch": "1.3.2", + "chokidar": "1.7.0", + "glob-parent": "3.1.0", + "gulp-util": "3.0.8", + "object-assign": "4.1.1", + "path-is-absolute": "1.0.1", + "readable-stream": "2.3.6", + "slash": "1.0.0", + "vinyl": "1.2.0", + "vinyl-file": "2.0.0" }, "dependencies": { "isarray": { @@ -6218,8 +6335,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -6237,7 +6354,7 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "vinyl": { @@ -6246,8 +6363,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -6259,16 +6376,16 @@ "integrity": "sha1-kPsLSieiZkM4Mv98YSLbXB7olMY=", "dev": true, "requires": { - "consolidate": "^0.14.1", - "es6-promise": "^3.1.2", - "fs-readfile-promise": "^2.0.1", - "gulp-util": "^3.0.3", - "js-yaml": "^3.2.6", - "lodash": "^4.11.1", - "node.extend": "^1.1.2", - "through2": "^2.0.1", - "tryit": "^1.0.1", - "vinyl-bufferstream": "^1.0.1" + "consolidate": "0.14.5", + "es6-promise": "3.3.1", + "fs-readfile-promise": "2.0.1", + "gulp-util": "3.0.8", + "js-yaml": "3.7.0", + "lodash": "4.17.11", + "node.extend": "1.1.6", + "through2": "2.0.3", + "tryit": "1.0.3", + "vinyl-bufferstream": "1.0.1" } }, "gulp-wrap-js": { @@ -6277,12 +6394,12 @@ "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" + "escodegen": "1.11.0", + "esprima": "2.7.3", + "estemplate": "0.5.1", + "gulp-util": "3.0.8", + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.1.4" }, "dependencies": { "source-map": { @@ -6291,7 +6408,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } }, "vinyl-sourcemaps-apply": { @@ -6300,7 +6417,7 @@ "integrity": "sha1-xfy9Q+LyOEI8LcmL3db3m3K8NFs=", "dev": true, "requires": { - "source-map": "^0.1.39" + "source-map": "0.1.43" } } } @@ -6311,7 +6428,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.1" } }, "har-schema": { @@ -6326,17 +6443,35 @@ "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true, "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" + "ajv": "4.11.8", + "har-schema": "1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + } } }, "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "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.0.2" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -6345,13 +6480,13 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -6383,7 +6518,7 @@ "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "has-symbols": { @@ -6398,9 +6533,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -6409,8 +6544,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -6419,7 +6554,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6430,8 +6565,8 @@ "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", "dev": true, "requires": { - "is-stream": "^1.0.1", - "pinkie-promise": "^2.0.0" + "is-stream": "1.1.0", + "pinkie-promise": "2.0.1" } }, "hawk": { @@ -6440,10 +6575,10 @@ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" } }, "hipchat-notifier": { @@ -6453,8 +6588,8 @@ "dev": true, "optional": true, "requires": { - "lodash": "^4.0.0", - "request": "^2.0.0" + "lodash": "4.17.11", + "request": "2.81.0" } }, "hoek": { @@ -6469,13 +6604,13 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha1-IyNbKasjDFdqqw1PE/wEawsDgiI=", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "dev": true }, "html-comment-regex": { @@ -6486,7 +6621,7 @@ }, "http-errors": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", "dev": true, "requires": { @@ -6495,36 +6630,36 @@ } }, "http-parser-js": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", - "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", "dev": true }, "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "3.1.0", + "follow-redirects": "1.5.8", + "requires-port": "1.0.0" } }, "http-proxy-agent": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "integrity": "sha1-5IIb7vWyFCogJr1zkm/lN2McVAU=", "dev": true, "requires": { - "agent-base": "4", + "agent-base": "4.2.1", "debug": "3.1.0" }, "dependencies": { "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -6538,9 +6673,9 @@ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" } }, "httpntlm": { @@ -6549,8 +6684,8 @@ "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", "dev": true, "requires": { - "httpreq": ">=0.4.22", - "underscore": "~1.7.0" + "httpreq": "0.4.24", + "underscore": "1.7.0" } }, "httpreq": { @@ -6562,34 +6697,40 @@ "https-proxy-agent": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "integrity": "sha1-UVUpcPoE1yPgTFbQQXjD+SWSu8A=", "dev": true, "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" + "agent-base": "4.2.1", + "debug": "3.2.5" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "iconv-lite": { "version": "0.4.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz", "integrity": "sha1-LstC/SlHRJIiCaLnxATayHk9it4=", "dev": true }, "ignore": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz", - "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==", + "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": { @@ -6605,14 +6746,27 @@ "integrity": "sha1-8Zwu7h5xumxlWMUV+fyWaAGJptQ=", "dev": true, "requires": { - "file-type": "^4.1.0", - "globby": "^6.1.0", - "make-dir": "^1.0.0", - "p-pipe": "^1.1.0", - "pify": "^2.3.0", - "replace-ext": "^1.0.0" + "file-type": "4.4.0", + "globby": "6.1.0", + "make-dir": "1.3.0", + "p-pipe": "1.2.0", + "pify": "2.3.0", + "replace-ext": "1.0.0" }, "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.3", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", @@ -6628,9 +6782,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "gifsicle": "^3.0.0", - "is-gif": "^1.0.0" + "exec-buffer": "3.2.0", + "gifsicle": "3.0.4", + "is-gif": "1.0.0" } }, "imagemin-jpegtran": { @@ -6640,9 +6794,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "is-jpg": "^1.0.0", - "jpegtran-bin": "^3.0.0" + "exec-buffer": "3.2.0", + "is-jpg": "1.0.1", + "jpegtran-bin": "3.2.0" } }, "imagemin-optipng": { @@ -6652,9 +6806,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "is-png": "^1.0.0", - "optipng-bin": "^3.0.0" + "exec-buffer": "3.2.0", + "is-png": "1.1.0", + "optipng-bin": "3.1.4" } }, "imagemin-svgo": { @@ -6664,11 +6818,18 @@ "dev": true, "optional": true, "requires": { - "buffer-from": "^0.1.1", - "is-svg": "^2.0.0", - "svgo": "^1.0.0" + "buffer-from": "0.1.2", + "is-svg": "2.1.0", + "svgo": "1.1.1" }, "dependencies": { + "buffer-from": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", + "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", + "dev": true, + "optional": true + }, "coa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz", @@ -6676,7 +6837,7 @@ "dev": true, "optional": true, "requires": { - "q": "^1.1.2" + "q": "1.5.1" } }, "colors": { @@ -6703,8 +6864,8 @@ "dev": true, "optional": true, "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" + "mdn-data": "1.1.4", + "source-map": "0.5.7" } } } @@ -6717,37 +6878,37 @@ "optional": true }, "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha1-LnhEFka9RoLpY/IrbpKCPDCcYtw=", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "optional": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "svgo": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.0.5.tgz", - "integrity": "sha1-cEA2TAYqBTirrP9EAc6momp6OJo=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.1.1.tgz", + "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==", "dev": true, "optional": true, "requires": { - "coa": "~2.0.1", - "colors": "~1.1.2", - "css-select": "~1.3.0-rc0", - "css-select-base-adapter": "~0.1.0", - "css-tree": "1.0.0-alpha25", - "css-url-regex": "^1.1.0", - "csso": "^3.5.0", - "js-yaml": "~3.10.0", - "mkdirp": "~0.5.1", - "object.values": "^1.0.4", - "sax": "~1.2.4", - "stable": "~0.1.6", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" + "coa": "2.0.1", + "colors": "1.1.2", + "css-select": "2.0.0", + "css-select-base-adapter": "0.1.0", + "css-tree": "1.0.0-alpha.28", + "css-url-regex": "1.1.0", + "csso": "3.5.1", + "js-yaml": "3.12.0", + "mkdirp": "0.5.1", + "object.values": "1.0.4", + "sax": "1.2.4", + "stable": "0.1.8", + "unquote": "1.1.1", + "util.promisify": "1.0.0" } } } @@ -6764,7 +6925,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "indexes-of": { @@ -6798,8 +6959,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -6815,24 +6976,24 @@ "dev": true }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "3.0.3", + "figures": "2.0.0", + "lodash": "4.17.11", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "run-async": "2.3.0", + "rxjs": "6.3.3", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" }, "dependencies": { "ansi-regex": { @@ -6847,7 +7008,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.3" } }, "chalk": { @@ -6856,18 +7017,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.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" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "has-flag": { @@ -6882,16 +7034,16 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "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" + "has-flag": "3.0.0" } } } @@ -6933,8 +7085,8 @@ "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", "dev": true, "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "is-relative": "1.0.0", + "is-windows": "1.0.2" } }, "is-absolute-url": { @@ -6949,7 +7101,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6958,7 +7110,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6975,7 +7127,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.12.0" } }, "is-buffer": { @@ -6986,7 +7138,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7011,7 +7163,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7020,7 +7172,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7037,9 +7189,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -7068,7 +7220,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -7089,7 +7241,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -7111,7 +7263,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } }, "is-gzip": { @@ -7130,22 +7282,22 @@ "is-my-ip-valid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "integrity": "sha1-ezUbjo7dTTmV1NBmaA5mTZRpaCQ=", "dev": true, "optional": true }, "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", "dev": true, "optional": true, "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "is-my-ip-valid": "^1.0.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.3.1", + "generate-object-property": "1.2.0", + "is-my-ip-valid": "1.0.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-natural-number": { @@ -7160,7 +7312,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7169,34 +7321,17 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", - "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": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", - "dev": true - } - } - }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -7206,10 +7341,10 @@ "is-path-in-cwd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.1" } }, "is-path-inside": { @@ -7218,7 +7353,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-obj": { @@ -7233,7 +7368,7 @@ "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-png": { @@ -7265,8 +7400,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true, - "optional": true + "dev": true }, "is-redirect": { "version": "1.0.0", @@ -7280,7 +7414,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.3" } }, "is-relative": { @@ -7289,13 +7423,13 @@ "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "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==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { @@ -7316,14 +7450,17 @@ "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", "dev": true, "requires": { - "html-comment-regex": "^1.1.0" + "html-comment-regex": "1.1.1" } }, "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true + "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-tar": { "version": "1.0.0", @@ -7343,7 +7480,7 @@ "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "unc-path-regex": "0.1.2" } }, "is-url": { @@ -7388,7 +7525,7 @@ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, "requires": { - "buffer-alloc": "^1.2.0" + "buffer-alloc": "1.2.0" } }, "isexe": { @@ -7422,21 +7559,21 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^2.0.0", - "bin-wrapper": "^3.0.0", - "logalot": "^2.0.0" + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" } }, "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha1-LlRewrDylX9BNWUQIFIU6Y+tZYI=", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", + "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==", "dev": true }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "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": { @@ -7445,8 +7582,8 @@ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" + "argparse": "1.0.10", + "esprima": "2.7.3" } }, "jsbn": { @@ -7465,7 +7602,7 @@ "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==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stable-stringify": { @@ -7474,7 +7611,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stable-stringify-without-jsonify": { @@ -7491,7 +7628,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -7543,34 +7680,34 @@ "karma": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.5.tgz", - "integrity": "sha512-rECezBeY7mjzGUWhFlB7CvPHgkHJLXyUmWg+6vHCEsdWNUTnmiS6jRrIMcJEWgU2DUGZzGWG0bTRVky8fsDTOA==", + "integrity": "sha1-NxDHoucbHEOTE/KDhG2I4E5PkYw=", "dev": true, "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.4", - "log4js": "^2.5.3", - "mime": "^1.3.4", - "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", + "bluebird": "3.5.2", + "body-parser": "1.18.3", + "chokidar": "2.0.4", + "colors": "1.3.2", + "combine-lists": "1.0.1", + "connect": "3.6.6", + "core-js": "2.5.7", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "expand-braces": "0.1.2", + "glob": "7.1.3", + "graceful-fs": "4.1.11", + "http-proxy": "1.17.0", + "isbinaryfile": "3.0.3", + "lodash": "4.17.11", + "log4js": "2.11.0", + "mime": "1.3.4", + "minimatch": "3.0.4", + "optimist": "0.6.1", + "qjobs": "1.2.0", + "range-parser": "1.2.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", "socket.io": "2.0.4", - "source-map": "^0.6.1", + "source-map": "0.6.1", "tmp": "0.0.33", "useragent": "2.2.1" }, @@ -7578,11 +7715,11 @@ "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" } }, "body-parser": { @@ -7592,15 +7729,15 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "~1.6.16" + "type-is": "1.6.16" } }, "bytes": { @@ -7612,22 +7749,22 @@ "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "integrity": "sha1-NW/04rDo5D4yLRijckYLvPOszSY=", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.4", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.1.0" } }, "connect": { @@ -7638,7 +7775,7 @@ "requires": { "debug": "2.6.9", "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "parseurl": "1.3.2", "utils-merge": "1.0.1" } }, @@ -7655,12 +7792,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" }, "dependencies": { "statuses": { @@ -7679,7 +7816,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -7692,10 +7829,10 @@ "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "is-glob": { @@ -7704,13 +7841,13 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", "dev": true }, "range-parser": { @@ -7722,7 +7859,7 @@ "raw-body": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "integrity": "sha1-GzJOzmtXBuFThVvBFIxlu39uoMM=", "dev": true, "requires": { "bytes": "3.0.0", @@ -7734,7 +7871,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "utils-merge": { @@ -7757,8 +7894,8 @@ "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=", "dev": true, "requires": { - "lodash": "^4.0.1", - "phantomjs-prebuilt": "^2.1.7" + "lodash": "4.17.11", + "phantomjs-prebuilt": "2.1.16" } }, "kew": { @@ -7779,7 +7916,7 @@ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true, "requires": { - "graceful-fs": "^4.1.9" + "graceful-fs": "4.1.11" }, "dependencies": { "graceful-fs": { @@ -7810,7 +7947,7 @@ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, "requires": { - "readable-stream": "^2.0.5" + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -7821,7 +7958,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -7837,7 +7974,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -7851,14 +7988,14 @@ "integrity": "sha1-zBJg9RyQCp7A2R+2mYE54CUHtjs=", "dev": true, "requires": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.2.11", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", + "errno": "0.1.7", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.3.4", + "mkdirp": "0.5.1", + "promise": "7.3.1", "request": "2.81.0", - "source-map": "^0.5.3" + "source-map": "0.5.7" }, "dependencies": { "graceful-fs": { @@ -7876,8 +8013,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "libbase64": { @@ -7899,7 +8036,7 @@ "dependencies": { "iconv-lite": { "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", "dev": true } @@ -7917,14 +8054,14 @@ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.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" + "extend": "3.0.2", + "findup-sync": "2.0.0", + "fined": "1.1.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.8.1" } }, "livereload-js": { @@ -7935,7 +8072,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7964,9 +8101,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha1-G3eTz3JZ6jj7NmHU04syYK+K5Oc=", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, "lodash._basecopy": { @@ -7993,7 +8130,7 @@ "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1" + "lodash._htmlescapes": "2.4.1" } }, "lodash._escapestringchar": { @@ -8056,8 +8193,8 @@ "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" }, "dependencies": { "lodash.keys": { @@ -8066,9 +8203,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } } } @@ -8085,7 +8222,7 @@ "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } }, "lodash.clone": { @@ -8112,7 +8249,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.flatten": { @@ -8145,7 +8282,7 @@ "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } }, "lodash.keys": { @@ -8154,9 +8291,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.memoize": { @@ -8195,15 +8332,15 @@ "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._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -8212,8 +8349,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "lodash.uniq": { @@ -8228,7 +8365,7 @@ "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", "dev": true, "requires": { - "lodash.keys": "~2.4.1" + "lodash.keys": "2.4.1" }, "dependencies": { "lodash.keys": { @@ -8237,9 +8374,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } } } @@ -8247,43 +8384,49 @@ "log4js": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.11.0.tgz", - "integrity": "sha512-z1XdwyGFg8/WGkOyF6DPJjivCWNLKrklGdViywdYnSKOvgtEBo2UyEMZS5sD2mZrQlU3TvO8wDWLc8mzE1ncBQ==", + "integrity": "sha1-vzkC7/ZcaSPZzpz70ttUFg40AFo=", "dev": true, "requires": { - "amqplib": "^0.5.2", - "axios": "^0.15.3", - "circular-json": "^0.5.4", - "date-format": "^1.2.0", - "debug": "^3.1.0", - "hipchat-notifier": "^1.1.0", - "loggly": "^1.1.0", - "mailgun-js": "^0.18.0", - "nodemailer": "^2.5.0", - "redis": "^2.7.1", - "semver": "^5.5.0", - "slack-node": "~0.2.0", + "amqplib": "0.5.2", + "axios": "0.15.3", + "circular-json": "0.5.7", + "date-format": "1.2.0", + "debug": "3.2.5", + "hipchat-notifier": "1.1.0", + "loggly": "1.1.1", + "mailgun-js": "0.18.1", + "nodemailer": "2.7.2", + "redis": "2.8.0", + "semver": "5.5.1", + "slack-node": "0.2.0", "streamroller": "0.7.0" }, "dependencies": { "circular-json": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", - "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.7.tgz", + "integrity": "sha512-/pXoV1JA847qRKPrHbBK6YIBGFF8GOP4wzSgUOA7q0ew0vAv0iJswP+2/nZQ9uzA3Azi7eTrg9L2yzXc/7ZMIA==", "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true } } @@ -8295,8 +8438,21 @@ "dev": true, "optional": true, "requires": { - "figures": "^1.3.5", - "squeak": "^1.0.0" + "figures": "1.7.0", + "squeak": "1.3.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.1" + } + } } }, "loggly": { @@ -8306,9 +8462,9 @@ "dev": true, "optional": true, "requires": { - "json-stringify-safe": "5.0.x", - "request": "2.75.x", - "timespan": "2.3.x" + "json-stringify-safe": "5.0.1", + "request": "2.75.0", + "timespan": "2.3.0" }, "dependencies": { "bl": { @@ -8318,7 +8474,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "~2.0.5" + "readable-stream": "2.0.6" } }, "caseless": { @@ -8329,9 +8485,9 @@ "optional": true }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", "dev": true, "optional": true }, @@ -8342,9 +8498,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.11" + "asynckit": "0.4.0", + "combined-stream": "1.0.7", + "mime-types": "2.1.20" } }, "har-validator": { @@ -8354,10 +8510,10 @@ "dev": true, "optional": true, "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" + "chalk": "1.1.3", + "commander": "2.18.0", + "is-my-json-valid": "2.19.0", + "pinkie-promise": "2.0.1" } }, "isarray": { @@ -8390,7 +8546,7 @@ }, "readable-stream": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "optional": true, @@ -8405,7 +8561,7 @@ }, "request": { "version": "2.75.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", + "resolved": "http://registry.npmjs.org/request/-/request-2.75.0.tgz", "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", "dev": true, "optional": true, @@ -8432,13 +8588,6 @@ "tough-cookie": "~2.3.0", "tunnel-agent": "~0.4.1" } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true } } }, @@ -8454,8 +8603,8 @@ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" } }, "lowercase-keys": { @@ -8471,10 +8620,10 @@ "dev": true, "optional": true, "requires": { - "get-stdin": "^4.0.1", - "indent-string": "^2.1.0", - "longest": "^1.0.0", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "indent-string": "2.1.0", + "longest": "1.0.1", + "meow": "3.7.0" } }, "lru-cache": { @@ -8483,12 +8632,6 @@ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", "dev": true }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, "mailcomposer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", @@ -8503,25 +8646,35 @@ "mailgun-js": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.1.tgz", - "integrity": "sha512-lvuMP14u24HS2uBsJEnzSyPMxzU2b99tQsIx1o6QNjqxjk8b3WvR+vq5oG1mjqz/IBYo+5gF+uSoDS0RkMVHmg==", + "integrity": "sha1-7jmqGNe7WYpc6e3oSvtoHe/IprA=", "dev": true, "optional": true, "requires": { - "async": "~2.6.0", - "debug": "~3.1.0", - "form-data": "~2.3.0", - "inflection": "~1.12.0", - "is-stream": "^1.1.0", - "path-proxy": "~1.0.0", - "promisify-call": "^2.0.2", - "proxy-agent": "~3.0.0", - "tsscmp": "~1.0.0" + "async": "2.6.1", + "debug": "3.1.0", + "form-data": "2.3.2", + "inflection": "1.12.0", + "is-stream": "1.1.0", + "path-proxy": "1.0.0", + "promisify-call": "2.0.4", + "proxy-agent": "3.0.3", + "tsscmp": "1.0.5" }, "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "optional": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "optional": true, "requires": { @@ -8535,9 +8688,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.20" } } } @@ -8548,7 +8701,7 @@ "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -8565,7 +8718,7 @@ "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "map-cache": { @@ -8581,9 +8734,9 @@ "dev": true }, "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, "map-visit": { @@ -8592,12 +8745,12 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "marked": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.2.tgz", + "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.2.tgz", "integrity": "sha1-AV2xWIZEOPJKZL3WGgQotBhwbQk=", "dev": true }, @@ -8631,21 +8784,21 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": 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" + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" }, "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -8657,7 +8810,7 @@ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -8668,8 +8821,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -8687,7 +8840,7 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -8699,9 +8852,9 @@ "dev": true, "requires": { "debug": "2.6.9", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" + "methods": "1.1.2", + "parseurl": "1.3.2", + "vary": "1.1.2" }, "dependencies": { "vary": { @@ -8724,19 +8877,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "mime": { @@ -8746,24 +8899,24 @@ "dev": true }, "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha1-o0kgUKXLm2NFBUHjnZeI0icng9s=", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", "dev": true }, "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha1-bzI/YKg9ERRvgx/xH9ZuL+VQO7g=", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.36.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==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimatch": { @@ -8772,12 +8925,12 @@ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, @@ -8787,8 +8940,8 @@ "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -8797,14 +8950,14 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -8813,7 +8966,7 @@ }, "morgan": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", + "resolved": "http://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", "integrity": "sha1-X9gYOYxoGcuiinzWZk8pL+HAu/I=", "dev": true, "requires": { @@ -8826,7 +8979,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -8853,8 +9006,8 @@ "integrity": "sha1-Nd5oBNwZZD5SSfPT473GyM4wHT8=", "dev": true, "requires": { - "readable-stream": "~1.1.9", - "stream-counter": "~0.2.0" + "readable-stream": "1.1.14", + "stream-counter": "0.2.0" } }, "multipipe": { @@ -8873,36 +9026,35 @@ "dev": true }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", "dev": true, "optional": true }, "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", + "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-odd": "^2.0.0", - "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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "natives": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.3.tgz", - "integrity": "sha1-RKV5vmRQfqLW7RygSpQVkVz3VVg=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.5.tgz", + "integrity": "sha512-1pJ+02gl2KJgCPFtpZGtuD4lGSJnIZvvFHCQTOeDRMSXjfu2GmYWuhI8NFMA4W2I5NNFRbfy/YCiVt4CgNpP8A==", "dev": true }, "natural-compare": { @@ -8925,9 +9077,9 @@ "optional": true }, "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "node-fs": { @@ -8948,7 +9100,7 @@ "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", "dev": true, "requires": { - "is": "^3.1.0" + "is": "3.2.1" } }, "nodemailer": { @@ -8967,6 +9119,13 @@ "socks": "1.1.9" }, "dependencies": { + "smart-buffer": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", + "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", + "dev": true, + "optional": true + }, "socks": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", @@ -8974,8 +9133,8 @@ "dev": true, "optional": true, "requires": { - "ip": "^1.1.2", - "smart-buffer": "^1.0.4" + "ip": "1.1.5", + "smart-buffer": "1.1.15" } } } @@ -9042,7 +9201,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.1.1" } }, "normalize-package-data": { @@ -9051,10 +9210,10 @@ "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "4.3.6", + "validate-npm-package-license": "3.0.4" } }, "normalize-path": { @@ -9063,7 +9222,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -9078,10 +9237,2706 @@ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true, "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" + "object-assign": "4.1.1", + "prepend-http": "1.0.4", + "query-string": "4.3.4", + "sort-keys": "1.1.2" + } + }, + "npm": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.4.1.tgz", + "integrity": "sha512-mXJL1NTVU136PtuopXCUQaNWuHlXCTp4McwlSW8S9/Aj8OEPAlSBgo8og7kJ01MjCDrkmqFQTvN5tTEhBMhXQg==", + "requires": { + "JSONStream": "1.3.4", + "abbrev": "1.1.1", + "ansicolors": "0.3.2", + "ansistyles": "0.1.3", + "aproba": "1.2.0", + "archy": "1.0.0", + "bin-links": "1.1.2", + "bluebird": "3.5.1", + "byte-size": "4.0.3", + "cacache": "11.2.0", + "call-limit": "1.1.0", + "chownr": "1.0.1", + "ci-info": "1.4.0", + "cli-columns": "3.1.2", + "cli-table3": "0.5.0", + "cmd-shim": "2.0.2", + "columnify": "1.5.4", + "config-chain": "1.1.11", + "debuglog": "1.0.1", + "detect-indent": "5.0.0", + "detect-newline": "2.1.0", + "dezalgo": "1.0.3", + "editor": "1.0.0", + "figgy-pudding": "3.4.1", + "find-npm-prefix": "1.0.2", + "fs-vacuum": "1.2.10", + "fs-write-stream-atomic": "1.0.10", + "gentle-fs": "2.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "has-unicode": "2.0.1", + "hosted-git-info": "2.7.1", + "iferr": "1.0.2", + "imurmurhash": "0.1.4", + "inflight": "1.0.6", + "inherits": "2.0.3", + "ini": "1.3.5", + "init-package-json": "1.10.3", + "is-cidr": "2.0.6", + "json-parse-better-errors": "1.0.2", + "lazy-property": "1.0.0", + "libcipm": "2.0.2", + "libnpmhook": "4.0.1", + "libnpx": "10.2.0", + "lock-verify": "2.0.2", + "lockfile": "1.0.4", + "lodash._baseindexof": "3.1.0", + "lodash._baseuniq": "4.6.0", + "lodash._bindcallback": "3.0.1", + "lodash._cacheindexof": "3.0.2", + "lodash._createcache": "3.1.2", + "lodash._getnative": "3.9.1", + "lodash.clonedeep": "4.5.0", + "lodash.restparam": "3.6.1", + "lodash.union": "4.6.0", + "lodash.uniq": "4.5.0", + "lodash.without": "4.4.0", + "lru-cache": "4.1.3", + "meant": "1.0.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "node-gyp": "3.8.0", + "nopt": "4.0.1", + "normalize-package-data": "2.4.0", + "npm-audit-report": "1.3.1", + "npm-cache-filename": "1.0.2", + "npm-install-checks": "3.0.0", + "npm-lifecycle": "2.1.0", + "npm-package-arg": "6.1.0", + "npm-packlist": "1.1.11", + "npm-pick-manifest": "2.1.0", + "npm-profile": "3.0.2", + "npm-registry-client": "8.6.0", + "npm-registry-fetch": "1.1.0", + "npm-user-validate": "1.0.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "opener": "1.5.0", + "osenv": "0.1.5", + "pacote": "8.1.6", + "path-is-inside": "1.0.2", + "promise-inflight": "1.0.1", + "qrcode-terminal": "0.12.0", + "query-string": "6.1.0", + "qw": "1.0.1", + "read": "1.0.7", + "read-cmd-shim": "1.0.1", + "read-installed": "4.0.3", + "read-package-json": "2.0.13", + "read-package-tree": "5.2.1", + "readable-stream": "2.3.6", + "readdir-scoped-modules": "1.0.2", + "request": "2.88.0", + "retry": "0.12.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "sha": "2.0.1", + "slide": "1.1.6", + "sorted-object": "2.0.1", + "sorted-union-stream": "2.1.3", + "ssri": "6.0.0", + "stringify-package": "1.0.0", + "tar": "4.4.6", + "text-table": "0.2.0", + "tiny-relative-date": "1.3.0", + "uid-number": "0.0.6", + "umask": "1.1.0", + "unique-filename": "1.1.0", + "unpipe": "1.0.0", + "update-notifier": "2.5.0", + "uuid": "3.3.2", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0", + "which": "1.3.1", + "worker-farm": "1.6.0", + "write-file-atomic": "2.3.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.4", + "bundled": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, + "agentkeepalive": { + "version": "3.4.1", + "bundled": true, + "requires": { + "humanize-ms": "1.2.1" + } + }, + "ajv": { + "version": "5.5.2", + "bundled": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "2.1.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "1.2.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.3.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "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.5" + } + }, + "bin-links": { + "version": "1.1.2", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "cmd-shim": "2.0.2", + "gentle-fs": "2.0.1", + "graceful-fs": "4.1.11", + "write-file-atomic": "2.3.0" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "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 + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "4.0.3", + "bundled": true + }, + "cacache": { + "version": "11.2.0", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "chownr": "1.0.1", + "figgy-pudding": "3.4.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "6.0.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" + } + }, + "call-limit": { + "version": "1.1.0", + "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.4.0" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "ci-info": { + "version": "1.4.0", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.9", + "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.1.1", + "strip-ansi": "3.0.1" + } + }, + "cli-table3": { + "version": "0.5.0", + "bundled": true, + "requires": { + "colors": "1.1.2", + "object-assign": "4.1.1", + "string-width": "2.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1" + } + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.1.2", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "3.0.1", + "wcwidth": "1.0.1" + } + }, + "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.3.6", + "typedarray": "0.0.6" + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ini": "1.3.5", + "proto-list": "1.2.4" + } + }, + "configstore": { + "version": "3.1.2", + "bundled": true, + "requires": { + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.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.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + }, + "dependencies": { + "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.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "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.5.1", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "1.0.4" + } + }, + "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.6", + "wrappy": "1.0.2" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "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.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "0.4.23" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "1.0.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.4.1", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.6" + } + }, + "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.19" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.6" + } + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "2.3.3" + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "path-is-inside": "1.0.2", + "rimraf": "2.6.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.6" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "genfun": { + "version": "4.0.1", + "bundled": true + }, + "gentle-fs": { + "version": "2.0.1", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "fs-vacuum": "1.2.10", + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "path-is-inside": "1.0.2", + "read-cmd-shim": "1.0.1", + "slide": "1.1.6" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "1.3.5" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.7.1", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "2.1.1" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "7.1.2", + "npm-package-arg": "6.1.0", + "promzard": "0.3.0", + "read": "1.0.7", + "read-package-json": "2.0.13", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-ci": { + "version": "1.1.0", + "bundled": true, + "requires": { + "ci-info": "1.4.0" + } + }, + "is-cidr": { + "version": "2.0.6", + "bundled": true, + "requires": { + "cidr-regex": "2.0.9" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" + } + }, + "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.2" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "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-schema-traverse": { + "version": "0.3.1", + "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.1" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "libcipm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "bin-links": "1.1.2", + "bluebird": "3.5.1", + "find-npm-prefix": "1.0.2", + "graceful-fs": "4.1.11", + "lock-verify": "2.0.2", + "mkdirp": "0.5.1", + "npm-lifecycle": "2.1.0", + "npm-logical-tree": "1.2.1", + "npm-package-arg": "6.1.0", + "pacote": "8.1.6", + "protoduck": "5.0.0", + "read-package-json": "2.0.13", + "rimraf": "2.6.2", + "worker-farm": "1.6.0" + } + }, + "libnpmhook": { + "version": "4.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "3.4.1", + "npm-registry-fetch": "3.1.1" + }, + "dependencies": { + "npm-registry-fetch": { + "version": "3.1.1", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "figgy-pudding": "3.4.1", + "lru-cache": "4.1.3", + "make-fetch-happen": "4.0.1", + "npm-package-arg": "6.1.0" + } + } + } + }, + "libnpx": { + "version": "10.2.0", + "bundled": true, + "requires": { + "dotenv": "5.0.1", + "npm-package-arg": "6.1.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "update-notifier": "2.5.0", + "which": "1.3.1", + "y18n": "4.0.0", + "yargs": "11.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lock-verify": { + "version": "2.0.2", + "bundled": true, + "requires": { + "npm-package-arg": "6.1.0", + "semver": "5.5.0" + } + }, + "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.3", + "lodash._root": "3.0.1" + } + }, + "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.9.1" + } + }, + "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": "4.1.3", + "bundled": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "3.0.0" + } + }, + "make-fetch-happen": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agentkeepalive": "3.4.1", + "cacache": "11.2.0", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "4.0.1", + "ssri": "6.0.0" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, + "mime-db": { + "version": "1.35.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "requires": { + "mime-db": "1.35.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "2.3.3" + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "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.12", + "json-parse-better-errors": "1.0.2", + "safe-buffer": "5.1.2" + } + }, + "node-gyp": { + "version": "3.8.0", + "bundled": true, + "requires": { + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.88.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.3.1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "bundled": true, + "requires": { + "abbrev": "1.1.1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "requires": { + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.4" + } + }, + "npm-audit-report": { + "version": "1.3.1", + "bundled": true, + "requires": { + "cli-table3": "0.5.0", + "console-control-strings": "1.1.0" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "requires": { + "semver": "5.5.0" + } + }, + "npm-lifecycle": { + "version": "2.1.0", + "bundled": true, + "requires": { + "byline": "5.0.0", + "graceful-fs": "4.1.11", + "node-gyp": "3.8.0", + "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-package-arg": { + "version": "6.1.0", + "bundled": true, + "requires": { + "hosted-git-info": "2.7.1", + "osenv": "0.1.5", + "semver": "5.5.0", + "validate-npm-package-name": "3.0.0" + } + }, + "npm-packlist": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.5" + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "6.1.0", + "semver": "5.5.0" + } + }, + "npm-profile": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "make-fetch-happen": "4.0.1" + } + }, + "npm-registry-client": { + "version": "8.6.0", + "bundled": true, + "requires": { + "concat-stream": "1.6.2", + "graceful-fs": "4.1.11", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.1.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "request": "2.88.0", + "retry": "0.10.1", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "slide": "1.1.6", + "ssri": "5.3.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "npm-registry-fetch": { + "version": "1.1.0", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "figgy-pudding": "2.0.1", + "lru-cache": "4.1.3", + "make-fetch-happen": "3.0.0", + "npm-package-arg": "6.1.0", + "safe-buffer": "5.1.2" + }, + "dependencies": { + "cacache": { + "version": "10.0.4", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.3.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" + }, + "dependencies": { + "mississippi": { + "version": "2.0.0", + "bundled": true, + "requires": { + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" + } + } + } + }, + "figgy-pudding": { + "version": "2.0.1", + "bundled": true + }, + "make-fetch-happen": { + "version": "3.0.0", + "bundled": true, + "requires": { + "agentkeepalive": "3.4.1", + "cacache": "10.0.4", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "3.0.1", + "ssri": "5.3.0" + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "1.1.15" + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "socks": "1.1.10" + } + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "2.0.1" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "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 + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "opener": { + "version": "1.5.0", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" + } + }, + "pacote": { + "version": "8.1.6", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "cacache": "11.2.0", + "get-stream": "3.0.0", + "glob": "7.1.2", + "lru-cache": "4.1.3", + "make-fetch-happen": "4.0.1", + "minimatch": "3.0.4", + "minipass": "2.3.3", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.1.0", + "npm-packlist": "1.1.11", + "npm-pick-manifest": "2.1.0", + "osenv": "0.1.5", + "promise-inflight": "1.0.1", + "promise-retry": "1.1.1", + "protoduck": "5.0.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "ssri": "6.0.0", + "tar": "4.4.6", + "unique-filename": "1.1.0", + "which": "1.3.1" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.6" + } + }, + "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 + }, + "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.1.2", + "retry": "0.10.1" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1.0.7" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "4.0.1" + } + }, + "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.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "3.6.0", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "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.1.0", + "bundled": true, + "requires": { + "decode-uri-component": "0.2.0", + "strict-uri-encode": "2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "0.0.7" + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "graceful-fs": "4.1.11", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2", + "semver": "5.5.0", + "slide": "1.1.6", + "util-extend": "1.0.3" + } + }, + "read-package-json": { + "version": "2.0.13", + "bundled": true, + "requires": { + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "json-parse-better-errors": "1.0.2", + "normalize-package-data": "2.4.0", + "slash": "1.0.0" + } + }, + "read-package-tree": { + "version": "5.2.1", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "once": "1.4.0", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2" + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "graceful-fs": "4.1.11", + "once": "1.4.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "bundled": true, + "requires": { + "rc": "1.2.7", + "safe-buffer": "5.1.2" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "1.2.7" + } + }, + "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": "1.0.1", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "1.2.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "5.5.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "readable-stream": "2.3.6" + } + }, + "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 + }, + "slash": { + "version": "1.0.0", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.0.1", + "bundled": true + }, + "socks": { + "version": "2.2.0", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "socks": "2.2.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.2.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "1.1.14" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "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.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" + } + }, + "ssri": { + "version": "6.0.0", + "bundled": true + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "2.3.6", + "stream-shift": "1.0.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.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "stringify-package": { + "version": "1.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "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.6", + "bundled": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.3", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "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.3.6", + "xtend": "4.0.1" + } + }, + "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.29", + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "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.0", + "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.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "1.0.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "uuid": { + "version": "3.3.2", + "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.3.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.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "widest-line": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "2.1.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "bundled": true, + "requires": { + "errno": "0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.3.0", + "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": "2.1.2", + "bundled": true + }, + "yargs": { + "version": "11.0.0", + "bundled": true, + "requires": { + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "4.1.0" + } + } } }, "npm-run-path": { @@ -9091,7 +11946,7 @@ "dev": true, "optional": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "nth-check": { @@ -9101,7 +11956,7 @@ "dev": true, "optional": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "1.0.0" } }, "num2fraction": { @@ -9140,9 +11995,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -9151,7 +12006,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "kind-of": { @@ -9160,7 +12015,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -9177,7 +12032,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.defaults": { @@ -9186,10 +12041,10 @@ "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" + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" } }, "object.getownpropertydescriptors": { @@ -9199,8 +12054,8 @@ "dev": true, "optional": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0" } }, "object.map": { @@ -9209,8 +12064,8 @@ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.omit": { @@ -9219,8 +12074,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" }, "dependencies": { "for-own": { @@ -9229,7 +12084,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } } } @@ -9240,7 +12095,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "object.values": { @@ -9250,10 +12105,10 @@ "dev": true, "optional": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has": "1.0.3" } }, "on-finished": { @@ -9277,14 +12132,17 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true + "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.2.0" + } }, "open": { "version": "0.0.5", @@ -9298,8 +12156,16 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } } }, "optionator": { @@ -9308,20 +12174,12 @@ "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" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "optipng-bin": { @@ -9331,9 +12189,9 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^2.0.0", - "bin-wrapper": "^3.0.0", - "logalot": "^2.0.0" + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" } }, "orchestrator": { @@ -9342,9 +12200,9 @@ "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "dev": true, "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.1" } }, "ordered-read-streams": { @@ -9385,20 +12243,20 @@ "dev": true }, "pac-proxy-agent": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz", - "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz", + "integrity": "sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q==", "dev": true, "optional": true, "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "get-uri": "^2.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "pac-resolver": "^3.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "^3.0.0" + "agent-base": "4.2.1", + "debug": "3.2.5", + "get-uri": "2.0.2", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "pac-resolver": "3.0.0", + "raw-body": "2.3.3", + "socks-proxy-agent": "4.0.1" }, "dependencies": { "bytes": { @@ -9409,13 +12267,13 @@ "optional": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "depd": { @@ -9427,7 +12285,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "optional": true, @@ -9441,17 +12299,24 @@ "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=", "dev": true, "optional": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "optional": true + }, "raw-body": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "integrity": "sha1-GzJOzmtXBuFThVvBFIxlu39uoMM=", "dev": true, "optional": true, "requires": { @@ -9460,32 +12325,30 @@ "iconv-lite": "0.4.23", "unpipe": "1.0.0" } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz", - "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", - "dev": true, - "optional": true, - "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" - } } } }, "pac-resolver": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", - "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", + "integrity": "sha1-auoweH2wqJFwTet4AKcip2FabyY=", "dev": true, "optional": true, "requires": { - "co": "^4.6.0", - "degenerator": "^1.0.4", - "ip": "^1.1.5", - "netmask": "^1.0.6", - "thunkify": "^2.1.2" + "co": "4.6.0", + "degenerator": "1.0.4", + "ip": "1.1.5", + "netmask": "1.0.6", + "thunkify": "2.1.2" + }, + "dependencies": { + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + } } }, "parse-filepath": { @@ -9494,9 +12357,9 @@ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-glob": { @@ -9505,10 +12368,10 @@ "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" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" }, "dependencies": { "is-extglob": { @@ -9523,7 +12386,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -9534,7 +12397,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.2" } }, "parse-passwd": { @@ -9549,7 +12412,7 @@ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseuri": { @@ -9558,7 +12421,7 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseurl": { @@ -9591,7 +12454,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -9613,9 +12476,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "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-proxy": { @@ -9625,7 +12488,7 @@ "dev": true, "optional": true, "requires": { - "inflection": "~1.3.0" + "inflection": "1.3.8" }, "dependencies": { "inflection": { @@ -9643,7 +12506,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -9658,9 +12521,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" }, "dependencies": { "graceful-fs": { @@ -9679,7 +12542,7 @@ }, "pause-stream": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { @@ -9704,21 +12567,27 @@ "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", "dev": true, "requires": { - "es6-promise": "^4.0.3", - "extract-zip": "^1.6.5", - "fs-extra": "^1.0.0", - "hasha": "^2.2.0", - "kew": "^0.7.0", - "progress": "^1.1.8", - "request": "^2.81.0", - "request-progress": "^2.0.1", - "which": "^1.2.10" + "es6-promise": "4.2.5", + "extract-zip": "1.6.7", + "fs-extra": "1.0.0", + "hasha": "2.2.0", + "kew": "0.7.0", + "progress": "1.1.8", + "request": "2.81.0", + "request-progress": "2.0.1", + "which": "1.3.1" }, "dependencies": { "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha1-3EIhwrFlGHYL2MOaUtjzVvwA7Sk=", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true } } @@ -9741,59 +12610,19 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "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-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - }, - "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 - } + "ansi-colors": "1.1.0", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" } }, "plugin-log": { @@ -9802,8 +12631,8 @@ "integrity": "sha1-hgSc9qsQgzOYqTHzaJy67nteEzM=", "dev": true, "requires": { - "chalk": "^1.1.1", - "dateformat": "^1.0.11" + "chalk": "1.1.3", + "dateformat": "1.0.12" }, "dependencies": { "dateformat": { @@ -9812,8 +12641,8 @@ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "meow": "3.7.0" } } } @@ -9824,13 +12653,13 @@ "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", "dev": true, "requires": { - "irregular-plurals": "^1.0.0" + "irregular-plurals": "1.4.0" } }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", "dev": true }, "posix-character-classes": { @@ -9845,15 +12674,15 @@ "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", "dev": true, "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.9", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "postcss-calc": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, "requires": { @@ -9868,9 +12697,9 @@ "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "dev": true, "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" + "colormin": "1.1.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" } }, "postcss-convert-values": { @@ -9879,13 +12708,13 @@ "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "dev": true, "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" } }, "postcss-discard-comments": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, "requires": { @@ -9898,12 +12727,12 @@ "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "dev": true, "requires": { - "postcss": "^5.0.4" + "postcss": "5.2.18" } }, "postcss-discard-empty": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, "requires": { @@ -9912,7 +12741,7 @@ }, "postcss-discard-overridden": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, "requires": { @@ -9921,7 +12750,7 @@ }, "postcss-discard-unused": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, "requires": { @@ -9930,13 +12759,12 @@ } }, "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", "dev": true, "requires": { - "postcss": "^5.0.4", - "uniqid": "^4.0.0" + "postcss": "5.2.18" } }, "postcss-load-config": { @@ -9945,10 +12773,10 @@ "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", "dev": true, "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1", + "postcss-load-options": "1.2.0", + "postcss-load-plugins": "2.3.0" } }, "postcss-load-options": { @@ -9957,8 +12785,8 @@ "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", "dev": true, "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" } }, "postcss-load-plugins": { @@ -9967,13 +12795,13 @@ "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", "dev": true, "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" } }, "postcss-merge-idents": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "resolved": "http://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "requires": { @@ -9988,7 +12816,7 @@ "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "dev": true, "requires": { - "postcss": "^5.0.4" + "postcss": "5.2.18" } }, "postcss-merge-rules": { @@ -9997,11 +12825,11 @@ "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "dev": true, "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", - "vendors": "^1.0.0" + "browserslist": "1.7.7", + "caniuse-api": "1.6.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3", + "vendors": "1.0.2" } }, "postcss-message-helpers": { @@ -10012,7 +12840,7 @@ }, "postcss-minify-font-values": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "requires": { @@ -10023,7 +12851,7 @@ }, "postcss-minify-gradients": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, "requires": { @@ -10033,7 +12861,7 @@ }, "postcss-minify-params": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, "requires": { @@ -10045,7 +12873,7 @@ }, "postcss-minify-selectors": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, "requires": { @@ -10057,7 +12885,7 @@ }, "postcss-normalize-charset": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, "requires": { @@ -10066,7 +12894,7 @@ }, "postcss-normalize-url": { "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "resolved": "http://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "requires": { @@ -10082,13 +12910,13 @@ "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" } }, "postcss-reduce-idents": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, "requires": { @@ -10098,7 +12926,7 @@ }, "postcss-reduce-initial": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, "requires": { @@ -10107,7 +12935,7 @@ }, "postcss-reduce-transforms": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "requires": { @@ -10122,14 +12950,14 @@ "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", "dev": true, "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } }, "postcss-svgo": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "resolved": "http://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "requires": { @@ -10141,7 +12969,7 @@ }, "postcss-unique-selectors": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "resolved": "http://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "requires": { @@ -10158,7 +12986,7 @@ }, "postcss-zindex": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "requires": { @@ -10204,9 +13032,9 @@ "dev": true }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, "promise": { @@ -10216,7 +13044,7 @@ "dev": true, "optional": true, "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "promisify-call": { @@ -10226,46 +13054,53 @@ "dev": true, "optional": true, "requires": { - "with-callback": "^1.0.2" + "with-callback": "1.0.2" } }, "proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.1.tgz", - "integrity": "sha512-mAZexaz9ZxQhYPWfAjzlrloEjW+JHiBFryE4AJXFDTnaXfmH/FKqC1swTRKuEPbHWz02flQNXFOyDUF7zfEG6A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.3.tgz", + "integrity": "sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA==", "dev": true, "optional": true, "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "pac-proxy-agent": "^2.0.1", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^4.0.1" + "agent-base": "4.2.1", + "debug": "3.2.5", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "pac-proxy-agent": "3.0.0", + "proxy-from-env": "1.0.0", + "socks-proxy-agent": "4.0.1" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "lru-cache": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", "dev": true, "optional": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "optional": true } } }, @@ -10291,9 +13126,9 @@ "optional": true }, "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "q": { @@ -10305,7 +13140,7 @@ "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==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -10320,8 +13155,8 @@ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true, "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" } }, "random-bytes": { @@ -10331,20 +13166,20 @@ "dev": true }, "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha1-01SQAw6091eN4pLObfsEqRoSiSM=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "dev": true, "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "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": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -10374,7 +13209,7 @@ }, "iconv-lite": { "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", "dev": true } @@ -10386,10 +13221,10 @@ "integrity": "sha1-zZJL9SAKB1uDwYjNa54hG3/A0+0=", "dev": true, "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -10406,8 +13241,8 @@ "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0", - "readable-stream": "^2.0.0" + "pinkie-promise": "2.0.1", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -10418,7 +13253,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -10434,7 +13269,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -10448,9 +13283,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -10459,13 +13294,13 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -10476,15 +13311,14 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "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.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" }, "dependencies": { "graceful-fs": { @@ -10501,8 +13335,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -10520,7 +13354,7 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -10531,7 +13365,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.8.1" } }, "redent": { @@ -10540,26 +13374,26 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "redis": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", "dev": true, "optional": true, "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "double-ended-queue": "2.1.0-0", + "redis-commands": "1.3.5", + "redis-parser": "2.6.0" } }, "redis-commands": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz", - "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==", + "integrity": "sha1-RJWIlBTx6IYmEYCxRC5ylWAtg6I=", "dev": true, "optional": true }, @@ -10572,7 +13406,7 @@ }, "reduce-css-calc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, "requires": { @@ -10595,7 +13429,7 @@ "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "dev": true, "requires": { - "balanced-match": "^0.4.2" + "balanced-match": "0.4.2" }, "dependencies": { "balanced-match": { @@ -10612,7 +13446,7 @@ "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regex-not": { @@ -10621,23 +13455,14 @@ "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexpp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", - "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", + "integrity": "sha1-sqdTSoXKGwM7z1zp/45W1OB1U2U=", "dev": true }, "remove-trailing-separator": { @@ -10647,9 +13472,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "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": { @@ -10664,7 +13489,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "replace-ext": { @@ -10679,28 +13504,28 @@ "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", "dev": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" + "aws-sign2": "0.6.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.7", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.20", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.2", + "stringstream": "0.0.6", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" }, "dependencies": { "qs": { @@ -10708,6 +13533,15 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", "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.1.2" + } } } }, @@ -10717,20 +13551,20 @@ "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", "dev": true, "requires": { - "throttleit": "^1.0.0" + "throttleit": "1.0.0" } }, "requestretry": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", + "integrity": "sha1-IT7BAG7rdQ6LjOVBdig9FajVXZQ=", "dev": true, "optional": true, "requires": { - "extend": "^3.0.0", - "lodash": "^4.15.0", - "request": "^2.74.0", - "when": "^3.7.7" + "extend": "3.0.2", + "lodash": "4.17.11", + "request": "2.81.0", + "when": "3.7.8" } }, "require-from-string": { @@ -10745,8 +13579,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" } }, "requires-port": { @@ -10756,12 +13590,12 @@ "dev": true }, "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha1-qt1lY3T9KYruiVvAJrgpdBhnf9M=", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.6" } }, "resolve-dir": { @@ -10770,8 +13604,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } }, "resolve-from": { @@ -10792,8 +13626,8 @@ "integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=", "dev": true, "requires": { - "depd": "~1.1.0", - "on-headers": "~1.0.1" + "depd": "1.1.2", + "on-headers": "1.0.1" }, "dependencies": { "depd": { @@ -10810,19 +13644,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "dependencies": { - "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" - } - } + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "ret": { @@ -10837,7 +13660,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -10846,7 +13669,7 @@ "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.3" } }, "rndm": { @@ -10861,7 +13684,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "^2.1.0" + "is-promise": "2.1.0" } }, "run-sequence": { @@ -10870,18 +13693,70 @@ "integrity": "sha1-HOZD2jb9jH6n4akynaM/wriJhJU=", "dev": true, "requires": { - "chalk": "^1.1.3", - "fancy-log": "^1.3.2", - "plugin-error": "^0.1.2" + "chalk": "1.1.3", + "fancy-log": "1.3.2", + "plugin-error": "0.1.2" + }, + "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.1.0", + "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": "http://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.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + } } }, "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "1.9.3" } }, "safe-buffer": { @@ -10896,13 +13771,13 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", "dev": true }, "sax": { @@ -10917,7 +13792,7 @@ "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, "requires": { - "commander": "~2.8.1" + "commander": "2.8.1" } }, "semver": { @@ -10940,13 +13815,13 @@ "dev": true, "optional": true, "requires": { - "semver": "^5.3.0" + "semver": "5.5.1" }, "dependencies": { "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true, "optional": true } @@ -10958,23 +13833,23 @@ "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=", "dev": true, "requires": { - "debug": "~2.2.0", - "depd": "~1.1.0", - "destroy": "~1.0.4", - "escape-html": "~1.0.3", - "etag": "~1.7.0", + "debug": "2.2.0", + "depd": "1.1.2", + "destroy": "1.0.4", + "escape-html": "1.0.3", + "etag": "1.7.0", "fresh": "0.3.0", - "http-errors": "~1.3.1", + "http-errors": "1.3.1", "mime": "1.3.4", "ms": "0.7.1", - "on-finished": "~2.3.0", - "range-parser": "~1.0.3", - "statuses": "~1.2.1" + "on-finished": "2.3.0", + "range-parser": "1.0.3", + "statuses": "1.2.1" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -11013,10 +13888,10 @@ "integrity": "sha1-3UGeJo3gEqtysxnTN/IQUBP5OB8=", "dev": true, "requires": { - "etag": "~1.7.0", + "etag": "1.7.0", "fresh": "0.3.0", "ms": "0.7.2", - "parseurl": "~1.3.1" + "parseurl": "1.3.2" }, "dependencies": { "ms": { @@ -11033,18 +13908,18 @@ "integrity": "sha1-egV/xu4o3GP2RWbl+lexEahq7NI=", "dev": true, "requires": { - "accepts": "~1.2.13", + "accepts": "1.2.13", "batch": "0.5.3", - "debug": "~2.2.0", - "escape-html": "~1.0.3", - "http-errors": "~1.3.1", - "mime-types": "~2.1.9", - "parseurl": "~1.3.1" + "debug": "2.2.0", + "escape-html": "1.0.3", + "http-errors": "1.3.1", + "mime-types": "2.1.20", + "parseurl": "1.3.2" }, "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -11065,8 +13940,8 @@ "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=", "dev": true, "requires": { - "escape-html": "~1.0.3", - "parseurl": "~1.3.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.13.2" } }, @@ -11082,10 +13957,10 @@ "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -11094,7 +13969,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -11102,7 +13977,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "shebang-command": { @@ -11111,7 +13986,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -11139,7 +14014,7 @@ "dev": true, "optional": true, "requires": { - "requestretry": "^1.2.2" + "requestretry": "1.13.0" } }, "slash": { @@ -11151,18 +14026,17 @@ "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "integrity": "sha1-BE8aSdiEL/MHqta1Be0Xi9lQE00=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0" + "is-fullwidth-code-point": "2.0.0" } }, "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true, - "optional": true + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", + "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", + "dev": true }, "smtp-connection": { "version": "2.12.0", @@ -11180,14 +14054,14 @@ "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "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" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.1" }, "dependencies": { "define-property": { @@ -11196,7 +14070,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -11205,7 +14079,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -11216,9 +14090,9 @@ "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -11227,7 +14101,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -11236,7 +14110,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -11245,7 +14119,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -11254,9 +14128,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -11267,7 +14141,7 @@ "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -11276,7 +14150,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -11287,7 +14161,7 @@ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "socket.io": { @@ -11296,11 +14170,11 @@ "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", "dev": true, "requires": { - "debug": "~2.6.6", - "engine.io": "~3.1.0", - "socket.io-adapter": "~1.1.0", + "debug": "2.6.9", + "engine.io": "3.1.5", + "socket.io-adapter": "1.1.1", "socket.io-client": "2.0.4", - "socket.io-parser": "~3.1.1" + "socket.io-parser": "3.1.3" } }, "socket.io-adapter": { @@ -11319,33 +14193,33 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~2.6.4", - "engine.io-client": "~3.1.0", + "debug": "2.6.9", + "engine.io-client": "3.1.6", "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.1.1", + "socket.io-parser": "3.1.3", "to-array": "0.1.4" } }, "socket.io-parser": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz", - "integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==", + "integrity": "sha1-7S2l7nnxCVUDbj2kE7/X8eTYbI4=", "dev": true, "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", - "has-binary2": "~1.0.2", + "debug": "3.1.0", + "has-binary2": "1.0.3", "isarray": "2.0.1" }, "dependencies": { "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -11360,45 +14234,23 @@ } }, "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", + "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", "dev": true, - "optional": true, "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" + "ip": "1.1.5", + "smart-buffer": "4.0.1" } }, "socks-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", - "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "integrity": "sha1-WTa/i3B6mTB5xvN9sgkYIb/6ZHM=", "dev": true, - "optional": true, "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" - }, - "dependencies": { - "smart-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", - "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", - "dev": true, - "optional": true - }, - "socks": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", - "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", - "dev": true, - "optional": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.0.1" - } - } + "agent-base": "4.2.1", + "socks": "2.2.1" } }, "sort-keys": { @@ -11407,7 +14259,7 @@ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { - "is-plain-obj": "^1.0.0" + "is-plain-obj": "1.1.0" } }, "source-map": { @@ -11417,16 +14269,16 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha1-etD1k/IoFZjoVN+A8ZquS5LXoRo=", + "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.0.0", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.2", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-url": { @@ -11436,25 +14288,25 @@ "dev": true }, "sparkles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", - "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "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.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.1.tgz", + "integrity": "sha512-hxSPZbRZvSDuOvADntOElzJpenIR7wXJkuoUcUtS0erbgt2fgeaoPIYretfKpslMhfFDY4k0MZ2F5CUzhBsSvQ==", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.1" } }, "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=", + "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": { @@ -11463,23 +14315,23 @@ "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.2.0", + "spdx-license-ids": "3.0.1" } }, "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", + "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", "dev": true }, "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "split-string": { @@ -11488,7 +14340,7 @@ "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { @@ -11504,25 +14356,26 @@ "dev": true, "optional": true, "requires": { - "chalk": "^1.0.0", - "console-stream": "^0.1.1", - "lpad-align": "^1.0.1" + "chalk": "1.1.3", + "console-stream": "0.1.1", + "lpad-align": "1.1.2" } }, "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": 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", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" }, "dependencies": { "assert-plus": { @@ -11552,8 +14405,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -11562,7 +14415,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -11574,12 +14427,13 @@ "dev": true }, "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "version": "0.2.2", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1", + "through": "2.3.8" } }, "stream-combiner2": { @@ -11588,8 +14442,8 @@ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "duplexer2": "0.1.4", + "readable-stream": "2.3.6" }, "dependencies": { "duplexer2": { @@ -11598,7 +14452,7 @@ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.3.6" } }, "isarray": { @@ -11609,7 +14463,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -11625,7 +14479,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -11645,7 +14499,7 @@ "integrity": "sha1-3tJmVWMZyLDiIoErnPOyb6fZR94=", "dev": true, "requires": { - "readable-stream": "~1.1.8" + "readable-stream": "1.1.14" } }, "stream-shift": { @@ -11657,22 +14511,22 @@ "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", "dev": true, "requires": { - "date-format": "^1.2.0", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" + "date-format": "1.2.0", + "debug": "3.2.5", + "mkdirp": "0.5.1", + "readable-stream": "2.3.6" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "isarray": { @@ -11681,9 +14535,15 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -11696,157 +14556,105 @@ "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" - } - } - } - }, - "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 - }, - "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": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - } - } - }, - "string.prototype.matchall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", - "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "regexp.prototype.flags": "^1.2.0" - } - }, - "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 - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "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": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", - "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", - "dev": true, - "requires": { - "first-chunk-stream": "^1.0.0", - "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" - }, - "dependencies": { - "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" - } - }, - "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": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", - "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": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } + } + } + }, + "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 + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "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 }, + "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" + } + } + } + }, + "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 + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://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": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + }, + "dependencies": { "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" + "is-utf8": "0.2.1" } } } }, "strip-dirs": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "dev": true, "requires": { @@ -11875,7 +14683,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -11894,7 +14702,7 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "get-stdin": "4.0.1" } }, "strip-json-comments": { @@ -11909,7 +14717,7 @@ "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.2" + "escape-string-regexp": "1.0.5" } }, "sum-up": { @@ -11918,7 +14726,7 @@ "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", "dev": true, "requires": { - "chalk": "^1.0.0" + "chalk": "1.1.3" } }, "supports-color": { @@ -11927,7 +14735,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } }, "svgo": { @@ -11936,13 +14744,13 @@ "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "dev": true, "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" }, "dependencies": { "colors": { @@ -11953,45 +14761,27 @@ } } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, "table": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", + "ajv": "6.5.4", + "ajv-keywords": "3.2.0", + "chalk": "2.4.1", + "lodash": "4.17.11", "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "string-width": "2.1.1" }, "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, "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" + "color-convert": "1.9.3" } }, "chalk": { @@ -12000,9 +14790,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "has-flag": { @@ -12012,29 +14802,29 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "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" + "has-flag": "3.0.0" } } } }, "tar-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", - "integrity": "sha1-+E7xaWJp1iI8pI9uHu7eP36B85U=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.1.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.0", - "xtend": "^4.0.0" + "bl": "1.2.2", + "buffer-alloc": "1.2.0", + "end-of-stream": "1.4.1", + "fs-constants": "1.0.0", + "readable-stream": "2.3.6", + "to-buffer": "1.1.1", + "xtend": "4.0.1" }, "dependencies": { "end-of-stream": { @@ -12043,7 +14833,7 @@ "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "isarray": { @@ -12054,7 +14844,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -12092,8 +14882,8 @@ "dev": true, "optional": true, "requires": { - "temp-dir": "^1.0.0", - "uuid": "^3.0.1" + "temp-dir": "1.0.0", + "uuid": "3.3.2" } }, "text-table": { @@ -12110,7 +14900,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -12120,8 +14910,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.1" }, "dependencies": { "isarray": { @@ -12132,8 +14922,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "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", @@ -12151,7 +14941,7 @@ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -12162,7 +14952,7 @@ "integrity": "sha1-EctOpMnjG8puTB5tukjRxyjDUks=", "dev": true, "requires": { - "through2": "^2.0.0" + "through2": "2.0.3" } }, "through2-filter": { @@ -12171,8 +14961,8 @@ "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "through2": "2.0.3", + "xtend": "4.0.1" } }, "thunkify": { @@ -12188,7 +14978,7 @@ "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "time-stamp": { @@ -12216,12 +15006,12 @@ "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", "dev": true, "requires": { - "body-parser": "~1.14.0", - "debug": "~2.2.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.2.0", - "parseurl": "~1.3.0", - "qs": "~5.1.0" + "body-parser": "1.14.2", + "debug": "2.2.0", + "faye-websocket": "0.10.0", + "livereload-js": "2.3.0", + "parseurl": "1.3.2", + "qs": "5.1.0" }, "dependencies": { "body-parser": { @@ -12231,15 +15021,15 @@ "dev": true, "requires": { "bytes": "2.2.0", - "content-type": "~1.0.1", - "debug": "~2.2.0", - "depd": "~1.1.0", - "http-errors": "~1.3.1", + "content-type": "1.0.4", + "debug": "2.2.0", + "depd": "1.1.2", + "http-errors": "1.3.1", "iconv-lite": "0.4.13", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "5.2.0", - "raw-body": "~2.1.5", - "type-is": "~1.6.10" + "raw-body": "2.1.7", + "type-is": "1.6.16" }, "dependencies": { "qs": { @@ -12258,7 +15048,7 @@ }, "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -12273,7 +15063,7 @@ }, "iconv-lite": { "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", "dev": true }, @@ -12294,10 +15084,10 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-absolute-glob": { @@ -12306,7 +15096,7 @@ "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", "dev": true, "requires": { - "extend-shallow": "^2.0.1" + "extend-shallow": "2.0.1" }, "dependencies": { "extend-shallow": { @@ -12315,7 +15105,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -12338,7 +15128,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -12347,7 +15137,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -12358,10 +15148,10 @@ "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -12370,8 +15160,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "tough-cookie": { @@ -12380,7 +15170,15 @@ "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", "dev": true, "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } } }, "trim-newlines": { @@ -12395,7 +15193,7 @@ "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.2" + "escape-string-regexp": "1.0.5" } }, "tryit": { @@ -12404,6 +15202,12 @@ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, "tsscmp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", @@ -12411,13 +15215,10 @@ "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" - } + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true }, "tweetnacl": { "version": "0.14.5", @@ -12432,7 +15233,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-is": { @@ -12442,7 +15243,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.20" } }, "typedarray": { @@ -12457,9 +15258,9 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -12475,13 +15276,13 @@ "integrity": "sha1-Otbzg2jG1MjHXsF2I/t5qh0HHYE=", "dev": true, "requires": { - "random-bytes": "~1.0.0" + "random-bytes": "1.0.0" } }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -12502,10 +15303,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -12514,7 +15315,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -12523,10 +15324,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -12537,15 +15338,6 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "^0.2.8" - } - }, "uniqs": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", @@ -12577,8 +15369,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -12587,9 +15379,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -12626,24 +15418,16 @@ "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", "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==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } + "punycode": "2.1.1" } }, "urix": { @@ -12658,7 +15442,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "url-regex": { @@ -12668,17 +15452,14 @@ "dev": true, "optional": true, "requires": { - "ip-regex": "^1.0.1" + "ip-regex": "1.0.3" } }, "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } + "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 }, "user-home": { "version": "1.1.1", @@ -12692,8 +15473,8 @@ "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", "dev": true, "requires": { - "lru-cache": "2.2.x", - "tmp": "0.0.x" + "lru-cache": "2.2.4", + "tmp": "0.0.33" }, "dependencies": { "lru-cache": { @@ -12717,8 +15498,8 @@ "dev": true, "optional": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "1.1.3", + "object.getownpropertydescriptors": "2.0.3" } }, "utils-merge": { @@ -12728,15 +15509,15 @@ "dev": true }, "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "uws": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz", - "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", + "integrity": "sha1-+sg4a+/DOno3BcvVjcR7Qwyk3ZU=", "dev": true, "optional": true }, @@ -12746,7 +15527,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" } }, "vali-date": { @@ -12756,13 +15537,13 @@ "dev": true }, "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", + "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" + "spdx-correct": "3.0.1", + "spdx-expression-parse": "3.0.0" } }, "vary": { @@ -12783,9 +15564,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" }, "dependencies": { "assert-plus": { @@ -12808,8 +15589,8 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -12819,8 +15600,8 @@ "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", "dev": true, "requires": { - "object-assign": "^4.0.1", - "readable-stream": "^2.0.0" + "object-assign": "4.1.1", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -12831,7 +15612,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -12847,7 +15628,7 @@ "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==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -12870,27 +15651,76 @@ "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" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0", + "strip-bom-stream": "2.0.0", + "vinyl": "1.2.0" }, "dependencies": { + "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.3.6" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "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.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "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.2" + } + }, "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" + "is-utf8": "0.2.1" + } + }, + "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" } }, "vinyl": { @@ -12899,8 +15729,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -12912,14 +15742,14 @@ "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", "dev": true, "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -12930,7 +15760,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -12946,8 +15776,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -12956,8 +15786,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -12968,7 +15798,7 @@ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { - "source-map": "^0.5.1" + "source-map": "0.5.7" } }, "void-elements": { @@ -12983,7 +15813,7 @@ "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", "dev": true, "requires": { - "wrap-fn": "^0.1.0" + "wrap-fn": "0.1.5" } }, "websocket-driver": { @@ -12992,8 +15822,8 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.4.13", + "websocket-extensions": "0.1.3" } }, "websocket-extensions": { @@ -13015,12 +15845,12 @@ "dev": true }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", + "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" + "isexe": "2.0.0" } }, "window-size": { @@ -13037,9 +15867,9 @@ "optional": true }, "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, "wrap-fn": { @@ -13049,14 +15879,6 @@ "dev": true, "requires": { "co": "3.1.0" - }, - "dependencies": { - "co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true - } } }, "wrappy": { @@ -13071,18 +15893,18 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "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==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" } }, "xmlhttprequest-ssl": { @@ -13113,7 +15935,7 @@ }, "yargs": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { @@ -13121,15 +15943,24 @@ "cliui": "^2.1.0", "decamelize": "^1.0.0", "window-size": "0.1.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + } } }, "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, "requires": { - "fd-slicer": "~1.0.1" + "buffer-crc32": "0.2.13", + "fd-slicer": "1.1.0" } }, "yeast": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c4b4ac0350..c05401a05c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,25 +1,12 @@ { - "author": "Umbraco HQ", - "name": "umbraco", - "homepage": "https://github.com/umbraco/umbraco-cms/", - "version": "0.0.0", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/umbraco/Umbraco-CMS.git" - }, - "bugs": { - "url": "https://issues.umbraco.org" - }, - "engines": { - "node": ">= 0.8.4" - }, "scripts": { "install": "bower-installer", "test": "karma start test/config/karma.conf.js --singlerun", "build": "gulp" }, - "dependencies": {}, + "dependencies": { + "npm": "^6.4.1" + }, "devDependencies": { "autoprefixer": "^6.5.0", "bower-installer": "^1.2.0", diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js index 8c0ec78025..5476404e6a 100644 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js +++ b/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js @@ -1,19 +1,19 @@ LazyLoad.js([ - '../lib/jquery/jquery.min.js', - '../lib/angular/1.1.5/angular.min.js', - '../lib/underscore/underscore-min.js', - '../lib/umbraco/Extensions.js', - '../js/app.js', - '../js/umbraco.resources.js', - '../js/umbraco.services.js', - '../js/umbraco.interceptors.js', - '../ServerVariables', - '../lib/signalr/jquery.signalR.js', - '/umbraco/BackOffice/signalr/hubs', - '../js/umbraco.canvasdesigner.js' + '../lib/jquery/jquery.min.js', + '../lib/angular/angular.js', + '../lib/underscore/underscore-min.js', + '../lib/umbraco/Extensions.js', + '../js/app.js', + '../js/umbraco.resources.js', + '../js/umbraco.services.js', + '../js/umbraco.interceptors.js', + '../ServerVariables', + '../lib/signalr/jquery.signalR.js', + '../BackOffice/signalr/hubs', + '../js/umbraco.canvasdesigner.js' ], function () { - jQuery(document).ready(function () { - angular.bootstrap(document, ['Umbraco.canvasdesigner']); - }); + jQuery(document).ready(function () { + angular.bootstrap(document, ['Umbraco.canvasdesigner']); + }); }); diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js deleted file mode 100644 index 975d490086..0000000000 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js +++ /dev/null @@ -1,117 +0,0 @@ - -/*********************************************************************************************************/ -/* Canvasdesigner panel app and controller */ -/*********************************************************************************************************/ - -var app = angular.module("Umbraco.canvasdesigner", ['umbraco.resources', 'umbraco.services']) - -.controller("Umbraco.canvasdesignerController", function ($scope, $http, $window, $timeout, $location, dialogService) { - - var isInit = $location.search().init; - if (isInit === "true") { - //do not continue, this is the first load of this new window, if this is passed in it means it's been - //initialized by the content editor and then the content editor will actually re-load this window without - //this flag. This is a required trick to get around chrome popup mgr. We don't want to double load preview.aspx - //since that will double prepare the preview documents - return; - } - - $scope.isOpen = false; - $scope.frameLoaded = false; - var pageId = $location.search().id; - $scope.pageId = pageId; - $scope.pageUrl = "../dialogs/Preview.aspx?id=" + pageId; - $scope.valueAreLoaded = false; - $scope.devices = [ - { name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" }, - { name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop", title: "Laptop" }, - { name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad", title: "Tablet portrait" }, - { name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip", title: "Tablet landscape" }, - { name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone", title: "Smartphone portrait" }, - { name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip", title: "Smartphone landscape" } - ]; - $scope.previewDevice = $scope.devices[0]; - - /*****************************************************************************/ - /* Preview devices */ - /*****************************************************************************/ - - // Set preview device - $scope.updatePreviewDevice = function (device) { - $scope.previewDevice = device; - }; - - /*****************************************************************************/ - /* Exit Preview */ - /*****************************************************************************/ - - $scope.exitPreview = function () { - window.top.location.href = "../endPreview.aspx?redir=%2f" + $scope.pageId; - }; - - - - /*****************************************************************************/ - /* Panel managment */ - /*****************************************************************************/ - - $scope.openPreviewDevice = function () { - $scope.showDevicesPreview = true; - $scope.closeIntelCanvasdesigner(); - } - - /*****************************************************************************/ - /* Call function into the front-end */ - /*****************************************************************************/ - - - var hideUmbracoPreviewBadge = function () { - var iframe = (document.getElementById("resultFrame").contentWindow || document.getElementById("resultFrame").contentDocument); - if(iframe.document.getElementById("umbracoPreviewBadge")) - iframe.document.getElementById("umbracoPreviewBadge").style.display = "none"; - }; - - /*****************************************************************************/ - /* Init */ - /*****************************************************************************/ - - - // signalr hub - var previewHub = $.connection.previewHub; - - previewHub.client.refreshed = function (message, sender) { - console.log("Notified by SignalR preview hub (" + message+ ")."); - - if ($scope.pageId != message) { - console.log("Not a notification for us (" + $scope.pageId + ")."); - return; - } - - var resultFrame = document.getElementById("resultFrame"); - var iframe = (resultFrame.contentWindow || resultFrame.contentDocument); - //setTimeout(function() { iframe.location.reload(); }, 1000); - iframe.location.reload(); - }; - - $.connection.hub.start() - .done(function () { console.log("Connected to SignalR preview hub (ID=" + $.connection.hub.id + ")"); }) - .fail(function () { console.log("Could not connect to SignalR preview hub."); }); -}) - - -.directive('iframeIsLoaded', function ($timeout) { - return { - restrict: 'A', - link: function (scope, element, attr) { - element.load(function () { - var iframe = (element.context.contentWindow || element.context.contentDocument); - if(iframe && iframe.document.getElementById("umbracoPreviewBadge")) - iframe.document.getElementById("umbracoPreviewBadge").style.display = "none"; - if (!document.getElementById("resultFrame").contentWindow.refreshLayout) { - scope.frameLoaded = true; - scope.$apply(); - } - }); - } - }; -}) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js index 461101a66c..3ddb7af0e1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js @@ -55,6 +55,11 @@ }); })); + scope.searchClick = function() { + var showSearch = appState.getSearchState("show"); + appState.setSearchState("show", !showSearch); + }; + // toggle the help dialog by raising the global app state to toggle the help drawer scope.helpClick = function () { var showDrawer = appState.getDrawerState("showDrawer"); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsearch.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsearch.directive.js new file mode 100644 index 0000000000..7ff76d5670 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsearch.directive.js @@ -0,0 +1,118 @@ +(function () { + 'use strict'; + + /** + * A component to render the pop up search field + */ + var umbSearch = { + templateUrl: 'views/components/application/umb-search.html', + controllerAs: 'vm', + controller: umbSearchController, + bindings: { + onClose: "&" + } + }; + + function umbSearchController($timeout, backdropService, searchService) { + + var vm = this; + + vm.$onInit = onInit; + vm.$onDestroy = onDestroy; + vm.search = search; + vm.clickItem = clickItem; + vm.clearSearch = clearSearch; + vm.handleKeyUp = handleKeyUp; + vm.closeSearch = closeSearch; + vm.focusSearch = focusSearch; + + function onInit() { + vm.searchQuery = ""; + vm.searchResults = []; + vm.hasResults = false; + focusSearch(); + backdropService.open(); + } + + function onDestroy() { + backdropService.close(); + } + + /** + * Handles when a search result is clicked + */ + function clickItem() { + closeSearch(); + } + + /** + * Clears the search query + */ + function clearSearch() { + vm.searchQuery = ""; + vm.searchResults = []; + vm.hasResults = false; + focusSearch(); + } + + /** + * Add focus to the search field + */ + function focusSearch() { + vm.searchHasFocus = false; + $timeout(function(){ + vm.searchHasFocus = true; + }); + } + + /** + * Handles all keyboard events + * @param {object} event + */ + function handleKeyUp(event) { + // esc + if(event.keyCode === 27) { + closeSearch(); + } + } + + /** + * Used to proxy a callback + */ + function closeSearch() { + if(vm.onClose) { + vm.onClose(); + } + } + + /** + * Used to search + * @param {string} searchQuery + */ + function search(searchQuery) { + if(searchQuery.length > 0) { + var search = {"term": searchQuery}; + searchService.searchAll(search).then(function(result){ + //result is a dictionary of group Title and it's results + var filtered = {}; + _.each(result, function (value, key) { + if (value.results.length > 0) { + filtered[key] = value; + } + }); + // bind to view model + vm.searchResults = filtered; + // check if search has results + vm.hasResults = Object.keys(vm.searchResults).length > 0; + }); + + } else { + clearSearch(); + } + } + + } + + angular.module('umbraco.directives').component('umbSearch', umbSearch); + +})(); \ No newline at end of file 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 e26f622d2e..1d52c4e451 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 @@ -65,6 +65,8 @@ Use this directive to render an umbraco button. The directive can be used to gen @param {string=} icon Set a button icon. @param {string=} size Set a button icon ("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. + **/ (function () { @@ -90,14 +92,15 @@ Use this directive to render an umbraco button. The directive can be used to gen icon: "@?", disabled: " 1; @@ -135,7 +145,31 @@ } - function createButtons(content) { + /** + * Create the save/publish/preview buttons for the view + * @param {any} content the content node + * @param {any} app the active content app + */ + function createButtons(content, app) { + + // only create the save/publish/preview buttons if the + // content app is "Conent" + if(app && app.alias !== "umbContent" && app.alias !== "umbInfo") { + $scope.defaultButton = null; + $scope.subButtons = null; + $scope.page.showSaveButton = false; + $scope.page.showPreviewButton = false; + return; + } + + // create the save button + if(_.contains($scope.content.allowedActions, "A")) { + $scope.page.showSaveButton = true; + // add ellipsis to the save button if it opens the variant overlay + $scope.page.saveButtonEllipsis = content.variants && content.variants.length > 1 ? "true" : "false"; + } + + // create the pubish combo button $scope.page.buttonGroupState = "init"; var buttons = contentEditingHelper.configureContentEditorButtons({ create: $scope.page.isNew, @@ -143,13 +177,13 @@ methods: { saveAndPublish: $scope.saveAndPublish, sendToPublish: $scope.sendToPublish, - save: $scope.save, - unPublish: $scope.unPublish + unpublish: $scope.unpublish } }); $scope.defaultButton = buttons.defaultButton; $scope.subButtons = buttons.subButtons; + $scope.page.showPreviewButton = true; } @@ -176,7 +210,7 @@ if (infiniteMode || !path) { return; } - + if (!$scope.content.isChildOfListView) { navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { $scope.page.menu.currentNode = syncArgs.node; @@ -187,7 +221,7 @@ //it's a child item, just sync the ui node to the parent navigationService.syncTree({ tree: $scope.treeAlias, path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true }); - //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node + //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node // from the server so that we can load in the actions menu. umbRequestHelper.resourcePromise( $http.get(content.treeNodeUrl), @@ -197,11 +231,89 @@ } } + function checkValidility(){ + //Get all controls from the 'contentForm' + var allControls = $scope.contentForm.$getControls(); + + //An array to store items in when we find child form fields (no matter how many deep nested forms) + var childFieldsToMarkAsValid = []; + + //Exclude known formControls 'contentHeaderForm' and 'tabbedContentForm' + //Check property - $name === "contentHeaderForm" + allControls = _.filter(allControls, function(obj){ + return obj.$name !== 'contentHeaderForm' && obj.$name !== 'tabbedContentForm' && obj.hasOwnProperty('$submitted'); + }); + + for (var i = 0; i < allControls.length; i++) { + var nestedForm = allControls[i]; + + //Get Nested Controls of this form in the loop + var nestedFormControls = nestedForm.$getControls(); + + //Need to recurse through controls (could be more nested forms) + childFieldsToMarkAsValid = recurseFormControls(nestedFormControls, childFieldsToMarkAsValid); + } + + return childFieldsToMarkAsValid; + } + + //Controls is the + function recurseFormControls(controls, array){ + + //Loop over the controls + for (var i = 0; i < controls.length; i++) { + var controlItem = controls[i]; + + //Check if the controlItem has a property '' + if(controlItem.hasOwnProperty('$submitted')){ + //This item is a form - so lets get the child controls of it & recurse again + var childFormControls = controlItem.$getControls(); + recurseFormControls(childFormControls, array); + } + else { + //We can assume its a field on a form + if(controlItem.hasOwnProperty('$error')){ + //Set the validlity of the error/s to be valid + //String of keys of error invalid messages + var errorKeys = []; + + for(var key in controlItem.$error){ + errorKeys.push(key); + controlItem.$setValidity(key, true); + } + + //Create a basic obj - storing the control item & the error keys + var obj = { 'control': controlItem, 'errorKeys': errorKeys }; + + //Push the updated control into the array - so we can set them back + array.push(obj); + } + } + } + return array; + } + + function resetNestedFieldValiation(array){ + for (var i = 0; i < array.length; i++) { + var item = array[i]; + //Item is an object containing two props + //'control' (obj) & 'errorKeys' (string array) + var fieldControl = item.control; + var fieldErrorKeys = item.errorKeys; + + for(var i = 0; i < fieldErrorKeys.length; i++) { + fieldControl.$setValidity(fieldErrorKeys[i], false); + } + } + } + // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish function performSave(args) { + - $scope.page.buttonGroupState = "busy"; - + //Used to check validility of nested form - coming from Content Apps mostly + //Set them all to be invalid + var fieldsToRollback = checkValidility(); eventsService.emit("content.saving", { content: $scope.content, action: args.action }); return contentEditingHelper.contentEditorPerformSave({ @@ -211,19 +323,17 @@ action: args.action, showNotifications: args.showNotifications }).then(function (data) { - //success + //success init($scope.content); - syncTreeNode($scope.content, data.path); - $scope.page.buttonGroupState = "success"; - eventsService.emit("content.saved", { content: $scope.content, action: args.action }); + resetNestedFieldValiation(fieldsToRollback); + return $q.when(data); }, function (err) { - syncTreeNode($scope.content, $scope.content.path); //error @@ -231,7 +341,7 @@ editorState.set($scope.content); } - $scope.page.buttonGroupState = "error"; + resetNestedFieldValiation(fieldsToRollback); return $q.reject(err); }); @@ -306,59 +416,86 @@ }); } - $scope.unPublish = function () { + $scope.unpublish = function() { + clearNotifications($scope.content); + if (formHelper.submitForm({ scope: $scope, action: "unpublish", skipValidation: true })) { + var dialog = { + parentScope: $scope, + view: "views/content/overlays/unpublish.html", + variants: $scope.content.variants, //set a model property for the dialog + skipFormValidation: true, //when submitting the overlay form, skip any client side validation + submitButtonLabelKey: "content_unpublish", + submit: function (model) { - //if there's any variants than we need to set the language and include the variants to publish - var culture = null; - if ($scope.content.variants.length > 0) { - _.each($scope.content.variants, - function (d) { - //set the culture if this is active - if (d.active === true) { - culture = d.language.culture; - } - }); + model.submitButtonState = "busy"; + + var selectedVariants = _.filter(model.variants, function(variant) { return variant.save; }); + var culturesForUnpublishing = _.map(selectedVariants, function(variant) { return variant.language.culture; }); + + contentResource.unpublish($scope.content.id, culturesForUnpublishing) + .then(function (data) { + formHelper.resetForm({ scope: $scope }); + contentEditingHelper.reBindChangedProperties($scope.content, data); + init($scope.content); + syncTreeNode($scope.content, data.path); + $scope.page.buttonGroupState = "success"; + eventsService.emit("content.unpublished", { content: $scope.content }); + overlayService.close(); + }, function (err) { + $scope.page.buttonGroupState = 'error'; + }); + + + }, + close: function () { + overlayService.close(); + } + }; + overlayService.open(dialog); } - - if (formHelper.submitForm({ scope: $scope, skipValidation: true })) { - - $scope.page.buttonGroupState = "busy"; - - eventsService.emit("content.unpublishing", { content: $scope.content }); - - contentResource.unPublish($scope.content.id, culture) - .then(function (data) { - - formHelper.resetForm({ scope: $scope }); - - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - savedContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - init($scope.content); - - syncTreeNode($scope.content, data.path); - - $scope.page.buttonGroupState = "success"; - - eventsService.emit("content.unpublished", { content: $scope.content }); - - }, function (err) { - $scope.page.buttonGroupState = 'error'; - }); - } - }; - + $scope.sendToPublish = function () { - return performSave({ saveMethod: contentResource.sendToPublish, action: "sendToPublish" }); + clearNotifications($scope.content); + if (showSaveOrPublishDialog()) { + //before we launch the dialog we want to execute all client side validations first + if (formHelper.submitForm({ scope: $scope, action: "publish" })) { + + var dialog = { + parentScope: $scope, + view: "views/content/overlays/sendtopublish.html", + variants: $scope.content.variants, //set a model property for the dialog + skipFormValidation: true, //when submitting the overlay form, skip any client side validation + submitButtonLabel: "Send for approval", + submit: function (model) { + model.submitButtonState = "busy"; + clearNotifications($scope.content); + //we need to return this promise so that the dialog can handle the result and wire up the validation response + console.log("saving need to happen here"); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(dialog); + } + } + else { + $scope.page.buttonGroupState = "busy"; + return performSave({ + saveMethod: contentResource.sendToPublish, + action: "sendToPublish" + }).then(function(){ + $scope.page.buttonGroupState = "success"; + }, function () { + $scope.page.buttonGroupState = "error"; + });; + } }; $scope.saveAndPublish = function () { clearNotifications($scope.content); - // TODO: Add "..." to publish button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant if (showSaveOrPublishDialog()) { //before we launch the dialog we want to execute all client side validations first if (formHelper.submitForm({ scope: $scope, action: "publish" })) { @@ -393,7 +530,7 @@ return $q.when(err); }); }, - close: function (oldModel) { + close: function () { overlayService.close(); } }; @@ -404,7 +541,15 @@ else { //ensure the publish flag is set $scope.content.variants[0].publish = true; - return performSave({ saveMethod: contentResource.publish, action: "publish" }); + $scope.page.buttonGroupState = "busy"; + return performSave({ + saveMethod: contentResource.publish, + action: "publish" + }).then(function(){ + $scope.page.buttonGroupState = "success"; + }, function () { + $scope.page.buttonGroupState = "error"; + });; } }; @@ -435,15 +580,14 @@ clearNotifications($scope.content); overlayService.close(); return $q.when(data); - }, - function (err) { - clearDirtyState($scope.content.variants); - model.submitButtonState = "error"; - //re-map the dialog model since we've re-bound the properties - dialog.variants = $scope.content.variants; - //don't reject, we've handled the error - return $q.when(err); - }); + }, function (err) { + clearDirtyState($scope.content.variants); + model.submitButtonState = "error"; + //re-map the dialog model since we've re-bound the properties + dialog.variants = $scope.content.variants; + //don't reject, we've handled the error + return $q.when(err); + }); }, close: function (oldModel) { overlayService.close(); @@ -454,7 +598,15 @@ } } else { - return performSave({ saveMethod: $scope.saveMethod(), action: "save" }); + $scope.page.saveButtonState = "busy"; + return performSave({ + saveMethod: $scope.saveMethod(), + action: "save" + }).then(function(){ + $scope.page.saveButtonState = "success"; + }, function () { + $scope.page.saveButtonState = "error"; + }); } }; @@ -464,13 +616,17 @@ if (!$scope.busy) { - // Chromes popup blocker will kick in if a window is opened + // Chromes popup blocker will kick in if a window is opened // without the initial scoped request. This trick will fix that. - // - var previewWindow = $window.open('preview/?init=true&id=' + content.id, 'umbpreview'); + // + var previewWindow = $window.open('preview/?init=true', 'umbpreview'); // Build the correct path so both /#/ and #/ work. - var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + content.id; + var query = 'id=' + content.id; + if ($scope.culture) { + query += "&culture=" + $scope.culture; + } + var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?' + query; //The user cannot save if they don't have access to do that, in which case we just want to preview //and that's it otherwise they'll get an unauthorized access message @@ -478,8 +634,22 @@ previewWindow.location.href = redirect; } else { - $scope.save().then(function (data) { + var selectedVariant; + if (!$scope.culture) { + selectedVariant = $scope.content.variants[0]; + } + else { + selectedVariant = _.find($scope.content.variants, function (v) { + return v.language.culture === $scope.culture; + }); + } + + //ensure the save flag is set + selectedVariant.save = true; + performSave({ saveMethod: contentResource.publish, action: "save" }).then(function (data) { previewWindow.location.href = redirect; + }, function (err) { + //validation issues .... }); } } @@ -555,6 +725,14 @@ }); }; + /** + * Call back when a content app changes + * @param {any} app + */ + $scope.appChanged = function(app) { + createButtons($scope.content, app); + }; + function moveNode(node, target) { contentResource.move({ "parentId": target.id, "id": node.id }) 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 062a7e2877..4831ecea8c 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 @@ -1,7 +1,7 @@ (function () { 'use strict'; - function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, editorService) { + function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) { function link(scope, element, attrs, ctrl) { @@ -72,6 +72,9 @@ // make sure dates are formatted to the user's locale formatDatesToLocal(); + //default setting for redirect url management + scope.urlTrackerDisabled = false; + // Declare a fallback URL for the directive if (scope.documentType !== null) { scope.previewOpenUrl = '#/settings/documenttypes/edit/' + scope.documentType.id; @@ -139,7 +142,7 @@ // get current backoffice user and format dates userService.getCurrentUser().then(function (currentUser) { - angular.forEach(data.items, function(item) { + angular.forEach(data.items, function (item) { item.timestampFormatted = dateHelper.getLocalDate(item.timestamp, currentUser.locale, 'LLL'); }); }); @@ -156,6 +159,25 @@ }); } + function loadRedirectUrls() { + scope.loadingRedirectUrls = true; + //check if Redirect Url Management is enabled + redirectUrlsResource.getEnableState().then(function (response) { + scope.urlTrackerDisabled = response.enabled !== true; + if (scope.urlTrackerDisabled === false) { + + redirectUrlsResource.getRedirectsForContentItem(scope.node.udi) + .then(function (data) { + scope.redirectUrls = data.searchResults; + scope.hasRedirects = (typeof data.searchResults !== 'undefined' && data.searchResults.length > 0); + scope.loadingRedirectUrls = false; + }); + } + else { + scope.loadingRedirectUrls = false; + } + }); + } function setAuditTrailLogTypeColor(auditTrail) { angular.forEach(auditTrail, function (item) { @@ -164,7 +186,7 @@ case "Publish": item.logTypeColor = "success"; break; - case "UnPublish": + case "Unpublish": case "Delete": item.logTypeColor = "danger"; break; @@ -312,12 +334,13 @@ }); } - // load audit trail when on the info tab + // load audit trail and redirects when on the info tab evts.push(eventsService.on("app.tabChange", function (event, args) { - $timeout(function(){ - if (args.alias === "info") { + $timeout(function () { + if (args.alias === "umbInfo") { isInfoTab = true; loadAuditTrail(); + loadRedirectUrls(); } else { isInfoTab = false; } @@ -325,13 +348,14 @@ })); // watch for content state updates - scope.$watch('node.updateDate', function(newValue, oldValue){ + scope.$watch('node.updateDate', function (newValue, oldValue) { - if(!newValue) { return; } - if(newValue === oldValue) { return; } + if (!newValue) { return; } + if (newValue === oldValue) { return; } if(isInfoTab) { loadAuditTrail(); + loadRedirectUrls(); formatDatesToLocal(); setNodePublishStatus(scope.node); } 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 5ad7a9079a..8545854992 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 @@ -28,12 +28,23 @@ var vm = this; + vm.$onInit = onInit; vm.$postLink = postLink; vm.$onDestroy = onDestroy; vm.selectVariant = selectVariant; vm.openSplitView = openSplitView; vm.selectApp = selectApp; + + function onInit() { + // disable the name field if the active content app is not "Content" + vm.nameDisabled = false; + angular.forEach(vm.editor.content.apps, function(app){ + if(app.active && app.alias !== "umbContent" && app.alias !== "umbInfo") { + vm.nameDisabled = true; + } + }); + } /** Called when the component has linked all elements, this is when the form controller is available */ function postLink() { @@ -67,6 +78,12 @@ * @param {any} item */ function selectApp(item) { + // disable the name field if the active content app is not "Content" or "Info" + vm.nameDisabled = false; + if(item && item.alias !== "umbContent" && item.alias !== "umbInfo") { + vm.nameDisabled = true; + } + // call the callback if any is registered if(vm.onSelectApp) { vm.onSelectApp({"app": item}); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js index 7d6d5dc131..a3a212a603 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js @@ -9,7 +9,8 @@ bindings: { page: "<", content: "<", //TODO: Not sure if this should be = since we are changing the 'active' property of a variant - culture: "<" + culture: "<", + onSelectApp: "&?" }, controllerAs: 'vm', controller: umbVariantContentEditorsController @@ -39,8 +40,6 @@ /** Called when the component initializes */ function onInit() { - // set first app to active - vm.content.apps[0].active = true; prevContentDateUpdated = angular.copy(vm.content.updateDate); setActiveCulture(); } @@ -179,21 +178,23 @@ variant.variants[i].active = false; } } + + // keep track of the open variants across the different split views + // push the first variant then update the variant index based on the editor index + if(vm.openVariants && vm.openVariants.length === 0) { + vm.openVariants.push(variant.language.culture); + } else { + vm.openVariants[editorIndex] = variant.language.culture; + } + } //then assign the variant to a view model to the content app var contentApp = _.find(variant.apps, function (a) { - return a.alias === "content"; + return a.alias === "umbContent"; }); - contentApp.viewModel = variant; - - // keep track of the open variants across the different split views - // push the first variant then update the variant index based on the editor index - if(vm.openVariants && vm.openVariants.length === 0) { - vm.openVariants.push(variant.language.culture); - } else { - vm.openVariants[editorIndex] = variant.language.culture; - } + + contentApp.viewModel = _.omit(variant, 'apps'); // make sure the same app it set to active in the new variant if(activeAppAlias) { @@ -221,7 +222,7 @@ var editor = vm.editors[e]; for (var i = 0; i < editor.content.apps.length; i++) { var app = editor.content.apps[i]; - if (app.alias === "content") { + if (app.alias === "umbContent") { app.active = true; } else { @@ -311,6 +312,9 @@ if(app && app.alias) { activeAppAlias = app.alias; } + if(vm.onSelectApp) { + vm.onSelectApp({"app": app}); + } } } 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 3cdcd5b12a..0d78aab0eb 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 @@ -97,7 +97,7 @@ templateUrl: 'views/components/editor/umb-editor-content-header.html', scope: { name: "=", - nameLocked: "=", + nameDisabled: " @param {array} colors (attribute): The array of colors. -@param {string} colors (attribute): The array of colors. @param {string} selectedColor (attribute): The selected color. @param {string} size (attribute): The size (s, m). +@param {string} useLabel (attribute): Specify if labels should be used. +@param {string} useColorClass (attribute): Specify if color values are css classes. @param {function} onSelect (expression): Callback function when the item is selected. **/ @@ -28,6 +28,11 @@ Use this directive to generate color swatches to pick from. function link(scope, el, attr, ctrl) { + // Set default to true if not defined + if (angular.isUndefined(scope.useColorClass)) { + scope.useColorClass = false; + } + scope.setColor = function (color) { scope.selectedColor = color; if (scope.onSelect) { @@ -45,7 +50,9 @@ Use this directive to generate color swatches to pick from. colors: '=?', size: '@', selectedColor: '=', - onSelect: '&' + onSelect: '&', + useLabel: '=', + useColorClass: '=?' }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js index 48291bfa87..87cd84ca40 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js @@ -1,93 +1,92 @@ -(function() { - 'use strict'; +(function () { + 'use strict'; + + angular + .module('umbraco.directives') + .component('umbLayoutSelector', { + templateUrl: 'views/components/umb-layout-selector.html', + controller: LayoutSelectorController, + controllerAs: 'vm', + bindings: { + layouts: '<', + activeLayout: '<', + onLayoutSelect: "&" + } + }); - function LayoutSelectorDirective() { + function LayoutSelectorController($scope, $element) { - function link(scope, el, attr, ctrl) { + var vm = this; - scope.layoutDropDownIsOpen = false; - scope.showLayoutSelector = true; + vm.$onInit = onInit; - function activate() { + vm.layoutDropDownIsOpen = false; + vm.showLayoutSelector = true; + vm.pickLayout = pickLayout; + vm.toggleLayoutDropdown = toggleLayoutDropdown; + vm.closeLayoutDropdown = closeLayoutDropdown; + function onInit() { + activate(); + } + + function closeLayoutDropdown() { + vm.layoutDropDownIsOpen = false; + } + + function toggleLayoutDropdown() { + vm.layoutDropDownIsOpen = !vm.layoutDropDownIsOpen; + } + + function pickLayout(selectedLayout) { + if (vm.onLayoutSelect) { + vm.onLayoutSelect({ layout: selectedLayout }); + vm.layoutDropDownIsOpen = false; + } + } + + function activate() { setVisibility(); + setActiveLayout(vm.layouts); + } - setActiveLayout(scope.layouts); + function setVisibility() { - } + var numberOfAllowedLayouts = getNumberOfAllowedLayouts(vm.layouts); - function setVisibility() { - - var numberOfAllowedLayouts = getNumberOfAllowedLayouts(scope.layouts); - - if(numberOfAllowedLayouts === 1) { - scope.showLayoutSelector = false; + if (numberOfAllowedLayouts === 1) { + vm.showLayoutSelector = false; } - } + } - function getNumberOfAllowedLayouts(layouts) { + function getNumberOfAllowedLayouts(layouts) { var allowedLayouts = 0; for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; + var layout = layouts[i]; - if(layout.selected === true) { - allowedLayouts++; - } + if (layout.selected === true) { + allowedLayouts++; + } } return allowedLayouts; - } + } - function setActiveLayout(layouts) { + function setActiveLayout(layouts) { for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; - if(layout.path === scope.activeLayout.path) { - layout.active = true; - } + var layout = layouts[i]; + if (layout.path === vm.activeLayout.path) { + layout.active = true; + } } - } - - scope.pickLayout = function(selectedLayout) { - if(scope.onLayoutSelect) { - scope.onLayoutSelect(selectedLayout); - scope.layoutDropDownIsOpen = false; - } - }; - - scope.toggleLayoutDropdown = function() { - scope.layoutDropDownIsOpen = !scope.layoutDropDownIsOpen; - }; - - scope.closeLayoutDropdown = function() { - scope.layoutDropDownIsOpen = false; - }; - - activate(); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-layout-selector.html', - scope: { - layouts: '=', - activeLayout: '=', - onLayoutSelect: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbLayoutSelector', LayoutSelectorDirective); + } + } })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblistviewlayout.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblistviewlayout.directive.js index d026a3f4e5..2c1cbea962 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblistviewlayout.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblistviewlayout.directive.js @@ -1,37 +1,37 @@ -(function() { - 'use strict'; +(function () { + 'use strict'; - function ListViewLayoutDirective() { + function ListViewLayoutDirective() { - function link(scope, el, attr, ctrl) { + function link(scope, el, attr, ctrl) { - scope.getContent = function(contentId) { - if(scope.onGetContent) { - scope.onGetContent(contentId); - } - }; + scope.getContent = function (contentId) { + if (scope.onGetContent) { + scope.onGetContent({ contentId: contentId}); + } + }; - } + } - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-list-view-layout.html', - scope: { - contentId: '=', - folders: '=', - items: '=', - selection: '=', - options: '=', - entityType: '@', - onGetContent: '=' - }, - link: link - }; + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-list-view-layout.html', + scope: { + contentId: '<', + folders: '<', + items: '<', + selection: '<', + options: '<', + entityType: '@', + onGetContent: '&' + }, + link: link + }; - return directive; - } + return directive; + } - angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective); + angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js index af562e04e3..b14f8418c5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js @@ -125,6 +125,14 @@ Use this directive to generate a thumbnail grid of media items. i--; } + if (scope.includeSubFolders !== 'true') { + if (item.parentId !== parseInt(scope.currentFolderId)) { + scope.items.splice(i, 1); + i--; + } + } + + } if (scope.items.length > 0) { @@ -316,7 +324,9 @@ Use this directive to generate a thumbnail grid of media items. itemMaxHeight: "@", itemMinWidth: "@", itemMinHeight: "@", - onlyImages: "@" + onlyImages: "@", + includeSubFolders: "@", + currentFolderId: "@" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js index 2139a14b48..e7abc81841 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js @@ -93,7 +93,7 @@ Use this directive to generate a pagination. function activate() { scope.pagination = []; - + var i = 0; if (scope.totalPages <= 10) { 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 399ce8877a..ae41073c0d 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 @@ -110,77 +110,72 @@ **/ (function () { - 'use strict'; + 'use strict'; - function TableDirective(iconHelper) { + function TableController(iconHelper) { - function link(scope, el, attr, ctrl) { + var vm = this; - scope.clickItem = function (item, $event) { - if (scope.onClick) { - scope.onClick(item); - $event.stopPropagation(); + vm.clickItem = function (item, $event) { + if (vm.onClick) { + vm.onClick({ item: item}); + $event.stopPropagation(); } - }; + }; - scope.selectItem = function (item, $index, $event) { - if (scope.onSelect) { - scope.onSelect(item, $index, $event); - $event.stopPropagation(); + vm.selectItem = function (item, $index, $event) { + if (vm.onSelect) { + vm.onSelect({ item: item, $index: $index, $event: $event }); + $event.stopPropagation(); } - }; + }; - scope.selectAll = function ($event) { - if (scope.onSelectAll) { - scope.onSelectAll($event); + vm.selectAll = function ($event) { + if (vm.onSelectAll) { + vm.onSelectAll({ $event: $event}); } - }; + }; - scope.isSelectedAll = function () { - if (scope.onSelectedAll && scope.items && scope.items.length > 0) { - return scope.onSelectedAll(); + vm.isSelectedAll = function () { + if (vm.onSelectedAll && vm.items && vm.items.length > 0) { + return vm.onSelectedAll(); } - }; + }; - scope.isSortDirection = function (col, direction) { - if (scope.onSortingDirection) { - return scope.onSortingDirection(col, direction); + vm.isSortDirection = function (col, direction) { + if (vm.onSortingDirection) { + return vm.onSortingDirection({ col: col, direction: direction }); } - }; + }; - scope.sort = function (field, allow, isSystem) { - if (scope.onSort) { - scope.onSort(field, allow, isSystem); + vm.sort = function (field, allow, isSystem) { + if (vm.onSort) { + vm.onSort({ field: field, allow: allow, isSystem: isSystem }); } - }; + }; - scope.getIcon = function (entry) { - return iconHelper.convertFromLegacyIcon(entry.icon); - }; + vm.getIcon = function (entry) { + return iconHelper.convertFromLegacyIcon(entry.icon); + }; + } - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-table.html', - scope: { - items: '=', - itemProperties: '=', - allowSelectAll: '=', - onSelect: '=', - onClick: '=', - onSelectAll: '=', - onSelectedAll: '=', - onSortingDirection: '=', - onSort: '=' - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbTable', TableDirective); + angular + .module('umbraco.directives') + .component('umbTable', { + templateUrl: 'views/components/umb-table.html', + controller: TableController, + controllerAs: 'vm', + bindings: { + items: '<', + itemProperties: '<', + allowSelectAll: '<', + onSelect: '&', + onClick: '&', + onSelectAll: '&', + onSelectedAll: '&', + onSortingDirection: '&', + onSort: '&' + } + }); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js index cf12585e15..0dec3f6e0b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js @@ -221,7 +221,8 @@ isClientSide: true }); - newVal += files[i].name + ","; + //special check for a comma in the name + newVal += files[i].name.replace(',', '-') + ","; if (isImage) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 35e9005cc6..eaf67bcb91 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -3,7 +3,7 @@ * @name umbraco.directives.directive:valFormManager * @restrict A * @require formController -* @description Used to broadcast an event to all elements inside this one to notify that form validation has +* @description Used to broadcast an event to all elements inside this one to notify that form validation has * changed. If we don't use this that means you have to put a watch for each directive on a form's validation * changing which would result in much higher processing. We need to actually watch the whole $error collection of a form * because just watching $valid or $invalid doesn't acurrately trigger form validation changing. @@ -19,7 +19,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var SAVED_EVENT_NAME = "formSubmitted"; return { - require: ["form", "^^?valFormManager"], + require: ["form", "^^?valFormManager", "^^?valSubView"], restrict: "A", controller: function($scope) { //This exposes an API for direct use with this directive @@ -46,9 +46,15 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location }, link: function (scope, element, attr, ctrls) { + function notifySubView() { + if (subView){ + subView.valStatusChanged({ form: formCtrl, showValidation: scope.showValidation }); + } + } + var formCtrl = ctrls[0]; var parentFormMgr = ctrls.length > 0 ? ctrls[1] : null; - + var subView = ctrls.length > 1 ? ctrls[2] : null; var labels = {}; var labelKeys = [ @@ -83,7 +89,9 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location return sum; }, function (e) { scope.$broadcast("valStatusChanged", { form: formCtrl }); - + + notifySubView(); + //find all invalid elements' .control-group's and apply the error class var inError = element.find(".control-group .ng-invalid").closest(".control-group"); inError.addClass("error"); @@ -94,7 +102,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location }); - //This tracks if the user is currently saving a new item, we use this to determine + //This tracks if the user is currently saving a new item, we use this to determine // if we should display the warning dialog that they are leaving the page - if a new item // is being saved we never want to display that dialog, this will also cause problems when there // are server side validation issues. @@ -104,6 +112,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location if (serverValidationManager.items.length > 0 || (parentFormMgr && parentFormMgr.showValidation)) { element.addClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = true; + notifySubView(); } var unsubscribe = []; @@ -112,6 +121,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location unsubscribe.push(scope.$on(SAVING_EVENT_NAME, function(ev, args) { element.addClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = true; + notifySubView(); //set the flag so we can check to see if we should display the error. isSavingNewItem = $routeParams.create; })); @@ -121,8 +131,9 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location //remove validation class element.removeClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = false; + notifySubView(); //clear form state as at this point we retrieve new data from the server - //and all validation will have cleared at this point + //and all validation will have cleared at this point formCtrl.$setPristine(); })); @@ -203,7 +214,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location $timeout(function(){ formCtrl.$setPristine(); }, 1000); - + } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valmulti.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valmulti.directive.js new file mode 100644 index 0000000000..1aca4c2528 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valmulti.directive.js @@ -0,0 +1,27 @@ +(function () { + /** + * @ngdoc directive + * @name umbraco.directives.directive:multi + * @restrict A + * @description Used on input fields when you want to validate multiple fields at once. + **/ + function multi($parse, $rootScope) { + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, elem, attrs, ngModelCtrl) { + var validate = $parse(attrs.multi)(scope); + ngModelCtrl.$viewChangeListeners.push(function () { + // ngModelCtrl.$setValidity('multi', validate()); + $rootScope.$broadcast('multi:valueChanged'); + }); + + var deregisterListener = scope.$on('multi:valueChanged', function (event) { + ngModelCtrl.$setValidity('multi', validate()); + }); + scope.$on('$destroy', deregisterListener); // optional, only required for $rootScope.$on + } + }; + } + angular.module('umbraco.directives.validation').directive('multi', ['$parse', '$rootScope', multi]); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js index a65a08d17e..097602fe20 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js @@ -10,6 +10,29 @@ function valSubViewDirective() { + function controller($scope, $element) { + //expose api + return { + valStatusChanged: function(args) { + if (!args.form.$valid) { + var subViewContent = $element.find(".ng-invalid"); + + if (subViewContent.length > 0) { + $scope.model.hasError = true; + $scope.model.errorClass = args.showValidation ? 'show-validation' : null; + } else { + $scope.model.hasError = false; + $scope.model.errorClass = null; + } + } + else { + $scope.model.hasError = false; + $scope.model.errorClass = null; + } + } + } + } + function link(scope, el, attr, ctrl) { //if there are no containing form or valFormManager controllers, then we do nothing @@ -43,7 +66,8 @@ var directive = { require: ['?^^form', '?^^valFormManager'], restrict: "A", - link: link + link: link, + controller: controller }; return directive; diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index 615a7aeec9..28aae834b3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -145,7 +145,7 @@ angular.module('umbraco.mocks'). "content_statistics": "Statistics", "content_titleOptional": "Title (optional)", "content_type": "Type", - "content_unPublish": "Unpublish", + "content_unpublish": "Unpublish", "content_updateDate": "Last edited", "content_updateDateDesc": "Date/time this document was created", "content_uploadClear": "Remove file", @@ -644,24 +644,14 @@ angular.module('umbraco.mocks'). "templateEditor_usedIfAllEmpty": "Will only be used when the field values above are empty", "templateEditor_usedIfEmpty": "This field will only be used if the primary field is empty", "templateEditor_withTime": "Yes, with time. Seperator: ", - "translation_assignedTasks": "Tasks assigned to you", - "translation_assignedTasksHelp": " The list below shows translation tasks assigned to you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link.
To close a translation task, please go to the Details view and click the 'Close' button. ", - "translation_closeTask": "close task", "translation_details": "Translation details", - "translation_downloadAllAsXml": "Download all translation tasks as xml", - "translation_downloadTaskAsXml": "Download xml", "translation_DownloadXmlDTD": "Download xml DTD", "translation_fields": "Fields", "translation_includeSubpages": "Include subpages", "translation_mailBody": " Hi %0% This is an automated mail to inform you that the document '%1%' has been requested for translation into '%5%' by %2%. Go to http://%3%/translation/details.aspx?id=%4% to edit. Or log into Umbraco to get an overview of your translation tasks http://%3% Have a nice day! Cheers from the Umbraco robot ", - "translation_mailSubject": "[%0%] Translation task for %1%", "translation_noTranslators": "No translator users found. Please create a translator user before you start sending content to translation", - "translation_ownedTasks": "Tasks created by you", - "translation_ownedTasksHelp": " The list below shows pages created by you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link. To close a translation task, please go to the Details view and click the 'Close' button. ", - "translation_pageHasBeenSendToTranslation": "The page '%0%' has been send to translation", + "translation_pageHasBeenSendToTranslation": "The page '%0%' has been send to translation", "translation_sendToTranslate": "Send the page '%0%' to translation", - "translation_taskAssignedBy": "Assigned by", - "translation_taskOpened": "Task opened", "translation_totalWords": "Total words", "translation_translateTo": "Translate to", "translation_translationDone": "Translation completed.", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 089637521a..721cd4da57 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -206,7 +206,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.contentResource#unPublish + * @name umbraco.resources.contentResource#unpublish * @methodOf umbraco.resources.contentResource * * @description @@ -214,7 +214,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * ##usage *
-          * contentResource.unPublish(1234)
+          * contentResource.unpublish(1234)
           *    .then(function() {
           *        alert("node was unpulished");
           *    }, function(err){
@@ -225,23 +225,60 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
           * @returns {Promise} resourcePromise object.
           *
           */
-        unPublish: function (id, culture) {
+        unpublish: function (id, cultures) {
             if (!id) {
                 throw "id cannot be null";
             }
 
-            if (!culture) {
-                culture = null;
+            if (!cultures) {
+                cultures = [];
             }
 
             return umbRequestHelper.resourcePromise(
                 $http.post(
                     umbRequestHelper.getApiUrl(
                         "contentApiBaseUrl",
-                        "PostUnPublish",
-                        { id: id, culture: culture })),
+                        "PostUnpublish"), { id: id, cultures: cultures }),
                 'Failed to publish content with id ' + id);
         },
+        /**
+          * @ngdoc method
+          * @name umbraco.resources.contentResource#getCultureAndDomains
+          * @methodOf umbraco.resources.contentResource
+          *
+          * @description
+          * Gets the culture and hostnames for a content item with the given Id
+          *
+          * ##usage
+          * 
+          * contentResource.getCultureAndDomains(1234)
+          *    .then(function(data) {
+          *        alert(data.Domains, data.Language);
+          *    });
+          * 
+ * @param {Int} id the ID of the node to get the culture and domains for. + * @returns {Promise} resourcePromise object. + * + */ + getCultureAndDomains: function (id) { + if (!id) { + throw "id cannot be null"; + } + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetCultureAndDomains", { id: id })), + 'Failed to retreive culture and hostnames for ' + id); + }, + saveLanguageAndDomains: function (model) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "PostSaveLanguageAndDomains"), + model)); + }, /** * @ngdoc method * @name umbraco.resources.contentResource#emptyRecycleBin @@ -334,26 +371,26 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { */ getById: function (id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetById", - { id: id })), - 'Failed to retrieve data for content id ' + id) - .then(function(result) { + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetById", + { id: id })), + 'Failed to retrieve data for content id ' + id) + .then(function (result) { return $q.when(umbDataFormatter.formatContentGetData(result)); }); }, getBlueprintById: function (id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetBlueprintById", - [{ id: id }])), - 'Failed to retrieve data for content id ' + id) - .then(function(result) { + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetBlueprintById", + [{ id: id }])), + 'Failed to retrieve data for content id ' + id) + .then(function (result) { return $q.when(umbDataFormatter.formatContentGetData(result)); }); }, @@ -410,15 +447,15 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }); return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetByIds", - idQuery)), - 'Failed to retrieve data for content with multiple ids') + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetByIds", + idQuery)), + 'Failed to retrieve data for content with multiple ids') .then(function (result) { //each item needs to be re-formatted - _.each(result, function(r) { + _.each(result, function (r) { umbDataFormatter.formatContentGetData(r) }); return $q.when(result); @@ -461,13 +498,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getScaffold: function (parentId, alias) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetEmpty", - [{ contentTypeAlias: alias }, { parentId: parentId }])), - 'Failed to retrieve data for empty content item type ' + alias) - .then(function(result) { + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }, { parentId: parentId }])), + 'Failed to retrieve data for empty content item type ' + alias) + .then(function (result) { return $q.when(umbDataFormatter.formatContentGetData(result)); }); }, @@ -475,13 +512,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getBlueprintScaffold: function (parentId, blueprintId) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetEmpty", - [{ blueprintId: blueprintId }, { parentId: parentId }])), - 'Failed to retrieve blueprint for id ' + blueprintId) - .then(function(result) { + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ blueprintId: blueprintId }, { parentId: parentId }])), + 'Failed to retrieve blueprint for id ' + blueprintId) + .then(function (result) { return $q.when(umbDataFormatter.formatContentGetData(result)); }); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 6647c6fb7f..f864696873 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -288,17 +288,19 @@ function entityResource($q, $http, umbRequestHelper) { * Gets ancestor entities for a given item * * - * @param {string} type Object type name + * @param {string} type Object type name + * @param {string} culture Culture * @returns {Promise} resourcePromise object containing the entity. * */ - getAncestors: function (id, type) { + getAncestors: function (id, type, culture) { + if (culture === undefined) culture = ""; return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetAncestors", - [{id: id}, {type: type}])), + [{ id: id }, { type: type }, { culture: culture }])), 'Failed to retrieve ancestor data for id ' + id); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js index 5eeaf5644b..dad5345e6c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js @@ -7,10 +7,60 @@ **/ function logResource($q, $http, umbRequestHelper) { + function isValidDate(input) { + if (input) { + if (Object.prototype.toString.call(input) === "[object Date]" && !isNaN(input.getTime())) { + return true; + } + } + + return false; + }; + + function dateToValidIsoString(input) { + if (isValidDate(input)) { + return input.toISOString(); + } + + return ''; + }; + //the factory object returned return { - getPagedEntityLog: function (options) { + /** + * @ngdoc method + * @name umbraco.resources.logResource#getPagedEntityLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets a paginated log history for a entity + * + * ##usage + *
+        * var options = {
+        *      id : 1234
+        *      pageSize : 10,
+        *      pageNumber : 1,
+        *      orderDirection : "Descending",
+        *      sinceDate : new Date(2018,0,1)
+        * };
+        * logResource.getPagedEntityLog(options)
+        *    .then(function(log) {
+        *        alert('its here!');
+        *    });
+        * 
+ * + * @param {Object} options options object + * @param {Int} options.id the id of the entity + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging + * @param {Int} options.pageNumber if paging data, current page index, default = 1 + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending` + * @param {Date} options.sinceDate if provided this will only get log entries going back to this date + * @returns {Promise} resourcePromise object containing the log. + * + */ + getPagedEntityLog: function(options) { var defaults = { pageSize: 10, @@ -24,11 +74,15 @@ function logResource($q, $http, umbRequestHelper) { angular.extend(defaults, options); //now copy back to the options we will use options = defaults; + + if (options.hasOwnProperty('sinceDate')) { + options.sinceDate = dateToValidIsoString(options.sinceDate); + } + //change asc/desct if (options.orderDirection === "asc") { options.orderDirection = "Ascending"; - } - else if (options.orderDirection === "desc") { + } else if (options.orderDirection === "desc") { options.orderDirection = "Descending"; } @@ -45,7 +99,37 @@ function logResource($q, $http, umbRequestHelper) { 'Failed to retrieve log data for id'); }, - getPagedUserLog: function (options) { + /** + * @ngdoc method + * @name umbraco.resources.logResource#getPagedUserLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets a paginated log history for the current user + * + * ##usage + *
+         * var options = {
+         *      pageSize : 10,
+         *      pageNumber : 1,
+         *      orderDirection : "Descending",
+         *      sinceDate : new Date(2018,0,1)
+         * };
+         * logResource.getPagedUserLog(options)
+         *    .then(function(log) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Object} options options object + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging + * @param {Int} options.pageNumber if paging data, current page index, default = 1 + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending` + * @param {Date} options.sinceDate if provided this will only get log entries going back to this date + * @returns {Promise} resourcePromise object containing the log. + * + */ + getPagedUserLog: function(options) { var defaults = { pageSize: 10, @@ -59,11 +143,15 @@ function logResource($q, $http, umbRequestHelper) { angular.extend(defaults, options); //now copy back to the options we will use options = defaults; + + if (options.hasOwnProperty('sinceDate')) { + options.sinceDate = dateToValidIsoString(options.sinceDate); + } + //change asc/desct if (options.orderDirection === "asc") { options.orderDirection = "Ascending"; - } - else if (options.orderDirection === "desc") { + } else if (options.orderDirection === "desc") { options.orderDirection = "Descending"; } @@ -71,7 +159,7 @@ function logResource($q, $http, umbRequestHelper) { $http.get( umbRequestHelper.getApiUrl( "logApiBaseUrl", - "GetPagedEntityLog", + "GetPagedCurrentUserLog", options)), 'Failed to retrieve log data for id'); }, @@ -82,6 +170,7 @@ function logResource($q, $http, umbRequestHelper) { * @methodOf umbraco.resources.logResource * * @description + * [OBSOLETE] use getPagedEntityLog instead
* Gets the log history for a give entity id * * ##usage @@ -96,23 +185,24 @@ function logResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the log. * */ - getEntityLog: function (id) { + getEntityLog: function(id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetEntityLog", - [{ id: id }])), - 'Failed to retrieve user data for id ' + id); + $http.get( + umbRequestHelper.getApiUrl( + "logApiBaseUrl", + "GetEntityLog", + [{ id: id }])), + 'Failed to retrieve user data for id ' + id); }, - + /** * @ngdoc method * @name umbraco.resources.logResource#getUserLog * @methodOf umbraco.resources.logResource * * @description - * Gets the current users' log history for a given type of log entry + * [OBSOLETE] use getPagedUserLog instead
+ * Gets the current user's log history for a given type of log entry * * ##usage *
@@ -127,14 +217,14 @@ function logResource($q, $http, umbRequestHelper) {
          * @returns {Promise} resourcePromise object containing the log.
          *
          */
-        getUserLog: function (type, since) {            
+        getUserLog: function(type, since) {
             return umbRequestHelper.resourcePromise(
-               $http.get(
-                   umbRequestHelper.getApiUrl(
-                       "logApiBaseUrl",
-                       "GetCurrentUserLog",
-                       [{ logtype: type}, {sinceDate: since }])),
-               'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
+                $http.get(
+                    umbRequestHelper.getApiUrl(
+                        "logApiBaseUrl",
+                        "GetCurrentUserLog",
+                        [{ logtype: type }, { sinceDate:  dateToValidIsoString(since) }])),
+                'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
         },
 
         /**
@@ -158,16 +248,16 @@ function logResource($q, $http, umbRequestHelper) {
          * @returns {Promise} resourcePromise object containing the log.
          *
          */
-        getLog: function (type, since) {            
+        getLog: function(type, since) {
             return umbRequestHelper.resourcePromise(
-               $http.get(
-                   umbRequestHelper.getApiUrl(
-                       "logApiBaseUrl",
-                       "GetLog",
-                       [{ logtype: type}, {sinceDate: since }])),
-               'Failed to retrieve log data of type ' + type + ' since ' + since);
-        }
-    };
+                $http.get(
+                    umbRequestHelper.getApiUrl(
+                        "logApiBaseUrl",
+                        "GetLog",
+                        [{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])),
+                'Failed to retrieve log data of type ' + type + ' since ' + since);
+        }      
+};
 }
 
 angular.module('umbraco.resources').factory('logResource', logResource);
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
index ea40c066f0..fb145418ed 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
@@ -40,6 +40,32 @@
                         { searchTerm: searchTerm, page: pageIndex, pageSize: pageSize })),
                 'Failed to retrieve data for searching redirect urls');
         }
+        /**
+   * @ngdoc function
+   * @name umbraco.resources.redirectUrlResource#getRedirectsForContentItem
+   * @methodOf umbraco.resources.redirectUrlResource
+   * @function
+   *
+   * @description
+   * Used to retrieve RedirectUrls for a specific item of content for Information tab
+   * ##usage
+   * 
+   * redirectUrlsResource.getRedirectsForContentItem("udi:123456")
+   *    .then(function(response) {
+   *
+   *    });
+   * 
+ * @param {String} contentUdi identifier for the content item to retrieve redirects for + */ + function getRedirectsForContentItem(contentUdi) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "redirectUrlManagementApiBaseUrl", + "RedirectUrlsForContentItem", + { contentUdi: contentUdi })), + 'Failed to retrieve redirects for content: ' + contentUdi); + } function getEnableState() { @@ -50,7 +76,7 @@ "GetEnableState")), 'Failed to retrieve data to check if the 301 redirect is enabled'); } - + /** * @ngdoc function * @name umbraco.resources.redirectUrlResource#deleteRedirectUrl @@ -107,7 +133,8 @@ searchRedirectUrls: searchRedirectUrls, deleteRedirectUrl: deleteRedirectUrl, toggleUrlTracker: toggleUrlTracker, - getEnableState: getEnableState + getEnableState: getEnableState, + getRedirectsForContentItem: getRedirectsForContentItem }; return resource; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js index 085ba52b7e..d1ac3d39cf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js @@ -74,6 +74,11 @@ function appState(eventsService) { showMenu: null }; + var searchState = { + //Whether the search is being shown or not + show: null + }; + var drawerState = { //this view to show view: null, @@ -221,6 +226,35 @@ function appState(eventsService) { setState(menuState, key, value, "menuState"); }, + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getSearchState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Returns the current search state value by key - we do not return an object here - we do NOT want this + * to be publicly mutable and allow setting arbitrary values + * + */ + getSearchState: function (key) { + return getState(searchState, key, "searchState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#setSearchState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Sets a section state value by key + * + */ + setSearchState: function (key, value) { + setState(searchState, key, value, "searchState"); + }, + /** * @ngdoc function * @name umbraco.services.angularHelper#getDrawerState diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index 39d9b686c7..a331af899b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -65,17 +65,14 @@ angular.module('umbraco.services') return path; } - /** - * Loads in moment.js requirements during the _loadInitAssets call - */ - function loadMomentLocaleForCurrentUser() { + function getMomentLocales(locales, supportedLocales) { - var self = this; - - function loadLocales(currentUser, supportedLocales) { - var locale = currentUser.locale.toLowerCase(); + var localeUrls = []; + var locales = locales.split(','); + for (var i = 0; i < locales.length; i++) { + var locale = locales[i].toString().toLowerCase(); if (locale !== 'en-us') { - var localeUrls = []; + if (supportedLocales.indexOf(locale + '.js') > -1) { localeUrls.push('lib/moment/' + locale + '.js'); } @@ -85,16 +82,35 @@ angular.module('umbraco.services') localeUrls.push('lib/moment/' + majorLocale); } } - return self.load(localeUrls, $rootScope); - } - else { - $q.when(true); } } + return localeUrls; + } + + /** + * Loads specific Moment.js Locales. + * @param {any} locales + * @param {any} supportedLocales + */ + function loadLocales(locales, supportedLocales) { + var localeUrls = getMomentLocales(locales, supportedLocales); + if (localeUrls.length >= 1) { + return assetsService.load(localeUrls, $rootScope); + } + else { + $q.when(true); + } + } + + /** + * Loads in moment.js requirements during the _loadInitAssets call + */ + function loadMomentLocaleForCurrentUser() { + userService.getCurrentUser().then(function (currentUser) { return javascriptLibraryResource.getSupportedLocalesForMoment().then(function (supportedLocales) { - return loadLocales(currentUser, supportedLocales); + return loadLocales(currentUser.locale, supportedLocales); }); }); @@ -136,7 +152,9 @@ angular.module('umbraco.services') return $q.when(true); } }, - + + loadLocales: loadLocales, + /** * @ngdoc method * @name umbraco.services.assetsService#loadCss diff --git a/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js b/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js index e463845a1c..4f977cb1b2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js @@ -58,8 +58,11 @@ * */ function close() { - args.element = null; - args.show = false; + args.opacity = null, + args.element = null, + args.elementPreventClick = false, + args.disableEventsOnClick = false, + args.show = false eventsService.emit("appState.backdrop", args); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 6e6b0f3c0f..ba2df1a68a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -146,7 +146,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica if (!args.methods) { throw "args.methods is not defined"; } - if (!args.methods.saveAndPublish || !args.methods.sendToPublish || !args.methods.save || !args.methods.unPublish) { + if (!args.methods.saveAndPublish || !args.methods.sendToPublish || !args.methods.unpublish) { throw "args.methods does not contain all required defined methods"; } @@ -161,11 +161,12 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica //publish action return { letter: ch, - labelKey: args.content.variants && args.content.variants.length > 1 ? "buttons_saveAndPublishMany" : "buttons_saveAndPublish", + labelKey: "buttons_saveAndPublish", handler: args.methods.saveAndPublish, hotKey: "ctrl+p", hotKeyWhenHidden: true, - alias: "saveAndPublish" + alias: "saveAndPublish", + addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false" }; case "H": //send to publish @@ -175,27 +176,19 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica handler: args.methods.sendToPublish, hotKey: "ctrl+p", hotKeyWhenHidden: true, - alias: "sendToPublish" - }; - case "A": - //save - return { - letter: ch, - labelKey: "buttons_save", - handler: args.methods.save, - hotKey: "ctrl+s", - hotKeyWhenHidden: true, - alias: "save" + alias: "sendToPublish", + addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false" }; case "Z": //unpublish return { letter: ch, - labelKey: "content_unPublish", - handler: args.methods.unPublish, + labelKey: "content_unpublish", + handler: args.methods.unpublish, hotKey: "ctrl+u", hotKeyWhenHidden: true, - alias: "unpublish" + alias: "unpublish", + addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false" }; default: return null; @@ -206,8 +199,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica buttons.subButtons = []; //This is the ideal button order but depends on circumstance, we'll use this array to create the button list - // Publish, SendToPublish, Save - var buttonOrder = ["U", "H", "A"]; + // Publish, SendToPublish + var buttonOrder = ["U", "H"]; //Create the first button (primary button) //We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item. @@ -249,7 +242,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica // so long as it's already published and if the user has access to publish // and the user has access to unpublish (may have been removed via Event) if (!args.create) { - if (args.content.publishDate && _.contains(args.content.allowedActions, "U") && _.contains(args.content.allowedActions, "Z")) { + var hasPublishedVariant = args.content.variants.filter(function(variant) { return (variant.state === "Published" || variant.state === "PublishedPendingChanges"); }).length > 0; + if (hasPublishedVariant && _.contains(args.content.allowedActions, "U") && _.contains(args.content.allowedActions, "Z")) { buttons.subButtons.push(createButtonDefinition("Z")); } } @@ -409,8 +403,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica case "Z": return { letter: ch, - labelKey: "content_unPublish", - handler: "unPublish" + labelKey: "content_unpublish", + handler: "unpublish" }; default: diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 3ad2864fa8..cb416e3974 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -85,6 +85,7 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q appState.setSectionState("showSearchResults", false); appState.setGlobalState("stickyNavigation", false); appState.setGlobalState("showTray", false); + appState.setMenuState("currentNode", null); if (appState.getGlobalState("isTablet") === true) { appState.setGlobalState("showNavigation", false); @@ -204,6 +205,29 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q }); }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#retainQueryStrings + * @methodOf umbraco.services.navigationService + * + * @description + * Will check the next route parameters to see if any of the query strings that should be retained from the previous route are missing, + * if they are they will be merged and an object containing all route parameters is returned. If nothing should be changed, then null is returned. + * @param {Object} currRouteParams The current route parameters + * @param {Object} nextRouteParams The next route parameters + */ + retainQueryStrings: function (currRouteParams, nextRouteParams) { + var toRetain = angular.copy(nextRouteParams); + var updated = false; + _.each(retainedQueryStrings, function (r) { + if (currRouteParams[r] && !nextRouteParams[r]) { + toRetain[r] = currRouteParams[r]; + updated = true; + } + }); + return updated ? toRetain : null; + }, + /** * @ngdoc method * @name umbraco.services.navigationService#load @@ -342,7 +366,8 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) { //reset it to whatever is in the url - appState.setSectionState("currentSection", $routeParams.section); + appState.setSectionState("currentSection", $routeParams.section); + setMode("default-hidesectiontree"); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 5b723d9328..ac35790127 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -143,7 +143,6 @@ angular.module('umbraco.services') /** Called to update the current user's timeout */ function setUserTimeoutInternal(newTimeout) { - var asNumber = parseFloat(newTimeout); if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) { currentUser.remainingAuthSeconds = newTimeout; diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 24318cce02..a10943c17e 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -15,6 +15,8 @@ function MainController($scope, $location, appState, treeService, notificationsS $scope.touchDevice = appState.getGlobalState("touchDevice"); $scope.editors = []; $scope.overlay = {}; + $scope.drawer = {}; + $scope.search = {}; $scope.removeNotification = function (index) { notificationsService.remove(index); @@ -41,6 +43,10 @@ function MainController($scope, $location, appState, treeService, notificationsS eventsService.emit("app.closeDialogs", event); }; + $scope.closeSearch = function() { + appState.setSearchState("show", false); + }; + var evts = []; //when a user logs out or timesout @@ -114,9 +120,15 @@ function MainController($scope, $location, appState, treeService, notificationsS }; })); + // events for search + evts.push(eventsService.on("appState.searchState.changed", function (e, args) { + if (args.key === "show") { + $scope.search.show = args.value; + } + })); + // events for drawer // manage the help dialog by subscribing to the showHelp appState - $scope.drawer = {}; evts.push(eventsService.on("appState.drawerState.changed", function (e, args) { // set view if (args.key === "view") { @@ -156,6 +168,7 @@ function MainController($scope, $location, appState, treeService, notificationsS $scope.backdrop = args; })); + // event for infinite editors evts.push(eventsService.on("appState.editors.add", function (name, args) { $scope.editors = args.editors; })); diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index 20b5fd8a12..1f6f2c75b8 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -260,6 +260,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); if (found) { //set the route param + found.active = true; $scope.selectedLanguage = found; } } @@ -407,6 +408,13 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar // promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true })); //} //execute them sequentially + + // set selected language to active + angular.forEach($scope.languages, function(language){ + language.active = false; + }); + language.active = true; + angularHelper.executeSequentialPromises(promises); }); diff --git a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js deleted file mode 100644 index bec3e97543..0000000000 --- a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js +++ /dev/null @@ -1,158 +0,0 @@ -/** - * @ngdoc controller - * @name Umbraco.SearchController - * @function - * - * @description - * Controls the search functionality in the site - * - */ -function SearchController($scope, searchService, $log, $location, navigationService, $q) { - - $scope.searchTerm = null; - $scope.searchResults = []; - $scope.isSearching = false; - $scope.selectedResult = -1; - - $scope.navigateResults = function (ev) { - //38: up 40: down, 13: enter - - switch (ev.keyCode) { - case 38: - iterateResults(true); - break; - case 40: - iterateResults(false); - break; - case 13: - if ($scope.selectedItem) { - $location.path($scope.selectedItem.editorPath); - navigationService.hideSearch(); - } - break; - } - }; - - var group = undefined; - var groupNames = []; - var groupIndex = -1; - var itemIndex = -1; - $scope.selectedItem = undefined; - - $scope.clearSearch = function () { - $scope.searchTerm = null; - }; - function iterateResults(up) { - //default group - if (!group) { - - for (var g in $scope.groups) { - if ($scope.groups.hasOwnProperty(g)) { - groupNames.push(g); - - } - } - - //Sorting to match the groups order - groupNames.sort(); - - group = $scope.groups[groupNames[0]]; - groupIndex = 0; - } - - if (up) { - if (itemIndex === 0) { - if (groupIndex === 0) { - gotoGroup(Object.keys($scope.groups).length - 1, true); - } else { - gotoGroup(groupIndex - 1, true); - } - } else { - gotoItem(itemIndex - 1); - } - } else { - if (itemIndex < group.results.length - 1) { - gotoItem(itemIndex + 1); - } else { - if (groupIndex === Object.keys($scope.groups).length - 1) { - gotoGroup(0); - } else { - gotoGroup(groupIndex + 1); - } - } - } - } - - function gotoGroup(index, up) { - groupIndex = index; - group = $scope.groups[groupNames[groupIndex]]; - - if (up) { - gotoItem(group.results.length - 1); - } else { - gotoItem(0); - } - } - - function gotoItem(index) { - itemIndex = index; - $scope.selectedItem = group.results[itemIndex]; - } - - //used to cancel any request in progress if another one needs to take it's place - var canceler = null; - - $scope.$watch("searchTerm", _.debounce(function (newVal, oldVal) { - $scope.$apply(function () { - $scope.hasResults = false; - if ($scope.searchTerm) { - if (newVal !== null && newVal !== undefined && newVal !== oldVal) { - - //Resetting for brand new search - group = undefined; - groupNames = []; - groupIndex = -1; - itemIndex = -1; - - $scope.isSearching = true; - navigationService.showSearch(); - $scope.selectedItem = undefined; - - //a canceler exists, so perform the cancelation operation and reset - if (canceler) { - canceler.resolve(); - canceler = $q.defer(); - } - else { - canceler = $q.defer(); - } - - searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function (result) { - - //result is a dictionary of group Title and it's results - var filtered = {}; - _.each(result, function (value, key) { - if (value.results.length > 0) { - filtered[key] = value; - } - }); - $scope.groups = filtered; - // check if search has results - $scope.hasResults = Object.keys($scope.groups).length > 0; - //set back to null so it can be re-created - canceler = null; - $scope.isSearching = false; - }); - } - } - else { - $scope.isSearching = false; - navigationService.hideSearch(); - $scope.selectedItem = undefined; - } - }); - }, 200)); - -} -//register it -angular.module('umbraco').controller("Umbraco.SearchController", SearchController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 50e5c21203..3f44638096 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -122,12 +122,25 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH $route.reload(); } else { - //check if the location being changed is only the mculture query string, if so, cancel the routing since this is just - //used as a global persistent query string that does not change routes. - + + //check if the location being changed is only due to global/state query strings which means the location change + //isn't actually going to cause a route change. if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) { - //continue the route - $route.reload(); + //The location change will cause a route change. We need to ensure that the global/state + //query strings have not been stripped out. If they have, we'll re-add them and re-route. + + var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params); + if (toRetain) { + $route.updateParams(toRetain); + } + else { + //continue the route + $route.reload(); + } + } + else { + //navigation is not changing but we should update the currentRouteParams to include all current parameters + currentRouteParams = angular.copy(next.params); } } }); diff --git a/src/Umbraco.Web.UI.Client/src/less/application/grid.less b/src/Umbraco.Web.UI.Client/src/less/application/grid.less index 4448bf2192..7ed2abc898 100644 --- a/src/Umbraco.Web.UI.Client/src/less/application/grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/application/grid.less @@ -124,10 +124,6 @@ body.umb-drawer-is-visible #mainwrapper{ height: 100%; } -#tree .umb-tree { - padding: 0px 0px 20px 0px; -} - #search-results { z-index: 200; } diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index b2f2f99dd5..cc98ec306b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -71,8 +71,7 @@ @import "sections.less"; @import "helveticons.less"; @import "main.less"; -@import "tree.less"; -@import "listview.less"; +@import "listview.less"; @import "gridview.less"; @import "footer.less"; @import "dashboards.less"; @@ -84,6 +83,7 @@ @import "components/application/umb-app-content.less"; @import "components/application/umb-tour.less"; @import "components/application/umb-backdrop.less"; +@import "components/application/umb-search.less"; @import "components/application/umb-drawer.less"; @import "components/application/umb-language-picker.less"; @import "components/application/umb-dashboard.less"; @@ -91,6 +91,11 @@ @import "components/html/umb-expansion-panel.less"; @import "components/html/umb-alert.less"; +@import "components/tree/umb-tree.less"; +@import "components/tree/umb-tree-root.less"; +@import "components/tree/umb-actions.less"; +@import "components/tree/umb-tree-item.less"; + @import "components/editor.less"; @import "components/overlays.less"; @import "components/card.less"; @@ -133,6 +138,7 @@ @import "components/umb-querybuilder.less"; @import "components/umb-pagination.less"; @import "components/umb-mini-list-view.less"; +@import "components/umb-multiple-textbox.less"; @import "components/umb-badge.less"; @import "components/umb-nested-content.less"; @import "components/umb-checkmark.less"; @@ -166,6 +172,7 @@ @import "utilities/_spacing.less"; @import "utilities/_text-align.less"; @import "utilities/_width.less"; +@import "utilities/_cursor.less"; //page specific styles @import "pages/document-type-editor.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index daa6757f44..b1e9671de3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -227,13 +227,29 @@ input[type="button"] { font-size: 16px; border: none; background: @green; - color: white; + color: @white; font-weight: bold; &:hover { background: @green-d1; } } +// outlined +.btn-outline { + border: 1px solid @gray-8; + background: @white; + color: @black; + padding: 5px 13px; +} + +.btn-outline:hover, +.btn-outline:focus, +.btn-outline:active { + border-color: @gray-7; + background: transparent; + color: @black; +} + // Cross-browser Jank // -------------------------------------------------- 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 7410c09580..78c1616bc6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -790,13 +790,13 @@ h4.panel-title { } .smartphone-portrait { - width: 320px; - height: 504px; + width: 360px; + height: 640px; } .smartphone-landscape { - width: 480px; - height: 256px; + width: 640px; + height: 360px; } .border { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-dashboard.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-dashboard.less index 42c8fceaa0..2a1d5eb6d3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-dashboard.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-dashboard.less @@ -9,7 +9,14 @@ } .umb-dashboard__header { + flex: 0 0 @editorHeaderHeight; + background: @white; + border-bottom: 1px solid @gray-9; padding: 20px 0 0 0; + box-sizing: border-box; + display: flex; + justify-content: flex-end; + flex-direction: column; } .umb-dashboard__content { @@ -17,6 +24,12 @@ overflow: auto; } -.umb-dashboard .umb-tabs-nav { +// we need to do some special styling for tabs inside the dashboard header +.umb-dashboard__header .umb-tabs-nav { margin-bottom: 0; + border: none; +} + +.umb-dashboard__header .umb-tabs-nav .umb-tab > a { + padding-bottom: 23px; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less index 5cf9ca21b3..78f631ac0d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-language-picker.less @@ -1,6 +1,10 @@ .umb-language-picker { position: relative; z-index: @zindexDropdown; + // When the language dropdown is present adjust height on the tree root + ~ #tree .umb-tree-root-link { + height: 50px; + } } .umb-language-picker__toggle { @@ -10,7 +14,7 @@ padding: 0 20px; cursor: pointer; border-bottom: 1px solid @gray-9; - height: 50px; + height: @editorHeaderHeight; box-sizing: border-box; } @@ -40,9 +44,13 @@ font-size: 14px; } - .umb-language-picker__dropdown a:hover, .umb-language-picker__dropdown a:focus { background: @gray-10; text-decoration: none; } + +.umb-language-picker__dropdown-item--current { + background-color: @gray-10; + border-left: 2px solid @turquoise; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less new file mode 100644 index 0000000000..a8fc9c7f8e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-search.less @@ -0,0 +1,110 @@ + +/* + Search wrapper +*/ +.umb-search { + position: fixed; + z-index: @zindexUmbOverlay; + width: 660px; + max-width: 90%; + transform: translate(-50%, 0); + left: 50%; + top: 20%; + border-radius: @baseBorderRadius; + background: @white; + position: fixed; + box-shadow: 0 10px 20px rgba(0,0,0,.12),0 6px 6px rgba(0,0,0,.14); +} + +/* + Search field +*/ + +.umb-search-input-icon { + font-size: 22px; + color: @gray-7; + padding-left: 20px; + display: flex; + align-items: center; + height: 70px; +} + +.umb-search-input.umb-search-input { + width: 100%; + height: 70px; + border: none; + padding: 20px 20px 20px 15px; + border-radius: @baseBorderRadius; + font-size: 22px; + margin-bottom: 0; +} + +.umb-search-input-clear { + background: none; + border: none; + font-size: 12px; + margin-right: 20px; + color: @gray-3; +} + +.umb-search-input-clear.ng-enter { + opacity: 0; + transition: opacity 100ms ease-in-out; +} + +.umb-search-input-clear.ng-enter.ng-enter-active { + opacity: 1; +} + +/* + Search results +*/ +.umb-search-results { + max-height: 50vh; + overflow-y: auto; +} + +.umb-search-group__title { + background: @gray-10; + padding: 3px 20px; +} + +.umb-search-items { + list-style: none; + margin: 0; + padding-top: 4px; + padding-bottom: 4px; +} + +.umb-search-item > a { + padding: 6px 20px; + display: flex; +} + +.umb-search-item > a:hover, +.umb-search-item > a:focus { + background-color: @gray-10; + text-decoration: none; + outline: none; +} + +.umb-search-item > a:focus { + padding-left: 25px; + transition: padding 60ms ease-in-out; +} + +.umb-search-result__icon { + font-size: 18px; + margin-right: 8px; + color: @gray-1; +} + +.umb-search-result__meta { + display: flex; + flex-direction: column; +} + +.umb-search-result__description { + color: @gray-5; + font-size: 13px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less index 388d3587c1..e40282cb58 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less @@ -9,15 +9,22 @@ left: auto; } +.umb-button-group__sub-buttons>li>a { + display: flex; +} + .umb-button-group { .umb-button__button { - border-radius: 3px 0px 0px 3px; + border-radius: @baseBorderRadius; } .umb-button-group__toggle { - border-radius: 0px 3px 3px 0; + border-radius: 0px @baseBorderRadius @baseBorderRadius 0; border-left: 1px solid rgba(0,0,0,0.09); + margin-left: -2px; + padding-left: 10px; + padding-right: 10px; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less index c0aa4fa9b7..e8b8325183 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/card.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -96,7 +96,6 @@ font-size: 12px; text-align: center; width: 100px; - height: 105px; box-sizing: border-box; position: relative; } @@ -114,6 +113,7 @@ width: 100%; height: 100%; border-radius: 3px; + padding-bottom: 5px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index 2e4f106898..f045b0adca 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -102,8 +102,14 @@ input.umb-editor-header__name-input { background: @white; border: 1px solid @gray-8; &:hover { - background-color: @gray-10; - border: 1px solid @gray-8; + border-color: @turquoise-d1; + } +} + +input.umb-editor-header__name-input:disabled { + background-color: @gray-10; + &:hover{ + border-color: @gray-8; } } @@ -248,15 +254,17 @@ a.umb-variant-switcher__toggle { height: @editorFooterHeight; padding: 10px 20px; background: @white; - // box-shadow: 0 -1px 3px 0 rgba(0, 0, 0, 0.16); border-top: 1px solid @gray-9; z-index: 1; bottom: 0; + display: flex; + align-items: center; } .umb-editor-footer-content { display: flex; align-items: center; + flex: 1 1 auto; } .umb-editor-footer-content__right-side { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 99f3ac36ee..10296b58e3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -4,7 +4,7 @@ background: @gray-10; display: flex; justify-content: space-between; - margin-top: -20px; + margin-top: -10px; position: relative; top: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less index 5363a8db9b..43f6697eb1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less @@ -10,6 +10,7 @@ .umb-prevalues-multivalues__left { display: flex; flex: 1 1 auto; + overflow: hidden; } .umb-prevalues-multivalues__right { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less new file mode 100644 index 0000000000..f52258333d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less @@ -0,0 +1,98 @@ +// Tree context menu +// ------------------------- +.umb-actions { + margin: 0; + list-style: none; + user-select: none; + + .sep { + display: block; + border-top: 1px solid @gray-9; + + &:first-child { + border-top: none; + } + } + + .menu-label { + display: inline-block; + vertical-align: middle; + padding-left: 15px; + } + + .icon { + font-size: 18px; + vertical-align: middle; + color: @gray-3; + } +} + +.umb-action-link { + white-space: nowrap; + font-size: 15px; + color: @black; + padding: 9px 25px 9px 20px; + text-decoration: none; + cursor: pointer; + display: flex; + align-items: center; + + body.touch & { + padding: 7px 25px 7px 20px; + font-size: 110%; + } +} + +.umb-action-link:hover, +.umb-action-link:focus, +.umb-action.selected { + color: @black !important; + background: @gray-10 !important; + text-decoration: none; +} + +.umb-actions-child { + + .umb-action { + display: block; + + &.add { + margin-top: 20px; + border-top: 1px solid @gray-8; + padding-top: 20px; + + i { + opacity: 0.4; + } + } + } + + .umb-action-link { + clear: both; + padding-left: 10px; + } + + .icon { + font-size: 30px; + min-width: 30px; + text-align: center; + line-height: 24px; + /* set line-height to ensure all icons use same line-height */ + } + + .menu-label { + font-size: 14px; + color: @black; + margin-left: 10px; + } + + small { + font-size: 12px; + display: block; + clear: right; + line-height: 14px; + color: @gray-6; + white-space: normal; + margin-top: 2px; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less new file mode 100644 index 0000000000..002d076461 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less @@ -0,0 +1,72 @@ +.umb-tree-item { + display: block; + min-width: 100%; + width: auto; + + &:hover ins { + visibility: visible; + cursor: pointer + } +} + +.umb-tree-item > .umb-tree-item__inner { + + &:hover .umb-tree-item__label { + overflow: hidden; + margin-right: 6px; + } + + // Loading Animation + // ------------------------ + .l { + width: 100%; + height: 1px; + overflow: hidden; + position: absolute; + left: 0; + bottom: 0; + + div { + .umb-loader; + } + } + + .umb-tree-item__label { + padding: 7px 0 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1 0 auto; + } +} + +.umb-tree-item.current > .umb-tree-item__inner { + + background: @turquoise-d1; + + // override small icon color. TODO => check usage +// &:before { +// color: @turquoise-l2; +// } + + .umb-options { + + &:hover i { + opacity: .7; + } + + i { + background: @white; + border-color: @turquoise-d1; + transition: opacity 120ms ease; + } + } + + a, + .umb-tree-icon, + ins { + color: @white !important; + background-color: @turquoise-d1; + border-color: @turquoise-d1; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-root.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-root.less new file mode 100644 index 0000000000..72a008b8b6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-root.less @@ -0,0 +1,23 @@ +.umb-tree-root { + + &-link { + display: flex; + align-items: center; + width: 100%; + padding-left: 20px; + color: @gray-2; + height: @editorHeaderHeight; + } + + h5, + h6 { + margin: 0; + width: 100%; + display: flex; + color: @gray-2; + } + + .umb-options { + align-self: center; + } +} 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 new file mode 100644 index 0000000000..4705b8cc3e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less @@ -0,0 +1,288 @@ +// Tree +// ------------------------- +.umb-tree { + margin: 0; + min-width: 100%; + width: auto; + padding: 0 0 20px; + list-style-type: none; + + * { + white-space: nowrap; + } + + a, a:hover { + outline: none; + text-decoration: none; + + // TODO => confirm not in use +// &.noSpr { +// background-position: 0 +// } + } + + ins { + margin: -4px 0 0 -16px; + width: 16px; + height: 16px; + visibility: hidden; + text-decoration: none; + font-size: 12px; + transition: opacity 120ms ease; + + &:hover { + opacity: .7; + } + } + + i.noSpr { + display: inline-block; + margin-top: 1px; + width: 16px; + height: 16px; + line-height: 16px; + } + + ul { + padding: 0; + margin: 0; + min-width: 100%; + width: 100%; + + &.collapsed { + display: none; + } + } + + //loader defaults + .umb-loader { + height: 10px; + margin: 10px 10px 10px 10px; + } + + .search-subtitle { + color: @gray-7; + display: block; + padding-left: 35px; + } +} + +body.touch .umb-tree { + ins { + font-size: 14px; + visibility: visible; + padding: 7px; + } + + .umb-tree-item > .umb-tree-item__inner { + padding-top: 8px; + padding-bottom: 8px; + font-size: 110%; + } + + // change height of this if touch devices should have a different height of preloader. + .umb-tree-item .l div { + padding: 0; + } +} + +.umb-tree-root, .umb-tree-item__inner { + padding: 0; + position: relative; + overflow: hidden; + display: flex; + flex-wrap: nowrap; + align-items: center; + + &.active { + background: @gray-10; + } + + &:hover { + background: @gray-10; + + > .umb-options { + visibility: visible; + } + } +} + +.umb-tree-header { + display: flex; + padding: 20px 0 20px 20px; + box-sizing: border-box; + color: @gray-2; + font-weight: bold; + font-size: 15px; +} + +.umb-tree-icon, +.umb-tree-node-search { + cursor: pointer; +} + +.umb-tree .umb-search-group { + position: inherit; + display: inherit; + + h6 { + padding: 10px 0 10px 20px; + font-weight: inherit; + background: @gray-10; + font-size: 14px; + } + + &:hover { + background: inherit; + } + + &-item { + padding-left: 20px; + } + + &-link { + display: flex; + flex-wrap: wrap; + flex-direction: column; + font-weight: normal !important; + } +} + +.umb-tree .umb-tree-node-checked i[class^="icon-"], +.umb-tree .umb-tree-node-checked i[class*=" icon-"] { + font-family: 'icomoon' !important; + color: @green !important; + + &::before { + content: "\e165" !important; + font-family: inherit; + } +} + +.umb-options { + visibility: hidden; + display: flex; + flex: 0 0 auto; + justify-content: flex-end; + padding: 9px 5px; + text-align: center; + margin: 0 10px 0 auto; + cursor: pointer; + border-radius: @baseBorderRadius; + + &:hover { + background: @btnBackgroundHighlight; + } + + i { + height: 5px !important; + width: 5px !important; + border-radius: 20px; + background: @black; + display: inline-block; + margin: 0 2px 0 0; + + &:last-child { + margin: 0; + } + } + + .hide-options & { + display: none !important; + } +} + + +// Tree item states +// ------------------------- +.not-published { + > i.icon, + a { + opacity: 0.6; + } +} + +.not-allowed { + > i.icon, + a { + cursor: not-allowed; + } +} + +.protected, +.has-unpublished-version, +.is-container, +.locked { + &::before { + font-family: 'icomoon'; + position: absolute; + font-size: 20px; + padding-left: 7px; + padding-top: 7px; + bottom: 0; + } +} + +.protected::before { + content: "\e256"; + color: @red; +} + +.has-unpublished-version::before { + content: "\e25a"; + color: @green; +} + +.is-container::before { + content: "\e04e"; + color: @turquoise; + font-size: 8px; + padding-left: 13px; + padding-top: 8px; + pointer-events: none; +} + + +.locked::before { + content: "\e0a7"; + color: @red; +} + +.no-access { + .umb-tree-icon, + .root-link, + .umb-tree-item__label { + color: @gray-7; + cursor: not-allowed; + } +} + + +// Tree icons +// ------------------------- +.umb-tree-icon { + vertical-align: middle; + margin: 0 13px 0 0; + color: @gray-1; + font-size: 20px; + + &.blue { + color: @blue; + } + + &.green { + color: @green; + } + + &.purple { + color: @purple; + } + + &.orange { + color: @orange; + } + + &.red { + color: @red; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less index c7905879d8..a987c5daa3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less @@ -1,18 +1,22 @@ .umb-color-swatches { + display: flex; + flex-flow: row wrap; .umb-color-box { border: none; color: white; cursor: pointer; - padding: 5px; + padding: 1px; text-align: center; text-decoration: none; - display: inline-block; margin: 5px; border-radius: 3px; width: 30px; height: 30px; transition: box-shadow .3s; + display: flex; + align-items: center; + justify-content: center; &:hover, &:focus { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); @@ -28,4 +32,55 @@ } } } + + &.with-labels { + + .umb-color-box { + width: 120px; + height: 100%; + display: flex; + flex-flow: row wrap; + + .umb-color-box-inner { + display: flex; + flex-flow: column wrap; + flex: 0 0 100%; + max-width: 100%; + min-height: 80px; + padding-top: 10px; + + .umb-color-box__label { + background: #fff; + font-size: 14px; + display: flex; + flex-flow: column wrap; + flex: 0 0 100%; + padding: 1px 5px; + max-width: 100%; + margin-top: auto; + margin-bottom: -3px; + margin-left: -1px; + margin-right: -1px; + text-indent: 0; + text-align: left; + border: 1px solid @gray-8; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + overflow: hidden; + + .umb-color-box__name { + color: @black; + font-weight: bold; + margin-top: 3px; + } + + .umb-color-box__description { + font-size: 12px; + line-height: 1.5em; + color: @gray-3; + } + } + } + } + } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index 15a319b520..b0c90483f7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -36,7 +36,7 @@ } .umb-content-grid__icon.-light { - color: @gray-8; + color: @gray-5; } @@ -58,7 +58,7 @@ } .umb-content-grid__item-name.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-list { @@ -69,7 +69,7 @@ } .umb-content-grid__details-list.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-label { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less index 07bf8955aa..4803c05f6e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less @@ -8,6 +8,7 @@ width: auto; padding: 50px 0; border: 1px dashed @gray-8; + background-color: @white; text-align: center; color: @gray-3; margin: 0 0 20px 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-folder-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-folder-grid.less index f4fecf89fe..d91f24cbca 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-folder-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-folder-grid.less @@ -7,7 +7,7 @@ } .umb-folder-grid__folder { - background: @gray-10; + background: @white; margin: 5px; display: flex; align-items: center; @@ -20,6 +20,8 @@ justify-content: space-between; cursor: pointer; user-select: none; + box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); + border-radius: 3px; } .umb-folder-grid__folder:focus, @@ -29,7 +31,8 @@ .umb-folder-grid__folder:hover { text-decoration: none; - background-color: @gray-9; + box-shadow: 0 3px 6px 0 rgba(0,0,0,0.16); + transition: box-shadow 150ms ease-in-out; } .umb-folder-grid__folder-description { 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 2eb2735279..be6729bf56 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 @@ -8,13 +8,12 @@ .umb-group-builder__group { min-height: 86px; - margin: 50px 0 0 0; - border: 2px solid @gray-7; - border-radius: 0 10px 10px 10px; - padding: 10px 10px 5px 10px; + border: 1px solid transparent; + border-radius: @baseBorderRadius; box-sizing: border-box; background-color: @white; - position:relative; + position: relative; + box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); } .umb-group-builder__group.-active { @@ -24,6 +23,7 @@ .umb-group-builder__group.-inherited { border-color: @gray-9; animation: fadeIn 0.5s; + box-shadow: none; } .umb-group-builder__group.-placeholder { @@ -32,10 +32,11 @@ justify-content: center; align-items: center; cursor: pointer; - border: 1px dashed @gray-8; + border: 1px dashed @gray-7; color: @turquoise-d1; font-weight: bold; position: relative; + box-shadow: none; } .umb-group-builder__group.-sortable { @@ -57,9 +58,8 @@ } .umb-group-builder__group-remove { - position: absolute; - top: -30px; - right: 20px; + position: relative; + margin-left: auto; font-size: 18px; } @@ -71,28 +71,15 @@ .umb-group-builder__group-title-wrapper { display: flex; align-items: center; - margin-left: -12px; - margin-top: -55px; -} - -.umb-group-builder__group-title-wrapper.-placeholder { - position: absolute; - left: -1px; - top: -44px; - margin-left: 0; - margin-top: 0; + border-bottom: 1px solid @gray-9; + padding: 10px 15px; } .umb-group-builder__group-title { - padding: 5px 9px 0 9px; - height: 38px; - background: @white; - border: 2px solid @gray-7; - border-bottom: none; - border-radius: 10px 10px 0 0; font-weight: bold; display: flex; align-items: center; + color: @black; } .umb-group-builder__group-title-icon { @@ -117,10 +104,14 @@ input.umb-group-builder__group-title-input { border-color: transparent; background: transparent; font-weight: bold; - color: @gray-3; + color: @black; margin-bottom: 0; } +input.umb-group-builder__group-title-input:disabled:hover { + border: none; +} + .umb-group-builder__group-title-input:hover { border-color: @inputBorder; } @@ -131,17 +122,14 @@ input.umb-group-builder__group-title-input { .umb-group-builder__group-inherited-label { font-size: 13px; - color: @gray-8; display: inline-block; position: relative; top: 2px; - margin-left: 10px; } .umb-group-builder__group-sort-order { - display: flex; - align-items: center; - margin-left: 10px; + margin-right: 10px; + margin-left: auto; } /* ---------- PROPERTIES ---------- */ @@ -149,7 +137,7 @@ input.umb-group-builder__group-title-input { .umb-group-builder__properties { list-style: none; margin: 0; - padding: 0; + padding: 15px; min-height: 35px; // the height of a sortable property } @@ -157,35 +145,23 @@ input.umb-group-builder__group-title-input { position: relative; display: flex; flex-flow: row; - margin-bottom: 5px; - border: 1px solid transparent; - border-radius: 5px; - padding: 5px; box-sizing: border-box; + border-bottom: 1px solid @gray-10; + padding: 10px 0; } -.umb-group-builder__property:hover { - border: 1px dashed @inputBorder; +.umb-group-builder__property:first-of-type { + padding-top: 0; } -.umb-group-builder__property.-placeholder { - background: @white; - border: 1px dashed @gray-8; - border-radius: 5px; - cursor: pointer; - align-items: center; - animation: fadeIn 0.5s; +.umb-group-builder__property:last-of-type { + margin-bottom: 15px; } .umb-group-builder__property.-inherited { - border: transparent; animation: fadeIn 0.5s; } -.umb-group-builder__property.-inherited:hover { - border: transparent; -} - .umb-group-builder__property.-locked { border: transparent; animation: fadeIn 0.5s; @@ -198,14 +174,16 @@ input.umb-group-builder__group-title-input { .umb-group-builder__property.-sortable, .umb-group-builder__property.-sortable-locked { min-height: 35px; - border-radius: 5px; + border-radius: @baseBorderRadius; border: none; animation: none; align-items: center; + padding: 5px 10px; + margin-bottom: 5px; } .umb-group-builder__property.-sortable { - background: @gray-9; + background: @gray-10; color: @gray-1; cursor: move; } @@ -217,16 +195,17 @@ input.umb-group-builder__group-title-input { .umb-group-builder__property-meta { - flex: 0 0 175px; + flex: 0 0 250px; margin-right: 20px; } .umb-group-builder__property-meta.-full-width { flex: 1; + margin-right: 0; } .umb-group-builder__property-meta-alias { - font-size: 10px; + font-size: 12px; color: @gray-3; word-break: break-word; line-height: 1.5; @@ -273,7 +252,7 @@ input.umb-group-builder__group-title-input { overflow: hidden; position: relative; padding: 35px 10px 25px 10px; - background: url("../img/checkered-background-20.png"); + background-color: @gray-10; } .umb-group-builder__property-preview:hover { @@ -357,42 +336,6 @@ input.umb-group-builder__group-title-input { margin-right: 3px; } - -/* ---------- PLACEHOLDER BOX ---------- */ - -.umb-group-builder__placeholder-box { - background: @gray-9; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - font-size: 13px; - font-weight: bold; - color: @turquoise-d1; -} - -.umb-group-builder__placeholder-box.-input { - height: 10px; - margin-bottom: 5px; -} - -.umb-group-builder__placeholder-box.-input-small { - height: 5px; - margin-bottom: 5px; - width: 25%; -} - -.umb-group-builder__placeholder-box.-text-full-width { - height: 3px; - margin-bottom: 3px; -} - -.umb-group-builder__placeholder-box.-text-short { - height: 3px; - margin-bottom: 3px; - width: 75%; -} - /* ---------- SORTABLE ---------- */ .umb-group-builder__group-sortable-placeholder { @@ -418,6 +361,11 @@ input.umb-group-builder__group-title-input { text-align: center; } +input.umb-group-builder__group-sort-value { + margin-bottom: 0; + margin-left: auto; +} + /* ---------- DIALOGS ---------- */ 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 8d3a880105..3356c1de68 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 @@ -30,7 +30,19 @@ } .umb-media-grid__item.-file { - background-color: @gray-10; + background-color: @white; +} + +.umb-media-grid__item-file-icon > span { + color: @white; + background: @gray-4; + padding: 1px 3px; + font-size: 10px; + line-height: 130%; + display: block; + margin-top: -30px; + margin-left: -10px; + position: relative; } .umb-media-grid__item.-selected { @@ -87,7 +99,7 @@ .umb-media-grid__item.-file .umb-media-grid__item-overlay { opacity: 1; color: @gray-4; - background: @gray-10; + background: @white; } .umb-media-grid__item.-file:hover .umb-media-grid__item-overlay, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less new file mode 100644 index 0000000000..21f59a3e2d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less @@ -0,0 +1,34 @@ +.umb-multiple-textbox .textbox-wrapper { + align-items: center; + margin-bottom: 15px; +} + +.umb-multiple-textbox .textbox-wrapper .umb-editor { + margin-bottom: 0; +} + +.umb-multiple-textbox .textbox-wrapper i { + margin-right: 5px; +} + +.umb-multiple-textbox .textbox-wrapper i.handle { + margin-left: 5px; +} + +.umb-multiple-textbox .textbox-wrapper a.remove { + margin-left: 5px; + text-decoration: none; +} + +.umb-multiple-textbox .add-link { + &:extend(.umb-node-preview-add); +} + +.umb-editor-wrapper .umb-multiple-textbox .add-link { + &:extend(.umb-editor-wrapper .umb-node-preview); +} + +.umb-modal .umb-multiple-textbox .textbox-wrapper .umb-editor { + flex: 1 1 auto; + width: auto; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index b302ed2654..b729aa92bc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -166,7 +166,7 @@ input.umb-table__input { } -.-content .-unpublished { +.-content :not(.with-unpublished-version).-unpublished { .umb-table__name > * { opacity: .4; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less index d3c556bc50..9da54f0bf9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less @@ -3,7 +3,7 @@ margin-bottom: 0; list-style: none; border-bottom: 1px solid @gray-9; - display: inline-block; + display: block; margin-bottom: 20px; } @@ -17,7 +17,7 @@ cursor: pointer; border-bottom: 2px solid transparent; color: @gray-3; - padding: 5px 20px 10px 20px; + padding: 5px 20px 15px 20px; transition: color 150ms ease-in-out; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less index c041f3f4ea..f5a81e3393 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less @@ -27,5 +27,5 @@ .umb-permission__description { font-size: 13px; - color: @gray-5; + color: @gray-4; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-cards.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-cards.less index e79e474935..7acf47d22e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-cards.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-cards.less @@ -68,8 +68,8 @@ position: relative; padding: 15px; flex: 1 1 auto; - background-color: @gray-10; - border: 1px solid @gray-9; + background-color: @white; + box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); border-radius: 3px; box-sizing: border-box; display: flex; diff --git a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less index 7a96c906e3..d40b59cf81 100644 --- a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less +++ b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less @@ -8,7 +8,6 @@ .umb-healthcheck-help-text { line-height: 1.6em; - margin-bottom: 30px; max-width: 750px; } @@ -37,6 +36,7 @@ .umb-healthcheck-group:hover { box-shadow: 0 3px 6px 0 rgba(0,0,0,0.16); cursor: pointer; + transition: box-shadow 150ms ease-in-out; } .umb-healthcheck-group__load-container { diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index d869d1d9af..6d567c5fb3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -116,7 +116,7 @@ h5.-black { /* FORM GRID */ .umb-pane { - margin: 30px 20px; + margin: 20px; } .umb-control-group { border-bottom: 1px solid @gray-10; @@ -650,3 +650,12 @@ input[type=checkbox]:checked + .input-label--small { .bootstrap-datetimepicker-widget td span { border-radius: 0 !important; } + +.diff del { + background-color: @red-l3; +} + +.diff ins { + background-color: @green-l3; + text-decoration: none; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index 5a85c19124..8ef2c8c859 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -75,6 +75,7 @@ bottom: 0px; position: absolute; padding: 0px; + background: #fff; } .umb-dialog .umb-btn-toolbar .umb-control-group{ 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 87a34962ab..4e7937830c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/login.less @@ -58,12 +58,13 @@ .login-overlay .form { background: @white; - padding: 25px; + padding: 30px; width: 500px; margin-left: 25px; margin-right: 25px; margin-top: auto; margin-bottom: auto; + border-radius: @baseBorderRadius; } .login-overlay .form input[type="text"], diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less index c7d156d44c..06f05ab0aa 100644 --- a/src/Umbraco.Web.UI.Client/src/less/properties.less +++ b/src/Umbraco.Web.UI.Client/src/less/properties.less @@ -95,6 +95,10 @@ font-size: 14px; } +.history-item__badge { + margin-right: 5px; +} + /* RESPONSIVE */ @media (min-width: 1101px) and (max-width: 1365px), (max-width: 979px) { 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 2b716b9b9f..43f5aa5a9a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -133,27 +133,6 @@ div.umb-codeeditor .umb-btn-toolbar { // // Color picker // -------------------------------------------------- -ul.color-picker li { - padding: 2px; - margin: 3px; - border: 2px solid transparent; - width: 60px; - - .thumbnail{ - min-width: auto; - width: inherit; - padding: 0; - } - - a { - height: 50px; - display:flex; - align-items: center; - justify-content: center; - cursor:pointer; - margin: 0 0 5px; - } -} /* pre-value editor */ .control-group.color-picker-preval { @@ -174,7 +153,7 @@ ul.color-picker li { div.color-picker-prediv { display: inline-flex; align-items: center; - max-width: 80%; + max-width: 85%; pre { display: inline-flex; @@ -357,13 +336,30 @@ ul.color-picker li { text-align: center; } -.umb-sortable-thumbnails .umb-icon-holder .icon{ +.umb-sortable-thumbnails .umb-icon-holder .icon { font-size: 40px; line-height: 50px; color: @gray-3; display: block; } +.umb-sortable-thumbnails .umb-icon-holder .file-icon > span { + color: @white; + background: @gray-4; + padding: 1px 3px; + font-size: 10px; + line-height: 130%; + display: block; + margin-top: -30px; + width: 2em; +} + +.umb-sortable-thumbnails .umb-icon-holder .file-icon + small { + display: block; + margin-top: 1em; +} + + .umb-sortable-thumbnails .umb-sortable-thumbnails__wrapper { width: 124px; height: 124px; diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less deleted file mode 100644 index cf163cd72d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ /dev/null @@ -1,553 +0,0 @@ -// item-list -// ------------------------- - - -.umb-item-list { - margin: 0; - width: auto; - display: block -} -.umb-item-list li { - display: block; - width: auto; -} - - - - -// Tree -// ------------------------- - -.umb-tree { - margin: 0; - min-width: 100%; - width: auto; -} - -.umb-tree li { - display: block; - min-width: 100%; - width: auto; -} -.umb-tree li.current > div, -.umb-tree div.selected { - background: @turquoise-d1; -} -.umb-tree li.current > div a.umb-options i, -.umb-tree div.selected i { - background: @white; - border-color: @turquoise-d1; - transition: opacity 120ms ease; -} - -.umb-tree li.current > div a.umb-options:hover i, -.umb-tree div.selected i { - opacity: .7; -} - -.umb-tree li.current > div a, -.umb-tree li.current > div i.icon, -.umb-tree li.current > div ins { - color: @white !important; - background-color: @turquoise-d1; - border-color: @turquoise-d1; -} - -.umb-tree li.root > div:first-child { - padding: 0; -} - -.umb-tree li.root > div h5, .umb-tree li.root > div h6 { - margin: 0; - width: 100%; - display: flex; - align-items: center; -} - -.umb-tree li.root > div:first-child h5 > a, .umb-tree-header { - display: flex; - padding: 20px 0 20px 20px; - box-sizing: border-box; -} - -.umb-tree * { - white-space: nowrap -} -.umb-tree ul { - padding: 0; - margin: 0; - min-width: 100%; - width: 100%; - //display: table -} - -.umb-tree ul.collapsed { - display:none; -} - -.umb-tree a { - cursor:pointer; - text-decoration: none; - outline: none; -} - -.umb-tree a:hover { - text-decoration: none -} - -/*.umb-tree div.tree-node { - padding: 5px 0 5px 0; - position: relative; - overflow: hidden; - display: flex; - flex-wrap: nowrap; - align-items: center; -}*/ - -.umb-tree div { - padding: 5px 0 5px 0; - position: relative; - overflow: hidden; - display: flex; - flex-wrap: nowrap; - align-items: center; -} - -.umb-tree a.noSpr { - background-position: 0 -} - -.umb-tree div > a.umb-options { - visibility: hidden; - flex: 0 0 auto; - margin-left: auto; -} - -.umb-tree div:hover > a.umb-options { - visibility: visible; -} - -.umb-tree li.root > div a, -.umb-tree li.root h5, .umb-tree-header { - color: @gray-2; - font-weight: bold; - font-size: 15px; -} - -.umb-tree ins { - margin: -4px 0 0 -16px; - width: 16px; - height: 16px; - visibility: hidden; - text-decoration: none; - font-size: 12px; - transition: opacity 120ms ease; -} - -.umb-tree ins:hover { - opacity: .7; -} - -.umb-tree li:hover ins { - visibility: visible; - cursor: pointer -} - -.umb-tree li div { - padding: 0; -} - -.umb-tree li > div a:not(.umb-options) { - padding: 6px 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex: 1 0 auto; -} - -.umb-tree li > div:hover a:not(.umb-options) { - overflow: hidden; - margin-right: 6px; -} - -.umb-tree .icon { - vertical-align: middle; - margin: 0 13px 0 0; - color: @gray-1; - font-size: 20px; -} - -.umb-tree-icon { - cursor: pointer; -} - -.umb-tree i.noSpr { - display: inline-block; - margin-top: 1px; - width: 16px; - height: 16px; - line-height: 16px; -} - -.umb-tree div:hover { - background: @gray-10; -} - -.umb-tree small.search-subtitle{ - color: @gray-7; - display: block; - padding-left: 35px; -} - -.umb-tree .umb-tree-node-search { - cursor:pointer; - /*color:@turquoise;*/ -} - -.umb-tree div.umb-search-group { - position: inherit; - display: inherit; -} - -.umb-tree div.umb-search-group:hover { - background: inherit; -} -.umb-tree div.umb-search-group h6 { - /*color: @gray-5;*/ - padding: 10px 0 10px 20px; - font-weight: inherit; - background: @gray-10; - font-size: 14px; - font-weight: bold; -} - -.umb-tree .umb-search-group-item { - padding-left: 20px; -} - -.umb-tree .umb-search-group-item-link { - display: flex; - flex-wrap: wrap; - flex-direction: column; - font-weight: normal !important; -} - -.icon-check:before { - content: "\e165"; -} - -.umb-tree .umb-tree-node-checked i[class^="icon-"], -.umb-tree .umb-tree-node-checked i[class*=" icon-"] { - font-family: 'icomoon' !important; - color:@green !important; -} -.umb-tree .umb-tree-node-checked i:before { - /*check box*/ - content: "\e165" !important; - font-family: inherit; -} - -a.umb-options { - visibility: hidden; - display: flex; - justify-content: flex-end; - padding: 9px 5px; - text-align: center; - cursor: pointer; - margin-right: 10px; -} - -a.umb-options i { - height: 5px !important; - width: 5px !important; - border-radius: 20px; - background: @black; - display: inline-block; - margin: 0 2px 0 0; -} - -a.umb-options i:last-child { - margin: 0; -} - -a.umb-options:hover { - background: @btnBackgroundHighlight; - .border-radius(@baseBorderRadius); -} - -li.root > div > a.umb-options { - top: 18px; - display: flex; - padding: 10px 5px; -} - -.hide-options a.umb-options{display: none !important} -.hide-header h5{display: none !important} - - -.umb-icon-item { - padding: 2px; - padding-left: 55px; - display: block; - position: relative; -} - -.umb-icon-item:hover { - background: @gray-10; -} -.umb-icon-item i.icon { - position: absolute; - top: 8px; - left: 19px; -} -.umb-icon-item a:hover div { - text-decoration: underline; -} - -.umb-icon-item a { - color: @gray-3; - padding-top: 3px; - height: 15px; - font-size: 12px; - text-decoration: none; -} -.umb-icon-item small { - color: @gray-6; - font-size: 10px; - display: block -} -.umb-icon-item:hover a.umb-options { - visibility: visible -} -.umb-icon-item .umb-spr { - float: left -} - - - -// Tree item states -// ------------------------- -div.not-published > i.icon,div.not-published > a{ - opacity: 0.6; -} -div.protected:before{ - content:"\e256"; - font-family: 'icomoon'; - color: @red; - position: absolute; - font-size: 20px; - padding-left: 7px; - padding-top: 7px; - bottom: 0; -} - -div.has-unpublished-version:before{ - content:"\e25a"; - font-family: 'icomoon'; - color: @green; - position: absolute; - font-size: 20px; - padding-left: 7px; - padding-top: 7px; - bottom: 0; -} - -div.not-allowed > i.icon,div.not-allowed > a{ - cursor: not-allowed; -} - -// override small icon color -.umb-tree li.current > div:before { - color: @turquoise-l2; -} -div.is-container:before{ - content:"\e04e"; - font-family: 'icomoon'; - color: @turquoise; - position: absolute; - font-size: 8px; - padding-left: 13px; - padding-top: 8px; - pointer-events: none; - bottom: 0; -} - -div.locked:before{ - content:"\e0a7"; - font-family: 'icomoon'; - color: @red; - position: absolute; - font-size: 20px; - padding-left: 7px; - padding-top: 7px; - bottom: 0; -} - -.umb-tree li div.no-access .umb-tree-icon, -.umb-tree li div.no-access .root-link, -.umb-tree li div.no-access .umb-tree-item__label { - color: @gray-7; - cursor: not-allowed; -} - -// Tree context menu -// ------------------------- -.umb-actions { - margin: 0; - padding: 0px; - list-style: none; - user-select: none; -} - -.umb-actions li.sep { - display: block; - border-top: 1px solid @gray-9; -} - -.umb-actions li.sep:first-child { - border-top: none; -} - -.umb-actions a { - white-space: nowrap; - display: block; - font-size: 15px; - color: @black; - padding: 9px 25px 9px 20px; - text-decoration: none; - cursor: pointer; - display: flex; - align-items: center; -} - -.umb-actions a:hover, .umb-actions a:focus, -.umb-actions li.selected { - color: @black !important; - background: @gray-10 !important; -} - -.umb-actions .menu-label { - display: inline-block; - vertical-align: middle; - padding-left: 15px; -} - -.umb-actions i { - color: @gray-6; - font-size: 18px; - vertical-align: middle; - color: @gray-3; -} - -.umb-actions-child { - list-style: none; - display: block; - margin: 0px; -} - -.umb-actions-child li { - display: block; -} - -.umb-actions-child a { - display: block; - clear: both; - text-decoration: none; - padding-left: 10px; -} -.umb-actions-child li .menu-label { - font-size: 14px; - color: @black; - margin-left: 10px; -} - -.umb-actions-child li .menu-label small { - font-size: 12px; - display: block; - clear: right; - line-height: 14px; - color: @gray-6; - white-space: normal; - margin-top: 2px; -} -.umb-actions-child li a:hover .menuLabel small { - text-decoration: none !important -} -.umb-actions-child i { - font-size: 30px; - min-width: 30px; - text-align: center; - line-height: 24px; /* set line-height to ensure all icons use same line-height */ -} - -.umb-actions-child li.add { - margin-top: 20px; - border-top: 1px solid @gray-8; - padding-top: 20px; -} -.umb-actions-child li.add i { - opacity: 0.4; -} - - -// Tree icon colors -// ------------------------- - -.umb-tree i.icon.blue { - color: @blue; -} -.umb-tree i.icon.green { - color: @green; -} -.umb-tree i.icon.purple { - color: @purple; -} -.umb-tree i.icon.orange { - color: @orange; -} -.umb-tree i.icon.red { - color: @red; -} - - - - -// Loading Animation -// ------------------------ - -.umb-tree li div.l{ - width:100%; - height:1px; - overflow:hidden; - position: absolute; - left: 0; - bottom: 0; -} - -.umb-tree li div.l div { - .umb-loader; -} - -//loader defaults -.umb-tree .umb-loader{ - height: 10px; margin: 10px 10px 10px 10px; -} - - -/*body.touch .umb-tree .icon{font-size: 19px;}*/ -body.touch .umb-tree ins{font-size: 14px; visibility: visible; padding: 7px;} -body.touch .umb-tree li > div { - padding-top: 8px; - padding-bottom: 8px; - font-size: 110%; -} - -// change height of this if touch devices should have a different height of preloader. -body.touch .umb-tree li div.l div { - padding: 0; -} - -body.touch .umb-actions a { - padding: 7px 25px 7px 20px; - font-size: 110%; -} diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/_cursor.less b/src/Umbraco.Web.UI.Client/src/less/utilities/_cursor.less new file mode 100644 index 0000000000..dade8e0aae --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/_cursor.less @@ -0,0 +1,39 @@ +/* + CURSORS +*/ + +.cursor-auto { cursor: auto; } +.cursor-default { cursor: default; } +.cursor-none { cursor: none; } +.cursor-context-menu { cursor: context-menu; } +.cursor-help { cursor: help; } +.cursor-pointer { cursor: pointer; } +.cursor-progress { cursor: progress; } +.cursor-wait { cursor: wait; } +.cursor-cell { cursor: cell; } +.cursor-crosshair { cursor: crosshair; } +.cursor-text { cursor: text; } +.cursor-vertical-text { cursor: vertical-text; } +.cursor-alias { cursor: alias; } +.cursor-copy { cursor: copy; } +.cursor-move { cursor: move; } +.cursor-no-drop { cursor: no-drop; } +.cursor-not-allowed { cursor: not-allowed; } +.cursor-e-resize { cursor: e-resize; } +.cursor-n-resize { cursor: n-resize; } +.cursor-ne-resize { cursor: ne-resize; } +.cursor-nw-resize { cursor: nw-resize; } +.cursor-s-resize { cursor: s-resize; } +.cursor-se-resize { cursor: se-resize; } +.cursor-sw-resize { cursor: sw-resize; } +.cursor-w-resize { cursor: w-resize; } +.cursor-ew-resize { cursor: ew-resize; } +.cursor-ns-resize { cursor: ns-resize; } +.cursor-nesw-resize { cursor: nesw-resize; } +.cursor-nwse-resize { cursor: nwse-resize; } +.cursor-col-resize { cursor: col-resize; } +.cursor-row-resize { cursor: row-resize; } +.cursor-all-scroll { cursor: all-scroll; } +.cursor-zoom-in { cursor: zoom-in; } +.cursor-zoom-out { cursor: zoom-out; } + diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 7bf71b8371..513c645365 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -195,7 +195,7 @@ @bodyBackground: @gray-10; @textColor: @gray-2; -@editorHeaderHeight: 80px; +@editorHeaderHeight: 70px; @editorFooterHeight: 50px; diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js new file mode 100644 index 0000000000..beb6353bde --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -0,0 +1,168 @@ + +/*********************************************************************************************************/ +/* Preview app and controller */ +/*********************************************************************************************************/ + +var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.services']) + + .controller("previewController", function ($scope, $http, $window, $timeout, $location, dialogService) { + + //gets a real query string value + function getParameterByName(name, url) { + if (!url) url = $window.location.href; + name = name.replace(/[\[\]]/g, '\\$&'); + var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); + } + + function configureSignalR(iframe) { + // signalr hub + var previewHub = $.connection.previewHub; + + // visibility tracking + var dirtyContent = false; + var visibleContent = true; + + document.addEventListener('visibilitychange', function () { + visibleContent = !document.hidden; + if (visibleContent && dirtyContent) { + dirtyContent = false; + console.log("Visible, reloading.") + var iframeDoc = (iframe.contentWindow || iframe.contentDocument); + iframeDoc.location.reload(); + } + }); + + previewHub.client.refreshed = function (message, sender) { + console.log("Notified by SignalR preview hub (" + message + ")."); + + if ($scope.pageId != message) { + console.log("Not a notification for us (" + $scope.pageId + ")."); + return; + } + + if (!visibleContent) { + console.log("Not visible, will reload."); + dirtyContent = true; + return; + } + + console.log("Reloading."); + var iframeDoc = (iframe.contentWindow || iframe.contentDocument); + iframeDoc.location.reload(); + }; + + $.connection.hub.start() + .done(function () { console.log("Connected to SignalR preview hub (ID=" + $.connection.hub.id + ")"); }) + .fail(function () { console.log("Could not connect to SignalR preview hub."); }); + } + + var isInit = getParameterByName("init"); + if (isInit === "true") { + //do not continue, this is the first load of this new window, if this is passed in it means it's been + //initialized by the content editor and then the content editor will actually re-load this window without + //this flag. This is a required trick to get around chrome popup mgr. + return; + } + + $scope.pageId = $location.search().id || getParameterByName("id"); + var culture = $location.search().culture || getParameterByName("culture"); + + if ($scope.pageId) { + var query = 'id=' + $scope.pageId; + if (culture) { + query += "&culture=" + culture; + } + $scope.pageUrl = "frame?" + query; + } + + $scope.isOpen = false; + $scope.frameLoaded = false; + + $scope.valueAreLoaded = false; + $scope.devices = [ + { name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" }, + { name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop", title: "Laptop" }, + { name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad", title: "Tablet portrait" }, + { name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip", title: "Tablet landscape" }, + { name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone", title: "Smartphone portrait" }, + { name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip", title: "Smartphone landscape" } + ]; + $scope.previewDevice = $scope.devices[0]; + + /*****************************************************************************/ + /* Preview devices */ + /*****************************************************************************/ + + // Set preview device + $scope.updatePreviewDevice = function (device) { + $scope.previewDevice = device; + }; + + /*****************************************************************************/ + /* Exit Preview */ + /*****************************************************************************/ + + $scope.exitPreview = function () { + window.top.location.href = "../endPreview.aspx?redir=%2f" + $scope.pageId; + }; + + $scope.onFrameLoaded = function (iframe) { + $scope.frameLoaded = true; + configureSignalR(iframe); + } + + /*****************************************************************************/ + /* Panel managment */ + /*****************************************************************************/ + + $scope.openPreviewDevice = function () { + $scope.showDevicesPreview = true; + } + + }) + + + .component('previewIFrame', { + + template: "
", + controller: function ($element, $scope, angularHelper) { + + var vm = this; + + vm.$postLink = function () { + var resultFrame = $element.find("#resultFrame"); + resultFrame.on("load", iframeReady); + vm.srcDelayed = vm.src; + }; + + function iframeReady() { + var iframe = $element.find("#resultFrame").get(0); + hideUmbracoPreviewBadge(iframe); + angularHelper.safeApply($scope, function () { + vm.onLoaded({ iframe: iframe }); + }); + } + + function hideUmbracoPreviewBadge (iframe) { + if (iframe && iframe.contentDocument && iframe.contentDocument.getElementById("umbracoPreviewBadge")) { + iframe.contentDocument.getElementById("umbracoPreviewBadge").style.display = "none"; + } + }; + + }, + controllerAs: "vm", + bindings: { + src: "<", + onLoaded: "&" + } + + }) + + .config(function ($locationProvider) { + $locationProvider.html5Mode(false); //turn html5 mode off + $locationProvider.hashPrefix(''); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js index fdd438e9cf..c965d16c0d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js @@ -29,6 +29,7 @@ function init() { // Check if it is a new user var inviteVal = $location.search().invite; + //1 = enter password, 2 = password set, 3 = invalid token if (inviteVal && (inviteVal === "1" || inviteVal === "2")) { $q.all([ @@ -58,6 +59,8 @@ $scope.inviteStep = Number(inviteVal); }); + } else if (inviteVal && inviteVal === "3") { + $scope.inviteStep = Number(inviteVal); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index e16bb2375b..eab74994e6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -104,10 +104,18 @@ - + + -
    -
  • -
    {{ property.header }}:
    -
    {{ item[property.alias] }}
    -
  • -
+
    +
  • +
    :
    +
    +
  • +
  • +
    {{ property.header }}:
    +
    {{ item[property.alias] }}
    +
  • +
@@ -32,4 +36,4 @@ There are no items to show - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 4aa0072b6f..2a5bc4a572 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -5,23 +5,26 @@ + action="openCompositionsDialog()" + size="xs"> + action="toggleSortingMode();" + size="xs"> @@ -38,29 +41,12 @@ - -
-
-
- - - - +
-
- - - -
-
@@ -89,8 +75,17 @@
- -
+
+ + : {{ tab.inheritedFromName }} + + {{ contentTypeName }} + , + +
+ + +
@@ -101,14 +96,14 @@
-
- - {{ tab.inheritedFromName }} - - - {{ contentTypeName }} - , - +
+ + +
@@ -117,34 +112,17 @@
  • - + - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    - +
    @@ -194,11 +172,11 @@
    -
    - +
    + {{ property.label }} - ({{ property.alias }}) - + ({{ property.alias }}) +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html index c7dba13bae..21840cb1d1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-layout-selector.html @@ -1,11 +1,19 @@ -
    +
    -
    - +
    +
    -
    -
    +
    + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index 6e8b70f4f1..2591789a36 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -22,8 +22,10 @@ {{item.name}} - - + + + .{{item.extension}} +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-pagination.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-pagination.html index e8effcd761..817a4f2a94 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-pagination.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-pagination.html @@ -1,26 +1,26 @@ -
  • [UmbracoRequireHttps] - [DisableClientCache] + [DisableBrowserCache] public class BackOfficeController : UmbracoController { private readonly ManifestParser _manifestParser; @@ -86,6 +86,16 @@ namespace Umbraco.Web.Editors [HttpGet] public async Task VerifyInvite(string invite) { + //if you are hitting VerifyInvite, you're already signed in as a different user, and the token is invalid + //you'll exit on one of the return RedirectToAction("Default") but you're still logged in so you just get + //dumped at the default admin view with no detail + if(Security.IsAuthenticated()) + { + AuthenticationManager.SignOut( + Core.Constants.Security.BackOfficeAuthenticationType, + Core.Constants.Security.BackOfficeExternalAuthenticationType); + } + if (invite == null) { Logger.Warn("VerifyUser endpoint reached with invalid token: NULL"); @@ -129,16 +139,15 @@ namespace Umbraco.Web.Editors if (result.Succeeded == false) { Logger.Warn("Could not verify email, Error: " + string.Join(",", result.Errors) + ", Token: " + invite); - return RedirectToAction("Default"); + return new RedirectResult(Url.Action("Default") + "#/login/false?invite=3"); } //sign the user in - - AuthenticationManager.SignOut( - Core.Constants.Security.BackOfficeAuthenticationType, - Core.Constants.Security.BackOfficeExternalAuthenticationType); - + DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc; await SignInManager.SignInAsync(identityUser, false, false); + //reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to SignInManager would be a breaking change + identityUser.LastLoginDateUtc = previousLastLoginDate; + await UserManager.UpdateAsync(identityUser); return new RedirectResult(Url.Action("Default") + "#/login/false?invite=1"); } @@ -196,7 +205,8 @@ namespace Umbraco.Web.Editors //get the legacy ActionJs file references to append as well var legacyActionJsRef = GetLegacyActionJs(LegacyJsActionType.JsUrl); - var result = initJs.GetJavascriptInitialization(HttpContext, JsInitialization.GetDefaultInitialization(), legacyActionJsRef); + var files = initJs.OptimizeBackOfficeScriptFiles(HttpContext, JsInitialization.GetDefaultInitialization(), legacyActionJsRef); + var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco"); result += initCss.GetStylesheetInitialization(HttpContext); return JavaScript(result); @@ -215,7 +225,7 @@ namespace Umbraco.Web.Editors var initJs = new JsInitialization(_manifestParser); var initCss = new CssInitialization(_manifestParser); var assets = new List(); - assets.AddRange(initJs.GetScriptFiles(HttpContext, Enumerable.Empty())); + assets.AddRange(initJs.OptimizeBackOfficeScriptFiles(HttpContext, Enumerable.Empty())); assets.AddRange(initCss.GetStylesheetFiles(HttpContext)); return new JArray(assets); } diff --git a/src/Umbraco.Web/Editors/BackOfficeModel.cs b/src/Umbraco.Web/Editors/BackOfficeModel.cs index 75a388ee80..9833121301 100644 --- a/src/Umbraco.Web/Editors/BackOfficeModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficeModel.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Features; namespace Umbraco.Web.Editors { + public class BackOfficeModel { public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings) diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs new file mode 100644 index 0000000000..1298575e50 --- /dev/null +++ b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Configuration; +using Umbraco.Web.Features; + +namespace Umbraco.Web.Editors +{ + public class BackOfficePreviewModel : BackOfficeModel + { + public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings) : base(features, globalSettings) + { + } + + public bool DisableDevicePreview => Features.Disabled.DisableDevicePreview; + public string PreviewExtendedHeaderView => Features.Enabled.PreviewExtendedView; + } +} diff --git a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs index e2e896f30d..a3cb1d229a 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs @@ -67,7 +67,7 @@ namespace Umbraco.Web.Editors.Binders //map the property dto collection with the culture of the current variant variant.PropertyCollectionDto = Mapper.Map( model.PersistedContent, - options => options.Items[ResolutionContextExtensions.CultureKey] = variant.Culture); + options => options.SetCulture(variant.Culture)); } //now map all of the saved values to the dto diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index b08d8826eb..1fcee6d727 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -24,6 +25,7 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Persistence.Querying; using Umbraco.Web.PublishedCache; using Umbraco.Core.Events; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Validation; using Umbraco.Web.Composing; using Umbraco.Web.Models; @@ -32,6 +34,7 @@ using Umbraco.Web._Legacy.Actions; using Constants = Umbraco.Core.Constants; using Language = Umbraco.Web.Models.ContentEditing.Language; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; @@ -54,6 +57,8 @@ namespace Umbraco.Web.Editors private readonly PropertyEditorCollection _propertyEditors; private readonly Lazy> _allLangs; + public object Domains { get; private set; } + public ContentController(IPublishedSnapshotService publishedSnapshotService, PropertyEditorCollection propertyEditors) { if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService)); @@ -227,7 +232,7 @@ namespace Umbraco.Web.Editors public ContentItemDisplay GetRecycleBin() { var apps = new List(); - apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, "recycleBin", "content"); + apps.Add(ListViewContentAppDefinition.CreateContentApp(Services.DataTypeService, _propertyEditors, "recycleBin", "content")); apps[0].Active = true; var display = new ContentItemDisplay { @@ -359,7 +364,7 @@ namespace Umbraco.Web.Editors var mapped = MapToDisplay(emptyContent); //remove the listview app if it exists - mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "childItems").ToList(); + mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); return mapped; } @@ -380,7 +385,7 @@ namespace Umbraco.Web.Editors var mapped = Mapper.Map(blueprint); //remove the listview app if it exists - mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "childItems").ToList(); + mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); return mapped; } @@ -457,10 +462,10 @@ namespace Umbraco.Web.Editors Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, string filter = "", - string cultureName = "") + string cultureName = "") // TODO it's not a NAME it's the ISO CODE { long totalChildren; - IContent[] children; + List children; if (pageNumber > 0 && pageSize > 0) { IQuery queryFilter = null; @@ -472,16 +477,14 @@ namespace Umbraco.Web.Editors } children = Services.ContentService - .GetPagedChildren( - id, (pageNumber - 1), pageSize, - out totalChildren, - orderBy, orderDirection, orderBySystemField, - queryFilter).ToArray(); + .GetPagedChildren(id, pageNumber - 1, pageSize, out totalChildren, + queryFilter, + Ordering.By(orderBy, orderDirection, cultureName, !orderBySystemField)).ToList(); } else { - children = Services.ContentService.GetChildren(id).ToArray(); - totalChildren = children.Length; + children = Services.ContentService.GetChildren(id).ToList(); + totalChildren = children.Count; } if (totalChildren == 0) @@ -494,15 +497,14 @@ namespace Umbraco.Web.Editors Mapper.Map>(content, opts => { - opts.Items[ResolutionContextExtensions.CultureKey] = cultureName; + + opts.SetCulture(cultureName); // if there's a list of property aliases to map - we will make sure to store this in the mapping context. - if (string.IsNullOrWhiteSpace(includeProperties) == false) - { - opts.Items["IncludeProperties"] = includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); - } - })); - + if (!includeProperties.IsNullOrWhiteSpace()) + opts.SetIncludedProperties(includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries)); + })) + .ToList(); // evaluate now return pagedResult; } @@ -584,6 +586,17 @@ namespace Umbraco.Web.Editors private ContentItemDisplay PostSaveInternal(ContentItemSave contentItem, Func saveMethod) { + //Recent versions of IE/Edge may send in the full clientside 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 + //uploaded files to being *only* the actual file name (as it should be). + if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) + { + foreach (var file in contentItem.UploadedFiles) + { + file.FileName = Path.GetFileName(file.FileName); + } + } + //If we've reached here it means: // * Our model has been bound // * and validated @@ -864,7 +877,7 @@ namespace Umbraco.Web.Editors var canPublish = true; //validate any mandatory variants that are not in the list - var mandatoryLangs = Mapper.Map, IEnumerable>(_allLangs.Value.Values).Where(x => x.Mandatory); + var mandatoryLangs = Mapper.Map, IEnumerable>(_allLangs.Value.Values).Where(x => x.IsMandatory); foreach (var lang in mandatoryLangs) { @@ -1112,39 +1125,87 @@ namespace Umbraco.Web.Editors /// /// Unpublishes a node with a given Id and returns the unpublished entity /// - /// The content id to unpublish - /// The culture variant for the content id to unpublish, if none specified will unpublish all variants of the content + /// The content and variants to unpublish /// - [EnsureUserPermissionForContent("id", 'U')] + [EnsureUserPermissionForContent("model.Id", 'U')] [OutgoingEditorModelEvent] - public ContentItemDisplay PostUnPublish(int id, string culture = null) + public ContentItemDisplay PostUnpublish(UnpublishContent model) { - var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id)); + var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(model.Id)); if (foundContent == null) - HandleContentNotFound(id); + HandleContentNotFound(model.Id); - var unpublishResult = Services.ContentService.Unpublish(foundContent, culture: culture, userId: Security.GetUserId().ResultOr(0)); - - var content = MapToDisplay(foundContent); - - if (!unpublishResult.Success) + var languageCount = _allLangs.Value.Count(); + if (model.Cultures.Length == 0 || model.Cultures.Length == languageCount) { - AddCancelMessage(content); - throw new HttpResponseException(Request.CreateValidationErrorResponse(content)); + //this means that the entire content item will be unpublished + var unpublishResult = Services.ContentService.Unpublish(foundContent, userId: Security.GetUserId().ResultOr(0)); + + var content = MapToDisplay(foundContent); + + if (!unpublishResult.Success) + { + AddCancelMessage(content); + throw new HttpResponseException(Request.CreateValidationErrorResponse(content)); + } + else + { + content.AddSuccessNotification( + Services.TextService.Localize("content/unpublish"), + Services.TextService.Localize("speechBubbles/contentUnpublished")); + return content; + } } else { - //fixme should have a better localized method for when we have the UnpublishResultType.SuccessMandatoryCulture status + //we only want to unpublish some of the variants + var results = new Dictionary(); + foreach(var c in model.Cultures) + { + var result = Services.ContentService.Unpublish(foundContent, culture: c, userId: Security.GetUserId().ResultOr(0)); + results[c] = result; + if (result.Result == UnpublishResultType.SuccessMandatoryCulture) + { + //if this happens, it means they are all unpublished, we don't need to continue + break; + } + } - content.AddSuccessNotification( - Services.TextService.Localize("content/unPublish"), - unpublishResult.Result == UnpublishResultType.SuccessCulture - ? Services.TextService.Localize("speechBubbles/contentVariationUnpublished", new[] { culture }) - : Services.TextService.Localize("speechBubbles/contentUnpublished")); + var content = MapToDisplay(foundContent); + //check for this status and return the correct message + if (results.Any(x => x.Value.Result == UnpublishResultType.SuccessMandatoryCulture)) + { + content.AddSuccessNotification( + Services.TextService.Localize("content/unpublish"), + Services.TextService.Localize("speechBubbles/contentMandatoryCultureUnpublished")); + return content; + } + + //otherwise add a message for each one unpublished + foreach (var r in results) + { + content.AddSuccessNotification( + Services.TextService.Localize("content/unpublish"), + Services.TextService.Localize("speechBubbles/contentCultureUnpublished", new[] { _allLangs.Value[r.Key].CultureName })); + } return content; + } + + } + + public ContentDomainsAndCulture GetCultureAndDomains(int id) + { + var nodeDomains = Services.DomainService.GetAssignedDomains(id, true).ToArray(); + var wildcard = nodeDomains.FirstOrDefault(d => d.IsWildcard); + var domains = nodeDomains.Where(d => !d.IsWildcard).Select(d => new DomainDisplay(d.DomainName, d.LanguageId.GetValueOrDefault(0))); + return new ContentDomainsAndCulture + { + Domains = domains, + Language = wildcard == null || !wildcard.LanguageId.HasValue ? "undefined" : wildcard.LanguageId.ToString() + }; } [HttpPost] diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 230fbbea36..6f42b17af6 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -28,14 +28,15 @@ namespace Umbraco.Web.Editors public class DashboardController : UmbracoApiController { public DashboardController() - { - } + { } public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger, IRuntimeState runtimeState) : base(globalSettings, umbracoContext, sqlContext, services, applicationCache, logger, profilingLogger, runtimeState) - { - } + { } + //we have just one instance of HttpClient shared for the entire application + private static readonly HttpClient HttpClient = new HttpClient(); + //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side [ValidateAngularAntiForgeryToken] public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") @@ -63,13 +64,10 @@ namespace Umbraco.Web.Editors //content is null, go get it try { - using (var web = new HttpClient()) - { - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - content = JObject.Parse(json); - result = content; - } + //fetch dashboard json and parse to JObject + var json = await HttpClient.GetStringAsync(url); + content = JObject.Parse(json); + result = content; ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); } @@ -102,17 +100,14 @@ namespace Umbraco.Web.Editors //content is null, go get it try { - using (var web = new HttpClient()) - { - //fetch remote css - content = await web.GetStringAsync(url); + //fetch remote css + content = await HttpClient.GetStringAsync(url); - //can't use content directly, modified closure problem - result = content; + //can't use content directly, modified closure problem + result = content; - //save server content for 30 mins - ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); - } + //save server content for 30 mins + ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); } catch (HttpRequestException ex) { diff --git a/src/Umbraco.Web/Editors/DashboardHelper.cs b/src/Umbraco.Web/Editors/DashboardHelper.cs index b40f289d04..75ccda1a9b 100644 --- a/src/Umbraco.Web/Editors/DashboardHelper.cs +++ b/src/Umbraco.Web/Editors/DashboardHelper.cs @@ -46,7 +46,9 @@ namespace Umbraco.Web.Editors var tabs = new List>(); var i = 1; - // The dashboard config can contain more than one area inserted by a package. + //disable packages section dashboard + if (section == "packages") return tabs; + foreach (var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) { //we need to validate access to this section diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 7f95fdde54..e11ea7cbef 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -139,7 +139,7 @@ namespace Umbraco.Web.Editors /// public IEnumerable GetPreValues(string editorAlias, int dataTypeId = -1) { - var propEd = Current.PropertyEditors[editorAlias]; + var propEd = _propertyEditors[editorAlias]; if (propEd == null) { throw new InvalidOperationException("Could not find property editor with alias " + editorAlias); @@ -290,7 +290,7 @@ namespace Umbraco.Web.Editors /// [UmbracoApplicationAuthorize( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Developer)] + Constants.Applications.Settings, Constants.Applications.Packages)] public IEnumerable GetAll() { return Services.DataTypeService @@ -307,7 +307,7 @@ namespace Umbraco.Web.Editors /// [UmbracoTreeAuthorize( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Developer)] + Constants.Applications.Settings, Constants.Applications.Packages)] public IDictionary> GetGroupedDataTypes() { var dataTypes = Services.DataTypeService @@ -340,7 +340,7 @@ namespace Umbraco.Web.Editors /// [UmbracoTreeAuthorize( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Developer)] + Constants.Applications.Settings, Constants.Applications.Packages)] public IDictionary> GetGroupedPropertyEditors() { var datatypes = new List(); @@ -373,7 +373,7 @@ namespace Umbraco.Web.Editors /// [UmbracoTreeAuthorize( Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members, - Constants.Applications.Settings, Constants.Applications.Developer)] + Constants.Applications.Settings, Constants.Applications.Packages)] public IEnumerable GetAllPropertyEditors() { return Current.PropertyEditors diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 313693c08c..fcbe4bdd4c 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -10,15 +10,18 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using System.Linq; using System.Net.Http; +using System.Net.Http.Formatting; using Umbraco.Core.Models; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using System.Web.Http.Controllers; using Umbraco.Core.Models.Entities; using Umbraco.Core.Xml; +using Umbraco.Web.Models.Mapping; using Umbraco.Web.Search; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { @@ -211,7 +214,7 @@ namespace Umbraco.Web.Editors } } - var ancestors = GetAncestors(id, type); + var ancestors = GetResultForAncestors(id, type); //if content, skip the first node for replicating NiceUrl defaults if(type == UmbracoEntityTypes.Document) { @@ -226,13 +229,7 @@ namespace Umbraco.Web.Editors }; } - [Obsolete("Use GetyById instead")] - [EditorBrowsable(EditorBrowsableState.Never)] - public EntityBasic GetByKey(Guid id, UmbracoEntityTypes type) - { - return GetResultForKey(id, type); - } - + /// /// Gets an entity by a xpath query /// @@ -588,9 +585,10 @@ namespace Umbraco.Web.Editors } } - public IEnumerable GetAncestors(int id, UmbracoEntityTypes type) + [HttpQueryStringFilter("queryStrings")] + public IEnumerable GetAncestors(int id, UmbracoEntityTypes type, FormDataCollection queryStrings) { - return GetResultForAncestors(id, type); + return GetResultForAncestors(id, type, queryStrings); } /// @@ -632,7 +630,7 @@ namespace Umbraco.Web.Editors } } - private IEnumerable GetResultForAncestors(int id, UmbracoEntityTypes entityType) + private IEnumerable GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormDataCollection queryStrings = null) { var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) @@ -672,12 +670,14 @@ namespace Umbraco.Web.Editors ids = lids.ToArray(); } + var culture = queryStrings?.GetValue("culture"); + return ids.Length == 0 ? Enumerable.Empty() : Services.EntityService.GetAll(objectType.Value, ids) .WhereNotNull() .OrderBy(x => x.Level) - .Select(Mapper.Map); + .Select(x => Mapper.Map(x, opts => { opts.SetCulture(culture);})); } //now we need to convert the unknown ones switch (entityType) diff --git a/src/Umbraco.Web/Editors/HelpController.cs b/src/Umbraco.Web/Editors/HelpController.cs index 50e6547e36..ccbbcaeee8 100644 --- a/src/Umbraco.Web/Editors/HelpController.cs +++ b/src/Umbraco.Web/Editors/HelpController.cs @@ -7,20 +7,22 @@ using System.Threading.Tasks; namespace Umbraco.Web.Editors { public class HelpController : UmbracoAuthorizedJsonController - { + { + private static HttpClient _httpClient; public async Task> GetContextHelpForPage(string section, string tree, string baseUrl = "https://our.umbraco.com") { var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree); - using (var web = new HttpClient()) - { - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - var result = JsonConvert.DeserializeObject>(json); - if (result != null) - return result; - return new List(); - } + if (_httpClient == null) + _httpClient = new HttpClient(); + + //fetch dashboard json and parse to JObject + var json = await _httpClient.GetStringAsync(url); + var result = JsonConvert.DeserializeObject>(json); + if (result != null) + return result; + + return new List(); } } diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 96019da702..a3e613670b 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; @@ -8,8 +7,6 @@ using System.Web.Http; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -56,27 +53,7 @@ namespace Umbraco.Web.Editors if (lang == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - var model = Mapper.Map(lang); - - //if there's only one language, by default it is the default - var allLangs = Services.LocalizationService.GetAllLanguages().OrderBy(x => x.Id).ToList(); - if (!lang.IsDefaultVariantLanguage) - { - if (allLangs.Count == 1) - { - model.IsDefaultVariantLanguage = true; - model.Mandatory = true; - } - else if (allLangs.All(x => !x.IsDefaultVariantLanguage)) - { - //if no language has the default flag, then the defaul language is the one with the lowest id - model.IsDefaultVariantLanguage = allLangs[0].Id == lang.Id; - model.Mandatory = allLangs[0].Id == lang.Id; - } - } - - - return model; + return Mapper.Map(lang); } /// @@ -88,16 +65,21 @@ namespace Umbraco.Web.Editors public IHttpActionResult DeleteLanguage(int id) { var language = Services.LocalizationService.GetLanguageById(id); - if (language == null) return NotFound(); - - var totalLangs = Services.LocalizationService.GetAllLanguages().Count(); - - if (language.IsDefaultVariantLanguage || totalLangs == 1) + if (language == null) { - var message = $"Language '{language.IsoCode}' is currently set to 'default' or it is the only installed language and can not be deleted."; + return NotFound(); + } + + // the service would not let us do it, but test here nevertheless + if (language.IsDefault) + { + var message = $"Language '{language.IsoCode}' is currently set to 'default' and can not be deleted."; throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); } + // service is happy deleting a language that's fallback for another language, + // will just remove it - so no need to check here + Services.LocalizationService.Delete(language); return Ok(); @@ -113,16 +95,17 @@ namespace Umbraco.Web.Editors if (!ModelState.IsValid) throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); - var found = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode); + // this is prone to race conds but the service will not let us proceed anyways + var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode); - if (found != null && language.Id != found.Id) + if (existing != null && language.Id != existing.Id) { - //someone is trying to create a language that alraedy exist + //someone is trying to create a language that already exist ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists"); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - if (found == null) + if (existing == null) { CultureInfo culture; try @@ -135,22 +118,66 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - //create it - var newLang = new Umbraco.Core.Models.Language(culture.Name) + // create it (creating a new language cannot create a fallback cycle) + var newLang = new Core.Models.Language(culture.Name) { CultureName = culture.DisplayName, - IsDefaultVariantLanguage = language.IsDefaultVariantLanguage, - Mandatory = language.Mandatory + IsDefault = language.IsDefault, + IsMandatory = language.IsMandatory, + FallbackLanguageId = language.FallbackLanguageId }; + Services.LocalizationService.Save(newLang); return Mapper.Map(newLang); } - found.Mandatory = language.Mandatory; - found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; - Services.LocalizationService.Save(found); - return Mapper.Map(found); + existing.IsMandatory = language.IsMandatory; + + // note that the service will prevent the default language from being "un-defaulted" + // but does not hurt to test here - though the UI should prevent it too + if (existing.IsDefault && !language.IsDefault) + { + ModelState.AddModelError("IsDefault", "Cannot un-default the default language."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + + existing.IsDefault = language.IsDefault; + existing.FallbackLanguageId = language.FallbackLanguageId; + + // modifying an existing language can create a fallback, verify + // note that the service will check again, dealing with race conds + if (existing.FallbackLanguageId.HasValue) + { + var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x); + if (!languages.ContainsKey(existing.FallbackLanguageId.Value)) + { + ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + if (CreatesCycle(existing, languages)) + { + ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + } + + Services.LocalizationService.Save(existing); + return Mapper.Map(existing); + } + + // see LocalizationService + private bool CreatesCycle(ILanguage language, IDictionary languages) + { + // a new language is not referenced yet, so cannot be part of a cycle + if (!language.HasIdentity) return false; + + var id = language.FallbackLanguageId; + while (true) // assuming languages does not already contains a cycle, this must end + { + if (!id.HasValue) return false; // no fallback means no cycle + if (id.Value == language.Id) return true; // back to language = cycle! + id = languages[id.Value].FallbackLanguageId; // else keep chaining + } } - } } diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs index e36fe05de3..1205226b8f 100644 --- a/src/Umbraco.Web/Editors/LogController.cs +++ b/src/Umbraco.Web/Editors/LogController.cs @@ -1,10 +1,11 @@ -using System; +using AutoMapper; +using System; using System.Collections.Generic; using System.Linq; -using AutoMapper; -using Umbraco.Web.Models.ContentEditing; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; namespace Umbraco.Web.Editors @@ -45,7 +46,7 @@ namespace Umbraco.Web.Editors var userId = Security.GetUserId().ResultOr(0); var result = Services.AuditService.GetPagedItemsByUser(userId, pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter:dateQuery); var mapped = Mapper.Map>(result); - return new PagedResult(totalRecords, pageNumber + 1, pageSize) + return new PagedResult(totalRecords, pageNumber, pageSize) { Items = MapAvatarsAndNames(mapped) }; diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index d625e3a575..3c576befb9 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -135,7 +135,6 @@ namespace Umbraco.Web.Editors var legacyPage = new global::umbraco.page(doc, _variationContextAccessor); - UmbracoContext.HttpContext.Items["pageID"] = doc.Id; UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 70aa7e4da8..635cdfaa17 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -31,9 +31,11 @@ using Umbraco.Web.UI; using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Umbraco.Core.Persistence; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Validation; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; @@ -85,7 +87,7 @@ namespace Umbraco.Web.Editors var mapped = Mapper.Map(emptyContent); //remove the listview app if it exists - mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "childItems").ToList(); + mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); return mapped; } @@ -97,7 +99,7 @@ namespace Umbraco.Web.Editors public MediaItemDisplay GetRecycleBin() { var apps = new List(); - apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, "recycleBin", "media"); + apps.Add(ListViewContentAppDefinition.CreateContentApp(Services.DataTypeService, _propertyEditors, "recycleBin", "media")); apps[0].Active = true; var display = new MediaItemDisplay { @@ -437,6 +439,17 @@ namespace Umbraco.Web.Editors [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) { + //Recent versions of IE/Edge may send in the full clientside 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 + //uploaded files to being *only* the actual file name (as it should be). + if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) + { + foreach (var file in contentItem.UploadedFiles) + { + file.FileName = Path.GetFileName(file.FileName); + } + } + //If we've reached here it means: // * Our model has been bound // * and validated @@ -686,15 +699,7 @@ namespace Umbraco.Web.Editors mediaType = result.FormData["contentTypeAlias"]; } - //TODO: make the media item name "nice" since file names could be pretty ugly, we have - // string extensions to do much of this but we'll need: - // * Pascalcase the name (use string extensions) - // * strip the file extension - // * underscores to spaces - // * probably remove 'ugly' characters - let's discuss - // All of this logic should exist in a string extensions method and be unit tested - // http://issues.umbraco.org/issue/U4-5572 - var mediaItemName = fileName; + var mediaItemName = fileName.ToFriendlyName(); var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, Security.CurrentUser.Id); @@ -908,4 +913,4 @@ namespace Umbraco.Web.Editors return hasPathAccess; } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index aa03628632..9f70c3c33b 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -26,7 +26,9 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using System.Collections.Generic; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; @@ -137,7 +139,7 @@ namespace Umbraco.Web.Editors var name = foundType != null ? foundType.Name : listName; var apps = new List(); - apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, listName, "member"); + apps.Add(ListViewContentAppDefinition.CreateContentApp(Services.DataTypeService, _propertyEditors, listName, "member")); apps[0].Active = true; var display = new MemberListDisplay diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 1c3bddb115..6111a931e3 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Editors /// A controller used for installing packages and managing all of the data in the packages section in the back office /// [PluginController("UmbracoApi")] - [UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)] + [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageInstallController : UmbracoAuthorizedJsonController { /// diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index 9c0d46cedb..6a91d20ae0 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -1,10 +1,16 @@ using System; +using System.Web; using System.Web.Mvc; +using System.Web.UI; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Web.Composing; using Umbraco.Web.Features; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.UI.JavaScript; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { @@ -13,21 +19,22 @@ namespace Umbraco.Web.Editors { private readonly UmbracoFeatures _features; private readonly IGlobalSettings _globalSettings; + private readonly IPublishedSnapshotService _publishedSnapshotService; + private readonly UmbracoContext _umbracoContext; - public PreviewController(UmbracoFeatures features, IGlobalSettings globalSettings) + public PreviewController(UmbracoFeatures features, IGlobalSettings globalSettings, IPublishedSnapshotService publishedSnapshotService, UmbracoContext umbracoContext) { _features = features; _globalSettings = globalSettings; + _publishedSnapshotService = publishedSnapshotService; + _umbracoContext = umbracoContext; } [UmbracoAuthorize(redirectToUmbracoLogin: true)] + [DisableBrowserCache] public ActionResult Index() { - var model = new BackOfficePreview - { - DisableDevicePreview = _features.Disabled.DisableDevicePreview, - PreviewExtendedHeaderView = _features.Enabled.PreviewExtendedView - }; + var model = new BackOfficePreviewModel(_features, _globalSettings); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { @@ -41,10 +48,45 @@ namespace Umbraco.Web.Editors return View(_globalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + "Index.cshtml", model); } - public ActionResult Editors(string editor) + /// + /// Returns the JavaScript file for preview + /// + /// + [MinifyJavaScriptResult(Order = 0)] + [OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)] + public JavaScriptResult Application() { - if (string.IsNullOrEmpty(editor)) throw new ArgumentNullException(nameof(editor)); - return View(_globalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + editor.Replace(".html", string.Empty) + ".cshtml"); + var files = JsInitialization.OptimizeScriptFiles(HttpContext, JsInitialization.GetPreviewInitialization()); + var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco.preview"); + + return JavaScript(result); } + + /// + /// The endpoint that is loaded within the preview iframe + /// + /// + [UmbracoAuthorize] + public ActionResult Frame(int id, string culture) + { + var user = _umbracoContext.Security.CurrentUser; + + var previewToken = _publishedSnapshotService.EnterPreview(user, id); + + Response.Cookies.Set(new HttpCookie(Constants.Web.PreviewCookieName, previewToken)); + + // use a numeric url because content may not be in cache and so .Url would fail + var query = culture.IsNullOrWhiteSpace() ? string.Empty : $"?culture={culture}"; + Response.Redirect($"../../{id}.aspx{query}", true); + + return null; + } + + ////fixme: not sure we need this anymore since there is no canvas editing - then we can remove that route too + //public ActionResult Editors(string editor) + //{ + // if (string.IsNullOrEmpty(editor)) throw new ArgumentNullException(nameof(editor)); + // return View(_globalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + editor.Replace(".html", string.Empty) + ".cshtml"); + //} } } diff --git a/src/Umbraco.Web/Editors/PublishedStatusController.cs b/src/Umbraco.Web/Editors/PublishedStatusController.cs index 087b96a241..937d3f3137 100644 --- a/src/Umbraco.Web/Editors/PublishedStatusController.cs +++ b/src/Umbraco.Web/Editors/PublishedStatusController.cs @@ -18,13 +18,13 @@ namespace Umbraco.Web.Editors public string GetPublishedStatusUrl() { if (_publishedSnapshotService is PublishedCache.XmlPublishedCache.PublishedSnapshotService) - return "views/dashboard/developer/xmldataintegrityreport.html"; + return "views/dashboard/settings/xmldataintegrityreport.html"; //if (service is PublishedCache.PublishedNoCache.PublishedSnapshotService) // return "views/dashboard/developer/nocache.html"; if (_publishedSnapshotService is PublishedCache.NuCache.PublishedSnapshotService) - return "views/dashboard/developer/nucache.html"; + return "views/dashboard/settings/nucache.html"; throw new NotSupportedException("Not supported: " + _publishedSnapshotService.GetType().FullName); } diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index d63eaa2b48..377b76f52a 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -12,6 +12,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using File = System.IO.File; +using Umbraco.Core; namespace Umbraco.Web.Editors { @@ -63,7 +64,28 @@ namespace Umbraco.Web.Editors return searchResult; } - + /// + /// This lists the RedirectUrls for a particular content item + /// Do we need to consider paging here? + /// + /// Udi of content item to retrieve RedirectUrls for + /// + [HttpGet] + public RedirectUrlSearchResult RedirectUrlsForContentItem(string contentUdi) + { + var redirectsResult = new RedirectUrlSearchResult(); + if (GuidUdi.TryParse(contentUdi, out var guidIdi)) + { + var redirectUrlService = Services.RedirectUrlService; + var redirects = redirectUrlService.GetContentRedirectUrls(guidIdi.Guid); + redirectsResult.SearchResults = Mapper.Map>(redirects).ToArray(); + //not doing paging 'yet' + redirectsResult.TotalCount = redirects.Count(); + redirectsResult.CurrentPage = 1; + redirectsResult.PageCount = 1; + } + return redirectsResult; + } [HttpPost] public IHttpActionResult DeleteRedirectUrl(Guid id) { diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs index 6412409488..ac64706292 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.HealthCheck /// /// The API controller used to display the health check info and execute any actions /// - [UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)] + [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class HealthCheckController : UmbracoAuthorizedJsonController { private readonly HealthCheckCollection _checks; diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 194fe2aac7..4e9fb27c25 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -19,6 +19,7 @@ namespace Umbraco.Web.Install { public sealed class InstallHelper { + private static HttpClient _httpClient; private readonly DatabaseBuilder _databaseBuilder; private readonly HttpContextBase _httpContext; private readonly ILogger _logger; @@ -161,16 +162,17 @@ namespace Umbraco.Web.Install internal IEnumerable GetStarterKits() { - var packages = new List(); + if (_httpClient == null) + _httpClient = new HttpClient(); + var packages = new List(); try { var requestUri = $"https://our.umbraco.com/webapi/StarterKit/Get/?umbracoVersion={UmbracoVersion.Current}"; using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) - using (var httpClient = new HttpClient()) - using (var response = httpClient.SendAsync(request).Result) { + var response = _httpClient.SendAsync(request).Result; packages = response.Content.ReadAsAsync>().Result.ToList(); } } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 8a67001cc6..0b225e158b 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -1,13 +1,15 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; +using System.Net; +using System.Net.Http; +using System.Text; using System.Web; using System.Web.Security; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Migrations.Install; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Web.Install.Models; @@ -27,6 +29,7 @@ namespace Umbraco.Web.Install.InstallSteps private readonly HttpContextBase _http; private readonly IUserService _userService; private readonly DatabaseBuilder _databaseBuilder; + private static HttpClient _httpClient; private readonly IGlobalSettings _globalSettings; public NewInstallStep(HttpContextBase http, IUserService userService, DatabaseBuilder databaseBuilder, IGlobalSettings globalSettings) @@ -79,15 +82,18 @@ namespace Umbraco.Web.Install.InstallSteps admin.Username = user.Email.Trim(); _userService.Save(admin); - - + if (user.SubscribeToNewsLetter) { + if (_httpClient == null) + _httpClient = new HttpClient(); + + var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email } }; + var content = new StringContent(JsonConvert.SerializeObject(values), Encoding.UTF8, "application/json"); + try { - var client = new System.Net.WebClient(); - var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; - client.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); + var response = _httpClient.PostAsync("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", content).Result; } catch { /* fail in silence */ } } @@ -114,11 +120,14 @@ namespace Umbraco.Web.Install.InstallSteps public override string View { - get { return RequiresExecution(null) - //the user UI + get + { + return RequiresExecution(null) + //the user UI ? "user" - //the continue install UI - : "continueinstall"; } + //the continue install UI + : "continueinstall"; + } } public override bool RequiresExecution(UserModel model) diff --git a/src/Umbraco.Web/Macros/PartialViewMacroPage.cs b/src/Umbraco.Web/Macros/PartialViewMacroPage.cs index 98b57272f7..7297fec6d3 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroPage.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroPage.cs @@ -1,6 +1,4 @@ -using System.Web.Mvc; -using Umbraco.Core.Models; -using Umbraco.Web.Models; +using Umbraco.Web.Models; using Umbraco.Web.Mvc; namespace Umbraco.Web.Macros diff --git a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs index cec9de7404..921036c75a 100644 --- a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs +++ b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs @@ -12,18 +12,20 @@ namespace Umbraco.Web.Media.EmbedProviders { //TODO: Make all Http calls async - public abstract class AbstractOEmbedProvider: IEmbedProvider + public abstract class AbstractOEmbedProvider : IEmbedProvider { + private static HttpClient _httpClient; + public virtual bool SupportsDimensions { get { return true; } } [ProviderSetting] - public string APIEndpoint{ get;set; } + public string APIEndpoint { get; set; } [ProviderSetting] - public Dictionary RequestParams{ get;set; } + public Dictionary RequestParams { get; set; } public abstract string GetMarkup(string url, int maxWidth, int maxHeight); @@ -51,9 +53,13 @@ namespace Umbraco.Web.Media.EmbedProviders public virtual string DownloadResponse(string url) { - using (var webClient = new WebClient()) + if (_httpClient == null) + _httpClient = new HttpClient(); + + using (var request = new HttpRequestMessage(HttpMethod.Get, url)) { - return webClient.DownloadString(url); + var response = _httpClient.SendAsync(request).Result; + return response.Content.ReadAsStringAsync().Result; } } diff --git a/src/Umbraco.Web/Media/Exif/ImageFile.cs b/src/Umbraco.Web/Media/Exif/ImageFile.cs index da60ea6ffa..d0c3ef7411 100644 --- a/src/Umbraco.Web/Media/Exif/ImageFile.cs +++ b/src/Umbraco.Web/Media/Exif/ImageFile.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.IO; using System.Text; +using Umbraco.Web.Media.TypeDetector; namespace Umbraco.Web.Media.Exif { @@ -118,22 +119,25 @@ namespace Umbraco.Web.Media.Exif /// The created from the file. public static ImageFile FromStream(Stream stream, Encoding encoding) { - stream.Seek (0, SeekOrigin.Begin); - byte[] header = new byte[8]; - stream.Seek (0, SeekOrigin.Begin); - if (stream.Read (header, 0, header.Length) != header.Length) - throw new NotValidImageFileException (); - // JPEG - if (header[0] == 0xFF && header[1] == 0xD8) - return new JPEGFile (stream, encoding); + if (JpegDetector.IsOfType(stream)) + { + return new JPEGFile(stream, encoding); + } // TIFF - string tiffHeader = Encoding.ASCII.GetString (header, 0, 4); - if (tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00") - return new TIFFFile (stream, encoding); + if (TIFFDetector.IsOfType(stream)) + { + return new TIFFFile(stream, encoding); + } - throw new NotValidImageFileException (); + // SVG + if (SvgDetector.IsOfType(stream)) + { + return new SvgFile(stream); + } + + throw new NotValidImageFileException(); } #endregion } diff --git a/src/Umbraco.Web/Media/Exif/ImageFileFormat.cs b/src/Umbraco.Web/Media/Exif/ImageFileFormat.cs index 0e3cb5010f..364877f7da 100644 --- a/src/Umbraco.Web/Media/Exif/ImageFileFormat.cs +++ b/src/Umbraco.Web/Media/Exif/ImageFileFormat.cs @@ -17,5 +17,9 @@ /// The file is a TIFF File. /// TIFF, + /// + /// The file is a SVG File. + /// + SVG, } } diff --git a/src/Umbraco.Web/Media/Exif/SvgFile.cs b/src/Umbraco.Web/Media/Exif/SvgFile.cs new file mode 100644 index 0000000000..8916ac0801 --- /dev/null +++ b/src/Umbraco.Web/Media/Exif/SvgFile.cs @@ -0,0 +1,37 @@ +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace Umbraco.Web.Media.Exif +{ + internal class SvgFile : ImageFile + { + public SvgFile(Stream fileStream) + { + fileStream.Position = 0; + + var document = XDocument.Load(fileStream); //if it throws an exception the ugly try catch in MediaFileSystem will catch it + + var width = document.Root?.Attributes().Where(x => x.Name == "width").Select(x => x.Value).FirstOrDefault(); + var height = document.Root?.Attributes().Where(x => x.Name == "height").Select(x => x.Value).FirstOrDefault(); + + Properties.Add(new ExifSInt(ExifTag.PixelYDimension, + height == null ? Core.Constants.Conventions.Media.DefaultSize : int.Parse(height))); + Properties.Add(new ExifSInt(ExifTag.PixelXDimension, + width == null ? Core.Constants.Conventions.Media.DefaultSize : int.Parse(width))); + + Format = ImageFileFormat.SVG; + } + + public override void Save(Stream stream) + { + } + + public override Image ToImage() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Web/Media/ImageHelper.cs b/src/Umbraco.Web/Media/ImageHelper.cs index 90eb3dc848..3fbc1e060a 100644 --- a/src/Umbraco.Web/Media/ImageHelper.cs +++ b/src/Umbraco.Web/Media/ImageHelper.cs @@ -27,10 +27,10 @@ namespace Umbraco.Web.Media /// in memory. Fallback to GDI which means loading the image in memory and thus /// use potentially large amounts of memory. public static Size GetDimensions(Stream stream) - { - //Try to load with exif + { try { + //Try to load with exif var jpgInfo = ImageFile.FromStream(stream); if (jpgInfo.Format != ImageFileFormat.Unknown @@ -44,20 +44,22 @@ namespace Umbraco.Web.Media return new Size(width, height); } } + + //we have no choice but to try to read in via GDI + using (var image = Image.FromStream(stream)) + { + + var fileWidth = image.Width; + var fileHeight = image.Height; + return new Size(fileWidth, fileHeight); + } } catch (Exception) { //We will just swallow, just means we can't read exif data, we don't want to log an error either + return new Size(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize); } - - //we have no choice but to try to read in via GDI - using (var image = Image.FromStream(stream)) - { - - var fileWidth = image.Width; - var fileHeight = image.Height; - return new Size(fileWidth, fileHeight); - } + } diff --git a/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs b/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs new file mode 100644 index 0000000000..287709f9c6 --- /dev/null +++ b/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs @@ -0,0 +1,14 @@ +using System.IO; + +namespace Umbraco.Web.Media.TypeDetector +{ + public class JpegDetector : RasterizedTypeDetector + { + public static bool IsOfType(Stream fileStream) + { + var header = GetFileHeader(fileStream); + + return header[0] == 0xff && header[1] == 0xD8; + } + } +} diff --git a/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs b/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs new file mode 100644 index 0000000000..4bb074e5a2 --- /dev/null +++ b/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Umbraco.Web.Media.TypeDetector +{ + public abstract class RasterizedTypeDetector + { + public static byte[] GetFileHeader(Stream fileStream) + { + fileStream.Seek(0, SeekOrigin.Begin); + byte[] header = new byte[8]; + fileStream.Seek(0, SeekOrigin.Begin); + + return header; + } + } +} diff --git a/src/Umbraco.Web/Media/TypeDetector/SvgDetector.cs b/src/Umbraco.Web/Media/TypeDetector/SvgDetector.cs new file mode 100644 index 0000000000..621e2237bc --- /dev/null +++ b/src/Umbraco.Web/Media/TypeDetector/SvgDetector.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Xml.Linq; + +namespace Umbraco.Web.Media.TypeDetector +{ + public class SvgDetector + { + public static bool IsOfType(Stream fileStream) + { + var document = new XDocument(); + + try + { + document = XDocument.Load(fileStream); + } + catch (System.Exception ex) + { + return false; + } + + return document.Root?.Name.LocalName == "svg"; + } + } +} diff --git a/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs b/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs new file mode 100644 index 0000000000..08126136b8 --- /dev/null +++ b/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Text; + +namespace Umbraco.Web.Media.TypeDetector +{ + public class TIFFDetector + { + public static bool IsOfType(Stream fileStream) + { + string tiffHeader = GetFileHeader(fileStream); + + return tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00"; + } + + public static string GetFileHeader(Stream fileStream) + { + var header = RasterizedTypeDetector.GetFileHeader(fileStream); + + string tiffHeader = Encoding.ASCII.GetString(header, 0, 4); + + return tiffHeader; + } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/BackOfficePreview.cs b/src/Umbraco.Web/Models/ContentEditing/BackOfficePreview.cs deleted file mode 100644 index f8633acdd0..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/BackOfficePreview.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Umbraco.Web.Models.ContentEditing -{ - /// - /// The model representing Previewing of a content item from the back office - /// - public class BackOfficePreview - { - public string PreviewExtendedHeaderView { get; set; } - - //TODO: We could potentially have a 'footer' view - public bool DisableDevicePreview { get; set; } - } -} diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs b/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs deleted file mode 100644 index f95d6ac6fd..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Runtime.Serialization; - -namespace Umbraco.Web.Models.ContentEditing -{ - /// - /// Defines a "Content App" which are editor extensions - /// - [DataContract(Name = "app", Namespace = "")] - public class ContentApp - { - [DataMember(Name = "name")] - public string Name { get; set; } - - [DataMember(Name = "alias")] - public string Alias { get; set; } - - [DataMember(Name = "icon")] - public string Icon { get; set; } - - [DataMember(Name = "view")] - public string View { get; set; } - - /// - /// The view model specific to this app - /// - [DataMember(Name = "viewModel")] - public object ViewModel { get; set; } - - /// - /// Normally reserved for Angular to deal with but in some cases this can be set on the server side - /// - [DataMember(Name = "active")] - public bool Active { get; set; } - } -} - diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentDomainsAndCulture.cs b/src/Umbraco.Web/Models/ContentEditing/ContentDomainsAndCulture.cs new file mode 100644 index 0000000000..9f0750cb7c --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/ContentDomainsAndCulture.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Umbraco.Web.Models.ContentEditing +{ + public class ContentDomainsAndCulture + { + public IEnumerable Domains { get; set; } + + public string Language { get; internal set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index 427dadb2c9..16eb3b9e87 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -4,9 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Validation; -using Umbraco.Web.WebApi; +using Newtonsoft.Json.Converters; namespace Umbraco.Web.Models.ContentEditing { @@ -22,8 +20,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "createDate")] public DateTime CreateDate { get; set; } + /// + /// Boolean indicating if this item is published or not based on it's + /// [DataMember(Name = "published")] - public bool Published { get; set; } + public bool Published => State == ContentSavedState.Published || State == ContentSavedState.PublishedPendingChanges; /// /// Determines if the content item is a draft @@ -44,6 +45,16 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "sortOrder")] public int SortOrder { get; set; } + /// + /// The saved/published state of an item + /// + /// + /// This is nullable since it's only relevant for content (non-content like media + members will be null) + /// + [DataMember(Name = "state")] + [JsonConverter(typeof(StringEnumConverter))] + public ContentSavedState? State { get; set; } + protected bool Equals(ContentItemBasic other) { return Id == other.Id; diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs index 750fdf5925..a729d51d13 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs @@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Serialization; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs index f433af58d6..2ceb682a13 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs @@ -8,21 +8,21 @@ /// /// The item isn't created yet /// - NotCreated, + NotCreated = 1, /// /// The item is saved but isn't published /// - Draft, + Draft = 2, /// /// The item is published and there are no pending changes /// - Published, + Published = 3, /// /// The item is published and there are pending changes /// - PublishedPendingChanges + PublishedPendingChanges = 4 } } diff --git a/src/Umbraco.Web/Models/ContentEditing/Language.cs b/src/Umbraco.Web/Models/ContentEditing/Language.cs index f78d2bd28f..75dd07bf09 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Language.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Language.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing @@ -20,9 +19,12 @@ namespace Umbraco.Web.Models.ContentEditing public string Name { get; set; } [DataMember(Name = "isDefault")] - public bool IsDefaultVariantLanguage { get; set; } + public bool IsDefault { get; set; } [DataMember(Name = "isMandatory")] - public bool Mandatory { get; set; } + public bool IsMandatory { get; set; } + + [DataMember(Name = "fallbackLanguageId")] + public int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs index d979ffbf4e..0118645b60 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs index ae9469989a..592bd14df5 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MemberListDisplay.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/UnpublishContent.cs b/src/Umbraco.Web/Models/ContentEditing/UnpublishContent.cs new file mode 100644 index 0000000000..22cb43f467 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/UnpublishContent.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// Used to unpublish content and variants + /// + [DataContract(Name = "unpublish", Namespace = "")] + public class UnpublishContent + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "cultures")] + public string[] Cultures { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs index 3821a56129..a199c7e60e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs @@ -1,56 +1,26 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - + // injected into ContentMapperProfile, + // maps ContentApps when mapping IContent to ContentItemDisplay internal class ContentAppResolver : IValueResolver> { - private readonly ContentApp _contentApp = new ContentApp - { - Alias = "content", - Name = "Content", - Icon = "icon-document", - View = "views/content/apps/content/content.html" - }; + private readonly ContentAppDefinitionCollection _contentAppDefinitions; - private readonly ContentApp _infoApp = new ContentApp + public ContentAppResolver(ContentAppDefinitionCollection contentAppDefinitions) { - Alias = "info", - Name = "Info", - Icon = "icon-info", - View = "views/content/apps/info/info.html" - }; - - private readonly IDataTypeService _dataTypeService; - private readonly PropertyEditorCollection _propertyEditorCollection; - - public ContentAppResolver(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection) - { - _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); - _propertyEditorCollection = propertyEditorCollection ?? throw new ArgumentNullException(nameof(propertyEditorCollection)); + _contentAppDefinitions = contentAppDefinitions; } public IEnumerable Resolve(IContent source, ContentItemDisplay destination, IEnumerable destMember, ResolutionContext context) { - var apps = new List(); - - if (source.ContentType.IsContainer) - { - //If it's a container then add the list view app and view model - apps.AppendListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "content"); - } - - apps.Add(_contentApp); - apps.Add(_infoApp); - - return apps; + return _contentAppDefinitions.GetContentAppsFor(source); } -} - + } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs deleted file mode 100644 index 8fc77beb6b..0000000000 --- a/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - internal static class ContentAppResolverExtensions - { - /// - /// Helper method to append a list view app to the content app collection - /// - /// - public static void AppendListViewApp( - this ICollection list, - IDataTypeService dataTypeService, PropertyEditorCollection propertyEditors, - string contentTypeAlias, string entityType) - { - var listViewApp = new ContentApp - { - Alias = "childItems", - Name = "Child items", - Icon = "icon-list", - View = "views/content/apps/listview/listview.html" - }; - - var customDtdName = Core.Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias; - var dtdId = Core.Constants.DataTypes.DefaultContentListView; - //first try to get the custom one if there is one - var dt = dataTypeService.GetDataType(customDtdName) - ?? dataTypeService.GetDataType(dtdId); - - if (dt == null) - { - throw new InvalidOperationException("No list view data type was found for this document type, ensure that the default list view data types exists and/or that your custom list view data type exists"); - } - - var editor = propertyEditors[dt.EditorAlias]; - if (editor == null) - { - throw new NullReferenceException("The property editor with alias " + dt.EditorAlias + " does not exist"); - } - - var listViewConfig = editor.GetConfigurationEditor().ToConfigurationEditor(dt.Configuration); - //add the entity type to the config - listViewConfig["entityType"] = entityType; - - //Override Tab Label if tabName is provided - if (listViewConfig.ContainsKey("tabName")) - { - var configTabName = listViewConfig["tabName"]; - if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false) - listViewApp.Name = configTabName.ToString(); - } - - //This is the view model used for the list view app - listViewApp.ViewModel = new List - { - new ContentPropertyDisplay - { - Alias = $"{Core.Constants.PropertyEditors.InternalGenericPropertiesPrefix}containerView", - Label = "", - Value = null, - View = editor.GetValueEditor().View, - HideLabel = true, - Config = listViewConfig - } - }; - - list.Add(listViewApp); - } - } - -} diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 5742b7b17c..a627eab184 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web.Models.Mapping { //We need to set the culture in the mapping context since this is needed to ensure that the correct property values //are resolved during the mapping - context.Items[ResolutionContextExtensions.CultureKey] = x.IsoCode; + context.Options.SetCulture(x.IsoCode); return context.Mapper.Map(source, null, context); }).ToList(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 4ce8edbdc2..8b23763763 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; @@ -31,7 +32,6 @@ namespace Umbraco.Web.Models.Mapping var contentTypeBasicResolver = new ContentTypeBasicResolver(); var defaultTemplateResolver = new DefaultTemplateResolver(); var variantResolver = new ContentVariantResolver(localizationService); - var contentSavedStateResolver = new ContentSavedStateResolver(); //FROM IContent TO ContentItemDisplay CreateMap() @@ -66,7 +66,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Segment, opt => opt.Ignore()) .ForMember(dest => dest.Language, opt => opt.Ignore()) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing(contentSavedStateResolver)) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)); //FROM IContent TO ContentItemBasic @@ -80,19 +80,60 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) .ForMember(dest => dest.Alias, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) - .ForMember(dest => dest.Name, opt => opt.ResolveUsing()); + .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()); //FROM IContent TO ContentPropertyCollectionDto //NOTE: the property mapping for cultures relies on a culture being set in the mapping context CreateMap(); } - } - internal class CultureNameResolver : IValueResolver, string> - { - public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) + /// + /// Resolves the update date for a content item/content variant + /// + private class UpdateDateResolver : IValueResolver, DateTime> { - return source.GetCultureName(context.GetCulture()); + public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) + { + // invariant = global date + if (!source.ContentType.VariesByCulture()) return source.UpdateDate; + + // variant = depends on culture + var culture = context.Options.GetCulture(); + + // if there's no culture here, the issue is somewhere else (UI, whatever) - throw! + if (culture == null) + throw new InvalidOperationException("Missing culture in mapping options."); + + // if we don't have a date for a culture, it means the culture is not available, and + // hey we should probably not be mapping it, but it's too late, return a fallback date + var date = source.GetUpdateDate(culture); + return date ?? source.UpdateDate; + } } - } + + /// + /// Resolves the name for a content item/content variant + /// + private class NameResolver : IValueResolver, string> + { + public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) + { + // invariant = only 1 name + if (!source.ContentType.VariesByCulture()) return source.Name; + + // variant = depends on culture + var culture = context.Options.GetCulture(); + + // if there's no culture here, the issue is somewhere else (UI, whatever) - throw! + if (culture == null) + throw new InvalidOperationException("Missing culture in mapping options."); + + // if we don't have a name for a culture, it means the culture is not available, and + // hey we should probably not be mapping it, but it's too late, return a fallback name + return source.CultureNames.TryGetValue(culture, out var name) && !name.IsNullOrWhiteSpace() ? name : $"(({source.Name}))"; + } + } + } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index d9506897c8..006954fcb2 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -57,17 +57,12 @@ namespace Umbraco.Web.Models.Mapping // if there's a set of property aliases specified, we will check if the current property's value should be mapped. // if it isn't one of the ones specified in 'includeProperties', we will just return the result without mapping the Value. - if (context.Options.Items.ContainsKey("IncludeProperties")) - { - if (context.Options.Items["IncludeProperties"] is IEnumerable includeProperties - && includeProperties.Contains(property.Alias) == false) - { - return result; - } - } + var includedProperties = context.Options.GetIncludedProperties(); + if (includedProperties != null && !includedProperties.Contains(property.Alias)) + return result; //Get the culture from the context which will be set during the mapping operation for each property - var culture = context.GetCulture(); + var culture = context.Options.GetCulture(); //a culture needs to be in the context for a property type that can vary if (culture == null && property.PropertyType.VariesByCulture()) diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs index 4fb4d3370a..2b0395b2c6 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs @@ -6,17 +6,39 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class ContentSavedStateResolver : IValueResolver + + /// + /// Returns the for an item + /// + /// + internal class ContentBasicSavedStateResolver : IValueResolver, ContentSavedState?> + where T : ContentPropertyBasic { - public ContentSavedState Resolve(IContent source, ContentVariantDisplay destination, ContentSavedState destMember, ResolutionContext context) + private readonly ContentSavedStateResolver _inner = new ContentSavedStateResolver(); + + public ContentSavedState? Resolve(IContent source, IContentProperties destination, ContentSavedState? destMember, ResolutionContext context) + { + return _inner.Resolve(source, destination, default, context); + } + } + + /// + /// Returns the for an item + /// + /// + internal class ContentSavedStateResolver : IValueResolver, ContentSavedState> + where T : ContentPropertyBasic + { + public ContentSavedState Resolve(IContent source, IContentProperties destination, ContentSavedState destMember, ResolutionContext context) { PublishedState publishedState; bool isEdited; + bool isCreated; if (source.ContentType.VariesByCulture()) { //Get the culture from the context which will be set during the mapping operation for each variant - var culture = context.GetCulture(); + var culture = context.Options.GetCulture(); //a culture needs to be in the context for a variant content item if (culture == null) @@ -29,6 +51,7 @@ namespace Umbraco.Web.Models.Mapping : PublishedState.Unpublished; isEdited = source.IsCultureEdited(culture); + isCreated = source.Id > 0 && source.IsCultureAvailable(culture); } else { @@ -37,10 +60,14 @@ namespace Umbraco.Web.Models.Mapping : PublishedState.Published; isEdited = source.Edited; + isCreated = source.Id > 0; } + if (!isCreated) + return ContentSavedState.NotCreated; + if (publishedState == PublishedState.Unpublished) - return isEdited && source.Id > 0 ? ContentSavedState.Draft : ContentSavedState.NotCreated; + return ContentSavedState.Draft; if (publishedState == PublishedState.Published) return isEdited ? ContentSavedState.PublishedPendingChanges : ContentSavedState.Published; diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeConfigurationFieldDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/DataTypeConfigurationFieldDisplayResolver.cs index e262753b83..cca64eb164 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeConfigurationFieldDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeConfigurationFieldDisplayResolver.cs @@ -11,10 +11,17 @@ namespace Umbraco.Web.Models.Mapping { internal class DataTypeConfigurationFieldDisplayResolver { + private readonly ILogger _logger; + + public DataTypeConfigurationFieldDisplayResolver(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + /// /// Maps pre-values in the dictionary to the values for the fields /// - internal static void MapConfigurationFields(DataTypeConfigurationFieldDisplay[] fields, IDictionary configuration) + internal static void MapConfigurationFields(ILogger logger, DataTypeConfigurationFieldDisplay[] fields, IDictionary configuration) { if (fields == null) throw new ArgumentNullException(nameof(fields)); if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -24,8 +31,12 @@ namespace Umbraco.Web.Models.Mapping { if (configuration.TryGetValue(field.Key, out var value)) field.Value = value; - else // weird - just leave the field without a value - but warn - Current.Logger.Warn("Could not find a value for configuration field '{ConfigField}'", field.Key); + else + { + // weird - just leave the field without a value - but warn + logger.Warn("Could not find a value for configuration field '{ConfigField}'", field.Key); + } + } } @@ -45,7 +56,7 @@ namespace Umbraco.Web.Models.Mapping var fields = configurationEditor.Fields.Select(Mapper.Map).ToArray(); var configurationDictionary = configurationEditor.ToConfigurationEditor(dataType.Configuration); - MapConfigurationFields(fields, configurationDictionary); + MapConfigurationFields(_logger, fields, configurationDictionary); return fields; } diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs index 92a0be1346..b74d563e88 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using AutoMapper; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -16,11 +17,11 @@ namespace Umbraco.Web.Models.Mapping /// internal class DataTypeMapperProfile : Profile { - public DataTypeMapperProfile(PropertyEditorCollection propertyEditors) + public DataTypeMapperProfile(PropertyEditorCollection propertyEditors, ILogger logger) { // create, capture, cache var availablePropertyEditorsResolver = new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content); - var configurationDisplayResolver = new DataTypeConfigurationFieldDisplayResolver(); + var configurationDisplayResolver = new DataTypeConfigurationFieldDisplayResolver(logger); var databaseTypeResolver = new DatabaseTypeResolver(); CreateMap(); @@ -119,7 +120,7 @@ namespace Umbraco.Web.Models.Mapping var defaultConfiguration = configurationEditor.DefaultConfiguration; if (defaultConfiguration != null) - DataTypeConfigurationFieldDisplayResolver.MapConfigurationFields(fields, defaultConfiguration); + DataTypeConfigurationFieldDisplayResolver.MapConfigurationFields(logger, fields, defaultConfiguration); return fields; }); diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs index 917f61a429..bfabfd799a 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs @@ -25,6 +25,7 @@ namespace Umbraco.Web.Models.Mapping var contentTypeUdiResolver = new ContentTypeUdiResolver(); CreateMap() + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key))) .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => GetContentTypeIcon(src))) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) @@ -179,5 +180,33 @@ namespace Umbraco.Web.Models.Mapping CreateMap, IEnumerable>() .ConvertUsing(results => results.Select(Mapper.Map).ToList()); } + + /// + /// Resolves the name for a content item/content variant + /// + private class NameResolver : IValueResolver + { + public string Resolve(EntitySlim source, EntityBasic destination, string destMember, ResolutionContext context) + { + if (!(source is DocumentEntitySlim doc)) + return source.Name; + + // invariant = only 1 name + if (!doc.Variations.VariesByCulture()) return source.Name; + + // variant = depends on culture + var culture = context.Options.GetCulture(); + + // if there's no culture here, the issue is somewhere else (UI, whatever) - throw! + if (culture == null) + //throw new InvalidOperationException("Missing culture in mapping options."); + // fixme we should throw, but this is used in various places that won't set a culture yet + return source.Name; + + // if we don't have a name for a culture, it means the culture is not available, and + // hey we should probably not be mapping it, but it's too late, return a fallback name + return doc.CultureNames.TryGetValue(culture, out var name) && !name.IsNullOrWhiteSpace() ? name : $"(({source.Name}))"; + } + } } } diff --git a/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs b/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs new file mode 100644 index 0000000000..e704272ddb --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs @@ -0,0 +1,45 @@ +using AutoMapper; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Provides extension methods for AutoMapper's . + /// + internal static class MappingOperationOptionsExtensions + { + private const string CultureKey = "MappingOperationOptions.Culture"; + private const string IncludedPropertiesKey = "MappingOperationOptions.IncludeProperties"; + + /// + /// Gets the context culture. + /// + public static string GetCulture(this IMappingOperationOptions options) + { + return options.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null; + } + + /// + /// Sets a context culture. + /// + public static void SetCulture(this IMappingOperationOptions options, string culture) + { + options.Items[CultureKey] = culture; + } + + /// + /// Get included properties. + /// + public static string[] GetIncludedProperties(this IMappingOperationOptions options) + { + return options.Items.TryGetValue(IncludedPropertiesKey, out var obj) && obj is string[] s ? s : null; + } + + /// + /// Sets included properties. + /// + public static void SetIncludedProperties(this IMappingOperationOptions options, string[] properties) + { + options.Items[IncludedPropertiesKey] = properties; + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs b/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs index 6880aff9a8..caaaacc5f2 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs @@ -1,57 +1,26 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { + // injected into ContentMapperProfile, + // maps ContentApps when mapping IMedia to MediaItemDisplay internal class MediaAppResolver : IValueResolver> { - private static readonly ContentApp _contentApp = new ContentApp - { - Alias = "content", - Name = "Content", - Icon = "icon-document", - View = "views/media/apps/content/content.html" - }; + private readonly ContentAppDefinitionCollection _contentAppDefinitions; - private static readonly ContentApp _infoApp = new ContentApp + public MediaAppResolver(ContentAppDefinitionCollection contentAppDefinitions) { - Alias = "info", - Name = "Info", - Icon = "icon-info", - View = "views/media/apps/info/info.html" - }; - - private readonly IDataTypeService _dataTypeService; - private readonly PropertyEditorCollection _propertyEditorCollection; - - public MediaAppResolver(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection) - { - _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); - _propertyEditorCollection = propertyEditorCollection ?? throw new ArgumentNullException(nameof(propertyEditorCollection)); + _contentAppDefinitions = contentAppDefinitions; } public IEnumerable Resolve(IMedia source, MediaItemDisplay destination, IEnumerable destMember, ResolutionContext context) { - var apps = new List(); - - if (source.ContentType.IsContainer || source.ContentType.Alias == Core.Constants.Conventions.MediaTypes.Folder) - { - apps.AppendListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "media"); - } - else - { - apps.Add(_contentApp); - } - - apps.Add(_infoApp); - - return apps; + return _contentAppDefinitions.GetContentAppsFor(source); } } - } diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index caa9f29e70..c07d66b6e2 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.TreeNodeUrl, opt => opt.ResolveUsing(contentTreeNodeUrlResolver)) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs index 22d499f3d6..f64d8dc529 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.MembershipScenario, opt => opt.ResolveUsing(src => membershipScenarioMappingResolver.Resolve(src))) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -91,7 +91,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -115,7 +115,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Path, opt => opt.Ignore()) .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(ContentSavedState.Draft)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs b/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs deleted file mode 100644 index 27f00ce91c..0000000000 --- a/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AutoMapper; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Extension methods for AutoMapper's - /// - internal static class ResolutionContextExtensions - { - public const string CultureKey = "ContextMapper.Culture"; - - /// - /// Returns the language Id in the mapping context if one is found - /// - /// - /// - public static string GetCulture(this ResolutionContext resolutionContext) - { - if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null; - - if (obj is string s) - return s; - - return null; - } - } -} - diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 47e4b3d872..d0218b6639 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -1,4 +1,8 @@ -using Umbraco.Core.Models.PublishedContent; +using System; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; namespace Umbraco.Web.Models.PublishedContent { @@ -7,86 +11,283 @@ namespace Umbraco.Web.Models.PublishedContent /// public class PublishedValueFallback : IPublishedValueFallback { - // this is our default implementation - // kinda reproducing what was available in v7 + private readonly ILocalizationService _localizationService; + private readonly IVariationContextAccessor _variationContextAccessor; - /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + /// + /// Initializes a new instance of the class. + /// + public PublishedValueFallback(ServiceContext serviceContext, IVariationContextAccessor variationContextAccessor) { - // no fallback here - return defaultValue; + _localizationService = serviceContext.LocalizationService; + _variationContextAccessor = variationContextAccessor; } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value) { - // no fallback here - return defaultValue; + return TryGetValue(property, culture, segment, fallback, defaultValue, out value); } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - // no fallback here - return defaultValue; + _variationContextAccessor.ContextualizeVariation(property.PropertyType.Variations, ref culture, ref segment); + + foreach (var f in fallback) + { + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(property, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "property"); + } + } + + value = defaultValue; + return false; } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) { - // no fallback here - return defaultValue; + return TryGetValue(content, alias, culture, segment, fallback, defaultValue, out value); } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - // no fallback here - if (!recurse) return defaultValue; + var propertyType = content.ContentType.GetPropertyType(alias); + if (propertyType == null) + { + value = default; + return false; + } + _variationContextAccessor.ContextualizeVariation(propertyType.Variations, ref culture, ref segment); + foreach (var f in fallback) + { + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "element"); + } + } + + value = defaultValue; + return false; + } + + /// + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, recurse); + return TryGetValue(content, alias, culture, segment, fallback, defaultValue, out value); } /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) + public virtual bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - // no fallback here - if (!recurse) return defaultValue; + var propertyType = content.ContentType.GetPropertyType(alias); + if (propertyType == null) + { + value = default; + return false; + } + _variationContextAccessor.ContextualizeVariation(propertyType.Variations, ref culture, ref segment); - // otherwise, implement recursion as it was implemented in PublishedContentBase + // note: we don't support "recurse & language" which would walk up the tree, + // looking at languages at each level - should someone need it... they'll have + // to implement it. - // fixme caching? - // - // all caches were using PublishedContentBase.GetProperty(alias, recurse) to get the property, - // then, - // NuCache.PublishedContent was storing the property in GetAppropriateCache() with key "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; - // XmlPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - // DictionaryPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - // - // at the moment, caching has been entirely removed, until we better understand caching + fallback + foreach (var f in fallback) + { + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + case Fallback.Ancestors: + if (TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "content"); + } + } + value = defaultValue; + return false; + } + + private NotSupportedException NotSupportedFallbackMethod(int fallback, string level) + { + return new NotSupportedException($"Fallback {GetType().Name} does not support fallback code '{fallback}' at {level} level."); + } + + // tries to get a value, recursing the tree + private static bool TryGetValueWithRecursiveFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + { IPublishedProperty property = null; // if we are here, content's property has no value IPublishedProperty noValueProperty = null; do { content = content.Parent; property = content?.GetProperty(alias); - if (property != null) noValueProperty = property; - } while (content != null && (property == null || property.HasValue(culture, segment) == false)); + if (property != null) + { + noValueProperty = property; + } + } + while (content != null && (property == null || property.HasValue(culture, segment) == false)); // if we found a content with the property having a value, return that property value if (property != null && property.HasValue(culture, segment)) - return property.Value(culture, segment); + { + value = property.Value(culture, segment); + return true; + } // if we found a property, even though with no value, return that property value // because the converter may want to handle the missing value. ie if defaultValue is default, // either specified or by default, the converter may want to substitute something else. if (noValueProperty != null) - return noValueProperty.Value(culture, segment, defaultValue: defaultValue); + { + value = noValueProperty.Value(culture, segment, defaultValue: defaultValue); + return true; + } - // else return default - return defaultValue; + value = defaultValue; + return false; + } + + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (property.HasValue(culture2, segment)) + { + value = property.Value(culture2, segment); + return true; + } + + language = language2; + } + } + + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedElement content, string alias, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (content.HasValue(alias, culture2, segment)) + { + value = content.Value(alias, culture2, segment); + return true; + } + + language = language2; + } + } + + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + // fixme + // _localizationService.GetXxx() is expensive, it deep clones objects + // we want _localizationService.GetReadOnlyXxx() returning IReadOnlyLanguage which cannot be saved back = no need to clone + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (content.HasValue(alias, culture2, segment)) + { + value = content.Value(alias, culture2, segment); + return true; + } + + language = language2; + } } } } diff --git a/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs b/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs index 221346cd23..65fc4ed6f3 100644 --- a/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs +++ b/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs @@ -13,22 +13,18 @@ namespace Umbraco.Web.Models.Trees /// /// The alias. /// The name. - /// The icon. /// The sort order. public ApplicationAttribute(string alias, string name, - string icon, int sortOrder = 0) { Alias = alias; Name = name; - Icon = icon; SortOrder = sortOrder; } public string Alias { get; private set; } public string Name { get; private set; } - public string Icon { get; private set; } public int SortOrder { get; private set; } } } diff --git a/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs b/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs index 002b96bc64..aa8e0cfda6 100644 --- a/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs +++ b/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs @@ -7,31 +7,31 @@ namespace Umbraco.Web.Models.Trees // the application.config. On app startup, Umbraco will look for any // unregistered classes with an ApplicationAttribute and add them to the cache - [Application(Constants.Applications.Content, "Content", ".traycontent")] + [Application(Constants.Applications.Content, "Content")] public class ContentApplicationDefinition : IApplication { } - [Application(Constants.Applications.Media, "Media", ".traymedia", sortOrder: 1)] + [Application(Constants.Applications.Media, "Media", sortOrder: 1)] public class MediaApplicationDefinition : IApplication { } - [Application(Constants.Applications.Settings, "Settings", ".traysettings", sortOrder: 2)] + [Application(Constants.Applications.Settings, "Settings", sortOrder: 2)] public class SettingsApplicationDefinition : IApplication { } - [Application(Constants.Applications.Developer, "Developer", ".traydeveloper", sortOrder: 3)] - public class DeveloperApplicationDefinition : IApplication + [Application(Constants.Applications.Packages, "Packages", sortOrder: 3)] + public class PackagesApplicationDefinition : IApplication { } - [Application(Constants.Applications.Users, "Users", ".trayusers", sortOrder: 4)] + [Application(Constants.Applications.Users, "Users", sortOrder: 4)] public class UsersApplicationDefinition : IApplication { } - [Application(Constants.Applications.Members, "Members", ".traymember", sortOrder: 5)] + [Application(Constants.Applications.Members, "Members", sortOrder: 5)] public class MembersApplicationDefinition : IApplication { } - [Application(Constants.Applications.Translation, "Translation", ".traytranslation", sortOrder: 6)] + [Application(Constants.Applications.Translation, "Translation", sortOrder: 6)] public class TranslationApplicationDefinition : IApplication { } } diff --git a/src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs b/src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs index 73329276af..9f8ca9b84d 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs +++ b/src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs @@ -54,7 +54,7 @@ /// Sets the node style to show that it is has unpublished versions (but is currently published) /// /// - public static void SetHasUnpublishedVersionStyle(this TreeNode treeNode) + public static void SetHasPendingVersionStyle(this TreeNode treeNode) { if (treeNode.CssClasses.Contains("has-unpublished-version") == false) { diff --git a/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs b/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs index bcf7b34b4f..380ec4cd4e 100644 --- a/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs +++ b/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs @@ -9,9 +9,9 @@ namespace Umbraco.Web.Mvc /// public class DisableBrowserCacheAttribute : ActionFilterAttribute { - public override void OnActionExecuted(ActionExecutedContext filterContext) + public override void OnResultExecuting(ResultExecutingContext filterContext) { - base.OnActionExecuted(filterContext); + base.OnResultExecuting(filterContext); // could happens if exception (but afaik this wouldn't happen in MVC) if (filterContext.HttpContext == null || filterContext.HttpContext.Response == null || @@ -20,6 +20,18 @@ namespace Umbraco.Web.Mvc return; } + if (filterContext.IsChildAction) + { + return; + } + + if (filterContext.HttpContext.Response.StatusCode != 200) + { + return; + } + + filterContext.HttpContext.Response.Cache.SetLastModified(DateTime.Now); + filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false); filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); filterContext.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero); filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); diff --git a/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs b/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs deleted file mode 100644 index a874d93d7a..0000000000 --- a/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Web; -using System.Web.Mvc; - -namespace Umbraco.Web.Mvc -{ - /// - /// Will ensure that client-side cache does not occur by sending the correct response headers - /// - public class DisableClientCacheAttribute : ActionFilterAttribute - { - public override void OnResultExecuting(ResultExecutingContext filterContext) - { - if (filterContext.IsChildAction) base.OnResultExecuting(filterContext); - - filterContext.HttpContext.Response.Cache.SetExpires(DateTime.Now.AddDays(-10)); - filterContext.HttpContext.Response.Cache.SetLastModified(DateTime.Now); - filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false); - filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); - filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); - filterContext.HttpContext.Response.Cache.SetNoStore(); - - base.OnResultExecuting(filterContext); - } - } -} diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs index 7afddbf8de..507251043b 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs @@ -8,6 +8,38 @@ namespace Umbraco.Web.PropertyEditors /// public class ListViewConfiguration { + public ListViewConfiguration() + { + // initialize defaults + + PageSize = 10; + DisplayAtTabNumber = 1; + OrderBy = "SortOrder"; + OrderDirection = "asc"; + + BulkActionPermissions = new BulkActionPermissionSettings + { + AllowBulkPublish = true, + AllowBulkUnpublish = true, + AllowBulkCopy = true, + AllowBulkMove = true, + AllowBulkDelete = true + }; + + Layouts = new[] + { + new Layout { Name = "List", Icon = "icon-list", IsSystem = 1, Selected = true, Path = "views/propertyeditors/listview/layouts/list/list.html" }, + new Layout { Name = "grid", Icon = "icon-thumbnails-small", IsSystem = 1, Selected = true, Path = "views/propertyeditors/listview/layouts/grid/grid.html" } + }; + + IncludeProperties = new [] + { + new Property { Alias = "sortOrder", Header = "Sort order", IsSystem = 1 }, + new Property { Alias = "updateDate", Header = "Last edited", IsSystem = 1 }, + new Property { Alias = "owner", Header = "Created by", IsSystem = 1 } + }; + } + [ConfigurationField("pageSize", "Page Size", "number", Description = "Number of items per page")] public int PageSize { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs index a94945df0b..8239c981d0 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewConfigurationEditor.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { @@ -7,36 +6,5 @@ namespace Umbraco.Web.PropertyEditors /// Represents the configuration editor for the listview value editor. /// public class ListViewConfigurationEditor : ConfigurationEditor - { - public override IDictionary DefaultConfiguration => new Dictionary - { - {"pageSize", "10"}, - {"displayAtTabNumber", "1"}, - {"orderBy", "SortOrder"}, - {"orderDirection", "asc"}, - { - "includeProperties", new[] - { - new {alias = "sortOrder", header = "Sort order", isSystem = 1}, - new {alias = "updateDate", header = "Last edited", isSystem = 1}, - new {alias = "owner", header = "Created by", isSystem = 1} - } - }, - { - "layouts", new[] - { - new {name = "List", path = "views/propertyeditors/listview/layouts/list/list.html", icon = "icon-list", isSystem = 1, selected = true}, - new {name = "Grid", path = "views/propertyeditors/listview/layouts/grid/grid.html", icon = "icon-thumbnails-small", isSystem = 1, selected = true} - } - }, - {"bulkActionPermissions", new - { - allowBulkPublish = true, - allowBulkUnpublish = true, - allowBulkCopy = true, - allowBulkMove = false, - allowBulkDelete = true - }} - }; - } -} \ No newline at end of file + { } +} diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 4cb8fde97a..9739b7a30b 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -272,6 +272,9 @@ namespace Umbraco.Web.PropertyEditors public IEnumerable Validate(object rawValue, string valueType, object dataTypeConfiguration) { + if (rawValue == null) + yield break; + var value = JsonConvert.DeserializeObject>(rawValue.ToString()); if (value == null) yield break; diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs b/src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs index a773dbc7d2..e00252b076 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/TextAreaConfiguration.cs @@ -9,5 +9,8 @@ namespace Umbraco.Web.PropertyEditors { [ConfigurationField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")] public int MaxChars { get; set; } + + [ConfigurationField("rows", "Number of rows", "number", Description = "If empty - 10 rows would be set as the default value")] + public int Rows { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 20ffc5db68..0612c85020 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs index f2873e9466..802f42ed9e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs @@ -27,9 +27,16 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } public override Type GetPropertyValueType(PublishedPropertyType propertyType) - => IsMultipleDataType(propertyType.DataType) - ? typeof (IEnumerable) - : typeof (IPublishedContent); + { + var isMultiple = IsMultipleDataType(propertyType.DataType); + var isOnlyImages = IsOnlyImagesDataType(propertyType.DataType); + + // hard-coding "image" here but that's how it works at UI level too + + return isMultiple + ? (isOnlyImages ? typeof(IEnumerable<>).MakeGenericType(ModelType.For("image")) : typeof(IEnumerable)) + : (isOnlyImages ? ModelType.For("image") : typeof(IPublishedContent)); + } public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; @@ -40,6 +47,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return config.Multiple; } + private bool IsOnlyImagesDataType(PublishedDataType dataType) + { + var config = ConfigurationEditor.ConfigurationAs(dataType.Configuration); + return config.OnlyImages; + } + public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index 50ebe35d11..ca5489ac1f 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -83,10 +83,21 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var guidUdi = udi as GuidUdi; if (guidUdi == null) continue; - var multiNodeTreePickerItem = - GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, id => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(guidUdi.Guid)) - ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, id => _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(guidUdi.Guid)) - ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, id => _publishedSnapshotAccessor.PublishedSnapshot.Members.GetByProviderKey(guidUdi.Guid)); + + IPublishedContent multiNodeTreePickerItem = null; + switch (udi.EntityType) + { + case Constants.UdiEntityType.Document: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, id => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(guidUdi.Guid)); + break; + case Constants.UdiEntityType.Media: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, id => _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(guidUdi.Guid)); + break; + case Constants.UdiEntityType.Member: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, id => _publishedSnapshotAccessor.PublishedSnapshot.Members.GetByProviderKey(guidUdi.Guid)); + break; + } + if (multiNodeTreePickerItem != null) { multiNodeTreePicker.Add(multiNodeTreePickerItem); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 7cd631d663..fee6ee8f8a 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -55,19 +55,18 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { using (_proflog.DebugDuration($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) { - var value = (string)inter; - if (string.IsNullOrWhiteSpace(value)) return null; - - var objects = JsonConvert.DeserializeObject>(value); - if (objects.Count == 0) - return Enumerable.Empty(); - var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.ContentTypes; var elements = contentTypes.Length > 1 ? new List() : PublishedModelFactory.CreateModelList(contentTypes[0].Alias); + var value = (string)inter; + if (string.IsNullOrWhiteSpace(value)) return elements; + + var objects = JsonConvert.DeserializeObject>(value); + if (objects.Count == 0) return elements; + foreach (var sourceObject in objects) { var element = ConvertToElement(sourceObject, referenceCacheLevel, preview); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 70c4ca0d0b..74a5fb313c 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -74,7 +74,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); // ensure string is parsed for macros and macros are executed correctly diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 13e45bb286..5fe731ffb3 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 8dd3bb8dc7..132c4b6d59 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -90,7 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // determines whether a property has value public override bool HasValue(string culture = null, string segment = null) { - ContextualizeVariation(ref culture, ref segment); + _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment); var value = GetSourceValue(culture, segment); var hasValue = PropertyType.IsValue(value, PropertyValueLevel.Source); @@ -194,7 +194,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override object GetSourceValue(string culture = null, string segment = null) { - ContextualizeVariation(ref culture, ref segment); + _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment); if (culture == "" && segment == "") return _sourceValue; @@ -206,19 +206,9 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - private void ContextualizeVariation(ref string culture, ref string segment) - { - if (culture != null && segment != null) return; - - // use context values - var publishedVariationContext = _content.VariationContextAccessor?.VariationContext; - if (culture == null) culture = _variations.VariesByCulture() ? publishedVariationContext?.Culture : ""; - if (segment == null) segment = _variations.VariesBySegment() ? publishedVariationContext?.Segment : ""; - } - public override object GetValue(string culture = null, string segment = null) { - ContextualizeVariation(ref culture, ref segment); + _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment); object value; lock (_locko) @@ -239,7 +229,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override object GetXPathValue(string culture = null, string segment = null) { - ContextualizeVariation(ref culture, ref segment); + _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment); lock (_locko) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 5bcb2f70d4..8786753e4f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -303,7 +303,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var kits = _dataSource.GetAllContentSources(scope); _contentStore.SetAll(kits); sw.Stop(); - _logger.Debug("Loaded content from database ({ElapsedMilliseconds}ms)", sw.ElapsedMilliseconds); + _logger.Debug("Loaded content from database ({Duration}ms)", sw.ElapsedMilliseconds); } private void LoadContentFromLocalDbLocked(IScope scope) @@ -317,7 +317,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var kits = _localContentDb.Select(x => x.Value).OrderBy(x => x.Node.Level); _contentStore.SetAll(kits); sw.Stop(); - _logger.Debug("Loaded content from local db ({ElapsedMilliseconds}ms)", sw.ElapsedMilliseconds); + _logger.Debug("Loaded content from local db ({Duration}ms)", sw.ElapsedMilliseconds); } // keep these around - might be useful @@ -370,7 +370,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var kits = _dataSource.GetAllMediaSources(scope); _mediaStore.SetAll(kits); sw.Stop(); - _logger.Debug("Loaded media from database ({ElapsedMilliseconds}ms)", sw.ElapsedMilliseconds); + _logger.Debug("Loaded media from database ({Duration}ms)", sw.ElapsedMilliseconds); } private void LoadMediaFromLocalDbLocked(IScope scope) @@ -384,7 +384,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var kits = _localMediaDb.Select(x => x.Value); _mediaStore.SetAll(kits); sw.Stop(); - _logger.Debug("Loaded media from local db ({ElapsedMilliseconds}ms)", sw.ElapsedMilliseconds); + _logger.Debug("Loaded media from local db ({Duration}ms)", sw.ElapsedMilliseconds); } // keep these around - might be useful @@ -1196,7 +1196,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var (culture, name) in names) { - cultureData[culture] = new CultureVariation { Name = name, Date = content.GetCultureDate(culture) ?? DateTime.MinValue }; + cultureData[culture] = new CultureVariation { Name = name, Date = content.GetUpdateDate(culture) ?? DateTime.MinValue }; } //the dictionary that will be serialized @@ -1277,7 +1277,7 @@ WHERE cmsContentNu.nodeId IN ( long total; do { - var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = new List(); foreach (var c in descendants) { @@ -1344,7 +1344,7 @@ WHERE cmsContentNu.nodeId IN ( long total; do { - var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => GetDto(m, false)).ToArray(); db.BulkInsertRecords(items); processed += items.Length; @@ -1402,7 +1402,7 @@ WHERE cmsContentNu.nodeId IN ( long total; do { - var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => GetDto(m, false)).ToArray(); db.BulkInsertRecords(items); processed += items.Length; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index 5b640f13e5..5fa89e3f7b 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -1746,7 +1746,7 @@ WHERE cmsContentXml.nodeId IN ( long total; do { - var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); const bool published = true; // contentXml contains published content! var items = descendants.Select(c => new ContentXmlDto { NodeId = c.Id, Xml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, c, published).ToDataString() }).ToArray(); @@ -1819,7 +1819,7 @@ WHERE cmsPreviewXml.nodeId IN ( { // .GetPagedResultsByQuery implicitely adds ({Constants.DatabaseSchema.Tables.Document}.newest = 1) which // is what we want for preview (ie latest version of a content, published or not) - var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); const bool published = true; // previewXml contains edit content! var items = descendants.Select(c => new PreviewXmlDto { @@ -1892,7 +1892,7 @@ WHERE cmsContentXml.nodeId IN ( long total; do { - var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = EntityXmlSerializer.Serialize(_serviceContext.MediaService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); @@ -1961,7 +1961,7 @@ WHERE cmsContentXml.nodeId IN ( long total; do { - var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = EntityXmlSerializer.Serialize(_serviceContext.DataTypeService, _serviceContext.LocalizationService, m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index dc70e7c348..f0ddf62074 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -186,30 +186,30 @@ namespace Umbraco.Web #region Value /// - /// Recursively the value of a content's property identified by its alias, if it exists, otherwise a default value. + /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. /// /// The content. /// The property alias. /// The variation language. /// The variation segment. - /// A value indicating whether to recurse. + /// Optional fallback strategy. /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. - /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, returns . - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property?.GetValue(culture, segment); } #endregion @@ -217,35 +217,35 @@ namespace Umbraco.Web #region Value /// - /// Recursively gets the value of a content's property identified by its alias, converted to a specified type. + /// Gets the value of a content's property identified by its alias, converted to a specified type. /// /// The target property type. /// The content. /// The property alias. /// The variation language. /// The variation segment. + /// Optional fallback strategy. /// The default value. - /// A value indicating whether to recurse. /// The value of the content's property identified by the alias, converted to the specified type. - /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns default(T). - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(culture, segment); } // fixme - .Value() refactoring - in progress - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", int fallback = 0) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs deleted file mode 100644 index fcbfc7f431..0000000000 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Web -{ - /// - /// Provides extension methods for IPublishedProperty. - /// - public static class PublishedPropertyExtension - { - // see notes in PublishedElementExtensions - // - private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; - - #region Value - - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) - { - if (property.HasValue(culture, segment)) - return property.GetValue(culture, segment); - - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); - } - - #endregion - - #region Value - - public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) - { - // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) - // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue - // comes right after property and conflicts with culture when T is string - so we're just not doing it - if defaultValue is - // default, whether specified or not, we give a chance to the converter - // - //if (!property.HasValue(culture, segment) && 'defaultValue is explicitely specified') return defaultValue; - - // give the converter a chance to handle the default value differently - // eg for IEnumerable it may return Enumerable.Empty instead of null - - var value = property.GetValue(culture, segment); - - // if value is null (strange but why not) it still is OK to call TryConvertTo - // because it's an extension method (hence no NullRef) which will return a - // failed attempt. So, no need to care for value being null here. - - // if already the requested type, return - if (value is T variable) return variable; - - // if can convert to requested type, return - var convert = value.TryConvertTo(); - if (convert.Success) return convert.Result; - - // at that point, the code tried with the raw value - // that makes no sense because it sort of is unpredictable, - // you wouldn't know when the converters run or don't run. - // so, it's commented out now. - - // try with the raw value - //var source = property.ValueSource; - //if (source is string) source = TextValueConverterHelper.ParseStringValueSource((string)source); - //if (source is T) return (T)source; - //convert = source.TryConvertTo(); - //if (convert.Success) return convert.Result; - - return defaultValue; - } - - #endregion - } -} diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 945270cb9e..cd6ede9a7c 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -98,6 +99,7 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. + /// Optional fallback strategy. /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// @@ -106,14 +108,21 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property?.GetValue(culture, segment); } #endregion @@ -128,6 +137,7 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. + /// Optional fallback strategy. /// The default value. /// The value of the content's property identified by the alias, converted to the specified type. /// @@ -136,14 +146,21 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(culture, segment); } #endregion diff --git a/src/Umbraco.Web/PublishedPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs new file mode 100644 index 0000000000..b431f24828 --- /dev/null +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web +{ + /// + /// Provides extension methods for IPublishedProperty. + /// + public static class PublishedPropertyExtension + { + // see notes in PublishedElementExtensions + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + + #region Value + + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) + { + if (property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return PublishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var value) + ? value + : property.GetValue(culture, segment); // give converter a chance to return it's own vision of "no value" + } + + #endregion + + #region Value + + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) + { + if (property.HasValue(culture, segment)) + { + // we have a value + // try to cast or convert it + var value = property.GetValue(culture, segment); + if (value is T valueAsT) return valueAsT; + var valueConverted = value.TryConvertTo(); + if (valueConverted) return valueConverted.Result; + + // cannot cast nor convert the value, nothing we can return but 'default' + // note: we don't want to fallback in that case - would make little sense + return default; + } + + // we don't have a value, try fallback + if (PublishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) + return fallbackValue; + + // we don't have a value - neither direct nor fallback + // give a chance to the converter to return something (eg empty enumerable) + var noValue = property.GetValue(culture, segment); + if (noValue is T noValueAsT) return noValueAsT; + var noValueConverted = noValue.TryConvertTo(); + if (noValueConverted) return noValueConverted.Result; + + // cannot cast noValue nor convert it, nothing we can return but 'default' + return default; + } + + #endregion + } +} diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs index 447d4e34af..8360ad7e38 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; +using System.Globalization; namespace Umbraco.Web.Routing { @@ -53,6 +54,13 @@ namespace Umbraco.Web.Routing if (node != null) { + //if we have a node, check if we have a culture in the query string + if (frequest.UmbracoContext.HttpContext.Request.QueryString.ContainsKey("culture")) + { + //we're assuming it will match a culture, if an invalid one is passed in, an exception will throw (there is no TryGetCultureInfo method), i think this is ok though + frequest.Culture = CultureInfo.GetCultureInfo(frequest.UmbracoContext.HttpContext.Request.QueryString["culture"]); + } + frequest.PublishedContent = node; _logger.Debug("Found node with id={PublishedContentId}", frequest.PublishedContent.Id); } diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs index d50951161c..ee495b6d7d 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing if (redirectUrl == null) { - _logger.Debug("No match for route: '{Route}'", route); + _logger.Debug("No match for route: {Route}", route); return false; } @@ -56,11 +56,11 @@ namespace Umbraco.Web.Routing var url = content == null ? "#" : content.Url; if (url.StartsWith("#")) { - _logger.Debug("Route '{Route}' matches content {ContentId} which has no url.", route, redirectUrl.ContentId); + _logger.Debug("Route {Route} matches content {ContentId} which has no url.", route, redirectUrl.ContentId); return false; } - _logger.Debug("Route '{Route}' matches content {ContentId} with url '{Url}', redirecting.", route, content.Id, url); + _logger.Debug("Route {Route} matches content {ContentId} with url '{Url}', redirecting.", route, content.Id, url); frequest.SetRedirectPermanent(url); return true; } diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs index a3c54406a8..94b2b9dbf2 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs @@ -46,9 +46,9 @@ namespace Umbraco.Web.Routing { if (docreq == null) throw new System.ArgumentNullException(nameof(docreq)); - Logger.Debug("Test route '{Route}'", route); + Logger.Debug("Test route {Route}", route); - var node = docreq.UmbracoContext.ContentCache.GetByRoute(route, culture: docreq.Culture?.Name); + var node = docreq.UmbracoContext.ContentCache.GetByRoute(docreq.UmbracoContext.InPreviewMode, route, culture: docreq.Culture?.Name); if (node != null) { docreq.PublishedContent = node; diff --git a/src/Umbraco.Web/Routing/LegacyRequestInitializer.cs b/src/Umbraco.Web/Routing/LegacyRequestInitializer.cs deleted file mode 100644 index d84ff25d73..0000000000 --- a/src/Umbraco.Web/Routing/LegacyRequestInitializer.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Web; - -namespace Umbraco.Web.Routing -{ - /// - /// A legacy class for old style handling of URL requests - /// - internal class LegacyRequestInitializer - { - private readonly Uri _requestUrl; - private readonly HttpContextBase _httpContext; - - public LegacyRequestInitializer(Uri requestUrl, HttpContextBase httpContext) - { - _requestUrl = requestUrl; - _httpContext = httpContext; - } - - public void InitializeRequest() - { - var uri = _requestUrl; - - // legacy - umbOriginalUrl used by default.aspx to rewritepath so forms are happy - // legacy - umbOriginalUrl used by presentation/umbraco/urlRewriter/UrlRewriterFormWriter which handles
    ("{TracePrefix}Uri='{RequestUri}'", tracePrefix, request.Uri); + _logger.Debug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri); var domainsCache = request.UmbracoContext.PublishedSnapshot.Domains; var domains = domainsCache.GetAll(includeWildcards: false).ToList(); @@ -314,7 +312,7 @@ namespace Umbraco.Web.Routing if (domainAndUri != null) { // matching an existing domain - _logger.Debug("{TracePrefix}Matches domain='{Domain}', rootId={RootContentId}, culture='{Culture}'", tracePrefix, domainAndUri.Name, domainAndUri.ContentId, domainAndUri.Culture); + _logger.Debug("{TracePrefix}Matches domain={Domain}, rootId={RootContentId}, culture={Culture}", tracePrefix, domainAndUri.Name, domainAndUri.ContentId, domainAndUri.Culture); request.Domain = domainAndUri; request.Culture = domainAndUri.Culture; @@ -334,7 +332,7 @@ namespace Umbraco.Web.Routing request.Culture = defaultCulture == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultCulture); } - _logger.Debug("{TracePrefix}Culture='{CultureName}'", tracePrefix, request.Culture.Name); + _logger.Debug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture.Name); return request.Domain != null; } @@ -350,7 +348,7 @@ namespace Umbraco.Web.Routing return; var nodePath = request.PublishedContent.Path; - _logger.Debug("{TracePrefix}Path='{NodePath}'", tracePrefix, nodePath); + _logger.Debug("{TracePrefix}Path={NodePath}", tracePrefix, nodePath); var rootNodeId = request.HasDomain ? request.Domain.ContentId : (int?)null; var domain = DomainHelper.FindWildcardDomainInPath(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId); @@ -358,7 +356,7 @@ namespace Umbraco.Web.Routing if (domain != null) { request.Culture = domain.Culture; - _logger.Debug("{TracePrefix}Got domain on node {DomainContentId}, set culture to '{CultureName}'", tracePrefix, domain.ContentId, request.Culture.Name); + _logger.Debug("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture.Name); } else { @@ -435,7 +433,7 @@ namespace Umbraco.Web.Routing /// A value indicating whether a document and template were found. private void FindPublishedContentAndTemplate(PublishedRequest request) { - _logger.Debug("FindPublishedContentAndTemplate: Path='{UriAbsolutePath}'", request.Uri.AbsolutePath); + _logger.Debug("FindPublishedContentAndTemplate: Path={UriAbsolutePath}", request.Uri.AbsolutePath); // run the document finders FindPublishedContent(request); @@ -541,7 +539,7 @@ namespace Umbraco.Web.Routing if (i == maxLoop || j == maxLoop) { - _logger.Debug("HandlePublishedContent: Looks like we're running into an infinite loop, abort"); + _logger.Debug("HandlePublishedContent: Looks like we are running into an infinite loop, abort"); request.PublishedContent = null; } @@ -713,7 +711,7 @@ namespace Umbraco.Web.Routing if (request.HasTemplate) _logger.Debug("FindTemplate: Has a template already, but also an alternative template."); - _logger.Debug("FindTemplate: Look for alternative template alias='{AltTemplate}'", altTemplate); + _logger.Debug("FindTemplate: Look for alternative template alias={AltTemplate}", altTemplate); // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings if (request.PublishedContent.IsAllowedTemplate(altTemplate)) @@ -724,16 +722,16 @@ namespace Umbraco.Web.Routing if (template != null) { request.TemplateModel = template; - _logger.Debug("FindTemplate: Got alternative template id={TemplateId} alias='{TemplateAlias}'", template.Id, template.Alias); + _logger.Debug("FindTemplate: Got alternative template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); } else { - _logger.Debug("FindTemplate: The alternative template with alias='{AltTemplate}' does not exist, ignoring.", altTemplate); + _logger.Debug("FindTemplate: The alternative template with alias={AltTemplate} does not exist, ignoring.", altTemplate); } } else { - _logger.Warn("FindTemplate: Alternative template '{TemplateAlias}' is not allowed on node {NodeId}, ignoring.", altTemplate, request.PublishedContent.Id); + _logger.Warn("FindTemplate: Alternative template {TemplateAlias} is not allowed on node {NodeId}, ignoring.", altTemplate, request.PublishedContent.Id); // no allowed, back to default var templateId = request.PublishedContent.TemplateId; @@ -756,7 +754,7 @@ namespace Umbraco.Web.Routing } else { - _logger.Debug("FindTemplate: Running with template id={TemplateId} alias='{TemplateAlias}'", request.TemplateModel.Id, request.TemplateModel.Alias); + _logger.Debug("FindTemplate: Running with template id={TemplateId} alias={TemplateAlias}", request.TemplateModel.Id, request.TemplateModel.Alias); } } @@ -773,7 +771,7 @@ namespace Umbraco.Web.Routing var template = _services.FileService.GetTemplate(templateId); if (template == null) throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render."); - _logger.Debug("GetTemplateModel: Got template id={TemplateId} alias=\"{TemplateAlias}\"", template.Id, template.Alias); + _logger.Debug("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); return template; } diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index f37b0fe896..36c3ba5533 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -190,8 +190,16 @@ namespace Umbraco.Web.Routing // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture // be nice with tests, assume things can be null, ultimately fall back to invariant - if (culture == null) - culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + // (but only for variant content of course) + if (content.ContentType.VariesByCulture()) + { + if (culture == null) + culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + } + else + { + culture = null; + } if (current == null) current = _umbracoContext.CleanedUmbracoUrl; diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index e007ca322e..98502d9eb9 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -31,11 +31,13 @@ using Umbraco.Core.Runtime; using Umbraco.Core.Services; using Umbraco.Web.Cache; using Umbraco.Web.Composing.Composers; +using Umbraco.Web.ContentApps; using Umbraco.Web.Dictionary; using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.HealthCheck; using Umbraco.Web.Install; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; @@ -197,6 +199,12 @@ namespace Umbraco.Web.Runtime // register properties fallback composition.Container.RegisterSingleton(); + + // register known content apps + composition.Container.RegisterCollectionBuilder() + .Append() + .Append() + .Append(); } internal void Initialize( diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index e52b46a42d..89fba7717d 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.Scheduling switch (_runtimeState.ServerRole) { case ServerRole.Replica: - _logger.Debug("Does not run on slave servers."); + _logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: _logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index 7100b1a03b..24f6775166 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -28,11 +28,11 @@ namespace Umbraco.Web.Scheduling public override async Task PerformRunAsync(CancellationToken token) { - // not on slaves nor unknown role servers + // not on replicas nor unknown role servers switch (_runtime.ServerRole) { case ServerRole.Replica: - _logger.Debug("Does not run on slave servers."); + _logger.Debug("Does not run on replica servers."); return true; // role may change! case ServerRole.Unknown: _logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index d2feb9923e..ae73da04c8 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -69,7 +69,7 @@ namespace Umbraco.Web.Scheduling switch (_runtime.ServerRole) { case ServerRole.Replica: - _logger.Debug("Does not run on slave servers."); + _logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: _logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 13a99daaea..471eb213c0 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.Scheduling switch (_runtime.ServerRole) { case ServerRole.Replica: - _logger.Debug("Does not run on slave servers."); + _logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: _logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index 76a909095a..fdaa4dfd6d 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -17,6 +17,7 @@ namespace Umbraco.Web.Scheduling internal class ScheduledTasks : RecurringTaskBase { + private static HttpClient _httpClient; private readonly IRuntimeState _runtime; private readonly IUmbracoSettingsSection _settings; private readonly ILogger _logger; @@ -65,27 +66,28 @@ namespace Umbraco.Web.Scheduling private async Task GetTaskByHttpAync(string url, CancellationToken token) { - using (var wc = new HttpClient()) + if (_httpClient == null) + _httpClient = new HttpClient + { + BaseAddress = _runtime.ApplicationUrl + }; + + var request = new HttpRequestMessage(HttpMethod.Get, url); + + //TODO: pass custom the authorization header, currently these aren't really secured! + //request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); + + try { - // url could be relative, so better set a base url for the http client - wc.BaseAddress = _runtime.ApplicationUrl; - - var request = new HttpRequestMessage(HttpMethod.Get, url); - - //TODO: pass custom the authorization header, currently these aren't really secured! - //request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); - - try - { - var result = await wc.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html - return result.StatusCode == HttpStatusCode.OK; - } - catch (Exception ex) - { - _logger.Error(ex, "An error occurred calling web task for url: {Url}", url); - } - return false; + var result = await _httpClient.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html + return result.StatusCode == HttpStatusCode.OK; } + catch (Exception ex) + { + _logger.Error(ex, "An error occurred calling web task for url: {Url}", url); + + } + return false; } public override async Task PerformRunAsync(CancellationToken token) @@ -93,7 +95,7 @@ namespace Umbraco.Web.Scheduling switch (_runtime.ServerRole) { case ServerRole.Replica: - _logger.Debug("Does not run on slave servers."); + _logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: _logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index 12f85c49ba..d9b2893c62 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -117,7 +117,7 @@ namespace Umbraco.Web.Scheduling private IBackgroundTask RegisterScheduledPublishing() { // scheduled publishing/unpublishing - // install on all, will only run on non-slaves servers + // install on all, will only run on non-replica servers var task = new ScheduledPublishing(_publishingRunner, 60000, 60000, _runtime, _contentService, _logger, _userService); _publishingRunner.TryAdd(task); return task; @@ -158,7 +158,7 @@ namespace Umbraco.Web.Scheduling private IBackgroundTask RegisterLogScrubber(IUmbracoSettingsSection settings) { // log scrubbing - // install on all, will only run on non-slaves servers + // install on all, will only run on non-replica servers var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger, _proflog); _scrubberRunner.TryAdd(task); return task; diff --git a/src/Umbraco.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs index 828aed588c..ce51f82c43 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserManager.cs @@ -191,7 +191,10 @@ namespace Umbraco.Web.Security if (dataProtectionProvider != null) { - manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")); + manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")) + { + TokenLifespan = TimeSpan.FromDays(3) + }; } manager.UserLockoutEnabledByDefault = true; @@ -703,6 +706,7 @@ namespace Umbraco.Web.Security var httpContext = HttpContext.Current == null ? (HttpContextBase)null : new HttpContextWrapper(HttpContext.Current); return httpContext.GetCurrentRequestIpAddress(); } + } } diff --git a/src/Umbraco.Web/Services/SectionService.cs b/src/Umbraco.Web/Services/SectionService.cs index 411d122d49..ff8279a411 100644 --- a/src/Umbraco.Web/Services/SectionService.cs +++ b/src/Umbraco.Web/Services/SectionService.cs @@ -92,7 +92,6 @@ namespace Umbraco.Web.Services doc.Root.Add(new XElement("add", new XAttribute("alias", attr.Alias), new XAttribute("name", attr.Name), - new XAttribute("icon", attr.Icon), new XAttribute("sortOrder", attr.SortOrder))); count++; } @@ -201,7 +200,7 @@ namespace Umbraco.Web.Services }, true); //raise event - OnNew(new Section(name, alias, icon, sortOrder), new EventArgs()); + OnNew(new Section(name, alias, sortOrder), new EventArgs()); } } @@ -255,7 +254,6 @@ namespace Umbraco.Web.Services var sortOrderAttr = addElement.Attribute("sortOrder"); tmp.Add(new Section(addElement.Attribute("name").Value, addElement.Attribute("alias").Value, - addElement.Attribute("icon").Value, sortOrderAttr != null ? Convert.ToInt32(sortOrderAttr.Value) : 0)); } return false; @@ -300,7 +298,7 @@ namespace Umbraco.Web.Services //we need to interrogate the attributes for the data. Would be better to have a base class that contains //metadata populated by the attribute. Oh well i guess. var attrs = types.Select(x => x.GetCustomAttributes(false).Single()); - return Enumerable.ToArray
    (attrs.Select(x => new Section(x.Name, x.Alias, x.Icon, x.SortOrder))); + return Enumerable.ToArray
    (attrs.Select(x => new Section(x.Name, x.Alias, x.SortOrder))); }); } diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index 44e9cca5a5..ecc1d3bd23 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -202,7 +202,6 @@ namespace Umbraco.Web.Templates // handlers like default.aspx will want it and most macros currently need it request.UmbracoPage = new page(request); //now, set the new ones for this page execution - _umbracoContext.HttpContext.Items["pageID"] = request.PublishedContent.Id; _umbracoContext.HttpContext.Items["pageElements"] = request.UmbracoPage.Elements; _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; _umbracoContext.PublishedRequest = request; @@ -214,8 +213,8 @@ namespace Umbraco.Web.Templates private void SaveExistingItems() { //Many objects require that these legacy items are in the http context items... before we render this template we need to first - //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal. - _oldPageId = _umbracoContext.HttpContext.Items["pageID"]; + //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal + _oldPageId = _umbracoContext.PageId; _oldPageElements = _umbracoContext.HttpContext.Items["pageElements"]; _oldPublishedRequest = _umbracoContext.PublishedRequest; _oldAltTemplate = _umbracoContext.HttpContext.Items[Umbraco.Core.Constants.Conventions.Url.AltTemplate]; @@ -227,7 +226,6 @@ namespace Umbraco.Web.Templates private void RestoreItems() { _umbracoContext.PublishedRequest = _oldPublishedRequest; - _umbracoContext.HttpContext.Items["pageID"] = _oldPageId; _umbracoContext.HttpContext.Items["pageElements"] = _oldPageElements; _umbracoContext.HttpContext.Items[Umbraco.Core.Constants.Conventions.Url.AltTemplate] = _oldAltTemplate; } diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 782878b5c0..9c13429b44 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -1,9 +1,10 @@ -using System; +using HtmlAgilityPack; +using System; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.IO; -using Umbraco.Core.Logging; using Umbraco.Web.Composing; using Umbraco.Web.Routing; @@ -17,13 +18,6 @@ namespace Umbraco.Web.Templates /// public static class TemplateUtilities { - //TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable - [Obsolete("Use the overload specifying an UmbracoContext")] - internal static string ParseInternalLinks(string text, bool preview) - { - return ParseInternalLinks(text, preview, UmbracoContext.Current); - } - internal static string ParseInternalLinks(string text, bool preview, UmbracoContext umbracoContext) { using (umbracoContext.ForcedPreview(preview)) // force for url provider @@ -44,6 +38,11 @@ namespace Umbraco.Web.Templates { if (urlProvider == null) throw new ArgumentNullException("urlProvider"); + if(string.IsNullOrEmpty(text)) + { + return text; + } + // Parse internal links var tags = LocalLinkPattern.Matches(text); foreach (Match tag in tags) @@ -72,6 +71,11 @@ namespace Umbraco.Web.Templates } } + if (UmbracoConfig.For.UmbracoSettings().Content.StripUdiAttributes) + { + text = StripUdiDataAttributes(text); + } + return text; } @@ -83,6 +87,9 @@ namespace Umbraco.Web.Templates private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + private static readonly Regex UdiDataAttributePattern = new Regex("data-udi=\"[^\\\"]*\"", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + /// /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. /// @@ -100,7 +107,7 @@ namespace Umbraco.Web.Templates { // find all relative urls (ie. urls that contain ~) var tags = ResolveUrlPattern.Matches(text); - Current.Logger.Debug(typeof(IOHelper), "After regex: {ElapsedMilliseconds} matched: {TagsCount}", timer.Stopwatch.ElapsedMilliseconds, tags.Count); + Current.Logger.Debug(typeof(IOHelper), "After regex: {Duration} matched: {TagsCount}", timer.Stopwatch.ElapsedMilliseconds, tags.Count); foreach (Match tag in tags) { var url = ""; @@ -126,5 +133,21 @@ namespace Umbraco.Web.Templates { return text.CleanForXss(ignoreFromClean); } + + /// + /// Strips data-udi attributes from rich text + /// + /// A html string + /// A string stripped from the data-uid attributes + public static string StripUdiDataAttributes(string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; + } + + + return UdiDataAttributePattern.Replace(input, string.Empty); + } } } diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 145a0f5947..5cb63d7409 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Trees /// This authorizes based on access to the content section even though it exists in the settings /// [UmbracoApplicationAuthorize(Constants.Applications.Content)] - [Tree(Constants.Applications.Settings, Constants.Trees.ContentBlueprints, null, sortOrder: 10)] + [Tree(Constants.Applications.Settings, Constants.Trees.ContentBlueprints, null, sortOrder: 12)] [PluginController("UmbracoTrees")] [CoreTree] public class ContentBlueprintTreeController : TreeController @@ -30,7 +30,7 @@ namespace Umbraco.Web.Trees var root = base.CreateRootNode(queryStrings); //this will load in a custom UI instead of the dashboard for the root node - root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Settings, Constants.Trees.ContentBlueprints, "intro"); + root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.ContentBlueprints}/intro"; return root; } @@ -58,7 +58,7 @@ namespace Umbraco.Web.Trees .Select(entity => { var treeNode = CreateTreeNode(entity, Constants.ObjectTypes.DocumentBlueprint, id, queryStrings, "icon-item-arrangement", true); - treeNode.Path = string.Format("-1,{0}", entity.Id); + treeNode.Path = $"-1,{entity.Id}"; treeNode.NodeType = "document-type-blueprints"; //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);"; @@ -92,8 +92,9 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService.Localize( + $"actions/{ActionRefresh.Instance.Alias}"), true); return menu; } var cte = Services.EntityService.Get(int.Parse(id), UmbracoObjectTypes.DocumentType); diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 33a347d3d4..3a990a7741 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.Trees Constants.Applications.Media, Constants.Applications.Users, Constants.Applications.Settings, - Constants.Applications.Developer, + Constants.Applications.Packages, Constants.Applications.Members)] [Tree(Constants.Applications.Content, Constants.Trees.Content)] [PluginController("UmbracoTrees")] @@ -47,10 +47,10 @@ namespace Umbraco.Web.Trees /// protected override TreeNode GetSingleTreeNode(IEntitySlim entity, string parentId, FormDataCollection queryStrings) { - var langId = queryStrings?["culture"]; + var culture = queryStrings?["culture"]; var allowedUserOptions = GetAllowedUserMenuItemsForNode(entity); - if (CanUserAccessNode(entity, allowedUserOptions, langId)) + if (CanUserAccessNode(entity, allowedUserOptions, culture)) { //Special check to see if it ia a container, if so then we'll hide children. var isContainer = entity.IsContainer; // && (queryStrings.Get("isDialog") != "true"); @@ -74,11 +74,23 @@ namespace Umbraco.Web.Trees { var documentEntity = (IDocumentEntitySlim) entity; - //fixme we need these statuses per variant but to do that we need to fix the issues listed in IDocumentEntitySlim - if (!documentEntity.Published) - node.SetNotPublishedStyle(); - //if (documentEntity.Edited) - // node.SetHasUnpublishedVersionStyle(); + if (!documentEntity.Variations.VariesByCulture()) + { + if (!documentEntity.Published) + node.SetNotPublishedStyle(); + else if (documentEntity.Edited) + node.SetHasPendingVersionStyle(); + } + else + { + if (!culture.IsNullOrWhiteSpace()) + { + if (!documentEntity.PublishedCultures.Contains(culture)) + node.SetNotPublishedStyle(); + else if (documentEntity.EditedCultures.Contains(culture)) + node.SetHasPendingVersionStyle(); + } + } node.AdditionalData.Add("contentType", documentEntity.ContentTypeAlias); } @@ -223,11 +235,10 @@ namespace Umbraco.Web.Trees //need to ensure some of these are converted to the legacy system - until we upgrade them all to be angularized. AddActionNode(item, menu, true); AddActionNode(item, menu); - AddActionNode(item, menu, convert: true); AddActionNode(item, menu, true); - AddActionNode(item, menu, convert: true); + AddActionNode(item, menu); AddActionNode(item, menu, convert: true); AddActionNode(item, menu); AddActionNode(item, menu, convert: true); diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index da1eae7b2c..2ba11997e6 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -20,6 +20,14 @@ namespace Umbraco.Web.Trees [CoreTree] public class ContentTypeTreeController : TreeController, ISearchableTree { + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); + //check if there are any types + root.HasChildren = Services.ContentTypeService.GetAll().Any(); + return root; + } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { var intId = id.TryConvertTo(); diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 99b94b544c..a2201f792a 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -17,7 +17,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.DataTypes)] - [Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, null, sortOrder:7)] + [Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, null, sortOrder:3)] [PluginController("UmbracoTrees")] [CoreTree] public class DataTypeTreeController : TreeController, ISearchableTree @@ -46,8 +46,8 @@ namespace Umbraco.Web.Trees //if the request is for folders only then just return if (queryStrings["foldersonly"].IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") return nodes; - //Normal nodes - var sysIds = GetSystemIds(); + //System ListView nodes + var systemListViewDataTypeIds = GetNonDeletableSystemListViewDataTypeIds(); nodes.AddRange( Services.EntityService.GetChildren(intId.Result, UmbracoObjectTypes.DataType) @@ -56,7 +56,7 @@ namespace Umbraco.Web.Trees { var node = CreateTreeNode(dt.Id.ToInvariantString(), id, queryStrings, dt.Name, "icon-autofill", false); node.Path = dt.Path; - if (sysIds.Contains(dt.Id)) + if (systemListViewDataTypeIds.Contains(dt.Id)) { node.Icon = "icon-thumbnail-list"; } @@ -66,15 +66,31 @@ namespace Umbraco.Web.Trees return nodes; } - private IEnumerable GetSystemIds() + /// + /// Get all integer identifiers for the non-deletable system datatypes. + /// + private static IEnumerable GetNonDeletableSystemDataTypeIds() { var systemIds = new[] + { + Constants.System.DefaultLabelDataTypeId + }; + + return systemIds.Concat(GetNonDeletableSystemListViewDataTypeIds()); + } + + /// + /// Get all integer identifiers for the non-deletable system listviews. + /// + private static IEnumerable GetNonDeletableSystemListViewDataTypeIds() + { + return new[] { Constants.DataTypes.DefaultContentListView, Constants.DataTypes.DefaultMediaListView, Constants.DataTypes.DefaultMembersListView + }; - return systemIds; } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) @@ -87,7 +103,7 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.Instance.Alias; // root actions - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); return menu; } @@ -98,30 +114,29 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.Instance.Alias; - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize(String.Format("actions/{0}", "rename"))) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { Icon = "icon icon-edit" }); if (container.HasChildren == false) - { + { //can delete data type - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize( + $"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); } else { - var sysIds = GetSystemIds(); + var nonDeletableSystemDataTypeIds = GetNonDeletableSystemDataTypeIds(); - if (sysIds.Contains(int.Parse(id)) == false) - { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - } + if (nonDeletableSystemDataTypeIds.Contains(int.Parse(id)) == false) + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), hasSeparator: true); } return menu; diff --git a/src/Umbraco.Web/Trees/DictionaryTreeController.cs b/src/Umbraco.Web/Trees/DictionaryTreeController.cs index c2491fefe0..27039832c9 100644 --- a/src/Umbraco.Web/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web/Trees/DictionaryTreeController.cs @@ -9,10 +9,11 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees { + [UmbracoTreeAuthorize(Constants.Trees.Dictionary)] [Mvc.PluginController("UmbracoTrees")] [CoreTree] - [Tree(Constants.Applications.Settings, Constants.Trees.Dictionary, null, sortOrder: 3)] + [Tree(Constants.Applications.Translation, Constants.Trees.Dictionary, null, sortOrder: 0)] public class DictionaryTreeController : TreeController { protected override TreeNode CreateRootNode(FormDataCollection queryStrings) @@ -21,7 +22,7 @@ namespace Umbraco.Web.Trees // the default section is settings, falling back to this if we can't // figure out where we are from the querystring parameters - var section = Constants.Applications.Settings; + var section = Constants.Applications.Translation; if (queryStrings["application"] != null) section = queryStrings["application"]; diff --git a/src/Umbraco.Web/Trees/LanguageTreeController.cs b/src/Umbraco.Web/Trees/LanguageTreeController.cs index eadb5c50d0..b65906d152 100644 --- a/src/Umbraco.Web/Trees/LanguageTreeController.cs +++ b/src/Umbraco.Web/Trees/LanguageTreeController.cs @@ -7,7 +7,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Languages)] - [Tree(Constants.Applications.Settings, Constants.Trees.Languages, null, sortOrder: 5)] + [Tree(Constants.Applications.Settings, Constants.Trees.Languages, null, sortOrder: 11)] [PluginController("UmbracoTrees")] [CoreTree] public class LanguageTreeController : TreeController @@ -33,7 +33,7 @@ namespace Umbraco.Web.Trees var root = base.CreateRootNode(queryStrings); //this will load in a custom UI instead of the dashboard for the root node - root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Settings, Constants.Trees.Languages, "overview"); + root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.Languages}/overview"; root.Icon = "icon-globe"; root.HasChildren = false; root.MenuUrl = null; diff --git a/src/Umbraco.Web/Trees/MacrosTreeController.cs b/src/Umbraco.Web/Trees/MacrosTreeController.cs index 2968408bdb..66f92ffdc0 100644 --- a/src/Umbraco.Web/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/MacrosTreeController.cs @@ -13,10 +13,11 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Macros)] - [Tree(Constants.Applications.Developer, Constants.Trees.Macros, "Macros", sortOrder: 2)] + [Tree(Constants.Applications.Settings, Constants.Trees.Macros, "Macros", sortOrder: 4)] [PluginController("UmbracoTrees")] [CoreTree] - public class MacrosTreeController : TreeController + public class + MacrosTreeController : TreeController { protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 53f1b0a97e..8eb37e6224 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Trees Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Settings, - Constants.Applications.Developer, + Constants.Applications.Packages, Constants.Applications.Members)] [Tree(Constants.Applications.Media, Constants.Trees.Media)] [PluginController("UmbracoTrees")] diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index 086c1a5194..55a15b683c 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -16,7 +16,7 @@ using Umbraco.Web.Search; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] - [Tree(Constants.Applications.Settings, Constants.Trees.MediaTypes, null, sortOrder:9)] + [Tree(Constants.Applications.Settings, Constants.Trees.MediaTypes, null, sortOrder:1)] [Mvc.PluginController("UmbracoTrees")] [CoreTree] public class MediaTypeTreeController : TreeController, ISearchableTree @@ -73,8 +73,9 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.Instance.Alias; // root actions - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService.Localize( + $"actions/{ActionRefresh.Instance.Alias}")); return menu; } @@ -84,9 +85,9 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.Instance.Alias; - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize(String.Format("actions/{0}", "rename"))) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { Icon = "icon icon-edit" }); @@ -94,9 +95,10 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete doc type - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize( + $"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); } else { @@ -105,28 +107,29 @@ namespace Umbraco.Web.Trees if (enableInheritedMediaTypes) { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), true); } } else { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}")); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), true); } } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionCopy.Instance.Alias}")); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); if (enableInheritedMediaTypes) - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(Services.TextService.Localize( + $"actions/{ActionRefresh.Instance.Alias}"), true); } return menu; diff --git a/src/Umbraco.Web/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web/Trees/MemberGroupTreeController.cs index d1a0d2c318..b9910c7b31 100644 --- a/src/Umbraco.Web/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberGroupTreeController.cs @@ -8,7 +8,7 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.MemberGroups)] - [Tree(Constants.Applications.Members, Constants.Trees.MemberGroups, null, sortOrder: 2)] + [Tree(Constants.Applications.Members, Constants.Trees.MemberGroups, null, sortOrder: 1)] [Mvc.PluginController("UmbracoTrees")] [CoreTree] public class MemberGroupTreeController : MemberTypeAndGroupTreeControllerBase diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs index 10b5d35b6e..56b836ce8a 100644 --- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs @@ -8,7 +8,7 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] - [Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, null, sortOrder: 1)] + [Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, null, sortOrder: 2)] public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase { protected override IEnumerable GetTreeNodesFromService(string id, FormDataCollection queryStrings) diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 31283acebf..31f577ad85 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -1,107 +1,106 @@ -//using System; -//using System.Linq; -//using System.Net.Http.Formatting; -//using Umbraco.Web.Models.Trees; -//using Umbraco.Web.Mvc; -//using Umbraco.Web.WebApi.Filters; -//using umbraco; -//using umbraco.cms.businesslogic.packager; -//using Umbraco.Core.Services; -//using Umbraco.Web._Legacy.Actions; -//using Constants = Umbraco.Core.Constants; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Formatting; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using umbraco; +using umbraco.cms.businesslogic.packager; +using Umbraco.Core.Services; +using Umbraco.Web._Legacy.Actions; +using Constants = Umbraco.Core.Constants; -//namespace Umbraco.Web.Trees -//{ -// [UmbracoTreeAuthorize(Constants.Trees.Packages)] -// [Tree(Constants.Applications.Developer, Constants.Trees.Packages, null, sortOrder: 0)] -// [PluginController("UmbracoTrees")] -// [CoreTree] -// public class PackagesTreeController : TreeController -// { -// /// -// /// Helper method to create a root model for a tree -// /// -// /// -// protected override TreeNode CreateRootNode(FormDataCollection queryStrings) -// { -// var root = base.CreateRootNode(queryStrings); +namespace Umbraco.Web.Trees +{ + [UmbracoTreeAuthorize(Constants.Trees.Packages)] + [Tree(Constants.Applications.Packages, Constants.Trees.Packages, null, sortOrder: 0)] + [PluginController("UmbracoTrees")] + [CoreTree] + public class PackagesTreeController : TreeController + { + /// + /// Helper method to create a root model for a tree + /// + /// + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); + + root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/overview"; + + root.Icon = "icon-box"; + + return root; + } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + var nodes = new TreeNodeCollection(); -// //this will load in a custom UI instead of the dashboard for the root node -// root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Developer, Constants.Trees.Packages, "overview"); -// root.Icon = "icon-box"; + var createdPackages = CreatedPackage.GetAllCreatedPackages(); -// return root; -// } -// protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) -// { -// var nodes = new TreeNodeCollection(); - -// var createdPackages = CreatedPackage.GetAllCreatedPackages(); - -// if (id == "created") -// { -// nodes.AddRange( -// createdPackages -// .OrderBy(entity => entity.Data.Name) -// .Select(dt => -// { -// var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, -// string.Format("/{0}/framed/{1}", -// queryStrings.GetValue("application"), -// Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id))); -// return node; -// })); -// } -// else -// { -// //must be root -// var node = CreateTreeNode( -// "created", -// id, -// queryStrings, -// Services.TextService.Localize("treeHeaders/createdPackages"), -// "icon-folder", -// createdPackages.Count > 0, -// string.Empty); + if (id == "created") + { + nodes.AddRange( + createdPackages + .OrderBy(entity => entity.Data.Name) + .Select(dt => + { + var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, + $"/{queryStrings.GetValue("application")}/framed/{Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id)}"); + return node; + })); + } + else + { + //must be root + var node = CreateTreeNode( + "created", + id, + queryStrings, + Services.TextService.Localize("treeHeaders/createdPackages"), + "icon-folder", + createdPackages.Count > 0, + string.Empty); -// //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. -// node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; + //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. + node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; -// nodes.Add(node); -// } + nodes.Add(node); + } -// return nodes; -// } + return nodes; + } -// protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) -// { -// var menu = new MenuItemCollection(); + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); -// // Root actions -// if (id == "-1") -// { -// menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) -// .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); -// } -// else if (id == "created") -// { -// menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) -// .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + // Root actions + if (id == "-1") + { + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")) + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + } + else if (id == "created") + { + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")) + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); -// menu.Items.Add( -// Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); -// } -// else -// { -// //it's a package node -// menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); -// } + menu.Items.Add( + Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), true); + } + else + { + //it's a package node + menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + } -// return menu; -// } -// } -//} + return menu; + } + } +} diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs index db97d5c3a4..882cfb2c9f 100644 --- a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Trees /// /// Tree for displaying partial view macros in the developer app /// - [Tree(Constants.Applications.Developer, Constants.Trees.PartialViewMacros, null, sortOrder: 6)] + [Tree(Constants.Applications.Settings, Constants.Trees.PartialViewMacros, null, sortOrder: 8)] [UmbracoTreeAuthorize(Constants.Trees.PartialViewMacros)] [PluginController("UmbracoTrees")] [CoreTree] diff --git a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs index aaaeb2d175..41c53fdc99 100644 --- a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Trees /// /// Tree for displaying partial views in the settings app /// - [Tree(Constants.Applications.Settings, Constants.Trees.PartialViews, null, sortOrder: 2)] + [Tree(Constants.Applications.Settings, Constants.Trees.PartialViews, null, sortOrder: 7)] [UmbracoTreeAuthorize(Constants.Trees.PartialViews)] [PluginController("UmbracoTrees")] [CoreTree] diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 14fd817a7c..e35a9a23b6 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -12,7 +12,7 @@ using Umbraco.Core.Models.Entities; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.RelationTypes)] - [Tree(Constants.Applications.Developer, Constants.Trees.RelationTypes, null, sortOrder: 4)] + [Tree(Constants.Applications.Settings, Constants.Trees.RelationTypes, null, sortOrder: 5)] [Mvc.PluginController("UmbracoTrees")] [CoreTree] public class RelationTypeTreeController : TreeController diff --git a/src/Umbraco.Web/Trees/ScriptsTreeController.cs b/src/Umbraco.Web/Trees/ScriptsTreeController.cs index 57a50cde5d..97053993b4 100644 --- a/src/Umbraco.Web/Trees/ScriptsTreeController.cs +++ b/src/Umbraco.Web/Trees/ScriptsTreeController.cs @@ -6,7 +6,7 @@ using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees { - [Tree(Constants.Applications.Settings, Constants.Trees.Scripts, "Scripts", "icon-folder", "icon-folder", sortOrder: 4)] + [Tree(Constants.Applications.Settings, Constants.Trees.Scripts, "Scripts", "icon-folder", "icon-folder", sortOrder: 10)] public class ScriptsTreeController : FileSystemTreeController { protected override IFileSystem FileSystem => Current.FileSystems.ScriptsFileSystem; // fixme inject diff --git a/src/Umbraco.Web/Trees/StylesheetsTreeController.cs b/src/Umbraco.Web/Trees/StylesheetsTreeController.cs index 650407296e..365f427e18 100644 --- a/src/Umbraco.Web/Trees/StylesheetsTreeController.cs +++ b/src/Umbraco.Web/Trees/StylesheetsTreeController.cs @@ -4,7 +4,7 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.Trees { - [Tree(Constants.Applications.Settings, Constants.Trees.Stylesheets, "Stylesheets", "icon-folder", "icon-folder", sortOrder: 3)] + [Tree(Constants.Applications.Settings, Constants.Trees.Stylesheets, "Stylesheets", "icon-folder", "icon-folder", sortOrder: 9)] public class StylesheetsTreeController : FileSystemTreeController { protected override IFileSystem FileSystem => Current.FileSystems.StylesheetsFileSystem; // fixme inject diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index d9aa0f21a0..521475413c 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -19,7 +19,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Templates)] - [Tree(Constants.Applications.Settings, Constants.Trees.Templates, null, sortOrder:1)] + [Tree(Constants.Applications.Settings, Constants.Trees.Templates, null, sortOrder:6)] [PluginController("UmbracoTrees")] [CoreTree] public class TemplatesTreeController : TreeController, ISearchableTree @@ -70,7 +70,7 @@ namespace Umbraco.Web.Trees //Create the normal create action var item = menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); - item.NavigateToRoute(string.Format("{0}/templates/edit/{1}?create=true", queryStrings.GetValue("application"), id)); + item.NavigateToRoute($"{queryStrings.GetValue("application")}/templates/edit/{id}?create=true"); if (id == Constants.System.Root.ToInvariantString()) { diff --git a/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs b/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs index 9c5e0f1ec2..cd7270ac62 100644 --- a/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs +++ b/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.UI.JavaScript return toParse.Split(new[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries); } - protected IEnumerable OptimizeAssetCollection(IEnumerable assets, ClientDependencyType assetType, HttpContextBase httpContext) + internal static IEnumerable OptimizeAssetCollection(IEnumerable assets, ClientDependencyType assetType, HttpContextBase httpContext) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); @@ -41,11 +41,11 @@ namespace Umbraco.Web.UI.JavaScript // ike lib/blah/blah.js so we need to turn them into absolutes here if (x.StartsWith("/") == false && Uri.IsWellFormedUriString(x, UriKind.Relative)) { - return (IClientDependencyFile) new BasicFile(assetType) { FilePath = new Uri(requestUrl, x).AbsolutePath }; + return new BasicFile(assetType) { FilePath = new Uri(requestUrl, x).AbsolutePath }; } return assetType == ClientDependencyType.Javascript - ? (IClientDependencyFile) new JavascriptFile(x) + ? new JavascriptFile(x) : (IClientDependencyFile) new CssFile(x); }).ToList(); diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs b/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs index 4b3ef62b58..0471b47e8e 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs @@ -33,16 +33,20 @@ namespace Umbraco.Web.UI.JavaScript private static readonly Regex Token = new Regex("(\"##\\w+?##\")", RegexOptions.Compiled); /// - /// Processes all found manifest files and outputs the main.js file containing all plugin manifests + /// Gets the JS initialization script to boot the back office application /// - public string GetJavascriptInitialization(HttpContextBase httpContext, IEnumerable umbracoInit, IEnumerable additionalJsFiles = null) + /// + /// + /// + /// The angular module name to boot + /// + /// + public static string GetJavascriptInitialization(HttpContextBase httpContext, IEnumerable scripts, string angularModule) { - var files = GetScriptFiles(httpContext, umbracoInit, additionalJsFiles); - var jarray = new StringBuilder(); jarray.AppendLine("["); var first = true; - foreach (var file in files) + foreach (var file in scripts) { if (first) first = false; else jarray.AppendLine(","); @@ -53,10 +57,22 @@ namespace Umbraco.Web.UI.JavaScript } jarray.Append("]"); - return WriteScript(jarray.ToString(), IOHelper.ResolveUrl(SystemDirectories.Umbraco)); + return WriteScript(jarray.ToString(), IOHelper.ResolveUrl(SystemDirectories.Umbraco), angularModule); } - public IEnumerable GetScriptFiles(HttpContextBase httpContext, IEnumerable umbracoInit, IEnumerable additionalJsFiles = null) + /// + /// Returns a list of optimized script paths for the back office + /// + /// + /// + /// + /// + /// Cache busted/optimized script paths for the back office including manifest and property editor scripts + /// + /// + /// Used to cache bust and optimize script paths for the back office + /// + public IEnumerable OptimizeBackOfficeScriptFiles(HttpContextBase httpContext, IEnumerable umbracoInit, IEnumerable additionalJsFiles = null) { var scripts = new HashSet(); foreach (var script in umbracoInit) @@ -75,6 +91,26 @@ namespace Umbraco.Web.UI.JavaScript return scripts.ToArray(); } + /// + /// Returns a list of optimized script paths + /// + /// + /// + /// + /// + /// Used to cache bust and optimize script paths + /// + public static IEnumerable OptimizeScriptFiles(HttpContextBase httpContext, IEnumerable scriptFiles) + { + var scripts = new HashSet(); + foreach (var script in scriptFiles) + scripts.Add(script); + + scripts = new HashSet(OptimizeAssetCollection(scripts, ClientDependencyType.Javascript, httpContext)); + + return scripts.ToArray(); + } + /// /// Returns the default config as a JArray /// @@ -85,15 +121,25 @@ namespace Umbraco.Web.UI.JavaScript return resources.Where(x => x.Type == JTokenType.String).Select(x => x.ToString()); } + /// + /// Returns the default config as a JArray + /// + /// + internal static IEnumerable GetPreviewInitialization() + { + var resources = JsonConvert.DeserializeObject(Resources.PreviewInitialize); + return resources.Where(x => x.Type == JTokenType.String).Select(x => x.ToString()); + } + /// /// Parses the JsResources.Main and replaces the replacement tokens accordingly. /// /// /// - internal static string WriteScript(params string[] replacements) + internal static string WriteScript(string scripts, string umbracoPath, string angularModule) { var count = 0; - + var replacements = new[] { scripts, umbracoPath, angularModule }; // replace, catering for the special syntax when we have // js function() objects contained in the json diff --git a/src/Umbraco.Web/UI/JavaScript/Main.js b/src/Umbraco.Web/UI/JavaScript/Main.js index 2998a98b6a..8aa431376a 100644 --- a/src/Umbraco.Web/UI/JavaScript/Main.js +++ b/src/Umbraco.Web/UI/JavaScript/Main.js @@ -1,10 +1,12 @@ LazyLoad.js("##JsInitialize##", function () { //we need to set the legacy UmbClientMgr path - UmbClientMgr.setUmbracoPath('"##UmbracoPath##"'); + if ((typeof UmbClientMgr) !== "undefined") { + UmbClientMgr.setUmbracoPath('"##UmbracoPath##"'); + } jQuery(document).ready(function () { - angular.bootstrap(document, ['umbraco']); + angular.bootstrap(document, ['"##AngularModule##"']); }); -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web/UI/JavaScript/PreviewInitialize.js b/src/Umbraco.Web/UI/JavaScript/PreviewInitialize.js new file mode 100644 index 0000000000..0b0c24f378 --- /dev/null +++ b/src/Umbraco.Web/UI/JavaScript/PreviewInitialize.js @@ -0,0 +1,14 @@ +[ + '../lib/jquery/jquery.min.js', + '../lib/angular/angular.js', + '../lib/underscore/underscore-min.js', + '../lib/umbraco/Extensions.js', + '../js/app.js', + '../js/umbraco.resources.js', + '../js/umbraco.services.js', + '../js/umbraco.interceptors.js', + '../ServerVariables', + '../lib/signalr/jquery.signalR.js', + '../BackOffice/signalr/hubs', + '../js/umbraco.preview.js' +] diff --git a/src/Umbraco.Web/UI/JavaScript/Resources.Designer.cs b/src/Umbraco.Web/UI/JavaScript/Resources.Designer.cs index 4ac359c360..2f320ef839 100644 --- a/src/Umbraco.Web/UI/JavaScript/Resources.Designer.cs +++ b/src/Umbraco.Web/UI/JavaScript/Resources.Designer.cs @@ -63,22 +63,22 @@ namespace Umbraco.Web.UI.JavaScript { /// /// Looks up a localized string similar to [ /// 'lib/jquery/jquery.min.js', - /// 'lib/angular/1.1.5/angular.min.js', + /// 'lib/jquery-ui/jquery-ui.min.js', + /// 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.js', + /// + /// 'lib/angular/angular.js', /// 'lib/underscore/underscore-min.js', /// /// 'lib/moment/moment.min.js', /// - /// 'lib/jquery-ui/jquery-ui.min.js', - /// 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.js', + /// 'lib/animejs/anime.min.js', /// - /// 'lib/angular/1.1.5/angular-cookies.min.js', - /// 'lib/angular/1.1.5/angular-mobile.js', - /// 'lib/angular/1.1.5/angular-sanitize.min.js', - /// - /// 'lib/angular/angular-ui-sortable.js', - /// - /// 'lib/angular-dynamic-locale/tmhDynamicLocale.min.js', - /// 'lib [rest of string was truncated]";. + /// 'lib/angular-route/angular-route.js', + /// 'lib/angular-cookies/angular-cookies.js', + /// 'lib/angular-touch/angular-touch.js', + /// 'lib/angular-sanitize/angular-sanitize.js', + /// 'lib/angular-animate/angular-animate.js', + /// [rest of string was truncated]";. /// internal static string JsInitialize { get { @@ -93,10 +93,11 @@ namespace Umbraco.Web.UI.JavaScript { /// /// jQuery(document).ready(function () { /// - /// angular.bootstrap(document, ['umbraco']); + /// angular.bootstrap(document, ['##AngularModule##']); /// /// }); - ///});. + ///}); + ///. /// internal static string Main { get { @@ -104,6 +105,29 @@ namespace Umbraco.Web.UI.JavaScript { } } + /// + /// Looks up a localized string similar to [ + /// '../lib/jquery/jquery.min.js', + /// '../lib/angular/angular.js', + /// '../lib/underscore/underscore-min.js', + /// '../lib/umbraco/Extensions.js', + /// '../js/app.js', + /// '../js/umbraco.resources.js', + /// '../js/umbraco.services.js', + /// '../js/umbraco.interceptors.js', + /// '../ServerVariables', + /// '../lib/signalr/jquery.signalR.js', + /// '../BackOffice/signalr/hubs', + /// '../js/umbraco.canvasdesigner.js' + ///] + ///. + /// + internal static string PreviewInitialize { + get { + return ResourceManager.GetString("PreviewInitialize", resourceCulture); + } + } + /// /// Looks up a localized string similar to //TODO: This would be nicer as an angular module so it can be injected into stuff... that'd be heaps nicer, but ///// how to do that when this is not a regular JS file, it is a server side JS file and RequireJS seems to only want diff --git a/src/Umbraco.Web/UI/JavaScript/Resources.resx b/src/Umbraco.Web/UI/JavaScript/Resources.resx index 2e03928e43..34cea3a2d4 100644 --- a/src/Umbraco.Web/UI/JavaScript/Resources.resx +++ b/src/Umbraco.Web/UI/JavaScript/Resources.resx @@ -124,6 +124,9 @@ Main.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + previewinitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + servervariables.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9a37407ee7..003102bc40 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -60,10 +60,10 @@ - + - + 2.6.2.25 @@ -113,6 +113,7 @@ + @@ -138,12 +139,24 @@ + + + + + + + + + + + + @@ -158,6 +171,7 @@ + @@ -259,9 +273,7 @@ - - @@ -300,10 +312,8 @@ - - @@ -557,7 +567,7 @@ - + @@ -640,7 +650,6 @@ - @@ -901,7 +910,7 @@ - + @@ -1134,9 +1143,6 @@ - - - @@ -1237,7 +1243,6 @@ - @@ -1305,13 +1310,6 @@ - - Preview.aspx - ASPXCodeBehind - - - Preview.aspx - insertMasterpageContent.aspx ASPXCodeBehind @@ -1352,13 +1350,6 @@ rollBack.aspx - - sendToTranslation.aspx - ASPXCodeBehind - - - sendToTranslation.aspx - Code @@ -1370,42 +1361,12 @@ - - default.aspx - ASPXCodeBehind - - - default.aspx - - - details.aspx - ASPXCodeBehind - - - details.aspx - - - preview.aspx - ASPXCodeBehind - - - preview.aspx - - - xml.aspx - ASPXCodeBehind - - - xml.aspx - - - CheckForUpgrade.asmx @@ -1419,9 +1380,6 @@ nodeSorter.asmx Component - - ASPXCodeBehind - @@ -1461,12 +1419,8 @@ + - - ASPXCodeBehind - - - @@ -1480,7 +1434,6 @@ - ASPXCodeBehind @@ -1492,14 +1445,12 @@ ASPXCodeBehind - ASPXCodeBehind ASPXCodeBehind - @@ -1555,6 +1506,7 @@ +