diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 426df0c341..e502271115 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -54,6 +54,5 @@ - diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index e81c199288..3c8fed78f5 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -59,8 +59,6 @@ - - diff --git a/build/build.ps1 b/build/build.ps1 index c186d9adaa..1088da33bc 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -387,13 +387,13 @@ &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" ` -Properties BuildTmp="$($this.BuildTemp)" ` -Version "$($this.Version.Semver.ToString())" ` - -Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log" + -Symbols -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log" if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Core." } &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Web.nuspec" ` -Properties BuildTmp="$($this.BuildTemp)" ` -Version "$($this.Version.Semver.ToString())" ` - -Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log" + -Symbols -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log" if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Web." } &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" ` diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index d71ccb04b9..3719bb0750 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -10,6 +10,18 @@ namespace Umbraco.Core /// public static class EnumerableExtensions { + internal static bool HasDuplicates(this IEnumerable items, bool includeNull) + { + var hs = new HashSet(); + foreach (var item in items) + { + if ((item != null || includeNull) && !hs.Add(item)) + return true; + } + return false; + } + + /// /// Wraps this object instance into an IEnumerable{T} consisting of a single item. /// @@ -100,7 +112,7 @@ namespace Umbraco.Core } } - + /// /// Returns true if all items in the other collection exist in this collection /// diff --git a/src/Umbraco.Core/Migrations/Expressions/Create/CreateBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Create/CreateBuilder.cs index 017c652be3..8d4cb39a87 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Create/CreateBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Create/CreateBuilder.cs @@ -83,17 +83,25 @@ namespace Umbraco.Core.Migrations.Expressions.Create } /// - public ICreateConstraintOnTableBuilder PrimaryKey() + public ICreateConstraintOnTableBuilder PrimaryKey() => PrimaryKey(true); + + /// + public ICreateConstraintOnTableBuilder PrimaryKey(bool clustered) { var expression = new CreateConstraintExpression(_context, ConstraintType.PrimaryKey); + expression.Constraint.IsPrimaryKeyClustered = clustered; return new CreateConstraintBuilder(expression); } /// - public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName) + public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName) => PrimaryKey(primaryKeyName, true); + + /// + public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName, bool clustered) { var expression = new CreateConstraintExpression(_context, ConstraintType.PrimaryKey); expression.Constraint.ConstraintName = primaryKeyName; + expression.Constraint.IsPrimaryKeyClustered = clustered; return new CreateConstraintBuilder(expression); } diff --git a/src/Umbraco.Core/Migrations/Expressions/Create/Expressions/CreateConstraintExpression.cs b/src/Umbraco.Core/Migrations/Expressions/Create/Expressions/CreateConstraintExpression.cs index b977f5f71f..d55efbe0ee 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Create/Expressions/CreateConstraintExpression.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Create/Expressions/CreateConstraintExpression.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Migrations.Expressions.Create.Expressions var constraintType = (Constraint.IsPrimaryKeyConstraint) ? "PRIMARY KEY" : "UNIQUE"; if (Constraint.IsPrimaryKeyConstraint && SqlSyntax.SupportsClustered()) - constraintType += " CLUSTERED"; + constraintType += Constraint.IsPrimaryKeyClustered ? " CLUSTERED" : " NONCLUSTERED"; if (Constraint.IsNonUniqueConstraint) constraintType = string.Empty; diff --git a/src/Umbraco.Core/Migrations/Expressions/Create/ICreateBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Create/ICreateBuilder.cs index 98716a42a4..2e4df55245 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Create/ICreateBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Create/ICreateBuilder.cs @@ -68,6 +68,16 @@ namespace Umbraco.Core.Migrations.Expressions.Create /// ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName); + /// + /// Builds a Create Primary Key expression. + /// + ICreateConstraintOnTableBuilder PrimaryKey(bool clustered); + + /// + /// Builds a Create Primary Key expression. + /// + ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName, bool clustered); + /// /// Builds a Create Unique Constraint expression. /// diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index f0e6dd2e5b..1de983636b 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -226,6 +226,8 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); } diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index d4e35cccdf..04bcb7424a 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -95,6 +95,21 @@ namespace Umbraco.Core.Models protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) { + //enable this to detect duplicate property aliases. We do want this, however making this change in a + //patch release might be a little dangerous + + ////detect if there are any duplicate aliases - this cannot be allowed + //if (e.Action == NotifyCollectionChangedAction.Add + // || e.Action == NotifyCollectionChangedAction.Replace) + //{ + // var allAliases = _noGroupPropertyTypes.Concat(PropertyGroups.SelectMany(x => x.PropertyTypes)).Select(x => x.Alias); + // if (allAliases.HasDuplicates(false)) + // { + // var newAliases = string.Join(", ", e.NewItems.Cast().Select(x => x.Alias)); + // throw new InvalidOperationException($"Other property types already exist with the aliases: {newAliases}"); + // } + //} + OnPropertyChanged(nameof(PropertyTypes)); } @@ -388,15 +403,16 @@ namespace Umbraco.Core.Models var group = PropertyGroups[propertyGroupName]; if (group == null) return; - // re-assign the group's properties to no group + // first remove the group + PropertyGroups.RemoveItem(propertyGroupName); + + // Then re-assign the group's properties to no group foreach (var property in group.PropertyTypes) { property.PropertyGroupId = null; _noGroupPropertyTypes.Add(property); } - // actually remove the group - PropertyGroups.RemoveItem(propertyGroupName); OnPropertyChanged(nameof(PropertyGroups)); } diff --git a/src/Umbraco.Core/Models/PropertyCollection.cs b/src/Umbraco.Core/Models/PropertyCollection.cs index 977600a2f7..c587a45424 100644 --- a/src/Umbraco.Core/Models/PropertyCollection.cs +++ b/src/Umbraco.Core/Models/PropertyCollection.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Models public class PropertyCollection : KeyedCollection, INotifyCollectionChanged, IDeepCloneable { private readonly object _addLocker = new object(); - internal Action OnAdd; + internal Func AdditionValidator { get; set; } /// @@ -49,10 +49,12 @@ namespace Umbraco.Core.Models /// internal void Reset(IEnumerable properties) { + //collection events will be raised in each of these calls Clear(); + + //collection events will be raised in each of these calls foreach (var property in properties) Add(property); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// @@ -60,8 +62,9 @@ namespace Umbraco.Core.Models /// protected override void SetItem(int index, Property property) { + var oldItem = index >= 0 ? this[index] : property; base.SetItem(index, property); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property, index)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, property, oldItem)); } /// @@ -120,10 +123,8 @@ namespace Umbraco.Core.Models } } + //collection events will be raised in InsertItem with Add base.Add(property); - - OnAdd?.Invoke(); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property)); } } diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs index 26e0fef178..5422dfb792 100644 --- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs +++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs @@ -19,9 +19,6 @@ namespace Umbraco.Core.Models { private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim(); - // TODO: this doesn't seem to be used anywhere - internal Action OnAdd; - internal PropertyGroupCollection() { } @@ -37,16 +34,19 @@ namespace Umbraco.Core.Models /// internal void Reset(IEnumerable groups) { + //collection events will be raised in each of these calls Clear(); + + //collection events will be raised in each of these calls foreach (var group in groups) Add(group); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } protected override void SetItem(int index, PropertyGroup item) { + var oldItem = index >= 0 ? this[index] : item; base.SetItem(index, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem)); } protected override void RemoveItem(int index) @@ -84,6 +84,7 @@ namespace Umbraco.Core.Models if (keyExists) throw new Exception($"Naming conflict: Changing the name of PropertyGroup '{item.Name}' would result in duplicates"); + //collection events will be raised in SetItem SetItem(IndexOfKey(item.Id), item); return; } @@ -96,16 +97,14 @@ namespace Umbraco.Core.Models var exists = Contains(key); if (exists) { + //collection events will be raised in SetItem SetItem(IndexOfKey(key), item); return; } } } - + //collection events will be raised in InsertItem base.Add(item); - OnAdd?.Invoke(); - - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } finally { diff --git a/src/Umbraco.Core/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs index 6181ee078b..6e41f0d12b 100644 --- a/src/Umbraco.Core/Models/PropertyTypeCollection.cs +++ b/src/Umbraco.Core/Models/PropertyTypeCollection.cs @@ -19,9 +19,6 @@ namespace Umbraco.Core.Models [IgnoreDataMember] private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim(); - // TODO: This doesn't seem to be used - [IgnoreDataMember] - internal Action OnAdd; internal PropertyTypeCollection(bool supportsPublishing) { @@ -43,36 +40,44 @@ namespace Umbraco.Core.Models /// internal void Reset(IEnumerable properties) { + //collection events will be raised in each of these calls Clear(); + + //collection events will be raised in each of these calls foreach (var property in properties) - Add(property); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + Add(property); } protected override void SetItem(int index, PropertyType item) { item.SupportsPublishing = SupportsPublishing; - base.SetItem(index, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); + var oldItem = index >= 0 ? this[index] : item; + base.SetItem(index, item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem)); + item.PropertyChanged += Item_PropertyChanged; } protected override void RemoveItem(int index) { var removed = this[index]; base.RemoveItem(index); + removed.PropertyChanged -= Item_PropertyChanged; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); } protected override void InsertItem(int index, PropertyType item) { item.SupportsPublishing = SupportsPublishing; - base.InsertItem(index, item); + base.InsertItem(index, item); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); + item.PropertyChanged += Item_PropertyChanged; } protected override void ClearItems() { base.ClearItems(); + foreach (var item in this) + item.PropertyChanged -= Item_PropertyChanged; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } @@ -91,6 +96,7 @@ namespace Umbraco.Core.Models var exists = Contains(key); if (exists) { + //collection events will be raised in SetItem SetItem(IndexOfKey(key), item); return; } @@ -103,10 +109,8 @@ namespace Umbraco.Core.Models item.SortOrder = this.Max(x => x.SortOrder) + 1; } + //collection events will be raised in InsertItem base.Add(item); - OnAdd?.Invoke(); - - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } finally { @@ -115,6 +119,17 @@ namespace Umbraco.Core.Models } } + /// + /// Occurs when a property changes on a PropertyType that exists in this collection + /// + /// + /// + private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + var propType = (PropertyType)sender; + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, propType, propType)); + } + /// /// Determines whether this collection contains a whose alias matches the specified PropertyType. /// diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs index 63cbbf332b..ee269130f0 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs @@ -18,5 +18,6 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions public string ConstraintName { get; set; } public string TableName { get; set; } public ICollection Columns = new HashSet(); + public bool IsPrimaryKeyClustered { get; set; } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 6b8e6515f5..aeb4c3774f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -379,6 +379,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return "variantName"; } + // content type alias is invariant + if(ordering.OrderBy.InvariantEquals("contentTypeAlias")) + { + var joins = Sql() + .InnerJoin("ctype").On((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype"); + + // see notes in ApplyOrdering: the field MUST be selected + aliased + sql = Sql(InsertBefore(sql, "FROM", ", " + SqlSyntax.GetFieldName(x => x.Alias, "ctype") + " 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."); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs index 194a00b7f2..645ab9f924 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs @@ -240,6 +240,25 @@ namespace Umbraco.Core.Persistence.Repositories.Implement propertyIx++; } contentType.NoGroupPropertyTypes = noGroupPropertyTypes; + + // ensure builtin properties + if (contentType is MemberType memberType) + { + // ensure that the group exists (ok if it already exists) + memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName); + + // ensure that property types exist (ok if they already exist) + foreach (var (alias, propertyType) in builtinProperties) + { + var added = memberType.AddPropertyType(propertyType, Constants.Conventions.Member.StandardPropertiesGroupName); + + if (added) + { + var access = new MemberTypePropertyProfileAccess(false, false, false); + memberType.MemberTypePropertyTypes[alias] = access; + } + } + } } } @@ -264,7 +283,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (contentType is MemberType memberType) { var access = new MemberTypePropertyProfileAccess(dto.ViewOnProfile, dto.CanEdit, dto.IsSensitive); - memberType.MemberTypePropertyTypes.Add(dto.Alias, access); + memberType.MemberTypePropertyTypes[dto.Alias] = access; } return new PropertyType(dto.DataTypeDto.EditorAlias, storageType, readonlyStorageType, dto.Alias) diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs index bb58a8ef72..8151753a43 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationEditor.cs @@ -13,12 +13,15 @@ namespace Umbraco.Core.PropertyEditors /// public class ConfigurationEditor : IConfigurationEditor { + private IDictionary _defaultConfiguration; + /// /// Initializes a new instance of the class. /// public ConfigurationEditor() { Fields = new List(); + _defaultConfiguration = new Dictionary(); } /// @@ -61,7 +64,10 @@ namespace Umbraco.Core.PropertyEditors /// [JsonProperty("defaultConfig")] - public virtual IDictionary DefaultConfiguration => new Dictionary(); + public virtual IDictionary DefaultConfiguration { + get => _defaultConfiguration; + internal set => _defaultConfiguration = value; + } /// public virtual object DefaultConfigurationObject => DefaultConfiguration; diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index ae6ace996e..43f4b68b99 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -173,7 +173,13 @@ namespace Umbraco.Core.PropertyEditors /// protected virtual IConfigurationEditor CreateConfigurationEditor() { - return new ConfigurationEditor(); + var editor = new ConfigurationEditor(); + // pass the default configuration if this is not a property value editor + if((Type & EditorType.PropertyValue) == 0) + { + editor.DefaultConfiguration = _defaultConfiguration; + } + return editor; } /// diff --git a/src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs b/src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs index d1c2d23c4f..0a9d750964 100644 --- a/src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/DropDownFlexibleConfiguration.cs @@ -1,8 +1,8 @@ namespace Umbraco.Core.PropertyEditors { - internal class DropDownFlexibleConfiguration : ValueListConfiguration + public class DropDownFlexibleConfiguration : ValueListConfiguration { [ConfigurationField("multiple", "Enable multiple choice", "boolean", Description = "When checked, the dropdown will be a select multiple / combo box style dropdown.")] public bool Multiple { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs index 55ca199121..dc71c2a48f 100644 --- a/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs @@ -9,18 +9,18 @@ public bool EnableRange { get; set; } [ConfigurationField("initVal1", "Initial value", "number")] - public int InitialValue { get; set; } + public decimal InitialValue { get; set; } [ConfigurationField("initVal2", "Initial value 2", "number", Description = "Used when range is enabled")] - public int InitialValue2 { get; set; } + public decimal InitialValue2 { get; set; } [ConfigurationField("minVal", "Minimum value", "number")] - public int MinimumValue { get; set; } + public decimal MinimumValue { get; set; } [ConfigurationField("maxVal", "Maximum value", "number")] - public int MaximumValue { get; set; } + public decimal MaximumValue { get; set; } [ConfigurationField("step", "Step increments", "number")] - public int StepIncrements { get; set; } + public decimal StepIncrements { get; set; } } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 784d04864e..48e577a8f0 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -315,8 +316,16 @@ namespace Umbraco.Core.Services /// /// Empties the recycle bin. /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] OperationResult EmptyRecycleBin(); + /// + /// Empties the Recycle Bin by deleting all that resides in the bin + /// + /// Optional Id of the User emptying the Recycle Bin + OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId); + /// /// Sorts documents. /// diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 78da440bc6..3fecb20035 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -162,8 +162,16 @@ namespace Umbraco.Core.Services /// /// Empties the Recycle Bin by deleting all that resides in the bin /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] OperationResult EmptyRecycleBin(); + /// + /// Empties the Recycle Bin by deleting all that resides in the bin + /// + /// Optional Id of the User emptying the Recycle Bin + OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId); + /// /// Deletes all media of specified type. All children of deleted media is moved to Recycle Bin. /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index a4def1d209..0d35915e72 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.Linq; using Umbraco.Core.Events; @@ -1939,7 +1940,14 @@ namespace Umbraco.Core.Services.Implement /// /// Empties the Recycle Bin by deleting all that resides in the bin /// - public OperationResult EmptyRecycleBin() + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] + public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId); + + /// + /// Empties the Recycle Bin by deleting all that resides in the bin + /// + public OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId) { var nodeObjectType = Constants.ObjectTypes.Document; var deleted = new List(); @@ -1974,7 +1982,7 @@ namespace Umbraco.Core.Services.Implement recycleBinEventArgs.RecycleBinEmptiedSuccessfully = true; // oh my?! scope.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); - Audit(AuditType.Delete, 0, Constants.System.RecycleBinContent, "Recycle bin emptied"); + Audit(AuditType.Delete, userId, Constants.System.RecycleBinContent, "Recycle bin emptied"); scope.Complete(); } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index fa72896239..343fb7e2c6 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.IO; using System.Linq; @@ -1024,7 +1025,15 @@ namespace Umbraco.Core.Services.Implement /// /// Empties the Recycle Bin by deleting all that resides in the bin /// - public OperationResult EmptyRecycleBin() + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")] + public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId); + + /// + /// Empties the Recycle Bin by deleting all that resides in the bin + /// + /// Optional Id of the User emptying the Recycle Bin + public OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId) { var nodeObjectType = Constants.ObjectTypes.Media; var deleted = new List(); @@ -1063,7 +1072,7 @@ namespace Umbraco.Core.Services.Implement args.CanCancel = false; scope.Events.Dispatch(EmptiedRecycleBin, this, args); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); - Audit(AuditType.Delete, 0, Constants.System.RecycleBinMedia, "Empty Media recycle bin"); + Audit(AuditType.Delete, userId, Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); } diff --git a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index c092306473..e446e049b6 100644 --- a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -159,6 +159,7 @@ namespace Umbraco.Tests.Cache TestObjects.GetUmbracoSettings(), TestObjects.GetGlobalSettings(), new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); // just assert it does not throw diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 2a6739df38..fbf828ad20 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -81,6 +81,7 @@ namespace Umbraco.Tests.Cache.PublishedCache new WebSecurity(_httpContextFactory.HttpContext, Current.Services.UserService, globalSettings), umbracoSettings, Enumerable.Empty(), + Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 7b7574ce47..7459ae848b 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,10 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - _typeLoader = new TypeLoader(NoAppCache.Instance, IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of(), Mock.Of())); - - foreach (var file in Directory.GetFiles(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache"))) - File.Delete(file); + _typeLoader = new TypeLoader(NoAppCache.Instance, IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of(), Mock.Of()), false); // for testing, we'll specify which assemblies are scanned for the PluginTypeResolver // TODO: Should probably update this so it only searches this assembly and add custom types to be found diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 5fc0d628c9..2a144f3aaa 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -381,6 +381,9 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } + public override IPublishedContent GetById(bool preview, Udi nodeId) + => throw new NotSupportedException(); + public override bool HasById(bool preview, int contentId) { return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture)); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 71490465d0..0c7ee98c6d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -97,6 +97,9 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache throw new NotImplementedException(); } + public override IPublishedContent GetById(bool preview, Udi nodeId) + => throw new NotSupportedException(); + public override bool HasById(bool preview, int contentId) { return GetUmbracoMedia(contentId) != null; diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index d9e65ba6c6..9c3b976bf3 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -15,13 +15,47 @@ namespace Umbraco.Tests.Models [TestFixture] public class ContentTypeTests : UmbracoTestBase { + [Test] + [Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")] + public void Cannot_Add_Duplicate_Property_Aliases() + { + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[] + { + new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" } + }))); + + Assert.Throws(() => + contentType.PropertyTypeCollection.Add( + new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType" })); + + } + + [Test] + [Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")] + public void Cannot_Update_Duplicate_Property_Aliases() + { + var contentType = MockedContentTypes.CreateBasicContentType(); + + contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[] + { + new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" } + }))); + + contentType.PropertyTypeCollection.Add(new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType2" }); + + var toUpdate = contentType.PropertyTypeCollection["myPropertyType2"]; + + Assert.Throws(() => toUpdate.Alias = "myPropertyType"); + + } [Test] public void Can_Deep_Clone_Content_Type_Sort() { var contentType = new ContentTypeSort(new Lazy(() => 3), 4, "test"); - var clone = (ContentTypeSort) contentType.DeepClone(); + var clone = (ContentTypeSort)contentType.DeepClone(); Assert.AreNotSame(clone, contentType); Assert.AreEqual(clone, contentType); Assert.AreEqual(clone.Id.Value, contentType.Id.Value); @@ -54,7 +88,7 @@ namespace Umbraco.Tests.Models contentType.Id = 10; contentType.CreateDate = DateTime.Now; contentType.CreatorId = 22; - contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate") + contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate") { Id = 88 }); @@ -117,12 +151,12 @@ namespace Umbraco.Tests.Models { group.Id = ++i; } - contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } }; + contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } }; contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2") }; contentType.Id = 10; contentType.CreateDate = DateTime.Now; contentType.CreatorId = 22; - contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate") + contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate") { Id = 88 }); @@ -167,12 +201,12 @@ namespace Umbraco.Tests.Models { group.Id = ++i; } - contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } }; - contentType.AllowedContentTypes = new[] {new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2")}; + contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } }; + contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2") }; contentType.Id = 10; contentType.CreateDate = DateTime.Now; contentType.CreatorId = 22; - contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate") + contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate") { Id = 88 }); @@ -264,12 +298,12 @@ namespace Umbraco.Tests.Models { propertyType.Id = ++i; } - contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } }; + contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } }; contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2") }; contentType.Id = 10; contentType.CreateDate = DateTime.Now; contentType.CreatorId = 22; - contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate") + contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate") { Id = 88 }); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index 2b3ab50a22..79e8e43804 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -171,7 +171,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - //NOTE: This tests for left join logic (rev 7b14e8eacc65f82d4f184ef46c23340c09569052) [Test] public void Can_Get_All_Members_When_No_Properties_Assigned() @@ -200,7 +199,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] public void Can_Get_Member_Type_By_Id() { @@ -233,22 +231,114 @@ namespace Umbraco.Tests.Persistence.Repositories } } + // See: https://github.com/umbraco/Umbraco-CMS/issues/4963#issuecomment-483516698 + [Test] + public void Bug_Changing_Built_In_Member_Type_Property_Type_Aliases_Results_In_Exception() + { + var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + + var provider = TestObjects.GetScopeProvider(Logger); + using (provider.CreateScope()) + { + var repository = CreateRepository(provider); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType("mtype"); + + // created without the stub properties + Assert.AreEqual(1, memberType.PropertyGroups.Count); + Assert.AreEqual(3, memberType.PropertyTypes.Count()); + + // saving *new* member type adds the stub properties + repository.Save(memberType); + + // saving has added (and saved) the stub properties + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); + + foreach (var stub in stubs) + { + var prop = memberType.PropertyTypes.First(x => x.Alias == stub.Key); + prop.Alias = prop.Alias + "__0000"; + } + + // saving *existing* member type does *not* ensure stub properties + repository.Save(memberType); + + // therefore, nothing has changed + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); + + // fetching ensures that the stub properties are there + memberType = repository.Get("mtype"); + Assert.IsNotNull(memberType); + + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count * 2, memberType.PropertyTypes.Count()); + } + } + [Test] public void Built_In_Member_Type_Properties_Are_Automatically_Added_When_Creating() { + var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = provider.CreateScope()) + using (provider.CreateScope()) { var repository = CreateRepository(provider); IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + + // created without the stub properties + Assert.AreEqual(1, memberType.PropertyGroups.Count); + Assert.AreEqual(3, memberType.PropertyTypes.Count()); + + // saving *new* member type adds the stub properties repository.Save(memberType); + // saving has added (and saved) the stub properties + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); + // getting with stub properties memberType = repository.Get(memberType.Id); - Assert.That(memberType.PropertyTypes.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count)); - Assert.That(memberType.PropertyGroups.Count(), Is.EqualTo(2)); + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); + } + } + + [Test] + public void Built_In_Member_Type_Properties_Missing_Are_Automatically_Added_When_Creating() + { + var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + + var provider = TestObjects.GetScopeProvider(Logger); + using (provider.CreateScope()) + { + var repository = CreateRepository(provider); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + + // created without the stub properties + Assert.AreEqual(1, memberType.PropertyGroups.Count); + Assert.AreEqual(3, memberType.PropertyTypes.Count()); + + // add one stub property, others are still missing + memberType.AddPropertyType(stubs.First().Value, Constants.Conventions.Member.StandardPropertiesGroupName); + + // saving *new* member type adds the (missing) stub properties + repository.Save(memberType); + + // saving has added (and saved) the (missing) stub properties + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); + + // getting with stub properties + memberType = repository.Get(memberType.Id); + + Assert.AreEqual(2, memberType.PropertyGroups.Count); + Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index 7a9a882baa..7e6ae75356 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -71,6 +71,7 @@ namespace Umbraco.Tests.PublishedContent new WebSecurity(httpContext, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), + Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 86017be820..9828a14597 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -92,6 +92,9 @@ namespace Umbraco.Tests.PublishedContent throw new NotImplementedException(); } + public override IPublishedContent GetById(bool preview, Udi nodeId) + => throw new NotSupportedException(); + public override bool HasById(bool preview, int contentId) { return _content.ContainsKey(contentId); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs new file mode 100644 index 0000000000..5de99fdd38 --- /dev/null +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Tests.PublishedContent; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class MediaUrlProviderTests : BaseWebTest + { + private DefaultMediaUrlProvider _mediaUrlProvider; + + public override void SetUp() + { + base.SetUp(); + + _mediaUrlProvider = new DefaultMediaUrlProvider(); + } + + public override void TearDown() + { + base.TearDown(); + + _mediaUrlProvider = null; + } + + [Test] + public void Get_Media_Url_Resolves_Url_From_Upload_Property_Editor() + { + const string expected = "/media/rfeiw584/test.jpg"; + + var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null); + + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null); + + Assert.AreEqual(expected, resolvedUrl); + } + + [Test] + public void Get_Media_Url_Resolves_Url_From_Image_Cropper_Property_Editor() + { + const string expected = "/media/rfeiw584/test.jpg"; + + var configuration = new ImageCropperConfiguration(); + var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue + { + Src = expected + }); + + var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); + + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null); + + Assert.AreEqual(expected, resolvedUrl); + } + + [Test] + public void Get_Media_Url_Can_Resolve_Absolute_Url() + { + const string mediaUrl = "/media/rfeiw584/test.jpg"; + var expected = $"http://localhost{mediaUrl}"; + + var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); + var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, mediaUrl, null); + + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Absolute, null, null); + + Assert.AreEqual(expected, resolvedUrl); + } + + [Test] + public void Get_Media_Url_Returns_Empty_String_When_PropertyType_Is_Not_Supported() + { + var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); + var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.Boolean, "0", null); + + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "test", UrlProviderMode.Absolute, null, null); + + Assert.AreEqual(string.Empty, resolvedUrl); + } + + [Test] + public void Get_Media_Url_Can_Resolve_Variant_Property_Url() + { + var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); + + var umbracoFilePropertyType = CreatePropertyType(Constants.PropertyEditors.Aliases.UploadField, null, ContentVariation.Culture); + + const string enMediaUrl = "/media/rfeiw584/en.jpg"; + const string daMediaUrl = "/media/uf8ewud2/da.jpg"; + + var property = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "umbracoFile", + PropertyType = umbracoFilePropertyType, + }; + + property.SetValue("en", enMediaUrl, true); + property.SetValue("da", daMediaUrl); + + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); + var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; + + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, "da", null); + Assert.AreEqual(daMediaUrl, resolvedUrl); + } + + private static TestPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) + { + var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); + + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), + new[] {umbracoFilePropertyType}, ContentVariation.Nothing); + + return new TestPublishedContent(contentType, 1234, Guid.NewGuid(), + new Dictionary {{"umbracoFile", propertyValue } }, false); + } + + private static PublishedPropertyType CreatePropertyType(string propertyEditorAlias, object dataTypeConfiguration, ContentVariation variation) + { + var uploadDataType = new PublishedDataType(1234, propertyEditorAlias, new Lazy(() => dataTypeConfiguration)); + + var propertyValueConverters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + { + new UploadPropertyConverter(), + new ImageCropperValueConverter(), + }); + + var publishedModelFactory = Mock.Of(); + var publishedContentTypeFactory = new Mock(); + publishedContentTypeFactory.Setup(x => x.GetDataType(It.IsAny())) + .Returns(uploadDataType); + + return new PublishedPropertyType("umbracoFile", 42, true, variation, propertyValueConverters, publishedModelFactory, publishedContentTypeFactory.Object); + } + } +} diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 1dcc928141..d969356ce9 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -118,6 +118,7 @@ namespace Umbraco.Tests.Scoping new WebSecurity(httpContext, Current.Services.UserService, globalSettings), umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), urlProviders ?? Enumerable.Empty(), + Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index e32df610b9..2b04a02f46 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Linq; using System.Web; using Microsoft.Owin; using Moq; @@ -33,7 +34,7 @@ namespace Umbraco.Tests.Security Mock.Of(), Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), new List(),globalSettings, + TestObjects.GetUmbracoSettings(), new List(), Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); @@ -53,7 +54,7 @@ namespace Umbraco.Tests.Security Mock.Of(), Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), new List(), globalSettings, + TestObjects.GetUmbracoSettings(), new List(), Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index c6bbebf550..b5712a52aa 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -139,6 +139,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting webSecurity.Object, Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), Enumerable.Empty(), + Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 75e9cd60cb..647824ab66 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -122,6 +122,7 @@ namespace Umbraco.Tests.TestHelpers var umbracoSettings = GetUmbracoSettings(); var globalSettings = GetGlobalSettings(); var urlProviders = new UrlProviderCollection(Enumerable.Empty()); + var mediaUrlProviders = new MediaUrlProviderCollection(Enumerable.Empty()); if (accessor == null) accessor = new TestUmbracoContextAccessor(); @@ -133,6 +134,7 @@ namespace Umbraco.Tests.TestHelpers umbracoSettings, globalSettings, urlProviders, + mediaUrlProviders, Mock.Of()); return umbracoContextFactory.EnsureUmbracoContext(httpContext).UmbracoContext; diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 7f3c855593..146cb23c1f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -353,7 +353,7 @@ namespace Umbraco.Tests.TestHelpers } } - protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) + protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IEnumerable mediaUrlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) { // ensure we have a PublishedCachesService var service = snapshotService ?? PublishedSnapshotService as PublishedSnapshotService; @@ -380,6 +380,7 @@ namespace Umbraco.Tests.TestHelpers Factory.GetInstance()), umbracoSettings ?? Factory.GetInstance(), urlProviders ?? Enumerable.Empty(), + mediaUrlProviders ?? Enumerable.Empty(), globalSettings ?? Factory.GetInstance(), new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 57381eb287..d85f610236 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -80,7 +80,7 @@ namespace Umbraco.Tests.Testing.TestingTests .Returns(UrlInfo.Url("/hello/world/1234")); var urlProvider = urlProviderMock.Object; - var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.VariationContextAccessor); + var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, Enumerable.Empty(), umbracoContext.VariationContextAccessor); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = Mock.Of(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 6188f577bb..ddd67df6e5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -143,6 +143,7 @@ + diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 0b76de9879..eeb1eb4b37 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -72,6 +72,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -101,6 +102,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -130,6 +132,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -159,6 +162,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 7de2dd1aad..1a789023a5 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -47,6 +47,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -74,6 +75,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -104,6 +106,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); @@ -141,6 +144,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == webRoutingSettings), globalSettings, new UrlProviderCollection(Enumerable.Empty()), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index d1395c6f2e..5c291c9601 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -440,6 +440,7 @@ namespace Umbraco.Tests.Web.Mvc new WebSecurity(http, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), + Enumerable.Empty(), globalSettings, new TestVariationContextAccessor()); diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 5e9208faf9..5e4112449e 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -105,6 +105,7 @@ namespace Umbraco.Tests.Web Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), globalSettings, new UrlProviderCollection(new[] { testUrlProvider.Object }), + new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of()); using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index 21b72a3832..9ba010642e 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; @@ -31,6 +32,7 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), + Enumerable.Empty(), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor()); var r1 = new RouteData(); @@ -49,6 +51,7 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), + Enumerable.Empty(), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor()); @@ -77,6 +80,7 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), + Enumerable.Empty(), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor()); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index b996db214c..a6a6f1c6e1 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1104,6 +1104,7 @@ "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", "dev": true, + "optional": true, "requires": { "file-type": "^3.1.0" }, @@ -1112,7 +1113,8 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "dev": true, + "optional": true } } }, @@ -1523,6 +1525,7 @@ "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1532,13 +1535,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1554,6 +1559,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1742,7 +1748,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "dev": true, + "optional": true }, "buffer-fill": { "version": "1.0.0", @@ -1761,6 +1768,7 @@ "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", "dev": true, + "optional": true, "requires": { "file-type": "^3.1.0", "readable-stream": "^2.0.2", @@ -1772,19 +1780,22 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "dev": true, + "optional": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1800,6 +1811,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1808,13 +1820,15 @@ "version": "2.0.3", "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true + "dev": true, + "optional": true }, "vinyl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -1935,7 +1949,8 @@ "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 + "dev": true, + "optional": true }, "caseless": { "version": "0.12.0", @@ -1948,6 +1963,7 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", "dev": true, + "optional": true, "requires": { "get-proxy": "^1.0.1", "is-obj": "^1.0.0", @@ -1959,7 +1975,8 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true + "dev": true, + "optional": true } } }, @@ -2220,7 +2237,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true + "dev": true, + "optional": true }, "coa": { "version": "2.0.1", @@ -2316,6 +2334,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, + "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -2528,6 +2547,7 @@ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, + "optional": true, "requires": { "capture-stack-trace": "^1.0.0" } @@ -2782,6 +2802,7 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", "dev": true, + "optional": true, "requires": { "buffer-to-vinyl": "^1.0.0", "concat-stream": "^1.4.6", @@ -2799,6 +2820,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -2807,13 +2829,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, + "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -2825,6 +2849,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, + "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -2834,6 +2859,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2843,6 +2869,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, + "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -2856,6 +2883,7 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, + "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -2871,13 +2899,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -2889,13 +2919,15 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "dev": true, + "optional": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -2907,19 +2939,22 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2928,13 +2963,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -2944,6 +2981,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, + "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -2964,13 +3002,15 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, + "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -2981,6 +3021,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2996,6 +3037,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3005,6 +3047,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3014,6 +3057,7 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, + "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3024,6 +3068,7 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, + "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3034,6 +3079,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3046,6 +3092,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3057,6 +3104,7 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, + "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3084,6 +3132,7 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", "dev": true, + "optional": true, "requires": { "is-tar": "^1.0.0", "object-assign": "^2.0.0", @@ -3097,19 +3146,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3122,6 +3174,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3132,6 +3185,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3144,6 +3198,7 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", "dev": true, + "optional": true, "requires": { "is-bzip2": "^1.0.0", "object-assign": "^2.0.0", @@ -3158,19 +3213,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3183,6 +3241,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3193,6 +3252,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3205,6 +3265,7 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", "dev": true, + "optional": true, "requires": { "is-gzip": "^1.0.0", "object-assign": "^2.0.0", @@ -3218,19 +3279,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3243,6 +3307,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3253,6 +3318,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3265,6 +3331,7 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", "dev": true, + "optional": true, "requires": { "is-zip": "^1.0.0", "read-all-stream": "^3.0.0", @@ -3280,6 +3347,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3292,7 +3360,8 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "dev": true, + "optional": true }, "deep-is": { "version": "0.1.3", @@ -3491,6 +3560,7 @@ "resolved": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", "dev": true, + "optional": true, "requires": { "caw": "^1.0.1", "concat-stream": "^1.4.7", @@ -3514,6 +3584,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -3522,13 +3593,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, + "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -3540,6 +3613,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, + "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -3549,6 +3623,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3558,6 +3633,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, + "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -3571,6 +3647,7 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, + "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -3586,13 +3663,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3604,13 +3683,15 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "dev": true, + "optional": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3622,19 +3703,22 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3643,13 +3727,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -3659,6 +3745,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, + "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -3679,13 +3766,15 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, + "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -3696,6 +3785,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3711,6 +3801,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3720,6 +3811,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3729,6 +3821,7 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, + "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3739,6 +3832,7 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, + "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3749,6 +3843,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3761,6 +3856,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3772,6 +3868,7 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, + "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3814,6 +3911,7 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, + "optional": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -3826,6 +3924,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, + "optional": true, "requires": { "once": "^1.4.0" } @@ -3834,13 +3933,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3850,6 +3951,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3865,6 +3967,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3876,6 +3979,7 @@ "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", "dev": true, + "optional": true, "requires": { "onetime": "^1.0.0", "set-immediate-shim": "^1.0.0" @@ -3885,7 +3989,8 @@ "version": "1.1.0", "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true + "dev": true, + "optional": true } } }, @@ -4825,6 +4930,7 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, + "optional": true, "requires": { "pend": "~1.2.0" } @@ -4872,13 +4978,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=", - "dev": true + "dev": true, + "optional": true }, "filenamify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", "dev": true, + "optional": true, "requires": { "filename-reserved-regex": "^1.0.0", "strip-outer": "^1.0.0", @@ -5125,7 +5233,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true + "dev": true, + "optional": true }, "fs-extra": { "version": "1.0.0", @@ -5189,7 +5298,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5210,12 +5320,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5230,17 +5342,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5357,7 +5472,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5369,6 +5485,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5383,6 +5500,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5390,12 +5508,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5414,6 +5534,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5494,7 +5615,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5506,6 +5628,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5591,7 +5714,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5627,6 +5751,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5646,6 +5771,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5689,12 +5815,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5730,6 +5858,7 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", "dev": true, + "optional": true, "requires": { "rc": "^1.1.2" } @@ -6036,6 +6165,7 @@ "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true, + "optional": true, "requires": { "create-error-class": "^3.0.1", "duplexer2": "^0.1.4", @@ -6059,6 +6189,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -6067,19 +6198,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, + "optional": true, "requires": { "error-ex": "^1.2.0" } @@ -6089,6 +6223,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6104,6 +6239,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6123,7 +6259,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "dev": true, + "optional": true }, "growly": { "version": "1.3.0", @@ -6419,6 +6556,7 @@ "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", "dev": true, + "optional": true, "requires": { "archive-type": "^3.0.0", "decompress": "^3.0.0", @@ -6430,13 +6568,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6452,6 +6592,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -7069,6 +7210,7 @@ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", "dev": true, + "optional": true, "requires": { "convert-source-map": "^1.1.1", "graceful-fs": "^4.1.2", @@ -7081,13 +7223,15 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "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, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -7097,6 +7241,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -7927,7 +8072,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", "integrity": "sha1-XuWOqlounIDiFAe+3yOuWsCRs/w=", - "dev": true + "dev": true, + "optional": true }, "is-callable": { "version": "1.1.4", @@ -8062,7 +8208,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", - "dev": true + "dev": true, + "optional": true }, "is-jpg": { "version": "1.0.1", @@ -8075,7 +8222,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", "integrity": "sha1-fUxXKDd+84bD4ZSpkRv1fG3DNec=", - "dev": true + "dev": true, + "optional": true }, "is-number": { "version": "3.0.0", @@ -8141,7 +8289,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true + "dev": true, + "optional": true }, "is-regex": { "version": "1.0.4", @@ -8171,7 +8320,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true + "dev": true, + "optional": true }, "is-stream": { "version": "1.1.0", @@ -8201,7 +8351,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", "integrity": "sha1-L2suF5LB9bs2UZrKqdZcDSb+hT0=", - "dev": true + "dev": true, + "optional": true }, "is-typedarray": { "version": "1.0.0", @@ -8222,7 +8373,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true + "dev": true, + "optional": true }, "is-utf8": { "version": "0.2.1", @@ -8234,7 +8386,8 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", - "dev": true + "dev": true, + "optional": true }, "is-windows": { "version": "1.0.2", @@ -8252,7 +8405,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", "integrity": "sha1-R7Co/004p2QxzP2ZqOFaTIa6IyU=", - "dev": true + "dev": true, + "optional": true }, "isarray": { "version": "0.0.1", @@ -8306,9 +8460,9 @@ } }, "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.0.tgz", + "integrity": "sha512-ggRCXln9zEqv6OqAGXFEcshF5dSBvCkzj6Gm2gzuR5fWawaX8t7cxKVkkygKODrDAzKdoYw3l/e3pm3vlT4IbQ==" }, "jquery-ui-dist": { "version": "1.12.1", @@ -8583,6 +8737,7 @@ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.5" }, @@ -8591,13 +8746,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8613,6 +8770,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -8918,7 +9076,8 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true + "dev": true, + "optional": true }, "lodash.isobject": { "version": "2.4.1", @@ -9115,7 +9274,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true + "dev": true, + "optional": true }, "lpad-align": { "version": "1.1.2", @@ -9511,7 +9671,8 @@ "version": "1.0.0", "resolved": "http://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", - "dev": true + "dev": true, + "optional": true }, "node.extend": { "version": "1.1.8", @@ -12561,7 +12722,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "dev": true, + "optional": true }, "p-pipe": { "version": "1.2.0", @@ -13272,7 +13434,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "dev": true, + "optional": true }, "preserve": { "version": "0.2.0", @@ -13404,6 +13567,7 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "optional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -13416,6 +13580,7 @@ "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0", "readable-stream": "^2.0.0" @@ -13425,13 +13590,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13447,6 +13614,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14063,6 +14231,7 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, + "optional": true, "requires": { "commander": "~2.8.1" } @@ -14208,7 +14377,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true + "dev": true, + "optional": true }, "set-value": { "version": "2.0.0", @@ -14713,7 +14883,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", - "dev": true + "dev": true, + "optional": true }, "static-extend": { "version": "0.1.2", @@ -14757,6 +14928,7 @@ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, + "optional": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -14767,6 +14939,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -14775,13 +14948,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -14797,6 +14972,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14813,7 +14989,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true + "dev": true, + "optional": true }, "streamroller": { "version": "0.7.0", @@ -14991,6 +15168,7 @@ "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "dev": true, + "optional": true, "requires": { "chalk": "^1.0.0", "get-stdin": "^4.0.1", @@ -15004,13 +15182,15 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "optional": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15024,6 +15204,7 @@ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", "integrity": "sha1-hHSREZ/MtftDYhfMc39/qtUPYD8=", "dev": true, + "optional": true, "requires": { "is-relative": "^0.1.0" } @@ -15032,13 +15213,15 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", "integrity": "sha1-kF/uiuhvRbPsYUvDwVyGnfCHboI=", - "dev": true + "dev": true, + "optional": true }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "optional": true } } }, @@ -15069,6 +15252,7 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15102,6 +15286,7 @@ "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", "dev": true, + "optional": true, "requires": { "chalk": "^1.0.0" }, @@ -15110,13 +15295,15 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "optional": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15129,7 +15316,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "optional": true } } }, @@ -15191,6 +15379,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, + "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15206,6 +15395,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, + "optional": true, "requires": { "once": "^1.4.0" } @@ -15214,13 +15404,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -15230,6 +15422,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15245,6 +15438,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15343,6 +15537,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -15367,7 +15562,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", - "dev": true + "dev": true, + "optional": true }, "timsort": { "version": "0.3.0", @@ -15430,6 +15626,7 @@ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1" }, @@ -15439,6 +15636,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -15455,7 +15653,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true + "dev": true, + "optional": true }, "to-fast-properties": { "version": "2.0.0", @@ -15534,6 +15733,7 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15782,7 +15982,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", - "dev": true + "dev": true, + "optional": true }, "upath": { "version": "1.1.0", @@ -15810,6 +16011,7 @@ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, + "optional": true, "requires": { "prepend-http": "^1.0.1" } @@ -15895,7 +16097,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "dev": true + "dev": true, + "optional": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -15940,6 +16143,7 @@ "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", "dev": true, + "optional": true, "requires": { "object-assign": "^4.0.1", "readable-stream": "^2.0.0" @@ -15949,19 +16153,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": 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, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15977,6 +16184,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -16116,6 +16324,7 @@ "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", "dev": true, + "optional": true, "requires": { "wrap-fn": "^0.1.0" } @@ -16206,6 +16415,7 @@ "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", "integrity": "sha1-8htuQQFv9KfjFyDbxjoJAWvfmEU=", "dev": true, + "optional": true, "requires": { "co": "3.1.0" } @@ -16309,6 +16519,7 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, + "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 3a5ff22f04..67d08f51d2 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -29,7 +29,7 @@ "diff": "3.5.0", "flatpickr": "4.5.2", "font-awesome": "4.7.0", - "jquery": "3.3.1", + "jquery": "^3.4.0", "jquery-ui-dist": "1.12.1", "jquery-ui-touch-punch": "0.2.3", "lazyload-js": "1.0.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js index 39058b32d7..672a00e27c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js @@ -126,6 +126,7 @@ function togglePassword() { var elem = $("form[name='vm.loginForm'] input[name='password']"); elem.attr("type", (elem.attr("type") === "text" ? "password" : "text")); + elem.focus(); $(".password-text.show, .password-text.hide").toggle(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js new file mode 100644 index 0000000000..709a3aaae4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtogglegroup.directive.js @@ -0,0 +1,105 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbToggleGroup +@restrict E +@scope + +@description +Use this directive to render a group of toggle buttons. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.toggle = toggle;
+
+            function toggle(item) {
+                if(item.checked) {
+                    // do something if item is checked
+                }
+                else {
+                    // do something else if item is unchecked
+                }
+            }
+
+            function init() {
+                vm.items = [{
+                    name: "Item 1",
+                    description: "Item 1 description",
+                    checked: false,
+                    disabled: false
+                }, {
+                    name: "Item 2",
+                    description: "Item 2 description",
+                    checked: true,
+                    disabled: true
+                }];
+            }
+
+            init();
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+
+    })();
+
+ +@param {Array} items The items to list in the toggle group +@param {callback} onClick The function which should be called when the toggle is clicked for one of the items. + +**/ + +(function () { + 'use strict'; + + function ToggleGroupDirective() { + + function link(scope, el, attr, ctrl) { + + scope.change = function(item) { + if (item.disabled) { + return; + } + + item.checked = !item.checked; + if(scope.onClick) { + scope.onClick({'item': item}); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/buttons/umb-toggle-group.html', + scope: { + items: "=", + onClick: "&" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbToggleGroup', ToggleGroupDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js index 4ba4cf96bb..b81e62a66b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js @@ -12,6 +12,7 @@ function treeSearchBox(localizationService, searchService, $q) { searchFromName: "@", showSearch: "@", section: "@", + ignoreUserStartNodes: "@", hideSearchCallback: "=", searchCallback: "=" }, @@ -34,6 +35,7 @@ function treeSearchBox(localizationService, searchService, $q) { scope.showSearch = "false"; } + //used to cancel any request in progress if another one needs to take it's place var canceler = null; @@ -60,6 +62,11 @@ function treeSearchBox(localizationService, searchService, $q) { searchArgs["searchFrom"] = scope.searchFromId; } + //append ignoreUserStartNodes value if there is one + if (scope.ignoreUserStartNodes) { + searchArgs["ignoreUserStartNodes"] = scope.ignoreUserStartNodes; + } + searcher(searchArgs).then(function (data) { scope.searchCallback(data); //set back to null so it can be re-created diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js index ca9e7a6712..ce6b90c1ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js @@ -60,6 +60,7 @@ function confirmDirective() { link: function (scope, element, attr, ctrl) { scope.showCancel = false; scope.showConfirm = false; + scope.confirmButtonState = "init"; if (scope.onConfirm) { scope.showConfirm = true; @@ -68,6 +69,15 @@ function confirmDirective() { if (scope.onCancel) { scope.showCancel = true; } + + scope.confirm = function () { + if (!scope.onConfirm) { + return; + } + + scope.confirmButtonState = "busy"; + scope.onConfirm(); + } } }; } 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 e7abc81841..4c1a8747d1 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 @@ -91,6 +91,10 @@ Use this directive to generate a pagination. function link(scope, el, attr, ctrl) { function activate() { + // page number is sometimes a string - let's make sure it's an int before we do anything with it + if (scope.pageNumber) { + scope.pageNumber = parseInt(scope.pageNumber); + } scope.pagination = []; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/umbpermission.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/umbpermission.directive.js deleted file mode 100644 index bae87789ca..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/umbpermission.directive.js +++ /dev/null @@ -1,36 +0,0 @@ -(function () { - 'use strict'; - - function PermissionDirective() { - - function link(scope, el, attr, ctrl) { - - scope.change = function() { - scope.selected = !scope.selected; - if(scope.onChange) { - scope.onChange({'selected': scope.selected}); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/users/umb-permission.html', - scope: { - name: "=", - description: "=?", - selected: "=", - onChange: "&" - }, - link: link - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbPermission', PermissionDirective); - -})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js b/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js index d187714c62..30daaf5837 100644 --- a/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js +++ b/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js @@ -59,20 +59,27 @@ } //A 401 means that the user is not logged in - if (rejection.status === 401 && !rejection.config.url.endsWith("umbraco/backoffice/UmbracoApi/Authentication/GetCurrentUser")) { + if (rejection.status === 401) { + //avoid an infinite loop + var umbRequestHelper = $injector.get('umbRequestHelper'); + var getCurrentUserPath = umbRequestHelper.getApiUrl("authenticationApiBaseUrl", "GetCurrentUser"); + if (!rejection.config.url.endsWith(getCurrentUserPath)) { - var userService = $injector.get('userService'); // see above + var userService = $injector.get('userService'); // see above - //Associate the user name with the retry to ensure we retry for the right user - return userService.getCurrentUser() - .then(function (user) { - var userName = user ? user.name : null; - //The request bounced because it was not authorized - add a new request to the retry queue - return requestRetryQueue.pushRetryFn('unauthorized-server', userName, function retryRequest() { - // We must use $injector to get the $http service to prevent circular dependency - return $injector.get('$http')(rejection.config); + //Associate the user name with the retry to ensure we retry for the right user + return userService.getCurrentUser() + .then(function(user) { + var userName = user ? user.name : null; + //The request bounced because it was not authorized - add a new request to the retry queue + return requestRetryQueue.pushRetryFn('unauthorized-server', + userName, + function retryRequest() { + // We must use $injector to get the $http service to prevent circular dependency + return $injector.get('$http')(rejection.config); + }); }); - }); + } } else if (rejection.status === 404) { 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 8b93666172..601ff2f671 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 @@ -215,14 +215,6 @@ angular.module('umbraco.mocks'). "placeholders_nameentity": "Name the %0%...", "placeholders_search": "Type to search...", "placeholders_filter": "Type to filter...", - "editcontenttype_allowedchildnodetypes": "Allowed child nodetypes", - "editcontenttype_create": "Create", - "editcontenttype_deletetab": "Delete tab", - "editcontenttype_description": "Description", - "editcontenttype_newtab": "New tab", - "editcontenttype_tab": "Tab", - "editcontenttype_thumbnail": "Thumbnail", - "editcontenttype_iscontainercontenttype": "Use as container content type", "editdatatype_addPrevalue": "Add prevalue", "editdatatype_dataBaseDatatype": "Database datatype", "editdatatype_guid": "Property editor GUID", 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 b807a4dc31..d571de0e2d 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 @@ -365,17 +365,28 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * * @param {Int} id id of content item to return - * @param {Int} culture optional culture to retrieve the item in + * @param {Bool} options.ignoreUserStartNodes set to true to allow a user to choose nodes that they normally don't have access to * @returns {Promise} resourcePromise object containing the content item. * */ - getById: function (id) { + getById: function (id, options) { + var defaults = { + ignoreUserStartNodes: false + }; + if (options === undefined) { + options = {}; + } + //overwrite the defaults if there are any specified + angular.extend(defaults, options); + //now copy back to the options we will use + options = defaults; + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "contentApiBaseUrl", "GetById", - { id: id })), + [{ id: id }, { ignoreUserStartNodes: options.ignoreUserStartNodes }])), 'Failed to retrieve data for content id ' + id) .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 753d180880..d5145727ac 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 @@ -284,15 +284,31 @@ function entityResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the entity. * */ - getAncestors: function (id, type, culture) { + getAncestors: function (id, type, culture, options) { + var defaults = { + ignoreUserStartNodes: false + }; + if (options === undefined) { + options = {}; + } + //overwrite the defaults if there are any specified + angular.extend(defaults, options); + //now copy back to the options we will use + options = defaults; if (culture === undefined) culture = ""; return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetAncestors", - [{ id: id }, { type: type }, { culture: culture }])), - 'Failed to retrieve ancestor data for id ' + id); + [ + { id: id }, + { type: type }, + { culture: culture }, + { ignoreUserStartNodes: options.ignoreUserStartNodes } + ])), + + 'Failed to retrieve ancestor data for id ' + id); }, /** @@ -424,7 +440,8 @@ function entityResource($q, $http, umbRequestHelper) { pageNumber: 1, filter: '', orderDirection: "Ascending", - orderBy: "SortOrder" + orderBy: "SortOrder", + ignoreUserStartNodes: false }; if (options === undefined) { options = {}; @@ -453,7 +470,8 @@ function entityResource($q, $http, umbRequestHelper) { pageSize: options.pageSize, orderBy: options.orderBy, orderDirection: options.orderDirection, - filter: encodeURIComponent(options.filter) + filter: encodeURIComponent(options.filter), + ignoreUserStartNodes: options.ignoreUserStartNodes } )), 'Failed to retrieve child data for id ' + parentId); @@ -481,12 +499,19 @@ function entityResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the entity array. * */ - search: function (query, type, searchFrom, canceler) { + search: function (query, type, options, canceler) { var args = [{ query: query }, { type: type }]; - if (searchFrom) { - args.push({ searchFrom: searchFrom }); + + if(options !== undefined) { + if (options.searchFrom) { + args.push({ searchFrom: options.searchFrom }); + } + if (options.ignoreUserStartNodes) { + args.push({ ignoreUserStartNodes: options.ignoreUserStartNodes }); + } } + var httpConfig = {}; if (canceler) { 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 dad5345e6c..bcdaddd22f 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 @@ -53,7 +53,7 @@ function logResource($q, $http, umbRequestHelper) { * * @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.pageSize if paging data, number of nodes per page, default = 10 * @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 @@ -122,7 +122,7 @@ function logResource($q, $http, umbRequestHelper) { * * * @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.pageSize if paging data, number of nodes per page, default = 10 * @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 diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js index 1d6d5171a1..462184c9f2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js @@ -329,7 +329,8 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { filter: '', orderDirection: "Ascending", orderBy: "SortOrder", - orderBySystemField: true + orderBySystemField: true, + ignoreUserStartNodes: false }; if (options === undefined) { options = {}; @@ -367,6 +368,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { "GetChildren", [ { id: parentId }, + { ignoreUserStartNodes: options.ignoreUserStartNodes }, { pageNumber: options.pageNumber }, { pageSize: options.pageSize }, { orderBy: options.orderBy }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js index 8aaddbf98f..20d014ab0f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js @@ -45,7 +45,7 @@ (function () { 'use strict'; - function listViewHelper(localStorageService) { + function listViewHelper($location, localStorageService, urlHelper) { var firstSelectedIndex = 0; var localStorageKey = "umblistViewLayout"; @@ -559,6 +559,32 @@ } + /** + * @ngdoc method + * @name umbraco.services.listViewHelper#editItem + * @methodOf umbraco.services.listViewHelper + * + * @description + * Method for opening an item in a list view for editing. + * + * @param {Object} item The item to edit + */ + function editItem(item) { + if (!item.editPath) { + return; + } + var parts = item.editPath.split("?"); + var path = parts[0]; + var params = parts[1] + ? urlHelper.getQueryStringParams("?" + parts[1]) + : {}; + + $location.path(path); + for (var p in params) { + $location.search(p, params[p]); + } + } + function isMatchingLayout(id, layout) { // legacy format uses "nodeId", be sure to look for both return layout.id === id || layout.nodeId === id; @@ -579,7 +605,8 @@ isSelectedAll: isSelectedAll, setSortingDirection: setSortingDirection, setSorting: setSorting, - getButtonPermissions: getButtonPermissions + getButtonPermissions: getButtonPermissions, + editItem: editItem }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js index 04c431767c..a2010d20f2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js @@ -42,7 +42,11 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) { + var options = { + searchFrom: args.searchFrom + } + + return entityResource.search(args.term, "Member", options).then(function (data) { _.each(data, function (item) { searchResultFormatter.configureMemberResult(item); }); @@ -67,7 +71,12 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) { + var options = { + searchFrom: args.searchFrom, + ignoreUserStartNodes: args.ignoreUserStartNodes + } + + return entityResource.search(args.term, "Document", options, args.canceler).then(function (data) { _.each(data, function (item) { searchResultFormatter.configureContentResult(item); }); @@ -92,7 +101,12 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) { + var options = { + searchFrom: args.searchFrom, + ignoreUserStartNodes: args.ignoreUserStartNodes + } + + return entityResource.search(args.term, "Media", options).then(function (data) { _.each(data, function (item) { searchResultFormatter.configureMediaResult(item); }); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index ce4bf6077c..2fd75184ca 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -48,7 +48,14 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s if (configuredStylesheets) { angular.forEach(configuredStylesheets, function (val, key) { - stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + val + ".css"); + if (val.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/") === 0) { + // current format (full path to stylesheet) + stylesheets.push(val); + } + else { + // legacy format (stylesheet name only) - must prefix with stylesheet folder and postfix with ".css" + stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + val + ".css"); + } promises.push(stylesheetResource.getRulesByName(val).then(function (rules) { angular.forEach(rules, function (rule) { @@ -1143,11 +1150,25 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s let self = this; + function getIgnoreUserStartNodes(args) { + var ignoreUserStartNodes = false; + // Most property editors have a "config" property with ignoreUserStartNodes on then + if (args.model.config) { + ignoreUserStartNodes = Object.toBoolean(args.model.config.ignoreUserStartNodes); + } + // EXCEPT for the grid's TinyMCE editor, that one wants to be special and the config is called "configuration" instead + else if (args.model.configuration) { + ignoreUserStartNodes = Object.toBoolean(args.model.configuration.ignoreUserStartNodes); + } + return ignoreUserStartNodes; + } + //create link picker self.createLinkPicker(args.editor, function (currentTarget, anchorElement) { var linkPicker = { currentTarget: currentTarget, anchors: editorState.current ? self.getAnchorNames(JSON.stringify(editorState.current.properties)) : [], + ignoreUserStartNodes: getIgnoreUserStartNodes(args), submit: function (model) { self.insertLinkInEditor(args.editor, model.target, anchorElement); editorService.close(); @@ -1161,13 +1182,25 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //Create the insert media plugin self.createMediaPicker(args.editor, function (currentTarget, userData) { + + var startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; + var startNodeIsVirtual = userData.startMediaIds.length !== 1; + + var ignoreUserStartNodes = getIgnoreUserStartNodes(args); + if (ignoreUserStartNodes) { + ignoreUserStartNodes = true; + startNodeId = -1; + startNodeIsVirtual = true; + } + var mediaPicker = { currentTarget: currentTarget, onlyImages: true, showDetails: true, disableFolderSelect: true, - startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], - startNodeIsVirtual: userData.startMediaIds.length !== 1, + startNodeId: startNodeId, + startNodeIsVirtual: startNodeIsVirtual, + ignoreUserStartNodes: ignoreUserStartNodes, submit: function (model) { self.insertMediaInEditor(args.editor, model.selection[0]); editorService.close(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index 6a15c0f553..d61d1c3ba1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -29,6 +29,15 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS return cacheKey; } + // Adapted from: https://stackoverflow.com/a/2140723 + // Please note, we can NOT test this functionality correctly in Phantom because it implements + // the localeCompare method incorrectly: https://github.com/ariya/phantomjs/issues/11063 + function invariantEquals(a, b) { + return typeof a === "string" && typeof b === "string" + ? a.localeCompare(b, undefined, { sensitivity: "base" }) === 0 + : a === b; + } + return { /** Internal method to return the tree cache */ @@ -165,8 +174,8 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS Umbraco.Sys.ServerVariables.umbracoPlugins.trees && angular.isArray(Umbraco.Sys.ServerVariables.umbracoPlugins.trees)) { - var found = _.find(Umbraco.Sys.ServerVariables.umbracoPlugins.trees, function(item) { - return item.alias === treeAlias; + var found = _.find(Umbraco.Sys.ServerVariables.umbracoPlugins.trees, function (item) { + return invariantEquals(item.alias, treeAlias); }); return found ? found.packageFolder : undefined; 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 c2b2ba26d7..3ca5cd2b2c 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -18,7 +18,20 @@ function MainController($scope, $location, appState, treeService, notificationsS $scope.drawer = {}; $scope.search = {}; $scope.login = {}; + $scope.tabbingActive = false; + // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. + // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 + function handleFirstTab(evt) { + if (evt.keyCode === 9) { + $scope.tabbingActive = true; + window.removeEventListener('keydown', handleFirstTab); + } + } + + window.addEventListener("keydown", handleFirstTab); + + $scope.removeNotification = function (index) { notificationsService.remove(index); }; diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 2a53c25f68..4c86f3289b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -164,6 +164,7 @@ @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; @import "components/buttons/umb-toggle.less"; +@import "components/buttons/umb-toggle-group.less"; @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; @@ -178,7 +179,6 @@ @import "components/users/umb-user-group-preview.less"; @import "components/users/umb-user-preview.less"; @import "components/users/umb-user-picker-list.less"; -@import "components/users/umb-permission.less"; // Utilities diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less index 7c45801f43..69a1e1978b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less @@ -3,10 +3,6 @@ display: inline-block; } - .umb-button__button:focus { - outline: none; -} - .umb-button__button { position: relative; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less new file mode 100644 index 0000000000..df60c8aed1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle-group.less @@ -0,0 +1,35 @@ +.umb-toggle-group { + .umb-toggle-group-item { + display: flex; + border-bottom: 1px solid @gray-9; + padding: 7px 0; + } + + .umb-toggle-group-item:last-of-type { + border-bottom: none; + } + + .umb-toggle-group-item__toggle { + padding-right: 20px; + cursor: pointer; + } + + .umb-toggle-group-item__content { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 1 auto; + cursor: pointer; + } + + .umb-toggle-group-item--disabled .umb-toggle-group-item__toggle, + .umb-toggle-group-item--disabled .umb-toggle-group-item__content { + cursor: not-allowed; + opacity: 0.8; + } + + .umb-toggle-group-item__description { + font-size: 13px; + color: @gray-4; + } +} 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 515efdfa00..78cccac57a 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 @@ -28,6 +28,7 @@ padding-left: 10px; padding-right: 10px; background-color: @pinkLight; + border-color: @pinkLight; border-radius: 3px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less index 2d1cbd10ff..2a8137e5f9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less @@ -1,7 +1,7 @@ .umb-expansion-panel { background: @white; border-radius: 3px; - margin-bottom: 16px; + margin-bottom: 20px; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.16); } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less index b66ab40335..8358dc4b67 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less @@ -228,40 +228,64 @@ body.touch .umb-tree { } } -.umb-tree-item__annotation { - &::before { - font-family: 'icomoon'; - position: absolute; - bottom: 0; - } -} +.has-unpublished-version, .is-container, .protected { + > .umb-tree-item__inner { + > .umb-tree-item__annotation { + background-color: @white; + border-radius: 50%; + width: 12px; + height: 12px; + position: absolute; + margin-left: 12px; + top: 17px; -.has-unpublished-version > .umb-tree-item__inner > .umb-tree-item__annotation::before { - content: "\e25a"; - color: @green; - font-size: 20px; - margin-left: -25px; + &::before { + font-family: 'icomoon'; + position: absolute; + top: -4px; + } + } + + &:hover > .umb-tree-item__annotation { + background-color: @ui-option-hover; + } + } + + &.current > .umb-tree-item__inner > .umb-tree-item__annotation { + background-color: @pinkLight; + } } .is-container > .umb-tree-item__inner > .umb-tree-item__annotation::before { content: "\e04e"; color: @blue; font-size: 9px; - margin-left: -20px; + margin-left: 2px; + left: 0px; +} + +.has-unpublished-version > .umb-tree-item__inner > .umb-tree-item__annotation::before { + content: "\e25a"; + color: @green; + font-size: 23px; + margin-left: 16px; + left: -21px; } .protected > .umb-tree-item__inner > .umb-tree-item__annotation::before { content: "\e256"; color: @red; - font-size: 20px; - margin-left: -25px; + font-size: 23px; + margin-left: -3px; + left: -2px; } .locked > .umb-tree-item__inner > .umb-tree-item__annotation::before { content: "\e0a7"; color: @red; font-size: 9px; - margin-left: -20px; + margin-left: 2px; + left: 0px; } .no-access { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-box.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-box.less index fb83504a1f..c0e91e28c2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-box.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-box.less @@ -1,7 +1,7 @@ .umb-box { background: @white; border-radius: 3px; - margin-bottom: 8px; + margin-bottom: 20px; box-shadow: 0 1px 1px 0 rgba(0,0,0,.16); } @@ -28,4 +28,4 @@ .umb-box-content { padding: 20px; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less index 75ac13bdd0..021fc8cc9b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-checkmark.less @@ -2,19 +2,21 @@ border: 2px solid @white; width: 25px; height: 25px; - background: @gray-7; - border-radius: 50%; + border: 1px solid @gray-7; + border-radius: 3px; box-sizing: border-box; display: flex; justify-content: center; align-items: center; - color: @white; + color: @gray-7; cursor: pointer; font-size: 15px; } .umb-checkmark--checked { - background: @green; + background: @ui-active; + border-color: @ui-active; + color: @white; } .umb-checkmark--xs { 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 e2a35c5fb5..4c23aef5f0 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 @@ -137,7 +137,7 @@ input.umb-group-builder__group-title-input:disabled:hover { .umb-group-builder__group-add-property { min-height: 46px; - margin-right: 30px; + margin-right: 45px; margin-left: 270px; border-radius: 3px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less index bfe41ccaac..12d7085b0a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less @@ -31,6 +31,11 @@ a.umb-list-item:focus { opacity: 1; } +.umb-list-item__description { + font-size: 13px; + color: @gray-4; +} + .umb-list-checkbox { position: absolute; opacity: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index e0abd3fd26..db0d96b79d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -127,6 +127,9 @@ border-radius: 200px; text-decoration: none !important; } +.umb-nested-content__icon.umb-nested-content__icon--disabled:hover { + cursor: default; +} .umb-nested-content__icon:hover, .umb-nested-content__icon--active 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 6674e01475..f387b6540b 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 @@ -136,9 +136,14 @@ input.umb-table__input { } .umb-table-body__link { + + color: @ui-option-type; + font-size: 14px; + font-weight: bold; text-decoration: none; - - &:hover { + + &:hover, &:focus { + color: @ui-option-type-hover; text-decoration: underline; } } @@ -259,6 +264,15 @@ input.umb-table__input { flex: 0 0 auto !important; } +.umb-table-cell--small { + flex: .5 .5 1%; + max-width: 12.5%; +} +.umb-table-cell--large { + flex: 1 1 25%; + max-width: 25%; +} + .umb-table-cell--faded { opacity: 0.4; } 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 deleted file mode 100644 index 0d74986d65..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less +++ /dev/null @@ -1,33 +0,0 @@ -.umb-permission { - display: flex; - border-bottom: 1px solid @gray-9; - padding: 7px 0; -} - -.umb-permission:last-of-type { - border-bottom: none; -} - -.umb-permission__toggle { - padding-right: 20px; - cursor: pointer; -} - -.umb-permission__content { - display: flex; - flex-direction: column; - justify-content: center; - flex: 1 1 auto; - cursor: pointer; -} - -.umb-permission--disabled .umb-permission__toggle, -.umb-permission--disabled .umb-permission__content { - cursor: not-allowed; - opacity: 0.8; -} - -.umb-permission__description { - font-size: 13px; - color: @gray-4; -} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-table.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-table.less index bc85ae90a9..0c61a5d113 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-table.less @@ -3,6 +3,10 @@ .umb-user-table-col-avatar { flex: 0 0 32px; padding: 15px 0; + + .umb-checkmark { + margin-left:5px; + } } .umb-table-cell a { @@ -14,15 +18,8 @@ } .umb-table-body .umb-table-cell.umb-table__name { - margin: 0; - padding: 0; a { display: flex; - padding: 6px 2px; - height: 42px; - span { - margin: auto 14px; - } } } .umb-table-cell.umb-table__name a { diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index dc63baf335..966fc1fb11 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -250,7 +250,10 @@ input[type="color"], &:focus { border-color: @inputBorderFocus; outline: 0; - outline: none \9; /* IE6-9 */ + + .tabbing-active & { + outline: 2px solid @inputBorderTabFocus; + } } } diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index 51f87d09dd..52a573d8c4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -52,7 +52,7 @@ bottom: 0px; left: 0px; right: 0px; - position: absolute; + position: absolute; } .--notInFront .umb-modalcolumn::after { @@ -67,7 +67,7 @@ /* re-align loader */ .umb-modal .umb-loader-wrapper, .umb-modalcolumn .umb-loader-wrapper, .umb-dialog .umb-loader-wrapper{ - position:relative; + position:relative; margin: 20px -20px; } @@ -106,7 +106,7 @@ .umb-dialog-footer{ position: absolute; overflow:auto; - text-align: right; + text-align: right; height: 32px; left: 0px; right: 0px; @@ -126,7 +126,7 @@ .umbracoDialog form{height: 100%;} /*ensures dialogs doesnt have side-by-side labels*/ -.umbracoDialog .controls-row, +.umbracoDialog .controls-row, .umb-modal .controls-row{margin-left: 0px !important;} /* modal and umb-modal are used for right.hand dialogs */ @@ -174,7 +174,7 @@ padding: 20px; background: @white; border: none; - height: auto; + height: auto; } .umb-modal .umb-panel-body{ padding: 0px 20px 0px 20px; @@ -186,7 +186,7 @@ } .umb-modal i { font-size: 20px; -} +} .umb-modal .breadcrumb { background: none; padding: 0 diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index a4d6387012..ccf52acc53 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -66,6 +66,13 @@ /* pre-value editor */ -.rte-editor-preval .control-group .controls > div > label .mce-ico { - line-height: 20px; +.rte-editor-preval .control-group .controls > div > label { + cursor: pointer !important; + + .mce-cmd .checkbox { + padding-right: 0; + } + .mce-ico { + line-height: 20px; + } } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 55be161cf8..d856714dd6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -376,6 +376,7 @@ @inputBackground: @white; @inputBorder: @gray-8; @inputBorderFocus: @gray-7; +@inputBorderTabFocus: @blueExtraDark; @inputBorderRadius: 0; @inputDisabledBackground: @gray-10; @formActionsBackground: @gray-9; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js index 46a4238c0c..7faaddde77 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js @@ -100,6 +100,10 @@ } function submit() { + if (!formHelper.submitForm({ scope: $scope })) { + return; + } + vm.saveButtonState = "busy"; var preValues = dataTypeHelper.createPreValueProps(vm.dataType.preValues); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html index 3faf74fdef..8acaa544c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html @@ -1,7 +1,8 @@
- -
+ + + - +
+ + -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js index f4725fa82d..1e3cf54450 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js @@ -28,9 +28,11 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", searchFromName: null, showSearch: false, results: [], - selectedSearchResults: [] + selectedSearchResults: [], + ignoreUserStartNodes: dialogOptions.ignoreUserStartNodes }; + $scope.customTreeParams = dialogOptions.ignoreUserStartNodes ? "ignoreUserStartNodes=" + dialogOptions.ignoreUserStartNodes : ""; $scope.showTarget = $scope.model.hideTarget !== true; // this ensures that we only sync the tree once and only when it's ready @@ -73,7 +75,11 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", }); // get the content properties to build the anchor name list - contentResource.getById(id).then(function (resp) { + + var options = {}; + options.ignoreUserStartNodes = dialogOptions.ignoreUserStartNodes; + + contentResource.getById(id, options).then(function (resp) { $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); $scope.model.target.url = resp.urls[0].text; }); @@ -119,7 +125,10 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", if (args.node.id < 0) { $scope.model.target.url = "/"; } else { - contentResource.getById(args.node.id).then(function (resp) { + var options = {}; + options.ignoreUserStartNodes = dialogOptions.ignoreUserStartNodes; + + contentResource.getById(args.node.id, options).then(function (resp) { $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); $scope.model.target.url = resp.urls[0].text; }); @@ -139,9 +148,17 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", $scope.switchToMediaPicker = function () { userService.getCurrentUser().then(function (userData) { + var startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; + var startNodeIsVirtual = userData.startMediaIds.length !== 1; + if (dialogOptions.ignoreUserStartNodes) { + startNodeId = -1; + startNodeIsVirtual = true; + } + var mediaPicker = { - startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], - startNodeIsVirtual: userData.startMediaIds.length !== 1, + startNodeId: startNodeId, + startNodeIsVirtual: startNodeIsVirtual, + ignoreUserStartNodes: dialogOptions.ignoreUserStartNodes, submit: function (model) { var media = model.selection[0]; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html index 71fcf2f493..414dbecfc2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html @@ -68,6 +68,7 @@ search-from-id="{{searchInfo.searchFromId}}" search-from-name="{{searchInfo.searchFromName}}" show-search="{{searchInfo.showSearch}}" + ignore-user-start-nodes="{{searchInfo.ignoreUserStartNodes}}" section="{{section}}"> @@ -84,6 +85,7 @@ section="content" hideheader="true" hideoptions="true" + customtreeparams="{{customTreeParams}}" api="dialogTreeApi" on-init="onTreeInit()" enablelistviewexpand="true" diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 2d6a2be471..f37e1156a8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { + function ($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, userService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { localizationService.localizeMany(["defaultdialogs_selectMedia", "general_includeFromsubFolders"]) @@ -20,11 +20,13 @@ angular.module("umbraco") $scope.showDetails = dialogOptions.showDetails; $scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false; $scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1; + $scope.ignoreUserStartNodes = Object.toBoolean(dialogOptions.ignoreUserStartNodes); $scope.cropSize = dialogOptions.cropSize; $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); $scope.lockedFolder = true; $scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false; + var userStartNodes = []; var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); if ($scope.onlyImages) { @@ -54,7 +56,8 @@ angular.module("umbraco") pageSize: 100, totalItems: 0, totalPages: 0, - filter: '' + filter: "", + ignoreUserStartNodes: $scope.model.ignoreUserStartNodes }; //preload selected item @@ -64,15 +67,19 @@ angular.module("umbraco") } function onInit() { - if ($scope.startNodeId !== -1) { - entityResource.getById($scope.startNodeId, "media") - .then(function (ent) { - $scope.startNodeId = ent.id; - run(); - }); - } else { - run(); - } + userService.getCurrentUser().then(function(userData) { + userStartNodes = userData.startMediaIds; + + if ($scope.startNodeId !== -1) { + entityResource.getById($scope.startNodeId, "media") + .then(function(ent) { + $scope.startNodeId = ent.id; + run(); + }); + } else { + run(); + } + }); } function run() { @@ -87,7 +94,7 @@ angular.module("umbraco") } else { //if a target is specified, go look it up - generally this target will just contain ids not the actual full //media object so we need to look it up - var id = $scope.target.udi ? $scope.target.udi : $scope.target.id + var id = $scope.target.udi ? $scope.target.udi : $scope.target.id; var altText = $scope.target.altText; mediaResource.getById(id) .then(function (node) { @@ -117,7 +124,7 @@ angular.module("umbraco") $scope.submitFolder = function() { if ($scope.model.newFolderName) { - $scope.creatingFolder = true; + $scope.model.creatingFolder = true; mediaResource .addFolder($scope.model.newFolderName, $scope.currentFolder.id) .then(function(data) { @@ -126,13 +133,13 @@ angular.module("umbraco") cacheKey: "__media", //this is the main media tree cache key childrenOf: data.parentId //clear the children of the parent }); - $scope.creatingFolder = false; + $scope.model.creatingFolder = false; $scope.gotoFolder(data); - $scope.showFolderInput = false; + $scope.model.showFolderInput = false; $scope.model.newFolderName = ""; }); } else { - $scope.showFolderInput = false; + $scope.model.showFolderInput = false; } }; @@ -143,7 +150,7 @@ angular.module("umbraco") } }; - $scope.gotoFolder = function(folder) { + $scope.gotoFolder = function (folder) { if (!$scope.multiPicker) { deselectAllImages($scope.model.selection); } @@ -152,8 +159,10 @@ angular.module("umbraco") folder = { id: -1, name: "Media", icon: "icon-folder" }; } + var options = {}; if (folder.id > 0) { - entityResource.getAncestors(folder.id, "media") + options.ignoreUserStartNodes = $scope.model.ignoreUserStartNodes; + entityResource.getAncestors(folder.id, "media", options) .then(function(anc) { $scope.path = _.filter(anc, function(f) { @@ -169,13 +178,26 @@ angular.module("umbraco") $scope.path = []; } - $scope.lockedFolder = folder.id === -1 && $scope.model.startNodeIsVirtual; + $scope.lockedFolder = (folder.id === -1 && $scope.model.startNodeIsVirtual) || hasFolderAccess(folder) === false; + $scope.currentFolder = folder; localStorageService.set("umbLastOpenedMediaNodeId", folder.id); - return getChildren(folder.id); + options.ignoreUserStartNodes = $scope.ignoreUserStartNodes; + return getChildren(folder.id, options); }; + function hasFolderAccess(node) { + var nodePath = node.path ? node.path.split(',') : [node.id]; + + for (var i = 0; i < nodePath.length; i++) { + if (userStartNodes.indexOf(parseInt(nodePath[i])) !== -1) + return true; + } + + return false; + } + $scope.clickHandler = function(image, event, index) { if (image.isFolder) { if ($scope.disableFolderSelect) { @@ -299,7 +321,8 @@ angular.module("umbraco") pageSize: 100, totalItems: 0, totalPages: 0, - filter: '' + filter: "", + ignoreUserStartNodes: $scope.model.ignoreUserStartNodes }; getChildren($scope.currentFolder.id); } @@ -367,9 +390,9 @@ angular.module("umbraco") } } - function getChildren(id) { + function getChildren(id, options) { $scope.loading = true; - return mediaResource.getChildren(id) + return mediaResource.getChildren(id, options) .then(function(data) { $scope.searchOptions.filter = ""; $scope.images = data.items ? data.items : []; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index da88b9321e..6eee269cee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -64,15 +64,15 @@
  • - + - +
  • -
    +
    - - + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js index 5883313753..a6e2838b56 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js @@ -36,6 +36,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", selectedSearchResults: [] } vm.startNodeId = $scope.model.startNodeId; + vm.ignoreUserStartNodes = $scope.model.ignoreUserStartNodes; //Used for toggling an empty-state message //Some trees can have no items (dictionary & forms email templates) vm.hasItems = true; @@ -171,6 +172,9 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", if (vm.startNodeId) { queryParams["startNodeId"] = $scope.model.startNodeId; } + if (vm.ignoreUserStartNodes) { + queryParams["ignoreUserStartNodes"] = $scope.model.ignoreUserStartNodes; + } if (vm.selectedLanguage && vm.selectedLanguage.id) { queryParams["culture"] = vm.selectedLanguage.culture; } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html index c592b4ec3b..acd838f7bf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html @@ -27,7 +27,7 @@ {{language.name}} - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js index 9e758a5b21..74a677b7c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js @@ -8,6 +8,10 @@ function ItemPickerOverlay($scope, localizationService) { $scope.model.title = value; }); } + + if (!$scope.model.orderBy) { + $scope.model.orderBy = "name"; + } } $scope.selectItem = function(item) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html index aac4830d52..328344ea84 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html @@ -12,7 +12,7 @@