diff --git a/build/build.ps1 b/build/build.ps1
index 5f85284431..964c29fb63 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -298,7 +298,11 @@
# copy libs
Write-Host "Copy SqlCE libraries"
- $nugetPackages = [System.Environment]::ExpandEnvironmentVariables("%userprofile%\.nuget\packages")
+ $nugetPackages = $env:NUGET_PACKAGES
+ if (-not $nugetPackages)
+ {
+ $nugetPackages = [System.Environment]::ExpandEnvironmentVariables("%userprofile%\.nuget\packages")
+ }
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x86\native", "*.*", "$tmp\bin\x86")
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x64\native", "*.*", "$tmp\bin\amd64")
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x86\native", "*.*", "$tmp\WebApp\bin\x86")
diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs
index cf46bff6d7..7f6f6e875f 100644
--- a/src/Umbraco.Core/Constants-Conventions.cs
+++ b/src/Umbraco.Core/Constants-Conventions.cs
@@ -34,11 +34,6 @@ namespace Umbraco.Core
public const string ListViewPrefix = "List View - ";
}
- public static class PropertyGroups
- {
- public const string ListViewGroupName = "umbContainerView";
- }
-
///
/// Constants for Umbraco Content property aliases.
///
diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
index ef6951df1a..d3402e69f8 100644
--- a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
+++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
@@ -17,11 +17,6 @@
///
public const string File = "50899F9C-023A-4466-B623-ABA9049885FE";
- ///
- /// Guid for a Image PropertyTypeGroup object.
- ///
- public const string Contents = "79995FA2-63EE-453C-A29B-2E66F324CDBE";
-
///
/// Guid for a Image PropertyTypeGroup object.
///
diff --git a/src/Umbraco.Core/Events/EventMessages.cs b/src/Umbraco.Core/Events/EventMessages.cs
index 2df1911249..6f2bf5b736 100644
--- a/src/Umbraco.Core/Events/EventMessages.cs
+++ b/src/Umbraco.Core/Events/EventMessages.cs
@@ -14,10 +14,7 @@ namespace Umbraco.Core.Events
_msgs.Add(msg);
}
- public int Count
- {
- get { return _msgs.Count; }
- }
+ public int Count => _msgs.Count;
public IEnumerable GetAll()
{
diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs
index 13352b0af0..be9a1b9b74 100644
--- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs
+++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs
@@ -205,7 +205,6 @@ namespace Umbraco.Core.Migrations.Install
{
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) });
- _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Contents) });
//membership property group
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) });
}
@@ -220,7 +219,6 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
- _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = Constants.DataTypes.DefaultMediaListView, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
//membership property types
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
@@ -234,7 +232,7 @@ namespace Umbraco.Core.Migrations.Install
private void CreateLanguageData()
{
- _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "en-US" });
+ _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)" });
}
private void CreateContentChildTypeData()
diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
index 5a4290aadd..14ffcda2ec 100644
--- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
+++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
@@ -141,13 +141,7 @@ namespace Umbraco.Core.Migrations.Install
//get the db index defs
result.DbIndexDefinitions = SqlSyntax.GetDefinedIndexes(_database)
- .Select(x => new DbIndexDefinition
- {
- TableName = x.Item1,
- IndexName = x.Item2,
- ColumnName = x.Item3,
- IsUnique = x.Item4
- }).ToArray();
+ .Select(x => new DbIndexDefinition(x)).ToArray();
result.TableDefinitions.AddRange(OrderedTables
.Select(x => DefinitionFactory.GetTableDefinition(x, SqlSyntax)));
@@ -160,6 +154,14 @@ namespace Umbraco.Core.Migrations.Install
return result;
}
+ ///
+ /// This validates the Primary/Foreign keys in the database
+ ///
+ ///
+ ///
+ /// This does not validate any database constraints that are not PKs or FKs because Umbraco does not create a database with non PK/FK contraints.
+ /// Any unique "constraints" in the database are done with unique indexes.
+ ///
private void ValidateDbConstraints(DatabaseSchemaResult result)
{
//MySql doesn't conform to the "normal" naming of constraints, so there is currently no point in doing these checks.
@@ -172,8 +174,7 @@ namespace Umbraco.Core.Migrations.Install
var constraintsInDatabase = SqlSyntax.GetConstraintsPerColumn(_database).DistinctBy(x => x.Item3).ToList();
var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("FK_")).Select(x => x.Item3).ToList();
var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("PK_")).Select(x => x.Item3).ToList();
- var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("IX_")).Select(x => x.Item3).ToList();
- var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList();
+
var unknownConstraintsInDatabase =
constraintsInDatabase.Where(
x =>
@@ -188,7 +189,7 @@ namespace Umbraco.Core.Migrations.Install
// In theory you could have: FK_ or fk_ ...or really any standard that your development department (or developer) chooses to use.
foreach (var unknown in unknownConstraintsInDatabase)
{
- if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown))
+ if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown))
{
result.ValidConstraints.Add(unknown);
}
@@ -230,23 +231,6 @@ namespace Umbraco.Core.Migrations.Install
result.Errors.Add(new Tuple("Constraint", primaryKey));
}
- //Constaints:
-
- //NOTE: SD: The colIndex checks above should really take care of this but I need to keep this here because it was here before
- // and some schema validation checks might rely on this data remaining here!
- //Add valid and invalid index differences to the result object
- var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase);
- foreach (var index in validIndexDifferences)
- {
- result.ValidConstraints.Add(index);
- }
- var invalidIndexDifferences =
- indexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase)
- .Union(indexesInSchema.Except(indexesInDatabase, StringComparer.InvariantCultureIgnoreCase));
- foreach (var index in invalidIndexDifferences)
- {
- result.Errors.Add(new Tuple("Constraint", index));
- }
}
private void ValidateDbColumns(DatabaseSchemaResult result)
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs
index baf4a9234e..f34ed9fb68 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs
@@ -1,5 +1,4 @@
-using System;
-using Umbraco.Core.Persistence.Dtos;
+using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
{
@@ -12,18 +11,22 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
public override void Migrate()
{
- var exists = Context.Database.FirstOrDefault("WHERE alias=@alias", new { alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias });
- if (exists == null)
+ var relationTypeCount = Context.Database.ExecuteScalar("SELECT COUNT(*) FROM umbracoRelationType WHERE alias=@alias",
+ new { alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias });
+
+ if (relationTypeCount > 0)
+ return;
+
+ var uniqueId = (Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias + "____" + Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName).ToGuid();
+ Insert.IntoTable("umbracoRelationType").Row(new
{
- Insert.IntoTable(Constants.DatabaseSchema.Tables.RelationType).Row(new
- {
- alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias,
- name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName,
- childObjectType = Constants.ObjectTypes.MediaType,
- parentObjectType = Constants.ObjectTypes.MediaType,
- dual = false
+ typeUniqueId = uniqueId,
+ alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias,
+ name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName,
+ childObjectType = Constants.ObjectTypes.MediaType,
+ parentObjectType = Constants.ObjectTypes.MediaType,
+ dual = false
}).Do();
- }
}
}
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs
index 400fc0bcdd..fc7a21f4fa 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs
@@ -1,4 +1,8 @@
-namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
+using System.Linq;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
{
public class IncreaseLanguageIsoCodeColumnLength : MigrationBase
{
@@ -8,7 +12,20 @@
public override void Migrate()
{
- Delete.Index("IX_umbracoLanguage_languageISOCode").OnTable("umbracoLanguage").Do();
+ // Some people seem to have a constraint in their DB instead of an index, we'd need to drop that one
+ // See: https://our.umbraco.com/forum/using-umbraco-and-getting-started/93282-upgrade-from-711-to-712-fails
+ var constraints = SqlSyntax.GetConstraintsPerTable(Context.Database).Distinct().ToArray();
+ if (constraints.Any(x => x.Item2.InvariantEquals("IX_umbracoLanguage_languageISOCode")))
+ {
+ Delete.UniqueConstraint("IX_umbracoLanguage_languageISOCode").FromTable("umbracoLanguage").Do();
+ }
+
+ //Now check for indexes of that name and drop that if it exists
+ var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoLanguage_languageISOCode")))
+ {
+ Delete.Index("IX_umbracoLanguage_languageISOCode").OnTable("umbracoLanguage").Do();
+ }
Alter.Table("umbracoLanguage")
.AlterColumn("languageISOCode")
@@ -19,8 +36,11 @@
Create.Index("IX_umbracoLanguage_languageISOCode")
.OnTable("umbracoLanguage")
.OnColumn("languageISOCode")
+ .Ascending()
+ .WithOptions()
.Unique()
.Do();
}
+
}
}
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/SetDefaultTagsStorageType.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/SetDefaultTagsStorageType.cs
index 08598f1779..d8f2d37067 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/SetDefaultTagsStorageType.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/SetDefaultTagsStorageType.cs
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
if (Context?.Database == null) return;
// We need to get all datatypes with an alias of "umbraco.tags" so we can loop over them and set the missing values if needed
- var datatypes = Context.Database.Fetch("SELECT * FROM cmsDataType");
+ var datatypes = Context.Database.Fetch();
var tagsDataTypes = datatypes.Where(x => string.Equals(x.EditorAlias, Constants.PropertyEditors.Aliases.Tags, StringComparison.InvariantCultureIgnoreCase));
foreach (var datatype in tagsDataTypes)
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/UpdateUmbracoConsent.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/UpdateUmbracoConsent.cs
index 942d32f83d..7596e3d7dd 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/UpdateUmbracoConsent.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/UpdateUmbracoConsent.cs
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0
public UpdateUmbracoConsent(IMigrationContext context)
: base(context)
{ }
-
- public override void Migrate()
{
Alter.Table("umbracoConsent").AlterColumn("comment").AsString().Nullable().Do();
}
+
+ public override void Migrate()
{
Alter.Table(Constants.DatabaseSchema.Tables.Consent).AlterColumn("comment").AsString().Nullable().Do();
}
}
}
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 10869c62da..caa7a2a3e4 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -213,17 +213,23 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
public IEnumerable PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty();
+ //fixme should this return false if ID == 0?
+ //fixme should this return false if IsCultureAvailable(culture) is false?
///
public bool IsCulturePublished(string culture)
- => _publishInfos != null && _publishInfos.ContainsKey(culture);
+ => _publishInfos != null && _publishInfos.ContainsKey(culture);
+ //fixme should this return false if ID == 0?
+ //fixme should this return false if IsCultureAvailable(culture) is false?
///
public bool WasCulturePublished(string culture)
- => _publishInfosOrig != null && _publishInfosOrig.ContainsKey(culture);
+ => _publishInfosOrig != null && _publishInfosOrig.ContainsKey(culture);
+ //fixme should this return false if ID == 0?
+ //fixme should this return false if IsCultureAvailable(culture) is false?
///
public bool IsCultureEdited(string culture)
- => !IsCulturePublished(culture) || (_editedCultures != null && _editedCultures.Contains(culture));
+ => !IsCulturePublished(culture) || (_editedCultures != null && _editedCultures.Contains(culture));
///
[IgnoreDataMember]
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 29e36829d2..d5d9fcfdac 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -376,30 +376,7 @@ namespace Umbraco.Core.Models
#endregion
#region Validation
-
- ///
- public bool IsValid(string culture = "*")
- {
- culture = culture.NullOrWhiteSpaceAsNull();
-
- if (culture == null)
- {
- if (Name.IsNullOrWhiteSpace()) return false;
- return ValidateProperties(null).Length == 0;
- }
-
- if (culture != "*")
- {
- var name = GetCultureName(culture);
- if (name.IsNullOrWhiteSpace()) return false;
- return ValidateProperties(culture).Length == 0;
- }
-
- // 'all cultures'
- // those that have a name are ok, those without a name... we don't validate
- return AvailableCultures.All(c => ValidateProperties(c).Length == 0);
- }
-
+
///
public virtual Property[] ValidateProperties(string culture = "*")
{
diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs
index 53c2adf7c2..ac236e1fdd 100644
--- a/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs
+++ b/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs
@@ -10,6 +10,20 @@
///
public string PropertyAlias { get; set; }
+ ///
+ /// When dealing with content variants, this is the culture for the variant
+ ///
+ public string Culture { get; set; }
+
+ ///
+ /// An array of metadata that is parsed out from the file info posted to the server which is set on the client.
+ ///
+ ///
+ /// This can be used for property types like Nested Content that need to have special unique identifiers for each file since there might be multiple files
+ /// per property.
+ ///
+ public string[] Metadata { get; set; }
+
///
/// Gets or sets the name of the file.
///
diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs
index 3c56b2c737..2cf2d85024 100644
--- a/src/Umbraco.Core/Models/IContentBase.cs
+++ b/src/Umbraco.Core/Models/IContentBase.cs
@@ -137,14 +137,7 @@ namespace Umbraco.Core.Models
void CopyFrom(IContent other, string culture = "*");
// fixme validate published cultures?
-
- ///
- /// Checks if the content and property values are valid in order to be persisted.
- ///
- /// If the content type is variant, then culture can be either '*' or an actual culture, but neither 'null' nor
- /// 'empty'. If the content type is invariant, then culture can be either '*' or null or empty.
- bool IsValid(string culture = "*");
-
+
///
/// Validates the content item's properties.
///
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index c0dd97ff87..bb922a740b 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -343,6 +343,8 @@ namespace Umbraco.Core.Models
///
internal bool IsValid(string culture = "*", string segment = "*")
{
+ //fixme - validating values shouldn't be done here, this calls in to IsValidValue
+
culture = culture.NullOrWhiteSpaceAsNull();
segment = segment.NullOrWhiteSpaceAsNull();
@@ -382,6 +384,7 @@ namespace Umbraco.Core.Models
/// True is property value is valid, otherwise false
private bool IsValidValue(object value)
{
+ //fixme this shouldn't exist here, the model itself shouldn't be responsible for it's own validation and this requires singleton access
return PropertyType.IsPropertyValueValid(value);
}
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index 3d5fac2077..a34fdb04ed 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -378,11 +378,11 @@ namespace Umbraco.Core.Models
}
- //fixme - perhaps this and other validation methods should be a service level (not a model) thing?
+ //fixme - this and other value validation methods should be a service level (not a model) thing. Changing this to internal for now
///
/// Determines whether a value is valid for this property type.
///
- public bool IsPropertyValueValid(object value)
+ internal bool IsPropertyValueValid(object value)
{
var editor = Current.PropertyEditors[_propertyEditorAlias]; // fixme inject?
var configuration = Current.Services.DataTypeService.GetDataType(_dataTypeId).Configuration; // fixme inject?
diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs
index 8b618a6619..63cbbf332b 100644
--- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs
@@ -6,13 +6,13 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
{
public ConstraintDefinition(ConstraintType type)
{
- constraintType = type;
+ _constraintType = type;
}
- private ConstraintType constraintType;
- public bool IsPrimaryKeyConstraint { get { return ConstraintType.PrimaryKey == constraintType; } }
- public bool IsUniqueConstraint { get { return ConstraintType.Unique == constraintType; } }
- public bool IsNonUniqueConstraint { get { return ConstraintType.NonUnique == constraintType; } }
+ private readonly ConstraintType _constraintType;
+ public bool IsPrimaryKeyConstraint => ConstraintType.PrimaryKey == _constraintType;
+ public bool IsUniqueConstraint => ConstraintType.Unique == _constraintType;
+ public bool IsNonUniqueConstraint => ConstraintType.NonUnique == _constraintType;
public string SchemaName { get; set; }
public string ConstraintName { get; set; }
diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs
index 33d9e20311..f447d6a560 100644
--- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs
@@ -1,13 +1,23 @@
-namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
+using System;
+
+namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
{
///
/// Represents a database index definition retreived by querying the database
///
internal class DbIndexDefinition
{
- public virtual string IndexName { get; set; }
- public virtual string TableName { get; set; }
- public virtual string ColumnName { get; set; }
- public virtual bool IsUnique { get; set; }
+ public DbIndexDefinition(Tuple data)
+ {
+ TableName = data.Item1;
+ IndexName = data.Item2;
+ ColumnName = data.Item3;
+ IsUnique = data.Item4;
+ }
+
+ public string IndexName { get; }
+ public string TableName { get; }
+ public string ColumnName { get; }
+ public bool IsUnique { get; }
}
}
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
index 3bea84e619..c920a18c3b 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
@@ -27,7 +27,11 @@ namespace Umbraco.Core.Persistence.Factories
if (xdtos.TryGetValue(propertyType.Id, out var propDtos))
{
foreach (var propDto in propDtos)
+ {
+ property.Id = propDto.Id;
property.FactorySetValue(languageRepository.GetIsoCodeById(propDto.LanguageId), propDto.Segment, propDto.VersionId == publishedVersionId, propDto.Value);
+ }
+
}
property.ResetDirtyProperties(false);
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
index d027cf44a9..af58604bf0 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
@@ -44,6 +44,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
string TruncateTable { get; }
string CreateConstraint { get; }
string DeleteConstraint { get; }
+
+ [Obsolete("This is never used, use the Format(ForeignKeyDefinition) instead")]
string CreateForeignKeyConstraint { get; }
string DeleteDefaultConstraint { get; }
string FormatDateTime(DateTime date, bool includeTime = true);
@@ -80,8 +82,32 @@ namespace Umbraco.Core.Persistence.SqlSyntax
IEnumerable GetTablesInSchema(IDatabase db);
IEnumerable GetColumnsInSchema(IDatabase db);
+
+ ///
+ /// Returns all constraints defined in the database (Primary keys, foreign keys, unique constraints...) (does not include indexes)
+ ///
+ ///
+ ///
+ /// A Tuple containing: TableName, ConstraintName
+ ///
IEnumerable> GetConstraintsPerTable(IDatabase db);
+
+ ///
+ /// Returns all constraints defined in the database (Primary keys, foreign keys, unique constraints...) (does not include indexes)
+ ///
+ ///
+ ///
+ /// A Tuple containing: TableName, ColumnName, ConstraintName
+ ///
IEnumerable> GetConstraintsPerColumn(IDatabase db);
+
+ ///
+ /// Returns all defined Indexes in the database excluding primary keys
+ ///
+ ///
+ ///
+ /// A Tuple containing: TableName, IndexName, ColumnName, IsUnique
+ ///
IEnumerable> GetDefinedIndexes(IDatabase db);
}
}
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
index e0eaa46048..fc967da9bf 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
@@ -79,6 +79,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return list;
}
+ ///
public override IEnumerable> GetConstraintsPerTable(IDatabase db)
{
List> list;
@@ -101,6 +102,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return list;
}
+ ///
public override IEnumerable> GetConstraintsPerColumn(IDatabase db)
{
List> list;
@@ -127,6 +129,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return list;
}
+ ///
public override IEnumerable> GetDefinedIndexes(IDatabase db)
{
List> list;
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs
index c41cd59f4e..75fc9c0b69 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs
@@ -107,40 +107,27 @@ namespace Umbraco.Core.Persistence.SqlSyntax
item.IS_NULLABLE, item.DATA_TYPE)).ToList();
}
+ ///
public override IEnumerable> GetConstraintsPerTable(IDatabase db)
{
var items = db.Fetch("SELECT TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS");
- var indexItems = db.Fetch("SELECT TABLE_NAME, INDEX_NAME FROM INFORMATION_SCHEMA.INDEXES");
- return
- items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME))
- .Union(
- indexItems.Select(
- indexItem => new Tuple(indexItem.TABLE_NAME, indexItem.INDEX_NAME)))
- .ToList();
+ return items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME)).ToList();
}
+ ///
public override IEnumerable> GetConstraintsPerColumn(IDatabase db)
{
- var items =
- db.Fetch(
- "SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE");
- var indexItems = db.Fetch("SELECT INDEX_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.INDEXES");
- return
- items.Select(
- item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME))
- .Union(
- indexItems.Select(
- indexItem =>
- new Tuple(indexItem.TABLE_NAME, indexItem.COLUMN_NAME,
- indexItem.INDEX_NAME))).ToList();
+ var items = db.Fetch("SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE");
+ return items.Select(item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)).ToList();
}
+ ///
public override IEnumerable> GetDefinedIndexes(IDatabase db)
{
var items =
db.Fetch(
@"SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, [UNIQUE] FROM INFORMATION_SCHEMA.INDEXES
-WHERE INDEX_NAME NOT LIKE 'PK_%'
+WHERE PRIMARY_KEY=0
ORDER BY TABLE_NAME, INDEX_NAME");
return
items.Select(
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs
index d7dadd9f08..9c6e95971c 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs
@@ -163,6 +163,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
item.IS_NULLABLE, item.DATA_TYPE)).ToList();
}
+ ///
public override IEnumerable> GetConstraintsPerTable(IDatabase db)
{
var items =
@@ -171,6 +172,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME)).ToList();
}
+ ///
public override IEnumerable> GetConstraintsPerColumn(IDatabase db)
{
var items =
@@ -179,6 +181,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return items.Select(item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)).ToList();
}
+ ///
public override IEnumerable> GetDefinedIndexes(IDatabase db)
{
var items =
@@ -188,7 +191,7 @@ CASE WHEN I.is_unique_constraint = 1 OR I.is_unique = 1 THEN 1 ELSE 0 END AS [U
from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id]
inner join sys.index_columns as IC on IC.[object_id] = I.[object_id] and IC.[index_id] = I.[index_id]
inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id]
-WHERE I.name NOT LIKE 'PK_%'
+WHERE I.is_primary_key = 0
order by T.name, I.name");
return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME,
item.UNIQUE == 1)).ToList();
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs
index b53f6224c4..02f1370b83 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs
@@ -10,13 +10,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public static IEnumerable GetDefinedIndexesDefinitions(this ISqlSyntaxProvider sql, IDatabase db)
{
return sql.GetDefinedIndexes(db)
- .Select(x => new DbIndexDefinition
- {
- TableName = x.Item1,
- IndexName = x.Item2,
- ColumnName = x.Item3,
- IsUnique = x.Item4
- }).ToArray();
+ .Select(x => new DbIndexDefinition(x)).ToArray();
}
///
diff --git a/src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs
similarity index 93%
rename from src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs
rename to src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs
index f91da8e68d..b2bf99bdc6 100644
--- a/src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs
+++ b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs
@@ -1,6 +1,4 @@
-using Umbraco.Core.PropertyEditors;
-
-namespace Umbraco.Web.PropertyEditors
+namespace Umbraco.Core.PropertyEditors
{
///
/// Represents the configuration for the slider value editor.
@@ -8,7 +6,7 @@ namespace Umbraco.Web.PropertyEditors
public class SliderConfiguration
{
[ConfigurationField("enableRange", "Enable range", "boolean")]
- public string Range { get; set; }
+ public bool EnableRange { get; set; }
[ConfigurationField("orientation", "Orientation", "views/propertyeditors/slider/orientation.prevalues.html")]
public string Orientation { get; set; }
@@ -38,7 +36,7 @@ namespace Umbraco.Web.PropertyEditors
public string Tooltip { get; set; }
[ConfigurationField("tooltipSplit", "Tooltip split", "boolean", Description = "If false show one tootip if true show two tooltips one for each handler")]
- public string TooltipSplit { get; set; } // fixme bool?
+ public bool TooltipSplit { get; set; } // fixme bool?
[ConfigurationField("tooltipFormat", "Tooltip format", "textstring", Description = "The value wanted to be displayed in the tooltip. Use {0} and {1} for current values - {1} is only for range slider and if not using tooltip split.")]
public string TooltipFormat { get; set; }
@@ -47,7 +45,7 @@ namespace Umbraco.Web.PropertyEditors
public string TooltipPosition { get; set; }
[ConfigurationField("reversed", "Reversed", "boolean", Description = "Whether or not the slider should be reversed")]
- public string Reversed { get; set; } // fixme bool?
+ public bool Reversed { get; set; } // fixme bool?
[ConfigurationField("ticks", "Ticks", "textstring", Description = "Comma-separated values. Used to define the values of ticks. Tick marks are indicators to denote special values in the range. This option overwrites min and max options.")]
public string Ticks { get; set; }
@@ -61,4 +59,4 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("ticksSnapBounds", "Ticks snap bounds", "number", Description = "Used to define the snap bounds of a tick. Snaps to the tick if value is within these bounds.")]
public int TicksSnapBounds { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs
deleted file mode 100644
index 974f9bfab0..0000000000
--- a/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Newtonsoft.Json;
-
-namespace Umbraco.Core.PropertyEditors
-{
- public class SliderPropertyEditorConfiguration
- {
- [JsonProperty("enableRange")]
- public bool EnableRange { get; set; }
- }
-}
diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs
index 0727c0d24f..e405fa3a3e 100644
--- a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs
+++ b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
+using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
+using Umbraco.Core.Services;
namespace Umbraco.Core.PropertyEditors.Validators
{
@@ -11,6 +13,7 @@ namespace Umbraco.Core.PropertyEditors.Validators
///
internal sealed class RegexValidator : IValueFormatValidator, IManifestValueValidator
{
+ private readonly ILocalizedTextService _textService;
private string _regex;
///
@@ -22,8 +25,8 @@ namespace Umbraco.Core.PropertyEditors.Validators
/// Use this constructor when the validator is used as an ,
/// and the regular expression is supplied at validation time. This constructor is also used when
/// the validator is used as an and the regular expression
- /// is supplied via the method.
- public RegexValidator()
+ /// is supplied via the method.
+ public RegexValidator() : this(Current.Services.TextService, null)
{ }
///
@@ -31,10 +34,9 @@ namespace Umbraco.Core.PropertyEditors.Validators
///
/// Use this constructor when the validator is used as an ,
/// and the regular expression must be supplied when the validator is created.
- public RegexValidator(string regex)
+ public RegexValidator(ILocalizedTextService textService, string regex)
{
- if (string.IsNullOrWhiteSpace(regex))
- throw new ArgumentNullOrEmptyException(nameof(regex));
+ _textService = textService;
_regex = regex;
}
@@ -66,7 +68,7 @@ namespace Umbraco.Core.PropertyEditors.Validators
{
if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullOrEmptyException(nameof(format));
if (value == null || !new Regex(format).IsMatch(value.ToString()))
- yield return new ValidationResult("Value is invalid, it does not match the correct pattern", new[] { "value" });
+ yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" });
}
}
}
diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
index bc3cf66caa..c51f572817 100644
--- a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
+++ b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Services;
namespace Umbraco.Core.PropertyEditors.Validators
{
@@ -8,6 +10,17 @@ namespace Umbraco.Core.PropertyEditors.Validators
///
internal sealed class RequiredValidator : IValueRequiredValidator, IManifestValueValidator
{
+ private readonly ILocalizedTextService _textService;
+
+ public RequiredValidator() : this(Current.Services.TextService)
+ {
+ }
+
+ public RequiredValidator(ILocalizedTextService textService)
+ {
+ _textService = textService;
+ }
+
///
public string ValidationName => "Required";
@@ -22,20 +35,20 @@ namespace Umbraco.Core.PropertyEditors.Validators
{
if (value == null)
{
- yield return new ValidationResult("Value cannot be null", new[] {"value"});
+ yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] {"value"});
yield break;
}
if (valueType.InvariantEquals(ValueTypes.Json))
{
if (value.ToString().DetectIsEmptyJson())
- yield return new ValidationResult("Value cannot be empty", new[] { "value" });
+ yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" });
yield break;
}
if (value.ToString().IsNullOrWhiteSpace())
{
- yield return new ValidationResult("Value cannot be empty", new[] { "value" });
+ yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" });
}
}
}
diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs
index 8e79e2273c..a43d391dbe 100644
--- a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs
+++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs
@@ -72,7 +72,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
return Storages.GetOrAdd(dataTypeId, id =>
{
var dataType = _dataTypeService.GetDataType(id);
- var configuration = dataType.ConfigurationAs();
+ var configuration = dataType.ConfigurationAs();
return configuration.EnableRange;
});
}
diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs
index f2d873f2ca..d29875d68b 100644
--- a/src/Umbraco.Core/Services/Implement/MediaService.cs
+++ b/src/Umbraco.Core/Services/Implement/MediaService.cs
@@ -1010,7 +1010,7 @@ namespace Umbraco.Core.Services.Implement
var originalPath = media.Path;
- if (scope.Events.DispatchCancelable(Trashing, this, new MoveEventArgs(new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia))))
+ if (scope.Events.DispatchCancelable(Trashing, this, new MoveEventArgs(new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia)), nameof(Trashing)))
{
scope.Complete();
return OperationResult.Attempt.Cancel(evtMsgs);
@@ -1022,7 +1022,7 @@ namespace Umbraco.Core.Services.Implement
var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId))
.ToArray();
- scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, evtMsgs, moveInfo));
+ scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, evtMsgs, moveInfo), nameof(Trashed));
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
scope.Complete();
@@ -1058,7 +1058,7 @@ namespace Umbraco.Core.Services.Implement
var moveEventInfo = new MoveEventInfo(media, media.Path, parentId);
var moveEventArgs = new MoveEventArgs(moveEventInfo);
- if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs))
+ if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs, nameof(Moving)))
{
scope.Complete();
return;
@@ -1082,7 +1082,7 @@ namespace Umbraco.Core.Services.Implement
.ToArray();
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
- scope.Events.Dispatch(Moved, this, moveEventArgs);
+ scope.Events.Dispatch(Moved, this, moveEventArgs, nameof(Moved));
Audit(AuditType.Move, "Move Media performed by user", userId, media.Id);
scope.Complete();
}
@@ -1382,7 +1382,7 @@ namespace Umbraco.Core.Services.Implement
/// Deletes media items of the specified type, and only that type. Does *not* handle content types
/// inheritance and compositions, which need to be managed outside of this method.
///
- /// Id of the
+ /// Id of the
/// Optional id of the user deleting the media
public void DeleteMediaOfTypes(IEnumerable mediaTypeIds, int userId = 0)
{
@@ -1434,7 +1434,7 @@ namespace Umbraco.Core.Services.Implement
var moveInfos = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId))
.ToArray();
if (moveInfos.Length > 0)
- scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, moveInfos), "Trashed");
+ scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, moveInfos), nameof(Trashed));
scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs());
Audit(AuditType.Delete, $"Delete Media of types {string.Join(",", mediaTypeIdsA)} performed by user", userId, Constants.System.Root);
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 6b875007ac..f0c8418bde 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -406,7 +406,7 @@
-
+
diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs
index 1c4624add2..0219ace270 100644
--- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs
+++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs
@@ -389,6 +389,8 @@ namespace Umbraco.Examine
//the value of the field 'as-is'.
foreach (var value in e.IndexItem.ValueSet.Values.ToList()) //ToList here to make a diff collection else we'll get collection modified errors
{
+ if (value.Value == null) continue;
+
if (value.Value.Count > 0)
{
if (value.Value.First() is string str)
diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
index 09411f6d20..c6365ba4a2 100644
--- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
+++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
@@ -272,7 +272,7 @@ AnotherContentFinder
public void Resolves_Actions()
{
var actions = _typeLoader.GetActions();
- Assert.AreEqual(35, actions.Count());
+ Assert.AreEqual(34, actions.Count());
}
[Test]
diff --git a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
index d50e940b1a..ebba8bc1cc 100644
--- a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
+++ b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
@@ -67,9 +67,6 @@ namespace Umbraco.Tests.Configurations
Assert.IsTrue(globalSettings.IsReservedPathOrUrl(url));
}
- [TestCase("/umbraco_client/Tree/treeIcons.css")]
- [TestCase("/umbraco_client/Tree/Themes/umbraco/style.css")]
- [TestCase("/umbraco_client/scrollingmenu/style.css")]
[TestCase("/base/somebasehandler")]
[TestCase("/")]
[TestCase("/home.aspx")]
diff --git a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
index e972ccb43f..91ac3aacc2 100644
--- a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
+++ b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
@@ -417,7 +417,6 @@ namespace Umbraco.Tests.CoreXml
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
- TestContent content;
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsTrue(nav.MoveToFirstChild());
diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs
index 3a7192536c..2bbd70e367 100644
--- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs
+++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs
@@ -2,14 +2,17 @@
using System.Linq;
using Moq;
using System.Text;
+using LightInject;
using NUnit.Framework;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Cache;
+using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.Validators;
+using Umbraco.Core.Services;
namespace Umbraco.Tests.Manifest
{
@@ -22,10 +25,25 @@ namespace Umbraco.Tests.Manifest
[SetUp]
public void Setup()
{
+ Current.Reset();
+ var container = Mock.Of();
+ Current.Container = container;
+
+ var serviceContext = new ServiceContext(
+ localizedTextService: Mock.Of());
+
+ Mock.Get(container)
+ .Setup(x => x.GetInstance(It.IsAny()))
+ .Returns(x =>
+ {
+ if (x == typeof(ServiceContext)) return serviceContext;
+ throw new Exception("oops");
+ });
+
var validators = new IManifestValueValidator[]
{
- new RequiredValidator(),
- new RegexValidator()
+ new RequiredValidator(Mock.Of()),
+ new RegexValidator(Mock.Of(), null)
};
_parser = new ManifestParser(NullCacheProvider.Instance, new ManifestValueValidatorCollection(validators), Mock.Of());
}
diff --git a/src/Umbraco.Tests/Models/Collections/Item.cs b/src/Umbraco.Tests/Models/Collections/Item.cs
index dcb192bcdf..5d1ab24fb0 100644
--- a/src/Umbraco.Tests/Models/Collections/Item.cs
+++ b/src/Umbraco.Tests/Models/Collections/Item.cs
@@ -12,7 +12,6 @@ namespace Umbraco.Tests.Models.Collections
public abstract class Item : IEntity, ICanBeDirty
{
private bool _hasIdentity;
- private int? _hash;
private int _id;
private Guid _key;
@@ -184,12 +183,6 @@ namespace Umbraco.Tests.Models.Collections
public static bool operator ==(Item left, Item right)
{
- /*if (ReferenceEquals(null, left))
- return false;
-
- if(ReferenceEquals(null, right))
- return false;*/
-
return ReferenceEquals(left, right);
}
diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
index d6ae92ebd3..03a59584a3 100644
--- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
+++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
@@ -56,7 +56,7 @@ namespace Umbraco.Tests.Models.Mapping
var content = MockedMedia.CreateMediaImage(contentType, -1);
FixUsers(content);
- var result = Mapper.Map>(content);
+ var result = Mapper.Map>(content);
AssertBasics(result, content);
@@ -73,7 +73,7 @@ namespace Umbraco.Tests.Models.Mapping
var content = MockedContent.CreateSimpleContent(contentType);
FixUsers(content);
- var result = Mapper.Map>(content);
+ var result = Mapper.Map>(content);
AssertBasics(result, content);
@@ -90,9 +90,12 @@ namespace Umbraco.Tests.Models.Mapping
var content = MockedContent.CreateSimpleContent(contentType);
FixUsers(content);
- var result = Mapper.Map>(content);
+ var result = Mapper.Map(content);
- AssertContentItem(result, content);
+ foreach (var p in content.Properties)
+ {
+ AssertProperty(result, p);
+ }
}
[Test]
@@ -102,9 +105,12 @@ namespace Umbraco.Tests.Models.Mapping
var content = MockedMedia.CreateMediaImage(contentType, -1);
FixUsers(content);
- var result = Mapper.Map>(content);
+ var result = Mapper.Map(content);
- AssertContentItem(result, content);
+ foreach (var p in content.Properties)
+ {
+ AssertProperty(result, p);
+ }
}
[Test]
@@ -123,12 +129,16 @@ namespace Umbraco.Tests.Models.Mapping
AssertBasics(result, content);
+ var invariantContent = result.Variants.First();
foreach (var p in content.Properties)
- AssertDisplayProperty(result, p);
+ {
+ AssertBasicProperty(invariantContent, p);
+ AssertDisplayProperty(invariantContent, p);
+ }
- Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count());
- Assert.IsTrue(result.Tabs.First().IsActive);
- Assert.IsTrue(result.Tabs.Except(new[] {result.Tabs.First()}).All(x => x.IsActive == false));
+ Assert.AreEqual(content.PropertyGroups.Count(), invariantContent.Tabs.Count());
+ Assert.IsTrue(invariantContent.Tabs.First().IsActive);
+ Assert.IsTrue(invariantContent.Tabs.Except(new[] { invariantContent.Tabs.First() }).All(x => x.IsActive == false));
}
[Test]
@@ -141,11 +151,15 @@ namespace Umbraco.Tests.Models.Mapping
var result = Mapper.Map(content);
AssertBasics(result, content);
+
+ var invariantContent = result.Variants.First();
foreach (var p in content.Properties)
{
- AssertDisplayProperty(result, p);
+ AssertBasicProperty(invariantContent, p);
+ AssertDisplayProperty(invariantContent, p);
}
- Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count());
+
+ Assert.AreEqual(content.PropertyGroups.Count(), invariantContent.Tabs.Count());
}
[Test]
@@ -186,23 +200,24 @@ namespace Umbraco.Tests.Models.Mapping
var result = Mapper.Map(content);
AssertBasics(result, content);
+
+ var invariantContent = result.Variants.First();
foreach (var p in content.Properties)
{
- AssertDisplayProperty(result, p);
+ AssertBasicProperty(invariantContent, p);
+ AssertDisplayProperty(invariantContent, p);
}
- Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count() - 1);
- Assert.IsTrue(result.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general/properties")));
- Assert.AreEqual(2, result.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count());
+
+ Assert.AreEqual(content.PropertyGroups.Count(), invariantContent.Tabs.Count() - 1);
+ Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general/properties")));
+ Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count());
}
#region Assertions
- private void AssertDisplayProperty(ContentItemBasic result, Property p)
- where T : ContentPropertyDisplay
- where TPersisted : IContentBase
+ private void AssertDisplayProperty(IContentProperties result, Property p)
+ where T : ContentPropertyBasic
{
- AssertBasicProperty(result, p);
-
var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias);
Assert.IsNotNull(pDto);
@@ -213,7 +228,33 @@ namespace Umbraco.Tests.Models.Mapping
}
- private void AssertBasics(ContentItemBasic result, TPersisted content)
+ private void AssertBasics(ContentItemDisplay result, IContent content)
+ {
+ Assert.AreEqual(content.Id, result.Id);
+
+ var ownerId = content.CreatorId;
+ if (ownerId != 0)
+ {
+ Assert.IsNotNull(result.Owner);
+ Assert.AreEqual(Constants.Security.SuperUserId, result.Owner.UserId);
+ Assert.AreEqual("Administrator", result.Owner.Name);
+ }
+ else
+ {
+ Assert.IsNull(result.Owner); // because, 0 is no user
+ }
+
+ var invariantContent = result.Variants.First();
+
+ Assert.AreEqual(content.ParentId, result.ParentId);
+ Assert.AreEqual(content.UpdateDate, invariantContent.UpdateDate);
+ Assert.AreEqual(content.CreateDate, invariantContent.CreateDate);
+ Assert.AreEqual(content.Name, invariantContent.Name);
+ Assert.AreEqual(content.Properties.Count(),
+ ((IContentProperties)invariantContent).Properties.Count(x => x.Alias.StartsWith("_umb_") == false));
+ }
+
+ private void AssertBasics(ContentItemBasic result, TPersisted content)
where T : ContentPropertyBasic
where TPersisted : IContentBase
{
@@ -238,9 +279,8 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(content.Properties.Count(), result.Properties.Count(x => x.Alias.StartsWith("_umb_") == false));
}
- private void AssertBasicProperty(ContentItemBasic result, Property p)
+ private void AssertBasicProperty(IContentProperties result, Property p)
where T : ContentPropertyBasic
- where TPersisted : IContentBase
{
var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias);
Assert.IsNotNull(pDto);
@@ -255,8 +295,7 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(pDto.Value, p.GetValue().ToString());
}
- private void AssertProperty(ContentItemBasic result, Property p)
- where TPersisted : IContentBase
+ private void AssertProperty(IContentProperties result, Property p)
{
AssertBasicProperty(result, p);
@@ -270,7 +309,7 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(Current.PropertyEditors[p.PropertyType.PropertyEditorAlias], pDto.PropertyEditor);
}
- private void AssertContentItem(ContentItemBasic result, T content)
+ private void AssertContentItem(ContentItemBasic result, T content)
where T : IContentBase
{
AssertBasics(result, content);
diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs
index 19da19bcf0..ba4283af49 100644
--- a/src/Umbraco.Tests/Models/VariationTests.cs
+++ b/src/Umbraco.Tests/Models/VariationTests.cs
@@ -46,7 +46,9 @@ namespace Umbraco.Tests.Models
.Setup(x => x.GetDataType(It.IsAny()))
.Returns(x => dataType);
- var serviceContext = new ServiceContext(dataTypeService: dataTypeService);
+ var serviceContext = new ServiceContext(
+ dataTypeService: dataTypeService,
+ localizedTextService: Mock.Of());
Mock.Get(container)
.Setup(x => x.GetInstance(It.IsAny()))
@@ -54,8 +56,10 @@ namespace Umbraco.Tests.Models
{
if (x == typeof(PropertyEditorCollection)) return propertyEditors;
if (x == typeof(ServiceContext)) return serviceContext;
+ if (x == typeof(ILocalizedTextService)) return serviceContext.LocalizationService;
throw new Exception("oops");
});
+
}
[Test]
diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs
index 9fb908acf5..5f42c8d3ae 100644
--- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs
+++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs
@@ -63,7 +63,6 @@ namespace Umbraco.Tests.Routing
// do not test for /base here as it's handled before EnsureUmbracoRoutablePage is called
[TestCase("/umbraco_client/Tree/treeIcons.css", false)]
[TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", false)]
- [TestCase("/umbraco_client/scrollingmenu/style.css?cdv=37", false)]
[TestCase("/umbraco/umbraco.aspx", false)]
[TestCase("/umbraco/editContent.aspx", false)]
[TestCase("/install/default.aspx", false)]
@@ -89,7 +88,6 @@ namespace Umbraco.Tests.Routing
[TestCase("/favicon.ico", true)]
[TestCase("/umbraco_client/Tree/treeIcons.css", true)]
[TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", true)]
- [TestCase("/umbraco_client/scrollingmenu/style.css?cdv=37", true)]
[TestCase("/base/somebasehandler", false)]
[TestCase("/", false)]
[TestCase("/home.aspx", false)]
diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
index 38990405c4..59cf30c0fb 100644
--- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
+++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
@@ -945,8 +945,6 @@ namespace Umbraco.Tests.Scheduling
throw new NotImplementedException();
}
- private int i;
-
public async Task RunAsync(CancellationToken token)
{
Console.WriteLine("boom");
diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs
index 13cd781666..5450ad0445 100644
--- a/src/Umbraco.Tests/Services/ContentServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs
@@ -63,7 +63,7 @@ namespace Umbraco.Tests.Services
///
/// Used to list out all ambiguous events that will require dispatching with a name
///
- [Test]
+ [Test, Explicit]
public void List_Ambiguous_Events()
{
var events = ServiceContext.ContentService.GetType().GetEvents(BindingFlags.Static | BindingFlags.Public);
diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs
index 02e8e3673e..68fd2c3e11 100644
--- a/src/Umbraco.Tests/Services/MediaServiceTests.cs
+++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs
@@ -1,16 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading;
using NUnit.Framework;
using Umbraco.Core;
+using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Services;
+using Umbraco.Core.Services.Implement;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Tests.Testing;
@@ -22,6 +25,32 @@ namespace Umbraco.Tests.Services
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)]
public class MediaServiceTests : TestWithSomeContentBase
{
+ ///
+ /// Used to list out all ambiguous events that will require dispatching with a name
+ ///
+ [Test, Explicit]
+ public void List_Ambiguous_Events()
+ {
+ var events = ServiceContext.MediaService.GetType().GetEvents(BindingFlags.Static | BindingFlags.Public);
+ var typedEventHandler = typeof(TypedEventHandler<,>);
+ foreach (var e in events)
+ {
+ //only continue if this is a TypedEventHandler
+ if (!e.EventHandlerType.IsGenericType) continue;
+ var typeDef = e.EventHandlerType.GetGenericTypeDefinition();
+ if (typedEventHandler != typeDef) continue;
+
+ //get the event arg type
+ var eventArgType = e.EventHandlerType.GenericTypeArguments[1];
+
+ var found = EventNameExtractor.FindEvent(typeof(MediaService), eventArgType, EventNameExtractor.MatchIngNames);
+ if (!found.Success && found.Result.Error == EventNameExtractorError.Ambiguous)
+ {
+ Console.WriteLine($"Ambiguous event, source: {typeof(MediaService)}, args: {eventArgType}");
+ }
+ }
+ }
+
[Test]
public void Get_Paged_Children_With_Media_Type_Filter()
{
diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
index ab473fd0c0..5031d178bf 100644
--- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
+++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
@@ -8,6 +8,9 @@ using Umbraco.Core.Security;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
+ ///
+ /// Ensures there's an admin user assigned to the request
+ ///
public class AuthenticateEverythingMiddleware : AuthenticationMiddleware
{
public AuthenticateEverythingMiddleware(OwinMiddleware next, IAppBuilder app, AuthenticationOptions options)
@@ -26,7 +29,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
var sessionId = Guid.NewGuid().ToString();
var identity = new UmbracoBackOfficeIdentity(
- -1, "admin", "Admin", null, null, "en-US", sessionId, sessionId, new[] { "content", "media", "members" }, new[] { "admin" });
+ -1, "admin", "Admin", new []{-1}, new[] { -1 }, "en-US", sessionId, sessionId, new[] { "content", "media", "members" }, new[] { "admin" });
return Task.FromResult(new AuthenticationTicket(identity,
new AuthenticationProperties()
diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs
index f64e8cb545..64a22926a0 100644
--- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs
+++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs
@@ -22,8 +22,16 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
_controllerFactory = controllerFactory;
}
- public async Task> Execute(string controllerName, string actionName, HttpMethod method, HttpContent content = null)
+ public async Task> Execute(string controllerName, string actionName, HttpMethod method,
+ HttpContent content = null,
+ MediaTypeWithQualityHeaderValue mediaTypeHeader = null,
+ bool assertOkResponse = true)
{
+ if (mediaTypeHeader == null)
+ {
+ mediaTypeHeader = new MediaTypeWithQualityHeaderValue("application/json");
+ }
+
var startup = new TestStartup(
configuration =>
{
@@ -44,7 +52,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
if (content != null)
request.Content = content;
- request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ request.Headers.Accept.Add(mediaTypeHeader);
Console.WriteLine(request);
var response = await server.HttpClient.SendAsync(request);
@@ -62,7 +70,11 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
Console.Write(JsonConvert.SerializeObject(deserialized, Formatting.Indented));
}
- Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
+ if (assertOkResponse)
+ {
+ Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
+ }
+
return Tuple.Create(response, json);
}
}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index bf9e9fefde..82fee84013 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -214,6 +214,7 @@
+
diff --git a/src/Umbraco.Tests/Web/AngularIntegration/ContentModelSerializationTests.cs b/src/Umbraco.Tests/Web/AngularIntegration/ContentModelSerializationTests.cs
index fcc434a596..0c7908de9e 100644
--- a/src/Umbraco.Tests/Web/AngularIntegration/ContentModelSerializationTests.cs
+++ b/src/Umbraco.Tests/Web/AngularIntegration/ContentModelSerializationTests.cs
@@ -16,59 +16,66 @@ namespace Umbraco.Tests.Web.AngularIntegration
{
//create 3 tabs with 3 properties each
var tabs = new List>();
- for (var tabIndex = 0; tabIndex < 3; tabIndex ++)
+ for (var tabIndex = 0; tabIndex < 3; tabIndex++)
{
var props = new List();
- for (var propertyIndex = 0; propertyIndex < 3; propertyIndex ++)
+ for (var propertyIndex = 0; propertyIndex < 3; propertyIndex++)
{
props.Add(new ContentPropertyDisplay
- {
- Alias = "property" + propertyIndex,
- Label = "Property " + propertyIndex,
- Id = propertyIndex,
- Value = "value" + propertyIndex,
- Config = new Dictionary {{ propertyIndex.ToInvariantString(), "value" }},
- Description = "Description " + propertyIndex,
- View = "~/Views/View" + propertyIndex,
- HideLabel = false
- });
+ {
+ Alias = "property" + propertyIndex,
+ Label = "Property " + propertyIndex,
+ Id = propertyIndex,
+ Value = "value" + propertyIndex,
+ Config = new Dictionary { { propertyIndex.ToInvariantString(), "value" } },
+ Description = "Description " + propertyIndex,
+ View = "~/Views/View" + propertyIndex,
+ HideLabel = false
+ });
}
tabs.Add(new Tab()
- {
- Alias = "Tab" + tabIndex,
- Label = "Tab" + tabIndex,
- Properties = props
- });
+ {
+ Alias = "Tab" + tabIndex,
+ Label = "Tab" + tabIndex,
+ Properties = props
+ });
}
var displayModel = new ContentItemDisplay
+ {
+ Id = 1234,
+ Variants = new List
{
- Id = 1234,
- Name = "Test",
- Tabs = tabs
- };
+ new ContentVariantDisplay
+ {
+ Name = "Test",
+ Tabs = tabs
+ }
+ }
+ };
var json = JsonConvert.SerializeObject(displayModel);
var jObject = JObject.Parse(json);
Assert.AreEqual("1234", jObject["id"].ToString());
- Assert.AreEqual("Test", jObject["name"].ToString());
- Assert.AreEqual(3, jObject["tabs"].Count());
- for (var tab = 0; tab < jObject["tabs"].Count(); tab++)
+ Assert.AreEqual("Test", jObject["variants"][0]["name"].ToString());
+ var jsonTabs = jObject["variants"][0]["tabs"];
+ Assert.AreEqual(3, jsonTabs.Count());
+ for (var tab = 0; tab < jsonTabs.Count(); tab++)
{
- Assert.AreEqual("Tab" + tab, jObject["tabs"][tab]["alias"].ToString());
- Assert.AreEqual("Tab" + tab, jObject["tabs"][tab]["label"].ToString());
- Assert.AreEqual(3, jObject["tabs"][tab]["properties"].Count());
- for (var prop = 0; prop < jObject["tabs"][tab]["properties"].Count(); prop++)
+ Assert.AreEqual("Tab" + tab, jsonTabs[tab]["alias"].ToString());
+ Assert.AreEqual("Tab" + tab, jsonTabs[tab]["label"].ToString());
+ Assert.AreEqual(3, jsonTabs[tab]["properties"].Count());
+ for (var prop = 0; prop < jsonTabs[tab]["properties"].Count(); prop++)
{
- Assert.AreEqual("property" + prop, jObject["tabs"][tab]["properties"][prop]["alias"].ToString());
- Assert.AreEqual("Property " + prop, jObject["tabs"][tab]["properties"][prop]["label"].ToString());
- Assert.AreEqual(prop, jObject["tabs"][tab]["properties"][prop]["id"].Value());
- Assert.AreEqual("value" + prop, jObject["tabs"][tab]["properties"][prop]["value"].ToString());
- Assert.AreEqual("{\"" + prop + "\":\"value\"}", jObject["tabs"][tab]["properties"][prop]["config"].ToString(Formatting.None));
- Assert.AreEqual("Description " + prop, jObject["tabs"][tab]["properties"][prop]["description"].ToString());
- Assert.AreEqual(false, jObject["tabs"][tab]["properties"][prop]["hideLabel"].Value());
+ Assert.AreEqual("property" + prop, jsonTabs[tab]["properties"][prop]["alias"].ToString());
+ Assert.AreEqual("Property " + prop, jsonTabs[tab]["properties"][prop]["label"].ToString());
+ Assert.AreEqual(prop, jsonTabs[tab]["properties"][prop]["id"].Value());
+ Assert.AreEqual("value" + prop, jsonTabs[tab]["properties"][prop]["value"].ToString());
+ Assert.AreEqual("{\"" + prop + "\":\"value\"}", jsonTabs[tab]["properties"][prop]["config"].ToString(Formatting.None));
+ Assert.AreEqual("Description " + prop, jsonTabs[tab]["properties"][prop]["description"].ToString());
+ Assert.AreEqual(false, jsonTabs[tab]["properties"][prop]["hideLabel"].Value());
}
}
}
diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs
new file mode 100644
index 0000000000..881fe79053
--- /dev/null
+++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs
@@ -0,0 +1,239 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Web.Http;
+using Moq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Entities;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Services;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.TestHelpers.ControllerTesting;
+using Umbraco.Tests.TestHelpers.Entities;
+using Umbraco.Tests.Testing;
+using Umbraco.Web;
+using Umbraco.Web.Editors;
+using Umbraco.Web.Models.ContentEditing;
+using Umbraco.Web.PublishedCache;
+using Umbraco.Web._Legacy.Actions;
+using Task = System.Threading.Tasks.Task;
+
+namespace Umbraco.Tests.Web.Controllers
+{
+ [TestFixture]
+ [UmbracoTest(Database = UmbracoTestOptions.Database.None)]
+ public class ContentControllerTests : TestWithDatabaseBase
+ {
+ protected override void ComposeApplication(bool withApplication)
+ {
+ base.ComposeApplication(withApplication);
+
+ //Replace with mockable services:
+
+ var userServiceMock = new Mock();
+ userServiceMock.Setup(service => service.GetUserById(It.IsAny()))
+ .Returns((int id) => id == 1234 ? new User(1234, "Test", "test@test.com", "test@test.com", "", new List(), new int[0], new int[0]) : null);
+ userServiceMock.Setup(service => service.GetPermissionsForPath(It.IsAny(), It.IsAny()))
+ .Returns(new EntityPermissionSet(123, new EntityPermissionCollection(new[]
+ {
+ new EntityPermission(0, 123, new[]
+ {
+ ActionBrowse.Instance.Letter.ToString(),
+ ActionUpdate.Instance.Letter.ToString(),
+ ActionPublish.Instance.Letter.ToString(),
+ ActionNew.Instance.Letter.ToString()
+ }),
+ })));
+
+ var entityService = new Mock();
+ entityService.Setup(x => x.GetAllPaths(UmbracoObjectTypes.Document, It.IsAny()))
+ .Returns((UmbracoObjectTypes objType, int[] ids) => ids.Select(x => new TreeEntityPath {Path = $"-1,{x}", Id = x}).ToList());
+
+ var dataTypeService = new Mock();
+ dataTypeService.Setup(service => service.GetDataType(It.IsAny()))
+ .Returns(MockedDataType());
+
+ Container.RegisterSingleton(f => Mock.Of());
+ Container.RegisterSingleton(f => userServiceMock.Object);
+ Container.RegisterSingleton(f => entityService.Object);
+ Container.RegisterSingleton(f => dataTypeService.Object);
+ }
+
+ private IDataType MockedDataType()
+ {
+ return Mock.Of(type => type.Id == 9876 && type.Name == "text");
+ }
+
+ private MultipartFormDataContent GetMultiPartRequestContent(string json)
+ {
+ var multiPartBoundary = "----WebKitFormBoundary123456789";
+ return new MultipartFormDataContent(multiPartBoundary)
+ {
+ new StringContent(json)
+ {
+ Headers =
+ {
+ ContentDisposition = new ContentDispositionHeaderValue("form-data")
+ {
+ Name = "contentItem"
+ }
+ }
+ }
+ };
+ }
+
+ private const string PublishJson1 = @"{
+ ""id"": 123,
+ ""contentTypeAlias"": ""page"",
+ ""parentId"": -1,
+ ""action"": ""save"",
+ ""variants"": [
+ {
+ ""name"": null,
+ ""properties"": [
+ {
+ ""id"": 1,
+ ""alias"": ""title"",
+ ""value"": ""asdf""
+ }
+ ],
+ ""culture"": ""en-US""
+ },
+ {
+ ""name"": null,
+ ""properties"": [
+ {
+ ""id"": 1,
+ ""alias"": ""title"",
+ ""value"": ""asdf""
+ }
+ ],
+ ""culture"": ""fr-FR""
+ },
+ {
+ ""name"": ""asdf"",
+ ""properties"": [
+ {
+ ""id"": 1,
+ ""alias"": ""title"",
+ ""value"": ""asdf""
+ }
+ ],
+ ""culture"": ""es-ES"",
+ ""save"": true,
+ ""publish"": true
+ }
+ ]
+}";
+
+ ///
+ /// Returns 404 if the content wasn't found based on the ID specified
+ ///
+ ///
+ [Test]
+ public async Task PostSave_Validate_Existing_Content()
+ {
+ ApiController Factory(HttpRequestMessage message, UmbracoHelper helper)
+ {
+ //var content = MockedContent.CreateSimpleContent(MockedContentTypes.CreateSimpleContentType());
+ //content.Id = 999999999; //this will not be found
+ //content.Path = "-1,999999999";
+
+ var contentServiceMock = Mock.Get(Current.Services.ContentService);
+ contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null);
+
+ var publishedSnapshot = Mock.Of();
+ var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()));
+ var usersController = new ContentController(publishedSnapshot, propertyEditorCollection);
+ Container.InjectProperties(usersController);
+ return usersController;
+ }
+
+ var runner = new TestRunner(Factory);
+ var response = await runner.Execute("Content", "PostSave", HttpMethod.Post,
+ content: GetMultiPartRequestContent(PublishJson1),
+ mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"),
+ assertOkResponse: false);
+
+ Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode);
+ Assert.AreEqual(")]}',\n{\"Message\":\"content was not found\"}", response.Item1.Content.ReadAsStringAsync().Result);
+
+ //var obj = JsonConvert.DeserializeObject>(response.Item2);
+ //Assert.AreEqual(0, obj.TotalItems);
+ }
+
+ [Test]
+ public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving()
+ {
+ ApiController Factory(HttpRequestMessage message, UmbracoHelper helper)
+ {
+ var contentServiceMock = Mock.Get(Current.Services.ContentService);
+ contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null);
+
+ var publishedSnapshot = Mock.Of();
+ var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()));
+ var usersController = new ContentController(publishedSnapshot, propertyEditorCollection);
+ Container.InjectProperties(usersController);
+ return usersController;
+ }
+
+ var json = JsonConvert.DeserializeObject(PublishJson1);
+ //remove all save flaggs
+ ((JArray)json["variants"])[2]["save"] = false;
+
+ var runner = new TestRunner(Factory);
+ var response = await runner.Execute("Content", "PostSave", HttpMethod.Post,
+ content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)),
+ mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"),
+ assertOkResponse: false);
+
+ Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode);
+ Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", response.Item1.Content.ReadAsStringAsync().Result);
+ }
+
+ ///
+ /// Returns 404 if any of the posted properties dont actually exist
+ ///
+ ///
+ [Test, Ignore("Not implemented yet")]
+ public async Task PostSave_Validate_Properties_Exist()
+ {
+ //TODO: Make this work! to finish it, we need to include a property in the POST data that doesn't exist on the content type
+ // or change the content type below to not include one of the posted ones
+
+ ApiController Factory(HttpRequestMessage message, UmbracoHelper helper)
+ {
+ var content = MockedContent.CreateSimpleContent(MockedContentTypes.CreateSimpleContentType());
+ content.Id = 123;
+ content.Path = "-1,123";
+
+ var contentServiceMock = Mock.Get(Current.Services.ContentService);
+ contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null);
+
+ var publishedSnapshot = Mock.Of();
+ var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()));
+ var usersController = new ContentController(publishedSnapshot, propertyEditorCollection);
+ Container.InjectProperties(usersController);
+ return usersController;
+ }
+
+ var runner = new TestRunner(Factory);
+ var response = await runner.Execute("Content", "PostSave", HttpMethod.Post,
+ content: GetMultiPartRequestContent(PublishJson1),
+ mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"),
+ assertOkResponse: false);
+
+ Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode);
+
+ //var obj = JsonConvert.DeserializeObject>(response.Item2);
+ //Assert.AreEqual(0, obj.TotalItems);
+ }
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/.eslintrc b/src/Umbraco.Web.UI.Client/.eslintrc
index 205d8dcf50..1b39098cfc 100644
--- a/src/Umbraco.Web.UI.Client/.eslintrc
+++ b/src/Umbraco.Web.UI.Client/.eslintrc
@@ -3,37 +3,8 @@
"browser": true
},
- "plugins": [
- "angular"
- ],
-
"rules": {
- "eqeqeq": 2,
- "curly": 2,
-
- "no-unused-vars": 1,
- "no-eval": 1,
- "no-delete-var": 1,
- "quotes": 1,
- "dot-notation": 1,
-
- "no-use-before-define": 0,
- "angular/ng_controller_as": 1,
- "angular/ng_controller_as_vm": 1,
-
-
- "strict": 0,
- "no-irregular-whitespace": 0,
- "no-mixed-spaces-and-tabs": 0,
- "no-multi-spaces": 0,
- "key-spacing": 0,
- "semi-spacing": 0,
- "space-infix-ops": 0,
- "comma-spacing": 0,
- "no-trailing-spaces": 0,
- "eol-last": 0,
- "no-underscore-dangle": 0,
- "camelcase": 0
+ "comma-dangle": ["error", "never"]
},
"globals": {
diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json
index 71b393ae9c..1d7096cb29 100644
--- a/src/Umbraco.Web.UI.Client/bower.json
+++ b/src/Umbraco.Web.UI.Client/bower.json
@@ -25,12 +25,14 @@
"angular-i18n": "~1.7.2",
"signalr": "^2.2.1",
"typeahead.js": "~0.10.5",
- "underscore": "~1.7.0",
+ "underscore": "~1.9.1",
"rgrove-lazyload": "*",
"bootstrap-social": "~4.8.0",
"jquery": "2.2.4",
"jquery-ui": "~1.12.0",
"jquery-migrate": "1.4.0",
+ "jquery-validate": "~1.17.0",
+ "jquery-validation-unobtrusive": "3.2.10",
"angular-dynamic-locale": "~0.1.36",
"ng-file-upload": "~12.2.13",
"tinymce": "~4.7.1",
@@ -41,7 +43,7 @@
"clipboard": "~2.0.0",
"font-awesome": "~4.2",
"animejs": "^2.2.0",
- "angular-ui-sortable": "0.14.3",
+ "angular-ui-sortable": "0.14.4",
"angular-messages": "^1.7.2"
},
"install": {
@@ -53,39 +55,41 @@
"ace-builds"
],
"sources": {
- "moment": [
- "bower_components/moment/min/moment.min.js",
- "bower_components/moment/min/moment-with-locales.js",
- "bower_components/moment/min/moment-with-locales.min.js",
- "bower_components/moment/locale/*.js"
- ],
- "underscore": [
- "bower_components/underscore/underscore-min.js",
- "bower_components/underscore/underscore-min.map"
- ],
- "jquery": [
- "bower_components/jquery/dist/jquery.min.js",
- "bower_components/jquery/dist/jquery.min.map"
- ],
- "angular-dynamic-locale": [
- "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js",
- "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map"
- ],
- "angular-local-storage": [
- "bower_components/angular-local-storage/dist/angular-local-storage.min.js",
- "bower_components/angular-local-storage/dist/angular-local-storage.min.js.map"
- ],
- "tinymce": [
- "bower_components/tinymce/tinymce.min.js"
- ],
- "angular-i18n": "bower_components/angular-i18n/angular-locale_*.js",
- "typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js",
- "rgrove-lazyload": "bower_components/rgrove-lazyload/lazyload.js",
- "ng-file-upload": "bower_components/ng-file-upload/ng-file-upload.min.js",
- "jquery-ui": "bower_components/jquery-ui/jquery-ui.min.js",
- "jquery-migrate": "bower_components/jquery-migrate/jquery-migrate.min.js",
- "clipboard": "bower_components/clipboard/dist/clipboard.min.js",
- "animejs": "bower_components/animejs/anime.min.js"
+ "moment": [
+ "bower_components/moment/min/moment.min.js",
+ "bower_components/moment/min/moment-with-locales.js",
+ "bower_components/moment/min/moment-with-locales.min.js",
+ "bower_components/moment/locale/*.js"
+ ],
+ "underscore": [
+ "bower_components/underscore/underscore-min.js",
+ "bower_components/underscore/underscore-min.map"
+ ],
+ "jquery": [
+ "bower_components/jquery/dist/jquery.min.js",
+ "bower_components/jquery/dist/jquery.min.map"
+ ],
+ "angular-dynamic-locale": [
+ "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js",
+ "bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map"
+ ],
+ "angular-local-storage": [
+ "bower_components/angular-local-storage/dist/angular-local-storage.min.js",
+ "bower_components/angular-local-storage/dist/angular-local-storage.min.js.map"
+ ],
+ "tinymce": [
+ "bower_components/tinymce/tinymce.min.js"
+ ],
+ "angular-i18n": "bower_components/angular-i18n/angular-locale_*.js",
+ "typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js",
+ "rgrove-lazyload": "bower_components/rgrove-lazyload/lazyload.js",
+ "ng-file-upload": "bower_components/ng-file-upload/ng-file-upload.min.js",
+ "jquery-ui": "bower_components/jquery-ui/jquery-ui.min.js",
+ "jquery-migrate": "bower_components/jquery-migrate/jquery-migrate.min.js",
+ "clipboard": "bower_components/clipboard/dist/clipboard.min.js",
+ "animejs": "bower_components/animejs/anime.min.js",
+ "jquery-validate": "bower_components/jquery-validate/dist/jquery.validate.min.js",
+ "jquery-validation-unobtrusive": "bower_components/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"
}
},
"devDependencies": {
diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js
index cc57648564..33bc80f1ef 100644
--- a/src/Umbraco.Web.UI.Client/gulpfile.js
+++ b/src/Umbraco.Web.UI.Client/gulpfile.js
@@ -12,6 +12,9 @@ const imagemin = require('gulp-imagemin');
var _ = require('lodash');
var MergeStream = require('merge-stream');
+// js
+const eslint = require('gulp-eslint');
+
//Less + css
var postcss = require('gulp-postcss');
var less = require('gulp-less');
@@ -30,6 +33,11 @@ Helper functions
function processJs(files, out) {
return gulp.src(files)
+ // check for js errors
+ .pipe(eslint())
+ // outputs the lint results to the console
+ .pipe(eslint.format())
+ // sort files in stream by path or any custom sort comparator
.pipe(sort())
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js
index 78124ac625..823d3d526d 100644
--- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js
+++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js
@@ -329,10 +329,15 @@
/** Converts a string/integer/bool to true/false */
Object.toBoolean = function (obj) {
+ if (obj === undefined || obj === null) {
+ return false;
+ }
+
if ((typeof obj) === "boolean") {
return obj;
}
- if (obj === "1" || obj === 1 || obj === "true") {
+
+ if (obj === "1" || obj === 1 || obj.toString().toLowerCase() === "true") {
return true;
}
return false;
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index 47f010634d..2497bdc592 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -50,6 +50,21 @@
}
}
},
+ "acorn": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
+ "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz",
+ "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.0.3"
+ }
+ },
"addressparser": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
@@ -64,9 +79,9 @@
"dev": true
},
"agent-base": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz",
- "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
"dev": true,
"requires": {
"es6-promisify": "^5.0.0"
@@ -82,6 +97,12 @@
"json-stable-stringify": "^1.0.1"
}
},
+ "ajv-keywords": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
+ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
+ "dev": true
+ },
"align-text": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
@@ -142,6 +163,15 @@
"integrity": "sha1-0XB8cn+K0N8hxKLewgzX/ElFtSo=",
"dev": true
},
+ "ansi-colors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "^0.1.0"
+ }
+ },
"ansi-cyan": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
@@ -151,6 +181,12 @@
"ansi-wrap": "0.1.0"
}
},
+ "ansi-escapes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
+ "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+ "dev": true
+ },
"ansi-gray": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
@@ -389,6 +425,12 @@
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
"dev": true
},
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -456,9 +498,9 @@
"dev": true
},
"atob": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz",
- "integrity": "sha1-qysVDlHXsSK578jXNAwGtsQQdrw=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz",
+ "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=",
"dev": true
},
"autoprefixer": {
@@ -482,9 +524,9 @@
"dev": true
},
"aws4": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
- "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
+ "integrity": "sha1-1NDpudv8p3vwjusKikcVUP454ok=",
"dev": true
},
"axios": {
@@ -509,6 +551,17 @@
}
}
},
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ }
+ },
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
@@ -544,6 +597,35 @@
"requires": {
"is-descriptor": "^1.0.0"
}
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
}
}
},
@@ -739,13 +821,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -754,7 +836,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -854,18 +936,16 @@
}
},
"braces": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz",
- "integrity": "sha1-cIbJE7TloI2+N6wO5qJQDEumkbs=",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=",
"dev": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
- "define-property": "^1.0.0",
"extend-shallow": "^2.0.1",
"fill-range": "^4.0.0",
"isobject": "^3.0.1",
- "kind-of": "^6.0.2",
"repeat-element": "^1.1.2",
"snapdragon": "^0.8.1",
"snapdragon-node": "^2.0.1",
@@ -873,15 +953,6 @@
"to-regex": "^3.0.1"
},
"dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
@@ -1059,12 +1130,27 @@
"unset-value": "^1.0.0"
}
},
+ "caller-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "dev": true,
+ "requires": {
+ "callsites": "^0.2.0"
+ }
+ },
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
"dev": true
},
+ "callsites": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+ "dev": true
+ },
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
@@ -1102,9 +1188,9 @@
}
},
"caniuse-db": {
- "version": "1.0.30000821",
- "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000821.tgz",
- "integrity": "sha1-P83GfERqlKnN2Egkik4+VLLadBk=",
+ "version": "1.0.30000836",
+ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000836.tgz",
+ "integrity": "sha1-eItsj28CmRdDsYzbvVT5bQW0uVo=",
"dev": true
},
"canonical-path": {
@@ -1182,6 +1268,12 @@
}
}
},
+ "chardet": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
+ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
+ "dev": true
+ },
"chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -1226,9 +1318,9 @@
}
},
"circular-json": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.4.tgz",
- "integrity": "sha512-vnJA8KS0BfOihugYEUkLRcnmq21FbuivbxgzDLXNs3zIk4KllV4Mx4UuTzBXht9F00C7QfD1YqMXg1zP6EXpig==",
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
"dev": true
},
"clap": {
@@ -1260,66 +1352,24 @@
"requires": {
"is-descriptor": "^0.1.0"
}
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
- "dev": true
}
}
},
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
"cliui": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
@@ -1367,9 +1417,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -1377,14 +1427,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -1470,9 +1520,9 @@
}
},
"colors": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.1.tgz",
- "integrity": "sha1-9KPTApdqrwQjVroa3jsaLGLZ15Q=",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.4.tgz",
+ "integrity": "sha1-4MtB0+SyCAazv8J/RVnwG5S8L3w=",
"dev": true
},
"combine-lists": {
@@ -1584,9 +1634,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -1594,14 +1644,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -1610,9 +1660,9 @@
}
},
"concat-with-sourcemaps": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.5.tgz",
- "integrity": "sha1-iWS8I0fQWBm2N5gQTYfW4AG+2NA=",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
+ "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=",
"dev": true,
"requires": {
"source-map": "^0.6.1"
@@ -2252,13 +2302,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -2267,7 +2317,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
},
"strip-bom": {
@@ -2585,6 +2635,37 @@
"requires": {
"is-descriptor": "^1.0.2",
"isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
}
},
"defined": {
@@ -2614,6 +2695,37 @@
}
}
},
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ }
+ }
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2650,6 +2762,15 @@
"integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
"dev": true
},
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"dom-serialize": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
@@ -2912,13 +3033,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -2927,7 +3048,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
},
"strip-bom": {
@@ -3030,7 +3151,7 @@
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"dev": true,
"requires": {
- "once": "^1.4.0"
+ "once": "1.4.0"
}
},
"isarray": {
@@ -3045,13 +3166,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -3060,7 +3181,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -3092,9 +3213,9 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.41",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.41.tgz",
- "integrity": "sha1-fjNkPgDNhe39F+BBlPbQDnNzcjU=",
+ "version": "1.3.45",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz",
+ "integrity": "sha1-RYrBscXHYM6IEaFtK/vZfsMLr7g=",
"dev": true
},
"encodeurl": {
@@ -3361,12 +3482,223 @@
}
}
},
+ "eslint": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz",
+ "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.5.0",
+ "babel-code-frame": "^6.26.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^3.1.0",
+ "doctrine": "^2.1.0",
+ "eslint-scope": "^4.0.0",
+ "eslint-utils": "^1.3.1",
+ "eslint-visitor-keys": "^1.0.0",
+ "espree": "^4.0.0",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^2.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "glob": "^7.1.2",
+ "globals": "^11.7.0",
+ "ignore": "^4.0.2",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^5.2.0",
+ "is-resolvable": "^1.1.0",
+ "js-yaml": "^3.11.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.5",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.2",
+ "path-is-inside": "^1.0.2",
+ "pluralize": "^7.0.0",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.0",
+ "require-uncached": "^1.0.3",
+ "semver": "^5.5.0",
+ "string.prototype.matchall": "^2.0.0",
+ "strip-ansi": "^4.0.0",
+ "strip-json-comments": "^2.0.1",
+ "table": "^4.0.3",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
+ "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
+ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "progress": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+ "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz",
+ "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.6.0",
+ "acorn-jsx": "^4.1.1"
+ }
+ },
"esprima": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
"integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
"dev": true
},
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
"estemplate": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/estemplate/-/estemplate-0.5.1.tgz",
@@ -3564,63 +3896,6 @@
"requires": {
"is-extendable": "^0.1.0"
}
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
- "dev": true
}
}
},
@@ -3634,14 +3909,14 @@
},
"dependencies": {
"fill-range": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
- "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
+ "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=",
"dev": true,
"requires": {
"is-number": "^2.1.0",
"isobject": "^2.0.0",
- "randomatic": "^1.1.3",
+ "randomatic": "^3.0.0",
"repeat-element": "^1.1.2",
"repeat-string": "^1.5.2"
}
@@ -3760,6 +4035,37 @@
}
}
},
+ "external-editor": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.4.0",
+ "iconv-lite": "^0.4.17",
+ "tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ }
+ }
+ },
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -3793,6 +4099,35 @@
"requires": {
"is-extendable": "^0.1.0"
}
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
}
}
},
@@ -3836,6 +4171,18 @@
"time-stamp": "^1.0.0"
}
},
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -3871,6 +4218,16 @@
"object-assign": "^4.1.0"
}
},
+ "file-entry-cache": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^1.2.1",
+ "object-assign": "^4.0.1"
+ }
+ },
"file-type": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz",
@@ -4031,6 +4388,26 @@
"integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=",
"dev": true
},
+ "flat-cache": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
+ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.3.1",
+ "del": "^2.0.2",
+ "graceful-fs": "^4.1.2",
+ "write": "^0.2.1"
+ },
+ "dependencies": {
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ }
+ }
+ },
"flatten": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
@@ -4038,9 +4415,9 @@
"dev": true
},
"follow-redirects": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz",
- "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==",
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.2.tgz",
+ "integrity": "sha512-kssLorP/9acIdpQ2udQVTiCS5LQmdEz9mvdIfDcl1gYX2tPKFADHSyFdvJS040XdFsPzemWtgI3q8mFVCxtX8A==",
"dev": true,
"requires": {
"debug": "^3.1.0"
@@ -4710,6 +5087,12 @@
"integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=",
"dev": true
},
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
"gaze": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz",
@@ -5004,6 +5387,12 @@
"which": "^1.2.14"
}
},
+ "globals": {
+ "version": "11.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
+ "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==",
+ "dev": true
+ },
"globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
@@ -5122,13 +5511,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -5137,7 +5526,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -5198,9 +5587,9 @@
},
"dependencies": {
"clone": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
- "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=",
"dev": true
},
"clone-stats": {
@@ -5268,13 +5657,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -5283,7 +5672,32 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
+ }
+ }
+ }
+ },
+ "gulp-eslint": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz",
+ "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==",
+ "dev": true,
+ "requires": {
+ "eslint": "^5.0.1",
+ "fancy-log": "^1.3.2",
+ "plugin-error": "^1.0.1"
+ },
+ "dependencies": {
+ "plugin-error": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
+ "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^1.0.1",
+ "arr-diff": "^4.0.0",
+ "arr-union": "^3.1.0",
+ "extend-shallow": "^3.0.2"
}
}
}
@@ -5803,9 +6217,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -5813,14 +6227,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -5972,6 +6386,12 @@
"sparkles": "^1.0.0"
}
},
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
"has-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@@ -6075,9 +6495,9 @@
}
},
"http-parser-js": {
- "version": "0.4.11",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz",
- "integrity": "sha1-W3IIScZQkDwn5SFjPZRpbulfNSk=",
+ "version": "0.4.12",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz",
+ "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=",
"dev": true
},
"http-proxy": {
@@ -6166,6 +6586,12 @@
"integrity": "sha1-LstC/SlHRJIiCaLnxATayHk9it4=",
"dev": true
},
+ "ignore": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz",
+ "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==",
+ "dev": true
+ },
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -6284,9 +6710,9 @@
}
},
"esprima": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
- "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=",
"dev": true,
"optional": true
},
@@ -6326,6 +6752,12 @@
}
}
},
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
"indent-string": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
@@ -6382,6 +6814,88 @@
"integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=",
"dev": true
},
+ "inquirer": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz",
+ "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.0",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^2.1.0",
+ "figures": "^2.0.0",
+ "lodash": "^4.3.0",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rxjs": "^5.5.2",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^4.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"interpret": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
@@ -6430,12 +6944,23 @@
"dev": true
},
"is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"requires": {
- "kind-of": "^6.0.0"
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
}
},
"is-arrayish": {
@@ -6475,18 +7000,29 @@
"dev": true
},
"is-callable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
- "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=",
"dev": true
},
"is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"requires": {
- "kind-of": "^6.0.0"
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
}
},
"is-date-object": {
@@ -6496,14 +7032,22 @@
"dev": true
},
"is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
"dev": true,
"requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
+ "dev": true
+ }
}
},
"is-directory": {
@@ -6548,6 +7092,12 @@
"number-is-nan": "^1.0.0"
}
},
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
"is-gif": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-gif/-/is-gif-1.0.0.tgz",
@@ -6647,6 +7197,30 @@
}
}
},
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
@@ -6681,6 +7255,12 @@
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
"dev": true
},
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
@@ -6712,6 +7292,12 @@
"is-unc-path": "^1.0.0"
}
},
+ "is-resolvable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+ "dev": true
+ },
"is-retry-allowed": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
@@ -6797,10 +7383,13 @@
"dev": true
},
"isbinaryfile": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz",
- "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=",
- "dev": true
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
+ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc": "^1.2.0"
+ }
},
"isexe": {
"version": "2.0.0",
@@ -6844,6 +7433,12 @@
"integrity": "sha1-LlRewrDylX9BNWUQIFIU6Y+tZYI=",
"dev": true
},
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
"js-yaml": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
@@ -6867,6 +7462,12 @@
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
},
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
"json-stable-stringify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
@@ -6876,6 +7477,12 @@
"jsonify": "~0.0.0"
}
},
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -6934,14 +7541,14 @@
}
},
"karma": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.2.tgz",
- "integrity": "sha1-TS25QChQpmVR+nhLAWT7CCTtjEs=",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.5.tgz",
+ "integrity": "sha512-rECezBeY7mjzGUWhFlB7CvPHgkHJLXyUmWg+6vHCEsdWNUTnmiS6jRrIMcJEWgU2DUGZzGWG0bTRVky8fsDTOA==",
"dev": true,
"requires": {
"bluebird": "^3.3.0",
"body-parser": "^1.16.1",
- "chokidar": "^1.4.1",
+ "chokidar": "^2.0.3",
"colors": "^1.1.0",
"combine-lists": "^1.0.0",
"connect": "^3.6.0",
@@ -6954,7 +7561,7 @@
"http-proxy": "^1.13.0",
"isbinaryfile": "^3.0.0",
"lodash": "^4.17.4",
- "log4js": "^2.3.9",
+ "log4js": "^2.5.3",
"mime": "^1.3.4",
"minimatch": "^3.0.2",
"optimist": "^0.6.1",
@@ -6968,6 +7575,16 @@
"useragent": "2.2.1"
},
"dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -6992,6 +7609,27 @@
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
"dev": true
},
+ "chokidar": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
+ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.0",
+ "braces": "^2.3.0",
+ "fsevents": "^1.2.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.1",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "lodash.debounce": "^4.0.8",
+ "normalize-path": "^2.1.1",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.0.0",
+ "upath": "^1.0.5"
+ }
+ },
"connect": {
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
@@ -7060,6 +7698,15 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "is-glob": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -7178,13 +7825,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -7193,7 +7840,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -7317,9 +7964,9 @@
}
},
"lodash": {
- "version": "4.17.5",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
- "integrity": "sha1-maktZcAnLevoyWtgV7yPv6O+1RE=",
+ "version": "4.17.10",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
+ "integrity": "sha1-G3eTz3JZ6jj7NmHU04syYK+K5Oc=",
"dev": true
},
"lodash._basecopy": {
@@ -7447,6 +8094,12 @@
"integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=",
"dev": true
},
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+ "dev": true
+ },
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
@@ -7592,9 +8245,9 @@
}
},
"log4js": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.9.0.tgz",
- "integrity": "sha512-pptn4+5Q3ysOW6Jgm9lzhDUCFEYv7FLrazEzPQQlxgSP+IVl5HMPgT8hm2DyRqGY4GUiVjZz4XXRvTZ9BELQyw==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.11.0.tgz",
+ "integrity": "sha512-z1XdwyGFg8/WGkOyF6DPJjivCWNLKrklGdViywdYnSKOvgtEBo2UyEMZS5sD2mZrQlU3TvO8wDWLc8mzE1ncBQ==",
"dev": true,
"requires": {
"amqplib": "^0.5.2",
@@ -7612,6 +8265,12 @@
"streamroller": "0.7.0"
},
"dependencies": {
+ "circular-json": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz",
+ "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==",
+ "dev": true
+ },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@@ -7670,9 +8329,9 @@
"optional": true
},
"commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"dev": true,
"optional": true
},
@@ -7842,9 +8501,9 @@
}
},
"mailgun-js": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.0.tgz",
- "integrity": "sha512-o0P6jjZlx5CQj12tvVgDTbgjTqVN0+5h6/6P1+3c6xmozVKBwniQ6Qt3MkCSF0+ueVTbobAfWyGpWRZMJu8t1g==",
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.1.tgz",
+ "integrity": "sha512-lvuMP14u24HS2uBsJEnzSyPMxzU2b99tQsIx1o6QNjqxjk8b3WvR+vq5oG1mjqz/IBYo+5gF+uSoDS0RkMVHmg==",
"dev": true,
"optional": true,
"requires": {
@@ -7901,23 +8560,12 @@
}
},
"make-iterator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz",
- "integrity": "sha1-V7713IXSOSO6I3ZzJNjo+PPZaUs=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=",
"dev": true,
"requires": {
- "kind-of": "^3.1.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
+ "kind-of": "^6.0.2"
}
},
"map-cache": {
@@ -7959,6 +8607,12 @@
"integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
"dev": true
},
+ "math-random": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
+ "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
+ "dev": true
+ },
"mdn-data": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
@@ -8013,9 +8667,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -8023,14 +8677,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -8106,6 +8760,12 @@
"mime-db": "~1.33.0"
}
},
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -8206,6 +8866,12 @@
"duplexer2": "0.0.2"
}
},
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "dev": true
+ },
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
@@ -8234,9 +8900,15 @@
}
},
"natives": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.2.tgz",
- "integrity": "sha1-RDfKHtin8EdTHM368nkoU99O+hw=",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.3.tgz",
+ "integrity": "sha1-RKV5vmRQfqLW7RygSpQVkVz3VVg=",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"negotiator": {
@@ -8252,6 +8924,12 @@
"dev": true,
"optional": true
},
+ "nice-try": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz",
+ "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==",
+ "dev": true
+ },
"node-fs": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/node-fs/-/node-fs-0.1.7.tgz",
@@ -8476,43 +9154,6 @@
"is-descriptor": "^0.1.0"
}
},
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
- "dev": true
- }
- }
- },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -8525,9 +9166,9 @@
}
},
"object-keys": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
- "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+ "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=",
"dev": true
},
"object-visit": {
@@ -8641,7 +9282,7 @@
},
"onetime": {
"version": "1.1.0",
- "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true
},
@@ -8819,6 +9460,17 @@
"iconv-lite": "0.4.23",
"unpipe": "1.0.0"
}
+ },
+ "socks-proxy-agent": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz",
+ "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "agent-base": "^4.1.0",
+ "socks": "^1.1.10"
+ }
}
}
},
@@ -8948,12 +9600,17 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true,
- "optional": true
+ "dev": true
},
"path-parse": {
"version": "1.0.5",
@@ -9170,6 +9827,12 @@
"irregular-plurals": "^1.0.0"
}
},
+ "pluralize": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+ "dev": true
+ },
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -9567,9 +10230,9 @@
}
},
"proxy-agent": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz",
- "integrity": "sha512-g6n6vnk8fRf705ShN+FEXFG/SDJaW++lSs0d9KaJh4uBWW/wi7en4Cpo5VYQW3SZzAE121lhB/KLQrbURoubZw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.1.tgz",
+ "integrity": "sha512-mAZexaz9ZxQhYPWfAjzlrloEjW+JHiBFryE4AJXFDTnaXfmH/FKqC1swTRKuEPbHWz02flQNXFOyDUF7zfEG6A==",
"dev": true,
"optional": true,
"requires": {
@@ -9580,7 +10243,7 @@
"lru-cache": "^4.1.2",
"pac-proxy-agent": "^2.0.1",
"proxy-from-env": "^1.0.0",
- "socks-proxy-agent": "^3.0.0"
+ "socks-proxy-agent": "^4.0.1"
},
"dependencies": {
"debug": {
@@ -9668,23 +10331,21 @@
"dev": true
},
"randomatic": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
- "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz",
+ "integrity": "sha1-01SQAw6091eN4pLObfsEqRoSiSM=",
"dev": true,
"requires": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "math-random": "^1.0.1"
},
"dependencies": {
- "kind-of": {
+ "is-number": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=",
+ "dev": true
}
}
},
@@ -9761,13 +10422,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -9776,7 +10437,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -9839,9 +10500,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -9849,14 +10510,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -9964,6 +10625,21 @@
"safe-regex": "^1.1.0"
}
},
+ "regexp.prototype.flags": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz",
+ "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2"
+ }
+ },
+ "regexpp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz",
+ "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==",
+ "dev": true
+ },
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -10063,6 +10739,16 @@
"integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=",
"dev": true
},
+ "require-uncached": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "dev": true,
+ "requires": {
+ "caller-path": "^0.1.0",
+ "resolve-from": "^1.0.0"
+ }
+ },
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -10070,9 +10756,9 @@
"dev": true
},
"resolve": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz",
- "integrity": "sha1-D70hJ4sntABEgcOVNJ56umCp/1w=",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
+ "integrity": "sha1-qt1lY3T9KYruiVvAJrgpdBhnf9M=",
"dev": true,
"requires": {
"path-parse": "^1.0.5"
@@ -10088,6 +10774,12 @@
"global-modules": "^1.0.0"
}
},
+ "resolve-from": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+ "dev": true
+ },
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -10112,6 +10804,27 @@
}
}
},
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ },
+ "dependencies": {
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ }
+ }
+ },
"ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -10142,6 +10855,15 @@
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=",
"dev": true
},
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
"run-sequence": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz",
@@ -10153,10 +10875,19 @@
"plugin-error": "^0.1.2"
}
},
+ "rxjs": {
+ "version": "5.5.11",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
+ "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
+ "dev": true,
+ "requires": {
+ "symbol-observable": "1.0.1"
+ }
+ },
"safe-buffer": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
- "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=",
"dev": true
},
"safe-regex": {
@@ -10379,7 +11110,6 @@
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dev": true,
- "optional": true,
"requires": {
"shebang-regex": "^1.0.0"
}
@@ -10388,8 +11118,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
- "dev": true,
- "optional": true
+ "dev": true
},
"sigmund": {
"version": "1.0.1",
@@ -10419,11 +11148,21 @@
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
"dev": true
},
+ "slice-ansi": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
"smart-buffer": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz",
"integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=",
- "dev": true
+ "dev": true,
+ "optional": true
},
"smtp-connection": {
"version": "2.12.0",
@@ -10468,63 +11207,6 @@
"requires": {
"is-extendable": "^0.1.0"
}
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
- "dev": true
}
}
},
@@ -10547,6 +11229,35 @@
"requires": {
"is-descriptor": "^1.0.0"
}
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
}
}
},
@@ -10653,19 +11364,41 @@
"resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz",
"integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=",
"dev": true,
+ "optional": true,
"requires": {
"ip": "^1.1.4",
"smart-buffer": "^1.0.13"
}
},
"socks-proxy-agent": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz",
- "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==",
"dev": true,
+ "optional": true,
"requires": {
- "agent-base": "^4.1.0",
- "socks": "^1.1.10"
+ "agent-base": "~4.2.0",
+ "socks": "~2.2.0"
+ },
+ "dependencies": {
+ "smart-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz",
+ "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==",
+ "dev": true,
+ "optional": true
+ },
+ "socks": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz",
+ "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ip": "^1.1.5",
+ "smart-buffer": "^4.0.1"
+ }
+ }
}
},
"sort-keys": {
@@ -10831,63 +11564,6 @@
"requires": {
"is-descriptor": "^0.1.0"
}
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
- "dev": true
}
}
},
@@ -10937,13 +11613,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -10952,7 +11628,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -11037,6 +11713,46 @@
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
},
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "string.prototype.matchall": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz",
+ "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.10.0",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "regexp.prototype.flags": "^1.2.0"
+ }
+ },
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
@@ -11094,9 +11810,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -11104,14 +11820,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -11237,6 +11953,75 @@
}
}
},
+ "symbol-observable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+ "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
+ "dev": true
+ },
+ "table": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz",
+ "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.0.1",
+ "ajv-keywords": "^3.0.0",
+ "chalk": "^2.1.0",
+ "lodash": "^4.17.4",
+ "slice-ansi": "1.0.0",
+ "string-width": "^2.1.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
+ "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.1"
+ }
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"tar-stream": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz",
@@ -11273,13 +12058,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -11288,7 +12073,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -11311,6 +12096,12 @@
"uuid": "^3.0.1"
}
},
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
"throttleit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
@@ -11340,9 +12131,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -11350,14 +12141,14 @@
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
+ "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -11832,6 +12623,29 @@
"integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=",
"dev": true
},
+ "upath": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
+ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ }
+ }
+ },
"urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
@@ -11958,9 +12772,9 @@
"dev": true
},
"vendors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz",
- "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
+ "integrity": "sha1-f8te759WI7FWvOqJ7DfWNnbyGAE=",
"dev": true
},
"verror": {
@@ -12021,13 +12835,13 @@
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"string_decoder": {
@@ -12036,7 +12850,7 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.2"
}
}
}
@@ -12251,6 +13065,15 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
+ "write": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 07e0f5a432..c4b4ac0350 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -27,6 +27,7 @@
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-connect": "5.0.0",
+ "gulp-eslint": "^5.0.0",
"gulp-imagemin": "^4.1.0",
"gulp-less": "^3.5.0",
"gulp-ngdocs": "^0.3.0",
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/_readme.md b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/_readme.md
deleted file mode 100644
index 8feb0377db..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/_readme.md
+++ /dev/null
@@ -1,3 +0,0 @@
-#Obsolete directives
-
-Folder contains directives we plan to remove in the next major version of umbraco (8.0) these are not recommended to use.
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/autoscale.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/autoscale.directive.js
deleted file mode 100644
index f9ca4350e2..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/autoscale.directive.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:autoScale
-* @element div
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @function
-* @description
-* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
-* So if you only want to scale the div to 70 pixels from the bottom you pass "70"
-
-* @example
-*
-*
-*
-*
-*
-**/
-
-angular.module("umbraco.directives")
- .directive('autoScale', function ($window) {
- return function (scope, el, attrs) {
-
- var totalOffset = 0;
- var offsety = parseInt(attrs.autoScale, 10);
- var window = angular.element($window);
- if (offsety !== undefined){
- totalOffset += offsety;
- }
-
- setTimeout(function () {
- el.height(window.height() - (el.offset().top + totalOffset));
- }, 500);
-
- window.bind("resize", function () {
- el.height(window.height() - (el.offset().top + totalOffset));
- });
-
- };
- });
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/detectfold.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/detectfold.directive.js
deleted file mode 100644
index ac2b86582e..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/detectfold.directive.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:detectFold
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor
-* exceeds the height of the window
-**/
-
-angular.module("umbraco.directives.html")
- .directive('detectFold', function ($timeout, $log, windowResizeListener) {
- return {
- require: "^?umbTabs",
- restrict: 'A',
- link: function (scope, el, attrs, tabsCtrl) {
-
- var firstRun = false;
- var parent = $(".umb-panel-body");
- var winHeight = $(window).height();
- var calculate = function () {
- if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) {
-
- //now that the element is visible, set the flag in a couple of seconds,
- // this will ensure that loading time of a current tab get's completed and that
- // we eventually stop watching to save on CPU time
- $timeout(function() {
- firstRun = true;
- }, 4000);
-
- //var parent = el.parent();
- var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
- //var belowFold = (el.offset().top + el.height()) > winHeight;
- if (hasOverflow) {
- el.addClass("umb-bottom-bar");
-
- //I wish we didn't have to put this logic here but unfortunately we
- // do. This needs to calculate the left offest to place the bottom bar
- // depending on if the left column splitter has been moved by the user
- // (based on the nav-resize directive)
- var wrapper = $("#mainwrapper");
- var contentPanel = $("#leftcolumn").next();
- var contentPanelLeftPx = contentPanel.css("left");
-
- el.css({ left: contentPanelLeftPx });
- }
- }
- return firstRun;
- };
-
- var resizeCallback = function(size) {
- winHeight = size.height;
- el.removeClass("umb-bottom-bar");
- calculate();
- };
-
- windowResizeListener.register(resizeCallback);
-
- //Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute
- // the watcher since it will be recalculated when the tab changes!
- if (el.closest(".umb-tab-pane").index() === 0) {
- //run a watcher to ensure that the calculation occurs until it's firstRun but ensure
- // the calculations are throttled to save a bit of CPU
- var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) {
- if (newVal !== oldVal) {
- listener();
- }
- });
- }
-
- //listen for tab changes
- if (tabsCtrl != null) {
- tabsCtrl.onTabShown(function (args) {
- calculate();
- });
- }
-
- //ensure to unregister
- scope.$on('$destroy', function() {
- windowResizeListener.unregister(resizeCallback);
- });
- }
- };
- });
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbItemSorter.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbItemSorter.directive.js
deleted file mode 100644
index 30a01e43c1..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbItemSorter.directive.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbItemSorter
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @function
-* @element ANY
-* @restrict E
-* @description A re-usable directive for sorting items
-**/
-
-function umbItemSorter(angularHelper) {
- return {
- scope: {
- model: "="
- },
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/directives/_obsolete/umb-item-sorter.html',
- link: function(scope, element, attrs, ctrl) {
- var defaultModel = {
- okButton: "Ok",
- successMsg: "Sorting successful",
- complete: false
- };
- //assign user vals to default
- angular.extend(defaultModel, scope.model);
- //re-assign merged to user
- scope.model = defaultModel;
-
- scope.performSort = function() {
- scope.$emit("umbItemSorter.sorting", {
- sortedItems: scope.model.itemsToSort
- });
- };
-
- scope.handleCancel = function () {
- scope.$emit("umbItemSorter.cancel");
- };
-
- scope.handleOk = function() {
- scope.$emit("umbItemSorter.ok");
- };
-
- //defines the options for the jquery sortable
- scope.sortableOptions = {
- axis: 'y',
- cursor: "move",
- placeholder: "ui-sortable-placeholder",
- update: function (ev, ui) {
- //highlight the item when the position is changed
- $(ui.item).effect("highlight", { color: "#049cdb" }, 500);
- },
- stop: function (ev, ui) {
- //the ui-sortable directive already ensures that our list is re-sorted, so now we just
- // need to update the sortOrder to the index of each item
- angularHelper.safeApply(scope, function () {
- angular.forEach(scope.itemsToSort, function (val, index) {
- val.sortOrder = index + 1;
- });
-
- });
- }
- };
- }
- };
-}
-
-angular.module('umbraco.directives').directive("umbItemSorter", umbItemSorter);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbcontentname.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbcontentname.directive.js
deleted file mode 100644
index fd7145ff80..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbcontentname.directive.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbContentName
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-
-angular.module("umbraco.directives")
- .directive('umbContentName', function ($timeout, localizationService) {
- return {
- require: "ngModel",
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-content-name.html',
-
- scope: {
- placeholder: '@placeholder',
- model: '=ngModel',
- ngDisabled: '='
- },
- link: function(scope, element, attrs, ngModel) {
-
- var inputElement = element.find("input");
- if(scope.placeholder && scope.placeholder[0] === "@"){
- localizationService.localize(scope.placeholder.substring(1))
- .then(function(value){
- scope.placeholder = value;
- });
- }
-
- var mX, mY, distance;
-
- function calculateDistance(elem, mouseX, mouseY) {
-
- var cx = Math.max(Math.min(mouseX, elem.offset().left + elem.width()), elem.offset().left);
- var cy = Math.max(Math.min(mouseY, elem.offset().top + elem.height()), elem.offset().top);
- return Math.sqrt((mouseX - cx) * (mouseX - cx) + (mouseY - cy) * (mouseY - cy));
- }
-
- var mouseMoveDebounce = _.throttle(function (e) {
- mX = e.pageX;
- mY = e.pageY;
- // not focused and not over element
- if (!inputElement.is(":focus") && !inputElement.hasClass("ng-invalid")) {
- // on page
- if (mX >= inputElement.offset().left) {
- distance = calculateDistance(inputElement, mX, mY);
- if (distance <= 155) {
-
- distance = 1 - (100 / 150 * distance / 100);
- inputElement.css("border", "1px solid rgba(175,175,175, " + distance + ")");
- inputElement.css("background-color", "rgba(255,255,255, " + distance + ")");
- }
- }
-
- }
-
- }, 15);
-
- $(document).bind("mousemove", mouseMoveDebounce);
-
- $timeout(function(){
- if(!scope.model){
- scope.goEdit();
- }
- }, 100, false);
-
- scope.goEdit = function(){
- scope.editMode = true;
-
- $timeout(function () {
- inputElement.focus();
- }, 100, false);
- };
-
- scope.exitEdit = function(){
- if(scope.model && scope.model !== ""){
- scope.editMode = false;
- }
- };
-
- //unbind doc event!
- scope.$on('$destroy', function () {
- $(document).unbind("mousemove", mouseMoveDebounce);
- });
- }
- };
- });
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbheader.directive.js
deleted file mode 100644
index ccf37b14a2..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbheader.directive.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbHeader
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @restrict E
-* @function
-* @description
-* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead
-**/
-
-angular.module("umbraco.directives")
-.directive('umbHeader', function ($parse, $timeout) {
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/directives/_obsolete/umb-header.html',
- //create a new isolated scope assigning a tabs property from the attribute 'tabs'
- //which is bound to the parent scope property passed in
- scope: {
- tabs: "="
- },
- link: function (scope, iElement, iAttrs) {
-
- scope.showTabs = iAttrs.tabs ? true : false;
- scope.visibleTabs = [];
-
- //since tabs are loaded async, we need to put a watch on them to determine
- // when they are loaded, then we can close the watch
- var tabWatch = scope.$watch("tabs", function (newValue, oldValue) {
-
- angular.forEach(newValue, function(val, index){
- var tab = {id: val.id, label: val.label};
- scope.visibleTabs.push(tab);
- });
-
- //don't process if we cannot or have already done so
- if (!newValue) {return;}
- if (!newValue.length || newValue.length === 0){return;}
-
- //we need to do a timeout here so that the current sync operation can complete
- // and update the UI, then this will fire and the UI elements will be available.
- $timeout(function () {
-
- //use bootstrap tabs API to show the first one
- iElement.find(".nav-tabs a:first").tab('show');
-
- //enable the tab drop
- iElement.find('.nav-pills, .nav-tabs').tabdrop();
-
- //ensure to destroy tabdrop (unbinds window resize listeners)
- scope.$on('$destroy', function () {
- iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy");
- });
-
- //stop watching now
- tabWatch();
- }, 200);
-
- });
- }
- };
-});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umblogin.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umblogin.directive.js
deleted file mode 100644
index 775c20181c..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umblogin.directive.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbLogin
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @function
-* @element ANY
-* @restrict E
-**/
-
-function loginDirective() {
- return {
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/directives/_obsolete/umb-login.html'
- };
-}
-
-angular.module('umbraco.directives').directive("umbLogin", loginDirective);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umboptionsmenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umboptionsmenu.directive.js
deleted file mode 100644
index d51fe73448..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umboptionsmenu.directive.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbOptionsMenu
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @function
-* @element ANY
-* @restrict E
-**/
-
-angular.module("umbraco.directives")
-.directive('umbOptionsMenu', function ($injector, treeService, navigationService, umbModelMapper, appState) {
- return {
- scope: {
- currentSection: "@",
- currentNode: "="
- },
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-optionsmenu.html',
- link: function (scope, element, attrs, ctrl) {
-
- //adds a handler to the context menu item click, we need to handle this differently
- //depending on what the menu item is supposed to do.
- scope.executeMenuItem = function (action) {
- navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
- };
-
- //callback method to go and get the options async
- scope.getOptions = function () {
-
- if (!scope.currentNode) {
- return;
- }
-
- //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
- appState.setMenuState("currentNode", scope.currentNode);
-
- if (!scope.actions) {
- treeService.getMenu({ treeNode: scope.currentNode })
- .then(function (data) {
- scope.actions = data.menuItems;
- });
- }
- };
-
- }
- };
-});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbphotofolder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbphotofolder.directive.js
deleted file mode 100644
index c6c628741f..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbphotofolder.directive.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbPhotoFolder
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-* @restrict E
-**/
-
-angular.module("umbraco.directives.html")
- .directive('umbPhotoFolder', function($compile, $log, $timeout, $filter, umbPhotoFolderHelper) {
-
- return {
- restrict: 'E',
- replace: true,
- require: '?ngModel',
- terminate: true,
- templateUrl: 'views/directives/_obsolete/umb-photo-folder.html',
- link: function(scope, element, attrs, ngModel) {
-
- var lastWatch = null;
-
- ngModel.$render = function() {
- if (ngModel.$modelValue) {
-
- $timeout(function() {
- var photos = ngModel.$modelValue;
-
- scope.clickHandler = scope.$eval(element.attr('on-click'));
-
-
- var imagesOnly = element.attr('images-only') === "true";
-
-
- var margin = element.attr('border') ? parseInt(element.attr('border'), 10) : 5;
- var startingIndex = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0;
- var minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420;
- var minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 100;
- var maxHeight = element.attr('max-height') ? parseInt(element.attr('max-height'), 10) : 300;
- var idealImgPerRow = element.attr('ideal-items-per-row') ? parseInt(element.attr('ideal-items-per-row'), 10) : 5;
- var fixedRowWidth = Math.max(element.width(), minWidth);
-
- scope.containerStyle = { width: fixedRowWidth + "px" };
- scope.rows = umbPhotoFolderHelper.buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
-
- if (attrs.filterBy) {
-
- //we track the watches that we create, we don't want to create multiple, so clear it
- // if it already exists before creating another.
- if (lastWatch) {
- lastWatch();
- }
-
- //TODO: Need to debounce this so it doesn't filter too often!
- lastWatch = scope.$watch(attrs.filterBy, function (newVal, oldVal) {
- if (newVal && newVal !== oldVal) {
- var p = $filter('filter')(photos, newVal, false);
- scope.baseline = 0;
- var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
- scope.rows = m;
- }
- });
- }
-
- }, 500); //end timeout
- } //end if modelValue
-
- }; //end $render
- }
- };
- });
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbsort.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbsort.directive.js
deleted file mode 100644
index aab837f916..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbsort.directive.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbSort
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-*
-* @element div
-* @function
-*
-* @description
-* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
-* So if you only want to scale the div to 70 pixels from the bottom you pass "70"
-*
-* @example
-*
-*
-*
-*
-*
-**/
-
-angular.module("umbraco.directives")
- .value('umbSortContextInternal',{})
- .directive('umbSort', function($log,umbSortContextInternal) {
- return {
- require: '?ngModel',
- link: function(scope, element, attrs, ngModel) {
- var adjustment;
-
- var cfg = scope.$eval(element.attr('umb-sort')) || {};
-
- scope.model = ngModel;
-
- scope.opts = cfg;
- scope.opts.containerSelector= cfg.containerSelector || ".umb-" + cfg.group + "-container",
- scope.opts.nested= cfg.nested || true,
- scope.opts.drop= cfg.drop || true,
- scope.opts.drag= cfg.drag || true,
- scope.opts.clone = cfg.clone || "";
- scope.opts.mode = cfg.mode || "list";
-
- scope.opts.itemSelectorFull = $.trim(scope.opts.itemPath + " " + scope.opts.itemSelector);
-
- /*
- scope.opts.isValidTarget = function(item, container) {
- if(container.el.is(".umb-" + scope.opts.group + "-container")){
- return true;
- }
- return false;
- };
- */
-
- element.addClass("umb-sort");
- element.addClass("umb-" + cfg.group + "-container");
-
- scope.opts.onDrag = function (item, position) {
- if(scope.opts.mode === "list"){
- item.css({
- left: position.left - adjustment.left,
- top: position.top - adjustment.top
- });
- }
- };
-
-
- scope.opts.onDrop = function (item, targetContainer, _super) {
-
- if(scope.opts.mode === "list"){
- //list mode
- var clonedItem = $(scope.opts.clone).css({height: 0});
- item.after(clonedItem);
- clonedItem.animate({'height': item.height()});
-
- item.animate(clonedItem.position(), function () {
- clonedItem.detach();
- _super(item);
- });
- }
-
- var children = $(scope.opts.itemSelectorFull, targetContainer.el);
- var targetIndex = children.index(item);
- var targetScope = $(targetContainer.el[0]).scope();
-
-
- if(targetScope === umbSortContextInternal.sourceScope){
- if(umbSortContextInternal.sourceScope.opts.onSortHandler){
- var _largs = {
- oldIndex: umbSortContextInternal.sourceIndex,
- newIndex: targetIndex,
- scope: umbSortContextInternal.sourceScope
- };
-
- umbSortContextInternal.sourceScope.opts.onSortHandler.call(this, item, _largs);
- }
- }else{
-
-
- if(targetScope.opts.onDropHandler){
- var args = {
- sourceScope: umbSortContextInternal.sourceScope,
- sourceIndex: umbSortContextInternal.sourceIndex,
- sourceContainer: umbSortContextInternal.sourceContainer,
-
- targetScope: targetScope,
- targetIndex: targetIndex,
- targetContainer: targetContainer
- };
-
- targetScope.opts.onDropHandler.call(this, item, args);
- }
-
- if(umbSortContextInternal.sourceScope.opts.onReleaseHandler){
- var _args = {
- sourceScope: umbSortContextInternal.sourceScope,
- sourceIndex: umbSortContextInternal.sourceIndex,
- sourceContainer: umbSortContextInternal.sourceContainer,
-
- targetScope: targetScope,
- targetIndex: targetIndex,
- targetContainer: targetContainer
- };
-
- umbSortContextInternal.sourceScope.opts.onReleaseHandler.call(this, item, _args);
- }
- }
- };
-
- scope.changeIndex = function(from, to){
- scope.$apply(function(){
- var i = ngModel.$modelValue.splice(from, 1)[0];
- ngModel.$modelValue.splice(to, 0, i);
- });
- };
-
- scope.move = function(args){
- var from = args.sourceIndex;
- var to = args.targetIndex;
-
- if(args.sourceContainer === args.targetContainer){
- scope.changeIndex(from, to);
- }else{
- scope.$apply(function(){
- var i = args.sourceScope.model.$modelValue.splice(from, 1)[0];
- args.targetScope.model.$modelvalue.splice(to,0, i);
- });
- }
- };
-
- scope.opts.onDragStart = function (item, container, _super) {
- var children = $(scope.opts.itemSelectorFull, container.el);
- var offset = item.offset();
-
- umbSortContextInternal.sourceIndex = children.index(item);
- umbSortContextInternal.sourceScope = $(container.el[0]).scope();
- umbSortContextInternal.sourceContainer = container;
-
- //current.item = ngModel.$modelValue.splice(current.index, 1)[0];
-
- var pointer = container.rootGroup.pointer;
- adjustment = {
- left: pointer.left - offset.left,
- top: pointer.top - offset.top
- };
-
- _super(item, container);
- };
-
- element.sortable( scope.opts );
- }
- };
-
- });
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbtabview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbtabview.directive.js
deleted file mode 100644
index 5fe813509e..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbtabview.directive.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTabView
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-*
-* @restrict E
-**/
-
-angular.module("umbraco.directives")
-.directive('umbTabView', function($timeout, $log){
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/directives/_obsolete/umb-tab-view.html'
- };
-});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbuploaddropzone.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbuploaddropzone.directive.js
deleted file mode 100644
index 64ca76a6ec..0000000000
--- a/src/Umbraco.Web.UI.Client/src/common/directives/_obsolete/umbuploaddropzone.directive.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbUploadDropzone
-* @deprecated
-* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
-*
-* @restrict E
-**/
-
-angular.module("umbraco.directives.html")
- .directive('umbUploadDropzone', function(){
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-upload-dropzone.html'
- };
- });
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js
index ced59653dd..39e4f10666 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js
@@ -103,7 +103,7 @@
backdropOpacity: "=?",
highlightElement: "=?",
highlightPreventClick: "=?",
- disableEventsOnClick: "=?",
+ disableEventsOnClick: "=?"
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js
index 2793a0ec57..7f906ddcc0 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js
@@ -10,62 +10,62 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
templateUrl: 'views/components/application/umb-sections.html',
link: function (scope, element, attr, ctrl) {
- var sectionItemsWidth = [];
- var evts = [];
- var maxSections = 7;
+ var sectionItemsWidth = [];
+ var evts = [];
+ var maxSections = 7;
//setup scope vars
- scope.maxSections = maxSections;
- scope.overflowingSections = 0;
+ scope.maxSections = maxSections;
+ scope.overflowingSections = 0;
scope.sections = [];
scope.currentSection = appState.getSectionState("currentSection");
scope.showTray = false; //appState.getGlobalState("showTray");
scope.stickyNavigation = appState.getGlobalState("stickyNavigation");
scope.needTray = false;
- function loadSections(){
- sectionService.getSectionsForUser()
- .then(function (result) {
- scope.sections = result;
- // store the width of each section so we can hide/show them based on browser width
- // we store them because the sections get removed from the dom and then we
- // can't tell when to show them gain
- $timeout(function(){
- $("#applications .sections li").each(function(index) {
- sectionItemsWidth.push($(this).outerWidth());
- });
- });
- calculateWidth();
- });
- }
-
- function calculateWidth(){
- $timeout(function(){
- //total width minus room for avatar, search, and help icon
- var windowWidth = $(window).width()-200;
- var sectionsWidth = 0;
- scope.totalSections = scope.sections.length;
- scope.maxSections = maxSections;
- scope.overflowingSections = 0;
- scope.needTray = false;
-
- // detect how many sections we can show on the screen
- for (var i = 0; i < sectionItemsWidth.length; i++) {
- var sectionItemWidth = sectionItemsWidth[i];
- sectionsWidth += sectionItemWidth;
+ function loadSections() {
+ sectionService.getSectionsForUser()
+ .then(function (result) {
+ scope.sections = result;
+ // store the width of each section so we can hide/show them based on browser width
+ // we store them because the sections get removed from the dom and then we
+ // can't tell when to show them gain
+ $timeout(function () {
+ $("#applications .sections li").each(function (index) {
+ sectionItemsWidth.push($(this).outerWidth());
+ });
+ });
+ calculateWidth();
+ });
+ }
- if(sectionsWidth > windowWidth) {
- scope.needTray = true;
- scope.maxSections = i - 1;
- scope.overflowingSections = scope.maxSections - scope.totalSections;
- break;
- }
- }
- });
- }
+ function calculateWidth() {
+ $timeout(function () {
+ //total width minus room for avatar, search, and help icon
+ var windowWidth = $(window).width() - 200;
+ var sectionsWidth = 0;
+ scope.totalSections = scope.sections.length;
+ scope.maxSections = maxSections;
+ scope.overflowingSections = 0;
+ scope.needTray = false;
+
+ // detect how many sections we can show on the screen
+ for (var i = 0; i < sectionItemsWidth.length; i++) {
+ var sectionItemWidth = sectionItemsWidth[i];
+ sectionsWidth += sectionItemWidth;
+
+ if (sectionsWidth > windowWidth) {
+ scope.needTray = true;
+ scope.maxSections = i - 1;
+ scope.overflowingSections = scope.maxSections - scope.totalSections;
+ break;
+ }
+ }
+ });
+ }
//Listen for global state changes
- evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
+ evts.push(eventsService.on("appState.globalState.changed", function (e, args) {
if (args.key === "showTray") {
scope.showTray = args.value;
}
@@ -74,44 +74,44 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
}
}));
- evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
+ evts.push(eventsService.on("appState.sectionState.changed", function (e, args) {
if (args.key === "currentSection") {
scope.currentSection = args.value;
}
}));
- evts.push(eventsService.on("app.reInitialize", function(e, args) {
+ evts.push(eventsService.on("app.reInitialize", function (e, args) {
//re-load the sections if we're re-initializing (i.e. package installed)
loadSections();
}));
//ensure to unregister from all events!
- scope.$on('$destroy', function () {
- for (var e in evts) {
- eventsService.unsubscribe(evts[e]);
- }
- });
+ scope.$on('$destroy', function () {
+ for (var e in evts) {
+ eventsService.unsubscribe(evts[e]);
+ }
+ });
- //on page resize
- window.onresize = calculateWidth;
+ //on page resize
+ window.onresize = calculateWidth;
- scope.sectionClick = function (event, section) {
+ scope.sectionClick = function (event, section) {
- if (event.ctrlKey ||
- event.shiftKey ||
- event.metaKey || // apple
- (event.button && event.button === 1) // middle click, >IE9 + everyone else
- ) {
- return;
- }
+ if (event.ctrlKey ||
+ event.shiftKey ||
+ event.metaKey || // apple
+ (event.button && event.button === 1) // middle click, >IE9 + everyone else
+ ) {
+ return;
+ }
if (scope.userDialog) {
closeUserDialog();
- }
-
+ }
- navigationService.hideSearch();
- navigationService.showTree(section.alias);
+
+ navigationService.hideSearch();
+ navigationService.showTree(section.alias);
//in some cases the section will have a custom route path specified, if there is one we'll use it
if (section.routePath) {
@@ -123,22 +123,22 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
$location.path(path);
}
navigationService.clearSearch();
-
- };
- scope.sectionDblClick = function(section){
- navigationService.reloadSection(section.alias);
- };
+ };
- scope.trayClick = function () {
- if (appState.getGlobalState("showTray") === true) {
- navigationService.hideTray();
- } else {
- navigationService.showTray();
- }
- };
+ scope.sectionDblClick = function (section) {
+ navigationService.reloadSection(section.alias);
+ };
- loadSections();
+ scope.trayClick = function () {
+ if (appState.getGlobalState("showTray") === true) {
+ navigationService.hideTray();
+ } else {
+ navigationService.showTray();
+ }
+ };
+
+ loadSections();
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js
index 047d67ffef..e26f622d2e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js
@@ -94,6 +94,7 @@ Use this directive to render an umbraco button. The directive can be used to gen
}
});
+ //TODO: This doesn't seem necessary?
UmbButtonController.$inject = ['$timeout'];
function UmbButtonController($timeout) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js
index d2cbe5fe86..171fec88e5 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js
@@ -21,21 +21,18 @@
$scope.page.listViewPath = null;
$scope.page.isNew = $scope.isNew ? true : false;
$scope.page.buttonGroupState = "init";
- $scope.page.culture = $scope.culture;
$scope.page.hideActionsMenu = infiniteMode ? true : false;
$scope.page.hideChangeVariant = infiniteMode ? true : false;
$scope.allowOpen = true;
// add all editors to an editors array to support split view
$scope.editors = [];
- $scope.splitView = {
- "leftIsOpen": true,
- "rightIsOpen": false
- };
+ $scope.initVariant = initVariant;
+ $scope.splitViewChanged = splitViewChanged;
function init(content) {
-
- if(infiniteMode) {
+
+ if (infiniteMode) {
createInfiniteModeButtons(content);
} else {
createButtons(content);
@@ -53,7 +50,166 @@
}
}
- //init can be called more than once and we don't want to have multiple bound events
+ bindEvents();
+
+ // set first app to active
+ // We need to track active
+ $scope.content.apps[0].active = true;
+
+ setActiveCulture();
+
+ resetVariantFlags();
+ }
+
+ /** This is called when the split view changes based on the umb-variant-content */
+ function splitViewChanged() {
+ //send an event downwards
+ $scope.$broadcast("editors.content.splitViewChanged", { editors: $scope.editors });
+ }
+
+ /**
+ * This will reset isDirty flags if save is true.
+ * When working with multiple variants, this will set the save/publish flags of each one to false.
+ * When working with a single variant, this will set the publish flag to false and the save flag to true.
+ */
+ function resetVariantFlags() {
+ if ($scope.content.variants.length > 1) {
+ for (var i = 0; i < $scope.content.variants.length; i++) {
+ var v = $scope.content.variants[i];
+ if (v.save) {
+ v.isDirty = false;
+ }
+ v.save = false;
+ v.publish = false;
+ }
+ }
+ else {
+ if ($scope.content.variants[0].save) {
+ $scope.content.variants[0].isDirty = false;
+ }
+ $scope.content.variants[0].save = true;
+ $scope.content.variants[0].publish = false;
+ }
+ }
+
+ function countDirtyVariants() {
+ var count = 0;
+ for (var i = 0; i < $scope.content.variants.length; i++) {
+ var v = $scope.content.variants[i];
+ if (v.isDirty) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /** Returns true if the save/publish dialog should be shown when pressing the button */
+ function showSaveOrPublishDialog() {
+ return $scope.content.variants.length > 1;
+ }
+
+ /**
+ * The content item(s) are loaded into an array and this will set the active content item based on the current culture (query string).
+ * If the content item is invariant, then only one item exists in the array.
+ */
+ function setActiveCulture() {
+ // set the active variant
+ var activeVariant = null;
+ _.each($scope.content.variants, function (v) {
+ if (v.language && v.language.culture === $scope.culture) {
+ v.active = true;
+ activeVariant = v;
+ }
+ else {
+ v.active = false;
+ }
+ });
+ if (!activeVariant) {
+ // set the first variant to active
+ $scope.content.variants[0].active = true;
+ activeVariant = $scope.content.variants[0];
+ }
+
+ initVariant(activeVariant);
+
+ //If there are no editors yet then create one with the current content.
+ //if there's already a main editor then update it with the current content.
+ if ($scope.editors.length === 0) {
+ var editor = {
+ content: activeVariant
+ };
+ $scope.editors.push(editor);
+ }
+ else {
+ //this will mean there is only one
+ $scope.editors[0].content = activeVariant;
+
+ if ($scope.editors.length > 1) {
+ //now re-sync any other editor content (i.e. if split view is open)
+ for (var s = 1; s < $scope.editors.length; s++) {
+ //get the variant from the scope model
+ var variant = _.find($scope.content.variants, function (v) {
+ return v.language.culture === $scope.editors[s].content.language.culture;
+ });
+ $scope.editors[s].content = initVariant(variant);
+ }
+ }
+
+ }
+ }
+
+ function initVariant(variant) {
+ //The model that is assigned to the editor contains the current content variant along
+ //with a copy of the contentApps. This is required because each editor renders it's own
+ //header and content apps section and the content apps contains the view for editing content itself
+ //and we need to assign a view model to the subView so that it is scoped to the current
+ //editor so that split views work.
+
+ //copy the apps from the main model if not assigned yet to the variant
+ if (!variant.apps) {
+ variant.apps = angular.copy($scope.content.apps);
+ }
+
+ //if this is a variant has a culture/language than we need to assign the language drop down info
+ if (variant.language) {
+ //if the variant list that defines the header drop down isn't assigned to the variant then assign it now
+ if (!variant.variants) {
+ variant.variants = _.map($scope.content.variants,
+ function (v) {
+ return _.pick(v, "active", "language", "state");
+ });
+ }
+ else {
+ //merge the scope variants on top of the header variants collection (handy when needing to refresh)
+ angular.extend(variant.variants,
+ _.map($scope.content.variants,
+ function (v) {
+ return _.pick(v, "active", "language", "state");
+ }));
+ }
+
+ //ensure the current culture is set as the active one
+ for (var i = 0; i < variant.variants.length; i++) {
+ if (variant.variants[i].language.culture === variant.language.culture) {
+ variant.variants[i].active = true;
+ }
+ else {
+ variant.variants[i].active = false;
+ }
+ }
+ }
+
+ //then assign the variant to a view model to the content app
+ var contentApp = _.find(variant.apps, function (a) {
+ return a.alias === "content";
+ });
+ contentApp.viewModel = variant;
+
+ return variant;
+ }
+
+ function bindEvents() {
+ //bindEvents can be called more than once and we don't want to have multiple bound events
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
@@ -68,84 +224,19 @@
evts.push(eventsService.on("editors.documentType.saved", function (name, args) {
// if this content item uses the updated doc type we need to reload the content item
- if(args && args.documentType && args.documentType.key === content.documentType.key) {
- if ($scope.page.culture) {
- loadContent($scope.page.culture);
- }
- else {
- loadContent();
- }
+ if (args && args.documentType && args.documentType.key === content.documentType.key) {
+ loadContent();
}
}));
-
- // We don't get the info tab from the server from version 7.8 so we need to manually add it
- //contentEditingHelper.addInfoTab($scope.content.tabs);
-
- // prototype content and info apps
- var contentApp = {
- "name": "Content",
- "alias": "content",
- "icon": "icon-document",
- "view": "views/content/apps/content/content.html"
- };
-
- var infoApp = {
- "name": "Info",
- "alias": "info",
- "icon": "icon-info",
- "view": "views/content/apps/info/info.html"
- };
-
- var listview = {
- "name": "Child items",
- "alias": "childItems",
- "icon": "icon-list",
- "view": "views/content/apps/listview/listview.html"
- };
-
- $scope.content.apps = [];
-
- if ($scope.content.isContainer) {
- // add list view app
- $scope.content.apps.push(listview);
-
- // remove the list view tab
- angular.forEach($scope.content.tabs, function (tab, index) {
- if (tab.alias === "umbContainerView") {
- tab.hide = true;
- }
- });
- }
-
- $scope.content.apps.push(contentApp);
- $scope.content.apps.push(infoApp);
-
- // set first app to active
- $scope.content.apps[0].active = true;
-
- // create new editor for split view
- if ($scope.editors.length === 0) {
- var editor = {
- content: $scope.content
- };
- $scope.editors.push(editor);
- }
- else if ($scope.editors.length === 1) {
- $scope.editors[0].content = $scope.content;
- }
- else {
- //fixme - need to fix something here if we are re-loading a content item that is in a split view
- }
}
/**
- * This does the content loading and initializes everything, called on load and changing variants
- * @param {any} culture
+ * This does the content loading and initializes everything, called on first load
*/
- function loadContent(culture) {
+ function loadContent() {
//we are editing so get the content item from the server
- return $scope.getMethod()($scope.contentId, culture)
+ return $scope.getMethod()($scope.contentId)
.then(function (data) {
$scope.content = data;
@@ -158,14 +249,8 @@
init($scope.content);
- //in one particular special case, after we've created a new item we redirect back to the edit
- // route but there might be server validation errors in the collection which we need to display
- // after the redirect, so we will bind all subscriptions which will show the server validation errors
- // if there are any and then clear them so the collection no longer persists them.
- serverValidationManager.executeAndClearAllSubscriptions();
-
- if(!infiniteMode) {
- syncTreeNode($scope.content, data.path, true);
+ if (!infiniteMode) {
+ syncTreeNode($scope.content, true);
}
resetLastListPageNumber($scope.content);
@@ -204,18 +289,20 @@
$scope.page.allowInfiniteSaveAndClose = false;
// check for publish rights
- if(_.contains(content.allowedActions, "U")) {
+ if (_.contains(content.allowedActions, "U")) {
$scope.page.allowInfinitePublishAndClose = true;
- // check for save rights
- } else if( _.contains(content.allowedActions, "A")) {
+ // check for save rights
+ } else if (_.contains(content.allowedActions, "A")) {
$scope.page.allowInfiniteSaveAndClose = true;
}
}
/** Syncs the content item to it's tree node - this occurs on first load and after saving */
- function syncTreeNode(content, path, initialLoad) {
+ function syncTreeNode(content, initialLoad) {
+
+ var path = content.path;
if (!$scope.content.isChildOfListView) {
navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
@@ -248,13 +335,14 @@
saveMethod: args.saveMethod,
scope: $scope,
content: $scope.content,
- action: args.action
+ action: args.action,
+ showNotifications: args.showNotifications
}).then(function (data) {
//success
init($scope.content);
- if(!infiniteMode) {
- syncTreeNode($scope.content, data.path);
+ if (!infiniteMode) {
+ syncTreeNode($scope.content);
}
$scope.page.buttonGroupState = "success";
@@ -264,12 +352,17 @@
return $q.when(data);
},
function (err) {
+
+ setActiveCulture();
+ syncTreeNode($scope.content);
+
//error
if (err) {
editorState.set($scope.content);
}
$scope.page.buttonGroupState = "error";
+
return $q.reject(err);
});
}
@@ -305,18 +398,11 @@
}
else {
- //Browse content nodes based on the selected tree language variant
$scope.page.loading = true;
- if ($scope.page.culture) {
- loadContent($scope.page.culture).then(function(){
- $scope.page.loading = false;
- });
- }
- else {
- loadContent().then(function(){
- $scope.page.loading = false;
- });
- }
+
+ loadContent().then(function () {
+ $scope.page.loading = false;
+ });
}
$scope.unPublish = function () {
@@ -326,15 +412,15 @@
if ($scope.content.variants.length > 0) {
_.each($scope.content.variants,
function (d) {
- //set the culture if this is current
- if (d.current === true) {
+ //set the culture if this is active
+ if (d.active === true) {
culture = d.language.culture;
}
});
}
if (formHelper.submitForm({ scope: $scope, skipValidation: true })) {
-
+
$scope.page.buttonGroupState = "busy";
eventsService.emit("content.unpublishing", { content: $scope.content });
@@ -352,8 +438,8 @@
init($scope.content);
- if(!infiniteMode) {
- syncTreeNode($scope.content, data.path);
+ if (!infiniteMode) {
+ syncTreeNode($scope.content);
}
$scope.page.buttonGroupState = "success";
@@ -374,12 +460,12 @@
$scope.saveAndPublish = function () {
// TODO: Add "..." to publish button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant
- if ($scope.content.variants.length > 1) {
+ if (showSaveOrPublishDialog()) {
//before we launch the dialog we want to execute all client side validations first
- if (formHelper.submitForm({ scope: $scope, action: "publish"})) {
+ if (formHelper.submitForm({ scope: $scope, action: "publish" })) {
var dialog = {
- view: "publish",
+ view: "views/content/overlays/publish.html",
variants: $scope.content.variants, //set a model property for the dialog
skipFormValidation: true, //when submitting the overlay form, skip any client side validation
submitButtonLabel: "Publish",
@@ -389,7 +475,8 @@
//we need to return this promise so that the dialog can handle the result and wire up the validation response
return performSave({
saveMethod: contentResource.publish,
- action: "publish"
+ action: "publish",
+ showNotifications: false
}).then(function (data) {
overlayService.close();
return $q.when(data);
@@ -399,21 +486,6 @@
//re-map the dialog model since we've re-bound the properties
dialog.variants = $scope.content.variants;
- //check the error list for specific variant errors, if none exist that means that only server side validation
- //for the current variant's properties failed, in this case we want to close the publish dialog since the user
- //will need to fix validation errors on the properties
- if (err.data && err.data.ModelState) {
- var keys = _.keys(err.data.ModelState);
- var foundVariantError = _.find(keys,
- function (k) {
- return k.startsWith("publish_variant_");
- });
- if (!foundVariantError) {
- //no variant errors, close the dialog
- overlayService.close();
- }
- }
-
return $q.reject(err);
});
},
@@ -426,12 +498,56 @@
}
}
else {
- return performSave({ saveMethod: contentResource.publish, action: "publish" }).catch(angular.noop);;
+ //ensure the publish flag is set
+ $scope.content.variants[0].publish = true;
+ return performSave({ saveMethod: contentResource.publish, action: "publish" });
}
};
$scope.save = function () {
- return performSave({ saveMethod: $scope.saveMethod(), action: "save" }).catch(angular.noop);
+
+ // TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant
+ if (showSaveOrPublishDialog()) {
+ //before we launch the dialog we want to execute all client side validations first
+ if (formHelper.submitForm({ scope: $scope, action: "save" })) {
+
+ var dialog = {
+ view: "views/content/overlays/save.html",
+ variants: $scope.content.variants, //set a model property for the dialog
+ skipFormValidation: true, //when submitting the overlay form, skip any client side validation
+ submitButtonLabel: "Save",
+ submit: function (model) {
+ model.submitButtonState = "busy";
+
+ //we need to return this promise so that the dialog can handle the result and wire up the validation response
+ return performSave({
+ saveMethod: $scope.saveMethod(),
+ action: "save",
+ showNotifications: false
+ }).then(function (data) {
+ overlayService.close();
+ return $q.when(data);
+ },
+ function (err) {
+ model.submitButtonState = "error";
+ //re-map the dialog model since we've re-bound the properties
+ dialog.variants = $scope.content.variants;
+
+ return $q.reject(err);
+ });
+ },
+ close: function (oldModel) {
+ overlayService.close();
+ }
+ };
+
+ overlayService.open(dialog);
+ }
+ }
+ else {
+ return performSave({ saveMethod: $scope.saveMethod(), action: "save" });
+ }
+
};
$scope.preview = function (content) {
@@ -455,7 +571,7 @@
else {
$scope.save().then(function (data) {
previewWindow.location.href = redirect;
- }).catch(angular.noop);
+ });
}
}
};
@@ -509,69 +625,29 @@
notificationsService.error(error.headline, error.content);
});
};
-
- $scope.closeSplitView = function (index, editor) {
- // hacky animation stuff - it will be much better when angular is upgraded
- editor.loading = true;
- editor.collapsed = true;
- $timeout(function () {
- $scope.editors.splice(index, 1);
- }, 400);
- };
-
- $scope.openInSplitView = function (selectedVariant) {
-
- console.log(selectedVariant);
-
- var editor = {};
- // hacking animation states - these should hopefully be easier to do when we upgrade angular
- editor.collapsed = true;
- editor.loading = true;
- $scope.editors.push(editor);
- var editorIndex = $scope.editors.length - 1;
- $timeout(function () {
- $scope.editors[editorIndex].collapsed = false;
- }, 100);
-
- // fake loading of content
- // TODO: Make this real, but how do we deal with saving since currently we only save one variant at a time?!
- $timeout(function () {
- $scope.editors[editorIndex].content = angular.copy($scope.content);
- $scope.editors[editorIndex].content.name = "What a variant";
- // set selected variant on split view content
- angular.forEach($scope.editors[editorIndex].content.variants, function (variant) {
- if (variant.culture === selectedVariant.culture) {
- variant.current = true;
- } else {
- variant.current = false;
- }
- });
- $scope.editors[editorIndex].loading = false;
- }, 500);
- };
/* publish method used in infinite editing */
- $scope.publishAndClose = function(content) {
+ $scope.publishAndClose = function (content) {
$scope.publishAndCloseButtonState = "busy";
- performSave({ saveMethod: contentResource.publish, action: "publish" }).then(function(){
- if($scope.infiniteModel.submit) {
+ performSave({ saveMethod: contentResource.publish, action: "publish" }).then(function () {
+ if ($scope.infiniteModel.submit) {
$scope.infiniteModel.contentNode = content;
$scope.infiniteModel.submit($scope.infiniteModel);
}
$scope.publishAndCloseButtonState = "success";
- }).catch(angular.noop);;
+ });
};
/* save method used in infinite editing */
- $scope.saveAndClose = function(content) {
+ $scope.saveAndClose = function (content) {
$scope.saveAndCloseButtonState = "busy";
- performSave({ saveMethod: $scope.saveMethod(), action: "save" }).then(function(){
- if($scope.infiniteModel.submit) {
+ performSave({ saveMethod: $scope.saveMethod(), action: "save" }).then(function () {
+ if ($scope.infiniteModel.submit) {
$scope.infiniteModel.contentNode = content;
$scope.infiniteModel.submit($scope.infiniteModel);
}
$scope.saveAndCloseButtonState = "success";
- }).catch(angular.noop);;
+ });
};
function moveNode(node, target) {
@@ -585,7 +661,7 @@
}
// sync the destination node
- if(!infiniteMode) {
+ if (!infiniteMode) {
navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false });
}
@@ -603,17 +679,28 @@
}
// methods for infinite editing
- $scope.close = function() {
- if($scope.infiniteModel.close) {
+ $scope.close = function () {
+ if ($scope.infiniteModel.close) {
$scope.infiniteModel.close($scope.infiniteModel);
}
};
+ $scope.$watch('culture', function (newVal, oldVal) {
+ if (newVal !== oldVal) {
+ setActiveCulture();
+ }
+ });
+
//ensure to unregister from all events!
$scope.$on('$destroy', function () {
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
+ //since we are not notifying and clearing server validation messages when they are received due to how the variant
+ //switching works, we need to ensure they are cleared when this editor is destroyed
+ if (!$scope.page.isNew) {
+ serverValidationManager.clear();
+ }
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
index c9d78a3551..b0ecb65eea 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
@@ -8,10 +8,11 @@
var evts = [];
var isInfoTab = false;
var labels = {};
- scope.publishStatus = {};
+ scope.publishStatus = [];
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
scope.allowChangeDocumentType = false;
+ scope.allowChangeTemplate = false;
function onInit() {
@@ -20,6 +21,7 @@
angular.forEach(user.sections, function(section){
if(section.alias === "settings") {
scope.allowChangeDocumentType = true;
+ scope.allowChangeTemplate = true;
}
});
});
@@ -28,15 +30,17 @@
"general_deleted",
"content_unpublished",
"content_published",
- "content_publishedPendingChanges"
+ "content_publishedPendingChanges",
+ "content_notCreated"
];
localizationService.localizeMany(keys)
.then(function(data){
labels.deleted = data[0];
- labels.unpublished = data[1];
+ labels.unpublished = data[1]; //aka draft
labels.published = data[2];
labels.publishedPendingChanges = data[3];
+ labels.notCreated = data[4];
setNodePublishStatus(scope.node);
@@ -92,15 +96,21 @@
};
scope.openTemplate = function () {
- var url = "/settings/templates/edit/" + scope.node.templateId;
- $location.url(url);
+ var templateEditor = {
+ id: scope.node.templateId,
+ submit: function(model) {
+ editorService.close();
+ },
+ close: function() {
+ editorService.close();
+ }
+ };
+ editorService.templateEditor(templateEditor);
}
scope.updateTemplate = function (templateAlias) {
-
// update template value
scope.node.template = templateAlias;
-
};
scope.datePickerChange = function (event, type) {
@@ -163,31 +173,50 @@
}
function setNodePublishStatus(node) {
-
+
+ scope.publishStatus = [];
+
// deleted node
- if(node.trashed === true) {
- scope.publishStatus.label = labels.deleted;
- scope.publishStatus.color = "danger";
+ if (node.trashed === true) {
+ scope.publishStatus.push({
+ label: labels.deleted,
+ color: "danger"
+ });
+ return;
}
- // unpublished node
- if(node.published === false && node.trashed === false) {
- scope.publishStatus.label = labels.unpublished;
- scope.publishStatus.color = "gray";
- }
+ if (node.variants) {
+ for (var i = 0; i < node.variants.length; i++) {
- // published node
- if(node.publishDate && node.published === true) {
- scope.publishStatus.label = labels.published;
- scope.publishStatus.color = "success";
- }
+ var variant = node.variants[i];
- // published node with pending changes
- if (node.edited === true && node.publishDate) {
- scope.publishStatus.label = labels.publishedPendingChanges;
- scope.publishStatus.color = "success"
- }
+ var status = {
+ culture: variant.language ? variant.language.culture : null
+ };
+ if (variant.state === "NotCreated") {
+ status.label = labels.notCreated;
+ status.color = "gray";
+ }
+ else if (variant.state === "Draft") {
+ // draft node
+ status.label = labels.unpublished;
+ status.color = "gray";
+ }
+ else if (variant.state === "Published") {
+ // published node
+ status.label = labels.published;
+ status.color = "success";
+ }
+ else if (variant.state === "PublishedPendingChanges") {
+ // published node with pending changes
+ status.label = labels.publishedPendingChanges;
+ status.color = "success";
+ }
+
+ scope.publishStatus.push(status);
+ }
+ }
}
function setPublishDate(date) {
@@ -293,7 +322,7 @@
});
}));
- // watch for content updates - reload content when node is saved, published etc.
+ // watch for content state updates
scope.$watch('node.updateDate', function(newValue, oldValue){
if(!newValue) { return; }
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js
new file mode 100644
index 0000000000..015255c577
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js
@@ -0,0 +1,45 @@
+(function () {
+ 'use strict';
+
+ /** This directive is used to render out the current variant tabs and properties and exposes an API for other directives to consume */
+ function tabbedContentDirective() {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/content/umb-tabbed-content.html',
+ controller: function ($scope) {
+
+ //expose the property/methods for other directives to use
+ this.content = $scope.content;
+
+ $scope.$watch("tabbedContentForm.$dirty",
+ function (newValue, oldValue) {
+ if (newValue === true) {
+ $scope.content.isDirty = true;
+ }
+ });
+ },
+ link: function(scope) {
+
+ function onInit() {
+ angular.forEach(scope.content.tabs, function (group) {
+ group.open = true;
+ });
+ }
+
+ onInit();
+
+ },
+ scope: {
+ content: "="
+ }
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbTabbedContent', tabbedContentDirective);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js
new file mode 100644
index 0000000000..cdc23ee985
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js
@@ -0,0 +1,134 @@
+(function () {
+ 'use strict';
+
+ /**
+ * A directive to encapsulate each variant editor which includes the name header and all content apps for a given variant
+ * @param {any} $timeout
+ * @param {any} $location
+ */
+ function variantContentDirective($timeout, $location) {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/content/umb-variant-content.html',
+ link: function (scope) {
+
+ /**
+ * Adds a new editor to the editors array to show content in a split view
+ * @param {any} selectedVariant
+ */
+ scope.openInSplitView = function (selectedVariant) {
+
+ var selectedCulture = selectedVariant.language.culture;
+
+ //only the content app can be selected since no other apps are shown, and because we copy all of these apps
+ //to the "editors" we need to update this across all editors
+ for (var e = 0; e < scope.editors.length; e++) {
+ var editor = scope.editors[e];
+ for (var i = 0; i < editor.content.apps.length; i++) {
+ var app = editor.content.apps[i];
+ if (app.alias === "content") {
+ app.active = true;
+ }
+ else {
+ app.active = false;
+ }
+ }
+ }
+
+ //Find the whole variant model based on the culture that was chosen
+ var variant = _.find(scope.content.variants, function (v) {
+ return v.language.culture === selectedCulture;
+ });
+
+ var editor = {
+ content: scope.initVariant({ variant: variant})
+ };
+ scope.editors.push(editor);
+
+ //TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
+ editor.collapsed = true;
+ editor.loading = true;
+ $timeout(function () {
+ editor.collapsed = false;
+ editor.loading = false;
+ scope.onSplitViewChanged();
+ }, 100);
+ };
+
+ /**
+ * Changes the currently selected variant
+ * @param {any} variantDropDownItem
+ */
+ scope.selectVariant = function (variantDropDownItem) {
+
+ var editorIndex = _.findIndex(scope.editors, function (e) {
+ return e === scope.editor;
+ });
+
+ //if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
+ //of a 2nd split view editor then update the model directly.
+ if (editorIndex === 0) {
+ //if we've made it this far, then update the query string
+ $location.search("cculture", variantDropDownItem.language.culture);
+ }
+ else {
+ //set all variant drop down items as inactive for this editor and then set the selected on as active
+ for (var i = 0; i < scope.editor.content.variants.length; i++) {
+ scope.editor.content.variants[i].active = false;
+ }
+ variantDropDownItem.active = true;
+
+ //get the variant content model and initialize the editor with that
+ var variant = _.find(scope.content.variants, function (v) {
+ return v.language.culture === variantDropDownItem.language.culture;
+ });
+ scope.editor.content = scope.initVariant({ variant: variant });
+ }
+ };
+
+ /** Closes the split view */
+ scope.closeSplitView = function () {
+ //TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
+ scope.editor.loading = true;
+ scope.editor.collapsed = true;
+ $timeout(function () {
+ var index = _.findIndex(scope.editors, function(e) {
+ return e === scope.editor;
+ });
+ scope.editors.splice(index, 1);
+ scope.onSplitViewChanged();
+ }, 400);
+ };
+
+ //set the content to dirty if the header changes
+ scope.$watch("contentHeaderForm.$dirty",
+ function (newValue, oldValue) {
+ if (newValue === true) {
+ scope.editor.content.isDirty = true;
+ }
+ });
+
+ },
+ scope: {
+ //TODO: This should be turned into a proper component
+
+ page: "=",
+ content: "=",
+ editor: "=",
+ editors: "=",
+ //TODO: I don't like having this callback defined and would like to make this directive a bit less
+ // coupled but right now don't have time
+ initVariant: "&",
+ onSplitViewChanged: "&"
+ }
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbVariantContent', variantContentDirective);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantstate.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantstate.directive.js
new file mode 100644
index 0000000000..1dc2d60450
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantstate.directive.js
@@ -0,0 +1,22 @@
+(function () {
+ 'use strict';
+
+ function umbVariantStateController($scope, $element) {
+
+ var vm = this;
+
+ }
+
+ var umbVariantStateComponent = {
+ templateUrl: 'views/components/content/umb-variant-state.html',
+ bindings: {
+ variant: "<"
+ },
+ controllerAs: 'vm',
+ controller: umbVariantStateController
+ };
+
+ angular.module("umbraco.directives")
+ .component('umbVariantState', umbVariantStateComponent);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js
index dd0b8baf71..d4b0fb158e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js
@@ -37,34 +37,35 @@ Use this directive to construct a main content area inside the main editor windo
**/
-(function() {
- 'use strict';
+(function () {
+ 'use strict';
- function EditorContainerDirective(overlayHelper) {
+ function EditorContainerDirective(overlayHelper) {
- function link(scope, el, attr, ctrl) {
+ function link(scope, el, attr, ctrl) {
- scope.numberOfOverlays = 0;
+ scope.numberOfOverlays = 0;
- scope.$watch(function(){
- return overlayHelper.getNumberOfOverlays();
- }, function (newValue) {
- scope.numberOfOverlays = newValue;
- });
+ //TODO: this shouldn't be a watch, this should be based on an event handler
+ scope.$watch(function () {
+ return overlayHelper.getNumberOfOverlays();
+ }, function (newValue) {
+ scope.numberOfOverlays = newValue;
+ });
- }
+ }
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-container.html',
- link: link
- };
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-container.html',
+ link: link
+ };
- return directive;
- }
+ return directive;
+ }
- angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective);
+ angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
index 980c2f4453..1d1c9d8f00 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
@@ -201,67 +201,49 @@ Use this directive to construct a header inside the main editor window.
**/
-(function() {
+(function () {
'use strict';
- function EditorHeaderDirective(iconHelper, $location, editorService) {
+ function EditorHeaderDirective(iconHelper, editorService, $location, $routeParams) {
function link(scope, el, attr, ctrl) {
+
scope.vm = {};
scope.vm.dropdownOpen = false;
scope.vm.currentVariant = "";
function onInit() {
- setCurrentVariant(scope.variants);
- setVariantStatusColor(scope.variants);
+ setCurrentVariant();
}
- function setCurrentVariant(variants) {
-
- angular.forEach(variants, function (variant) {
- if(variant.current) {
+ function setCurrentVariant() {
+ angular.forEach(scope.variants, function (variant) {
+ if (variant.active) {
scope.vm.currentVariant = variant;
}
});
}
- //TODO: This doesn't really affect any UI currently, need some feedback from mads
- function setVariantStatusColor(variants) {
- angular.forEach(variants, function (variant) {
-
- //TODO: What about variant.exists? If we are applying colors/styles, this should be one of them
-
- switch (variant.state) {
- case "Published":
- variant.stateColor = "success";
- break;
- case "Unpublished":
- //TODO: Not sure if these statuses will ever bubble up to the UI?
- case "Publishing":
- case "Unpublishing":
- default:
- variant.stateColor = "gray";
- }
- });
- }
-
scope.goBack = function () {
if (scope.onBack) {
- scope.onBack();
+ $location.path('/' + $routeParams.section + '/' + $routeParams.tree + '/' + $routeParams.method + '/' + scope.menu.currentNode.parentId);
}
};
scope.selectVariant = function (event, variant) {
- scope.vm.dropdownOpen = false;
- $location.search("cculture", variant.language.culture);
+
+ if (scope.onSelectVariant) {
+ scope.vm.dropdownOpen = false;
+ scope.onSelectVariant({ "variant": variant });
+ }
};
- scope.openIconPicker = function() {
+ scope.openIconPicker = function () {
var iconPicker = {
icon: scope.icon.split(' ')[0],
color: scope.icon.split(' ')[1],
- submit: function(model) {
+ submit: function (model) {
if (model.icon) {
if (model.color) {
scope.icon = model.icon + " " + model.color;
@@ -273,37 +255,47 @@ Use this directive to construct a header inside the main editor window.
}
editorService.close();
},
- close: function() {
+ close: function () {
editorService.close();
}
};
editorService.iconPicker(iconPicker);
};
- scope.closeSplitView = function() {
- if(scope.onCloseSplitView) {
+ scope.closeSplitView = function () {
+ if (scope.onCloseSplitView) {
scope.onCloseSplitView();
}
};
- scope.openInSplitView = function(event, variant) {
- if(scope.onOpenInSplitView) {
+ scope.openInSplitView = function (event, variant) {
+ if (scope.onOpenInSplitView) {
scope.vm.dropdownOpen = false;
- scope.onOpenInSplitView({"variant": variant});
+ scope.onOpenInSplitView({ "variant": variant });
}
};
- scope.$watch('variants', function(newValue, oldValue){
- if(!newValue) return;
- if(newValue === oldValue) return;
- setCurrentVariant(newValue);
- setVariantStatusColor(newValue);
- }, true);
-
onInit();
+ //watch for the active culture changing, if it changes, update the current variant
+ if (scope.variants) {
+ scope.$watch(function () {
+ for (var i = 0; i < scope.variants.length; i++) {
+ var v = scope.variants[i];
+ if (v.active) {
+ return v.language.culture;
+ }
+ }
+ return scope.vm.currentVariant.language.culture; //should never get here
+ }, function (newValue, oldValue) {
+ if (newValue !== scope.vm.currentVariant.language.culture) {
+ setCurrentVariant();
+ }
+ });
+ }
}
+
var directive = {
transclude: true,
restrict: 'E',
@@ -331,7 +323,8 @@ Use this directive to construct a header inside the main editor window.
showBackButton: "",
splitViewOpen: "=?",
onOpenInSplitView: "&?",
- onCloseSplitView: "&?"
+ onCloseSplitView: "&?",
+ onSelectVariant: "&?"
},
link: link
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js
index 6a8896b609..3eda8974d6 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js
@@ -1,18 +1,79 @@
(function () {
'use strict';
- function EditorNavigationDirective(eventsService) {
+ function EditorNavigationDirective($window, $timeout, eventsService, windowResizeListener) {
- function link(scope, el, attr, ctrl) {
+ function link(scope) {
scope.showNavigation = true;
+ scope.showMoreButton = false;
+ scope.showDropdown = false;
+ scope.overflowingItems = 0;
+ scope.itemsLimit = 6;
+
+ scope.moreButton = {
+ alias: "more",
+ active: false,
+ name: "More"
+ };
scope.clickNavigationItem = function (selectedItem) {
- setItemToActive(selectedItem);
+ scope.showDropdown = false;
runItemAction(selectedItem);
eventsService.emit("app.tabChange", selectedItem);
+ setItemToActive(selectedItem);
};
+ scope.toggleDropdown = function () {
+ scope.showDropdown = !scope.showDropdown;
+ };
+
+ scope.hideDropdown = function() {
+ scope.showDropdown = false;
+ };
+
+ function onInit() {
+
+ // hide navigation if there is only 1 item
+ if (scope.navigation.length <= 1) {
+ scope.showNavigation = false;
+ }
+
+ $timeout(function(){
+ if($window && $window.innerWidth) {
+ calculateVisibleItems($window.innerWidth);
+ }
+ });
+
+ }
+
+ function calculateVisibleItems(windowWidth) {
+
+ // if we don't get a windowWidth stick with the default item limit
+ if(!windowWidth) {
+ return;
+ }
+
+ scope.itemsLimit = 0;
+
+ // set visible items based on browser width
+ if (windowWidth > 1500) {
+ scope.itemsLimit = 6;
+ }
+ else if (windowWidth > 700) {
+ scope.itemsLimit = 4;
+ }
+
+ // toggle more button
+ if(scope.navigation.length > scope.itemsLimit) {
+ scope.showMoreButton = true;
+ scope.overflowingItems = scope.itemsLimit - scope.navigation.length;
+ } else {
+ scope.showMoreButton = false;
+ scope.overflowingItems = 0;
+ }
+ }
+
function runItemAction(selectedItem) {
if (selectedItem.action) {
selectedItem.action(selectedItem);
@@ -20,29 +81,41 @@
}
function setItemToActive(selectedItem) {
- // set all other views to inactive
if (selectedItem.view) {
-
- for (var index = 0; index < scope.navigation.length; index++) {
- var item = scope.navigation[index];
+
+ // deselect all items
+ angular.forEach(scope.navigation, function(item, index){
item.active = false;
+ });
+
+ // set clicked item to active
+ selectedItem.active = true;
+
+ // set more button to active if item in dropdown is clicked
+ var selectedItemIndex = scope.navigation.indexOf(selectedItem);
+ if (selectedItemIndex + 1 > scope.itemsLimit) {
+ scope.moreButton.active = true;
+ } else {
+ scope.moreButton.active = false;
}
- // set view to active
- selectedItem.active = true;
}
}
- function activate() {
-
- // hide navigation if there is only 1 item
- if (scope.navigation.length <= 1) {
- scope.showNavigation = false;
+ var resizeCallback = function(size) {
+ if(size && size.width) {
+ calculateVisibleItems(size.width);
}
+ };
- }
+ windowResizeListener.register(resizeCallback);
- activate();
+ //ensure to unregister from all events and kill jquery plugins
+ scope.$on('$destroy', function () {
+ windowResizeListener.unregister(resizeCallback);
+ });
+
+ onInit();
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js
new file mode 100644
index 0000000000..e8b41529ae
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js
@@ -0,0 +1,33 @@
+(function () {
+ 'use strict';
+
+ /**
+ * A directive that renders a defined view with a view model and a the whole content model.
+ **/
+ function EditorSubViewDirective() {
+
+ function link(scope, el, attr, ctrl) {
+ //The model can contain: view, viewModel, name, alias, icon
+
+ if (!scope.model.view) {
+ throw "No view defined for the content app";
+ }
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-sub-view.html',
+ scope: {
+ model: "=",
+ content: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubView', EditorSubViewDirective);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js
index db0dad8de7..7033a03943 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js
@@ -1,48 +1,31 @@
-(function() {
- 'use strict';
+(function () {
+ 'use strict';
- function EditorSubViewsDirective() {
+ /**
+ * A directive that just repeats over a list of defined views which are all able to access the same common model.
+ * This is only used in simple cases, whereas media and content use umbEditorSubView (singular) which allows
+ * passing in a view model specific to the view and the entire content model for support if required.
+ **/
+ function EditorSubViewsDirective() {
- function link(scope, el, attr, ctrl) {
+ function link(scope, el, attr, ctrl) {
- scope.activeView = {};
+ }
- // set toolbar from selected navigation item
- function setActiveView(items) {
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-sub-views.html',
+ scope: {
+ subViews: "=",
+ model: "="
+ },
+ link: link
+ };
- for (var index = 0; index < items.length; index++) {
+ return directive;
+ }
- var item = items[index];
-
- if (item.active && item.view) {
- scope.activeView = item;
- }
- }
- }
-
- // watch for navigation changes
- scope.$watch('subViews', function(newValue, oldValue) {
- if (newValue) {
- setActiveView(newValue);
- }
- }, true);
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-sub-views.html',
- scope: {
- subViews: "=",
- model: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
+ angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/checklistmodel.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/checklistmodel.directive.js
index 181d8a1881..a89477b001 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/checklistmodel.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/checklistmodel.directive.js
@@ -1,6 +1,6 @@
/*
- http://vitalets.github.io/checklist-model/
+ https://vitalets.github.io/checklist-model/
@@ -19,7 +19,7 @@ angular.module('umbraco.directives')
return false;
}
- // add
+ // add
function add(arr, item) {
arr = angular.isArray(arr) ? arr : [];
for (var i = 0; i < arr.length; i++) {
@@ -44,7 +44,7 @@ angular.module('umbraco.directives')
return arr;
}
- // http://stackoverflow.com/a/19228302/1458162
+ // https://stackoverflow.com/a/19228302/1458162
function postLinkFn(scope, elem, attrs) {
// compile with `ng-model` pointing to `checked`
$compile(elem)(scope);
@@ -98,4 +98,4 @@ angular.module('umbraco.directives')
return postLinkFn;
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js
index 135d806aa4..cdcdb7313c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js
@@ -53,7 +53,7 @@ angular.module("umbraco.directives")
var $container = element.find(".crop-container");
//default constraints for drag n drop
- var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin}, };
+ var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin} };
scope.constraints = constraints;
@@ -264,7 +264,7 @@ angular.module("umbraco.directives")
});
//ie hack
- if(window.navigator.userAgent.indexOf("MSIE ")){
+ if(window.navigator.userAgent.indexOf("MSIE ") >= 0){
var ranger = element.find("input");
ranger.bind("change",function(){
scope.$apply(function(){
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js
index e47032fed3..4f783ac9a9 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js
@@ -1,139 +1,199 @@
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbImageGravity
-* @restrict E
-* @function
-* @description
-**/
-angular.module("umbraco.directives")
- .directive('umbImageGravity', function ($timeout, localizationService, $log) {
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/imaging/umb-image-gravity.html',
- scope: {
- src: '=',
- center: "=",
- onImageLoaded: "="
- },
- link: function(scope, element, attrs) {
+(function() {
+ 'use strict';
- //Internal values for keeping track of the dot and the size of the editor
- scope.dimensions = {
- width: 0,
- height: 0,
- left: 0,
- top: 0
- };
+ function umbImageGravityController($scope, $element, $timeout) {
- scope.loaded = false;
+ var vm = this;
- //elements
- var $viewport = element.find(".viewport");
- var $image = element.find("img");
- var $overlay = element.find(".overlay");
+ //Internal values for keeping track of the dot and the size of the editor
+ vm.dimensions = {
+ width: 0,
+ height: 0,
+ left: 0,
+ top: 0
+ };
- scope.style = function () {
- if(scope.dimensions.width <= 0){
- setDimensions();
- }
+ var htmlImage = null; //DOM element reference
+ var htmlOverlay = null; //DOM element reference
+ var draggable = null;
- return {
- 'top': scope.dimensions.top + 'px',
- 'left': scope.dimensions.left + 'px'
- };
- };
+ vm.loaded = false;
+ vm.$onInit = onInit;
+ vm.$onChanges = onChanges;
+ vm.$postLink = postLink;
+ vm.$onDestroy = onDestroy;
+ vm.style = style;
+ vm.setFocalPoint = setFocalPoint;
- scope.setFocalPoint = function(event) {
+ /** Sets the css style for the Dot */
+ function style() {
- scope.$emit("imageFocalPointStart");
+ if (vm.dimensions.width <= 0) {
+ //this initializes the dimensions since when the image element first loads
+ //there will be zero dimensions
+ setDimensions();
+ }
- var offsetX = event.offsetX - 10;
- var offsetY = event.offsetY - 10;
+ return {
+ 'top': vm.dimensions.top + 'px',
+ 'left': vm.dimensions.left + 'px'
+ };
+ };
- calculateGravity(offsetX, offsetY);
+ function setFocalPoint (event) {
- lazyEndEvent();
+ $scope.$emit("imageFocalPointStart");
- };
+ var offsetX = event.offsetX - 10;
+ var offsetY = event.offsetY - 10;
- var setDimensions = function(){
- scope.dimensions.width = $image.width();
- scope.dimensions.height = $image.height();
+ calculateGravity(offsetX, offsetY);
- if(scope.center){
- scope.dimensions.left = scope.center.left * scope.dimensions.width -10;
- scope.dimensions.top = scope.center.top * scope.dimensions.height -10;
- }else{
- scope.center = { left: 0.5, top: 0.5 };
- }
- };
+ lazyEndEvent();
- var calculateGravity = function(offsetX, offsetY){
- scope.dimensions.left = offsetX;
- scope.dimensions.top = offsetY;
+ };
- scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width;
- scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height;
- };
+ /** Initializes the component */
+ function onInit() {
+ if (!vm.center) {
+ vm.center = { left: 0.5, top: 0.5 };
+ }
+ }
- var lazyEndEvent = _.debounce(function(){
- scope.$apply(function(){
- scope.$emit("imageFocalPointStop");
- });
- }, 2000);
+ /** Called when the component has linked everything and the DOM is available */
+ function postLink() {
+ //elements
+ htmlImage = $element.find("img");
+ htmlOverlay = $element.find(".overlay");
-
- //Drag and drop positioning, using jquery ui draggable
- //TODO ensure that the point doesnt go outside the box
- $overlay.draggable({
- containment: "parent",
- start: function(){
- scope.$apply(function(){
- scope.$emit("imageFocalPointStart");
- });
- },
- stop: function() {
- scope.$apply(function(){
- var offsetX = $overlay[0].offsetLeft;
- var offsetY = $overlay[0].offsetTop;
- calculateGravity(offsetX, offsetY);
- });
-
- lazyEndEvent();
- }
- });
-
- //// INIT /////
- $image.load(function() {
- $timeout(function() {
- setDimensions();
- scope.loaded = true;
- if (angular.isFunction(scope.onImageLoaded)) {
- scope.onImageLoaded();
- }
- });
- });
-
- $(window).on('resize.umbImageGravity', function(){
- scope.$apply(function(){
- $timeout(function(){
- setDimensions();
- });
- // Make sure we can find the offset values for the overlay(dot) before calculating
- // fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte)
- if($overlay.is(':visible')) {
- var offsetX = $overlay[0].offsetLeft;
- var offsetY = $overlay[0].offsetTop;
- calculateGravity(offsetX, offsetY);
- }
- });
+ //Drag and drop positioning, using jquery ui draggable
+ draggable = htmlOverlay.draggable({
+ containment: "parent",
+ start: function () {
+ $scope.$apply(function () {
+ $scope.$emit("imageFocalPointStart");
+ });
+ },
+ stop: function () {
+ $scope.$apply(function () {
+ var offsetX = htmlOverlay[0].offsetLeft;
+ var offsetY = htmlOverlay[0].offsetTop;
+ calculateGravity(offsetX, offsetY);
});
- scope.$on('$destroy', function() {
- $(window).off('.umbImageGravity');
- });
+ lazyEndEvent();
+ }
+ });
- }
- };
- });
+ $(window).on('resize.umbImageGravity', function () {
+ $scope.$apply(function () {
+ resized();
+ });
+ });
+
+ //if any ancestor directive emits this event, we need to resize
+ $scope.$on("editors.content.splitViewChanged", function () {
+ $timeout(resized, 200);
+ });
+
+ //listen for the image DOM element loading
+ htmlImage.on("load", function () {
+ $timeout(function () {
+ setDimensions();
+ vm.loaded = true;
+ if (vm.onImageLoaded) {
+ vm.onImageLoaded();
+ }
+ }, 100);
+ });
+ }
+
+ function onDestroy() {
+ $(window).off('resize.umbImageGravity');
+ if (htmlOverlay) {
+ //TODO: This should be destroyed but this will throw an exception:
+ // "cannot call methods on draggable prior to initialization; attempted to call method 'destroy'"
+ // I've tried lots of things and cannot get this to work, we weren't destroying before so hopefully
+ // there's no mem leaks?
+ //htmlOverlay.draggable("destroy");
+ }
+ if (htmlImage) {
+ htmlImage.off("load");
+ }
+ }
+
+ /** Called when we need to resize based on window or DOM dimensions to re-center the focal point */
+ function resized() {
+ $timeout(function () {
+ setDimensions();
+ });
+ // Make sure we can find the offset values for the overlay(dot) before calculating
+ // fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte)
+ if (htmlOverlay.is(':visible')) {
+ var offsetX = htmlOverlay[0].offsetLeft;
+ var offsetY = htmlOverlay[0].offsetTop;
+ calculateGravity(offsetX, offsetY);
+ }
+ }
+
+ /** Watches the one way binding changes */
+ function onChanges(changes) {
+ if (changes.center && !changes.center.isFirstChange()
+ && changes.center.currentValue
+ && !angular.equals(changes.center.currentValue, changes.center.previousValue)) {
+ //when center changes update the dimensions
+ setDimensions();
+ }
+ }
+
+ /** Sets the width/height/left/top dimentions based on the image size and the "center" value */
+ function setDimensions() {
+ if (htmlImage && vm.center) {
+ vm.dimensions.width = htmlImage.width();
+ vm.dimensions.height = htmlImage.height();
+ vm.dimensions.left = vm.center.left * vm.dimensions.width - 10;
+ vm.dimensions.top = vm.center.top * vm.dimensions.height - 10;
+ }
+ return vm.dimensions.width;
+ };
+
+ /**
+ * based on the offset selected calculates the "center" value and calls the callback
+ * @param {any} offsetX
+ * @param {any} offsetY
+ */
+ function calculateGravity(offsetX, offsetY) {
+
+ vm.onValueChanged({
+ left: (offsetX + 10) / vm.dimensions.width,
+ top: (offsetY + 10) / vm.dimensions.height
+ });
+
+ //vm.center.left = (offsetX + 10) / scope.dimensions.width;
+ //vm.center.top = (offsetY + 10) / scope.dimensions.height;
+ };
+
+ var lazyEndEvent = _.debounce(function () {
+ $scope.$apply(function () {
+ $scope.$emit("imageFocalPointStop");
+ });
+ }, 2000);
+
+ }
+
+ var umbImageGravityComponent = {
+ templateUrl: 'views/components/imaging/umb-image-gravity.html',
+ bindings: {
+ src: "<",
+ center: "<",
+ onImageLoaded: "&?",
+ onValueChanged: "&"
+ },
+ controllerAs: 'vm',
+ controller: umbImageGravityController
+ };
+
+ angular.module("umbraco.directives")
+ .component('umbImageGravity', umbImageGravityComponent);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlaybackdrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlaybackdrop.directive.js
index cfcd7e064b..927481952a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlaybackdrop.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlaybackdrop.directive.js
@@ -1,31 +1,32 @@
-(function() {
- 'use strict';
+(function () {
+ 'use strict';
- function OverlayBackdropDirective(overlayHelper) {
+ function OverlayBackdropDirective(overlayHelper) {
- function link(scope, el, attr, ctrl) {
+ function link(scope, el, attr, ctrl) {
- scope.numberOfOverlays = 0;
+ scope.numberOfOverlays = 0;
- scope.$watch(function(){
- return overlayHelper.getNumberOfOverlays();
- }, function (newValue) {
- scope.numberOfOverlays = newValue;
- });
+ //TODO: this shouldn't be a watch, this should be based on an event handler
+ scope.$watch(function () {
+ return overlayHelper.getNumberOfOverlays();
+ }, function (newValue) {
+ scope.numberOfOverlays = newValue;
+ });
- }
+ }
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/overlays/umb-overlay-backdrop.html',
- link: link
- };
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/overlays/umb-overlay-backdrop.html',
+ link: link
+ };
- return directive;
+ return directive;
- }
+ }
- angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective);
+ angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
index 93c9ff8c29..251683edc8 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
@@ -350,6 +350,9 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
if (args.cacheKey) {
$scope.cachekey = args.cacheKey;
}
+ if (args.customTreeParams) {
+ $scope.customtreeparams = args.customTreeParams;
+ }
}
//load the tree
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js
index ca389a74a7..a215bca645 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js
@@ -161,7 +161,7 @@
/**
* ACE editor session.
* @type object
- * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
+ * @see [EditSession]{@link https://ace.c9.io/#nav=api&api=edit_session}
*/
var session = acee.getSession();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js
index 6ed65a9431..2b4004da11 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js
@@ -29,9 +29,7 @@ Use this directive to generate color swatches to pick from.
function link(scope, el, attr, ctrl) {
scope.setColor = function (color) {
- //scope.selectedColor({color: color });
scope.selectedColor = color;
-
if (scope.onSelect) {
scope.onSelect(color);
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
index 19ab789363..8d2b83d065 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
@@ -7,7 +7,7 @@
@description
Added in Umbraco version 7.6
This directive is a wrapper of the bootstrap datetime picker version 3.1.3. Use it to render a date time picker.
-For extra details about options and events take a look here: http://eonasdan.github.io/bootstrap-datetimepicker/
+For extra details about options and events take a look here: https://eonasdan.github.io/bootstrap-datetimepicker/
Use this directive to render a date time picker
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdropdownitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdropdownitem.directive.js
index 59b2b827eb..1c302ca90f 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdropdownitem.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdropdownitem.directive.js
@@ -17,7 +17,7 @@
restrict: 'E',
replace: true,
transclude: true,
- templateUrl: 'views/components/umb-dropdown-item.html',
+ templateUrl: 'views/components/umb-dropdown-item.html'
};
return directive;
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
index 75d26c7fac..11c94d04c0 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
@@ -57,7 +57,7 @@
},
stop: function(e, ui) {
updateTabsSortOrder();
- },
+ }
};
scope.sortableOptionsProperty = {
@@ -245,51 +245,21 @@
scope.openCompositionsDialog = function() {
scope.compositionsDialogModel = {
- title: "Compositions",
contentType: scope.model,
compositeContentTypes: scope.model.compositeContentTypes,
- view: "views/common/overlays/contenttypeeditor/compositions/compositions.html",
- confirmSubmit: {
- title: "Warning",
- description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?",
- checkboxLabel: "I know what I'm doing",
- enable: true
- },
- submit: function(model, oldModel, confirmed) {
+ view: "views/common/infiniteeditors/compositions/compositions.html",
+ size: "small",
+ submit: function() {
+
+ // make sure that all tabs has an init property
+ if (scope.model.groups.length !== 0) {
+ angular.forEach(scope.model.groups, function(group) {
+ addInitProperty(group);
+ });
+ }
- var compositionRemoved = false;
-
- // check if any compositions has been removed
- for(var i = 0; oldModel.compositeContentTypes.length > i; i++) {
-
- var oldComposition = oldModel.compositeContentTypes[i];
-
- if(_.contains(model.compositeContentTypes, oldComposition) === false) {
- compositionRemoved = true;
- }
-
- }
-
- // show overlay confirm box if compositions has been removed.
- if(compositionRemoved && confirmed === false) {
-
- scope.compositionsDialogModel.confirmSubmit.show = true;
-
- // submit overlay if no compositions has been removed
- // or the action has been confirmed
- } else {
-
- // make sure that all tabs has an init property
- if (scope.model.groups.length !== 0) {
- angular.forEach(scope.model.groups, function(group) {
- addInitProperty(group);
- });
- }
-
- // remove overlay
- scope.compositionsDialogModel.show = false;
- scope.compositionsDialogModel = null;
- }
+ // remove overlay
+ editorService.close();
},
close: function(oldModel) {
@@ -299,8 +269,7 @@
scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes;
// remove overlay
- scope.compositionsDialogModel.show = false;
- scope.compositionsDialogModel = null;
+ editorService.close();
},
selectCompositeContentType: function (selectedContentType) {
@@ -348,39 +317,40 @@
}
};
- //select which resource methods to use, eg document Type or Media Type versions
- var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
- var whereUsedContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getWhereCompositionIsUsedInContentTypes : mediaTypeResource.getWhereCompositionIsUsedInContentTypes;
- var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount;
- //get the currently assigned property type aliases - ensure we pass these to the server side filer
- var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
- return _.map(g.properties, function(p) {
- return p.alias;
- });
- })), function(f) {
- return f !== null && f !== undefined;
- });
- $q.all([
- //get available composite types
- availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) {
- setupAvailableContentTypesModel(result);
- }),
- //get where used document types
- whereUsedContentTypeResource(scope.model.id).then(function (whereUsed) {
- //pass to the dialog model the content type eg documentType or mediaType
- scope.compositionsDialogModel.section = scope.contentType;
- //pass the list of 'where used' document types
- scope.compositionsDialogModel.whereCompositionUsed = whereUsed;
- }),
- //get content type count
- countContentTypeResource().then(function(result) {
- scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10);
- })
- ]).then(function() {
- //resolves when both other promises are done, now show it
- scope.compositionsDialogModel.show = true;
- });
+ //select which resource methods to use, eg document Type or Media Type versions
+ var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
+ var whereUsedContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getWhereCompositionIsUsedInContentTypes : mediaTypeResource.getWhereCompositionIsUsedInContentTypes;
+ var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount;
+
+ //get the currently assigned property type aliases - ensure we pass these to the server side filer
+ var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
+ return _.map(g.properties, function(p) {
+ return p.alias;
+ });
+ })), function(f) {
+ return f !== null && f !== undefined;
+ });
+ $q.all([
+ //get available composite types
+ availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) {
+ setupAvailableContentTypesModel(result);
+ }),
+ //get where used document types
+ whereUsedContentTypeResource(scope.model.id).then(function (whereUsed) {
+ //pass to the dialog model the content type eg documentType or mediaType
+ scope.compositionsDialogModel.section = scope.contentType;
+ //pass the list of 'where used' document types
+ scope.compositionsDialogModel.whereCompositionUsed = whereUsed;
+ }),
+ //get content type count
+ countContentTypeResource().then(function(result) {
+ scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10);
+ })
+ ]).then(function() {
+ //resolves when both other promises are done, now show it
+ editorService.open(scope.compositionsDialogModel);
+ });
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
index 6619af3cc0..3fb1b3eb36 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
@@ -4,7 +4,6 @@
* @restrict E
* @function
* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
**/
/*
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js
new file mode 100644
index 0000000000..cf12585e15
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js
@@ -0,0 +1,305 @@
+(function() {
+ 'use strict';
+
+ /**
+ * A component to manage file uploads for content properties
+ * @param {any} $scope
+ * @param {any} fileManager
+ * @param {any} mediaHelper
+ * @param {any} angularHelper
+ */
+ function umbPropertyFileUploadController($scope, $q, fileManager, mediaHelper, angularHelper) {
+
+ //NOTE: this component supports multiple files, though currently the uploader does not but perhaps sometime in the future
+ // we'd want it to, so i'll leave the multiple file support in place
+
+ var vm = this;
+
+ vm.$onInit = onInit;
+ vm.$onChanges = onChanges;
+ vm.$postLink = postLink;
+ vm.clear = clearFiles;
+
+ /** Clears the file collections when content is saving (if we need to clear) or after saved */
+ function clearFiles() {
+ //clear the files collection (we don't want to upload any!)
+ fileManager.setFiles({
+ propertyAlias: vm.propertyAlias,
+ culture: vm.culture,
+ files: []
+ });
+ //clear the current files
+ vm.files = [];
+
+ //notify the callback
+ notifyFilesSelected(null);
+ notifyFilesChanged(null);
+ }
+
+ function notifyFilesSelected(val, files) {
+
+ if (!val) {
+ val = null;
+ }
+ if (!files) {
+ files = null;
+ }
+
+ //notify the callback
+ vm.onFilesSelected({ value: val, files: files });
+
+ //need to explicity setDirty here to track changes
+ vm.fileUploadForm.$setDirty();
+ }
+
+ function notifyFilesChanged(files) {
+ if (!files) {
+ files = null;
+ }
+
+ //notify the callback
+ vm.onFilesChanged({ files: files });
+ }
+
+ function notifyInit(val, files) {
+ if (!val) {
+ val = null;
+ }
+ if (!files) {
+ files = null;
+ }
+
+ if (vm.onInit) {
+ vm.onInit({ value: val, files: files });
+ }
+ }
+
+ /** Called when the component initializes */
+ function onInit() {
+ $scope.$on("filesSelected", onFilesSelected);
+ initialize();
+ }
+
+ /** Called when the component has linked all elements, this is when the form controller is available */
+ function postLink() {
+
+ }
+
+ function initialize() {
+
+ //normalize culture to null if it's not there
+ if (!vm.culture) {
+ vm.culture = null;
+ }
+
+ //TODO: need to figure out what we can do for things like Nested Content
+
+ var existingClientFiles = checkPendingClientFiles();
+ //create the property to show the list of files currently saved
+ if (existingClientFiles.length > 0) {
+ updateModelFromSelectedFiles(existingClientFiles).then(function (newVal) {
+ //notify the callback
+ notifyInit(newVal, vm.files);
+ });
+ }
+ else if (vm.value) {
+
+ var files = vm.value.split(",");
+
+ vm.files = _.map(files, function (file) {
+ var f = {
+ fileName: file,
+ isImage: mediaHelper.detectIfImageByExtension(file),
+ extension: getExtension(file)
+ };
+ f.fileSrc = getThumbnail(f);
+ return f;
+ });
+
+ //notify the callback
+ notifyInit();
+ }
+ else {
+ vm.files = [];
+
+ //notify the callback
+ notifyInit();
+ }
+ }
+
+ function checkPendingClientFiles() {
+
+ //normalize culture to null if it's not there
+ if (!vm.culture) {
+ vm.culture = null;
+ }
+
+ //check the file manager to see if there's already local files pending for this editor
+ var existingClientFiles = _.map(
+ _.filter(fileManager.getFiles(),
+ function (f) {
+ return f.alias === vm.propertyAlias && f.culture === vm.culture;
+ }),
+ function (f) {
+ return f.file;
+ });
+ return existingClientFiles;
+ }
+
+ /**
+ * Watch for model changes
+ * @param {any} changes
+ */
+ function onChanges(changes) {
+
+ if (changes.value && !changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) {
+
+ if (!changes.value.currentValue && changes.value.previousValue) {
+ //if the value has been cleared, clear the files (ignore if the previous value is also falsy)
+ vm.files = [];
+ }
+ else if (changes.value.currentValue && !changes.value.previousValue && vm.files.length === 0) {
+ //if a new value has been added after being cleared
+
+ var existingClientFiles = checkPendingClientFiles();
+ //create the property to show the list of files currently saved
+ if (existingClientFiles.length > 0) {
+ updateModelFromSelectedFiles(existingClientFiles).then(function () {
+ //raise this event which means the files have changed but this wasn't the instance that
+ //added the file
+ notifyFilesChanged(vm.files);
+ });
+ }
+ }
+
+ }
+ }
+
+ function getThumbnail(file) {
+ if (!file.isImage) {
+ return null;
+ }
+
+ var thumbnailUrl = mediaHelper.getThumbnailFromPath(file.fileName);
+
+ return thumbnailUrl;
+ }
+
+ function getExtension(fileName) {
+ var extension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length);
+ return extension.toLowerCase();
+ }
+
+ /**
+ * Updates the vm.files model from the selected files and returns a promise containing the csv of all file names selected
+ * @param {any} files
+ */
+ function updateModelFromSelectedFiles(files) {
+
+ //we return a promise because the FileReader api is async
+ var promises = [];
+
+ //clear the current files
+ vm.files = [];
+ var newVal = "";
+
+ var reader = new FileReader();
+
+ //for each file load in the contents from the file reader and set it as an fileSrc
+ //property of the vm.files array item
+ var fileCount = files.length;
+ for (var i = 0; i < fileCount; i++) {
+ var index = i; //capture
+
+ var isImage = mediaHelper.detectIfImageByExtension(files[i].name);
+
+ //save the file object to the files collection
+ vm.files.push({
+ isImage: isImage,
+ extension: getExtension(files[i].name),
+ fileName: files[i].name,
+ isClientSide: true
+ });
+
+ newVal += files[i].name + ",";
+
+ if (isImage) {
+
+ var deferred = $q.defer();
+
+ reader.onload = function(e) {
+ vm.files[index].fileSrc = e.target.result;
+ deferred.resolve(newVal);
+ };
+ promises.push(deferred.promise);
+ reader.readAsDataURL(files[i]);
+ }
+ else {
+ promises.push($q.when(newVal));
+ }
+ }
+
+ return $q.all(promises).then(function (p) {
+ //return the last value in the list of promises which will be the final value
+ return $q.when(p[p.length - 1]);
+ });
+ }
+
+ /**
+ * listen for when a file is selected
+ * @param {any} event
+ * @param {any} args
+ */
+ function onFilesSelected(event, args) {
+
+ if (args.files && args.files.length > 0) {
+
+ //set the files collection
+ fileManager.setFiles({
+ propertyAlias: vm.propertyAlias,
+ files: args.files,
+ culture: vm.culture
+ });
+
+ updateModelFromSelectedFiles(args.files).then(function(newVal) {
+ angularHelper.safeApply($scope,
+ function() {
+ //pass in the file names and the model files
+ notifyFilesSelected(newVal, vm.files);
+ notifyFilesChanged(vm.files);
+ });
+ });
+ }
+ else {
+ angularHelper.safeApply($scope);
+ }
+ }
+
+ };
+
+ var umbPropertyFileUploadComponent = {
+ templateUrl: 'views/components/upload/umb-property-file-upload.html',
+ bindings: {
+ culture: "@?",
+ propertyAlias: "@",
+ value: "<",
+ hideSelection: "<",
+ /**
+ * Called when a file is selected on this instance
+ */
+ onFilesSelected: "&",
+ /**
+ * Called when the file collection changes (i.e. a new file has been selected but maybe it wasn't this instance that caused the change)
+ */
+ onFilesChanged: "&",
+ onInit: "&"
+ },
+ transclude: true,
+ controllerAs: 'vm',
+ controller: umbPropertyFileUploadController
+ };
+
+ angular.module("umbraco.directives")
+ .component('umbPropertyFileUpload', umbPropertyFileUploadComponent);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/autoscale.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/autoscale.directive.js
new file mode 100644
index 0000000000..029a4e420f
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/autoscale.directive.js
@@ -0,0 +1,49 @@
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:autoScale
+* @element div
+* @function
+* @description
+* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
+* So if you only want to scale the div to 70 pixels from the bottom you pass "70"
+
+* @example
+*
+*
+*
+*
+*
+**/
+
+angular.module("umbraco.directives")
+ .directive('autoScale', function ($window, $timeout, windowResizeListener) {
+ return function (scope, el, attrs) {
+
+ var totalOffset = 0;
+ var offsety = parseInt(attrs.autoScale, 10);
+ var window = angular.element($window);
+ if (offsety !== undefined) {
+ totalOffset += offsety;
+ }
+
+ $timeout(function () {
+ setElementSize();
+ });
+
+ function setElementSize() {
+ el.height(window.height() - (el.offset().top + totalOffset));
+ }
+
+ var resizeCallback = function() {
+ setElementSize();
+ };
+
+ windowResizeListener.register(resizeCallback);
+
+ //ensure to unregister from all events and kill jquery plugins
+ scope.$on('$destroy', function () {
+ windowResizeListener.unregister(resizeCallback);
+ });
+
+ };
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js
index 7c96e0e050..7914dfc3f0 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/konami.directive.js
@@ -1,7 +1,7 @@
/**
* Konami Code directive for AngularJS
* @version v0.0.1
- * @license MIT License, http://www.opensource.org/licenses/MIT
+ * @license MIT License, https://www.opensource.org/licenses/MIT
*/
angular.module('umbraco.directives')
@@ -59,4 +59,4 @@ angular.module('umbraco.directives')
scope.$on('$destroy', stopListening);
}
};
- }]);
\ No newline at end of file
+ }]);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js
index 104736530f..800ac87480 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js
@@ -2,7 +2,7 @@
* @ngdoc directive
* @name umbraco.directives.directive:noDirtyCheck
* @restrict A
-* @description Can be attached to form inputs to prevent them from setting the form as dirty (http://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs)
+* @description Can be attached to form inputs to prevent them from setting the form as dirty (https://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs)
**/
function noDirtyCheck() {
return {
@@ -20,4 +20,4 @@ function noDirtyCheck() {
}
};
}
-angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck);
\ No newline at end of file
+angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/showvalidationonsubmit.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/showvalidationonsubmit.directive.js
index 41823bb2fd..c46a3a9f9a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/showvalidationonsubmit.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/showvalidationonsubmit.directive.js
@@ -8,7 +8,14 @@
link: function (scope, element, attr, ctrl) {
- element.hide();
+ //We can either get the form submitted status by the parent directive valFormManager (if we add a property to it)
+ //or we can just check upwards in the DOM for the css class (easier for now).
+ //The initial hidden state can't always be hidden because when we switch variants in the content editor we cannot
+ //reset the status.
+ var submitted = element.closest(".show-validation").length > 0;
+ if (!submitted) {
+ element.hide();
+ }
var unsubscribe = [];
@@ -20,6 +27,7 @@
element.hide();
}));
+ //no isolate scope to listen to element destroy
element.bind('$destroy', function () {
for (var u in unsubscribe) {
unsubscribe[u]();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/umbsetdirtyonchange.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/umbsetdirtyonchange.directive.js
index 86cb3614dd..c13680a037 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/umbsetdirtyonchange.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/umbsetdirtyonchange.directive.js
@@ -3,21 +3,27 @@
function SetDirtyOnChange() {
- function link(scope, el, attr, ctrl) {
-
- if(attr.ngModel) {
- scope.$watch(attr.ngModel, function(newValue, oldValue) {
- if (!newValue) {return;}
- if (newValue === oldValue) {return;}
- ctrl.$setDirty();
- }, true);
+ function link(scope, el, attr, ctrls) {
+ var formCtrl = ctrls[0];
+
+ if (ctrls.length > 1) {
+ //if an ngModel is supplied, assign a render function which is called when the model is changed
+ var modelCtrl = ctrls[1];
+ var oldRender = modelCtrl.$render;
+ modelCtrl.$render = function () {
+ formCtrl.$setDirty();
+ //call any previously set render method
+ if (oldRender) {
+ oldRender();
+ }
+ };
} else {
var initValue = attr.umbSetDirtyOnChange;
attr.$observe("umbSetDirtyOnChange", function (newValue) {
if(newValue !== initValue) {
- ctrl.$setDirty();
+ formCtrl.$setDirty();
}
});
}
@@ -25,7 +31,7 @@
}
var directive = {
- require: "^^form",
+ require: ["^^form", "?ngModel"],
restrict: 'A',
link: link
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js
index 194565476d..726103caf9 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js
@@ -12,7 +12,7 @@
* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will
* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly.
**/
-function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams) {
+function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams, navigationService) {
var SHOW_VALIDATION_CLASS_NAME = "show-validation";
var SAVING_EVENT_NAME = "formSubmitting";
@@ -111,22 +111,27 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
return;
}
- var path = nextLocation.split("#")[1];
- if (path) {
- if (path.indexOf("%253") || path.indexOf("%252")) {
- path = decodeURIComponent(path);
+ var nextPath = nextLocation.split("#")[1];
+
+ if (nextPath) {
+
+ if (navigationService.isRouteChangingNavigation(currentLocation, nextLocation)) {
+ if (!notificationsService.hasView()) {
+
+ if (nextPath.indexOf("%253") || nextPath.indexOf("%252")) {
+ nextPath = decodeURIComponent(nextPath);
+ }
+
+ var msg = { view: "confirmroutechange", args: { path: nextPath, listener: locationEvent } };
+ notificationsService.add(msg);
+ }
+
+ //prevent the route!
+ event.preventDefault();
+
+ //raise an event
+ eventsService.emit("valFormManager.pendingChanges", true);
}
-
- if (!notificationsService.hasView()) {
- var msg = { view: "confirmroutechange", args: { path: path, listener: locationEvent } };
- notificationsService.add(msg);
- }
-
- //prevent the route!
- event.preventDefault();
-
- //raise an event
- eventsService.emit("valFormManager.pendingChanges", true);
}
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
index 71c59860df..5568b4b276 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
@@ -12,37 +12,38 @@
function valPropertyMsg(serverValidationManager) {
return {
- scope: {
- property: "="
- },
- require: ['^^form', '^^valFormManager'],
+ require: ['^^form', '^^valFormManager', '^^umbProperty'],
replace: true,
restrict: "E",
template: "
{{errorMsg}}
",
-
+ scope: {},
link: function (scope, element, attrs, ctrl) {
//the property form controller api
var formCtrl = ctrl[0];
-
//the valFormManager controller api
var valFormManager = ctrl[1];
+ //the property controller api
+ var umbPropCtrl = ctrl[2];
+
+ scope.currentProperty = umbPropCtrl.property;
+ var currentCulture = scope.currentProperty.culture;
var watcher = null;
// Gets the error message to display
function getErrorMsg() {
//this can be null if no property was assigned
- if (scope.property) {
+ if (scope.currentProperty) {
//first try to get the error msg from the server collection
- var err = serverValidationManager.getPropertyError(scope.property.alias, "");
+ var err = serverValidationManager.getPropertyError(scope.currentProperty.alias, null, "");
//if there's an error message use it
if (err && err.errorMsg) {
return err.errorMsg;
}
else {
//TODO: localize
- return scope.property.propertyErrorMessage ? scope.property.propertyErrorMessage : "Property has errors";
+ return scope.currentProperty.propertyErrorMessage ? scope.currentProperty.propertyErrorMessage : "Property has errors";
}
}
@@ -59,29 +60,30 @@ function valPropertyMsg(serverValidationManager) {
function startWatch() {
//if there's not already a watch
if (!watcher) {
- watcher = scope.$watch("property.value", function (newValue, oldValue) {
-
- if (!newValue || angular.equals(newValue, oldValue)) {
- return;
- }
+ watcher = scope.$watch("currentProperty.value",
+ function (newValue, oldValue) {
- var errCount = 0;
- for (var e in formCtrl.$error) {
- if (angular.isArray(formCtrl.$error[e])) {
- errCount++;
+ if (!newValue || angular.equals(newValue, oldValue)) {
+ return;
}
- }
- //we are explicitly checking for valServer errors here, since we shouldn't auto clear
- // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
- // is the only one, then we'll clear.
+ var errCount = 0;
+ for (var e in formCtrl.$error) {
+ if (angular.isArray(formCtrl.$error[e])) {
+ errCount++;
+ }
+ }
- if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
- scope.errorMsg = "";
- formCtrl.$setValidity('valPropertyMsg', true);
- stopWatch();
- }
- }, true);
+ //we are explicitly checking for valServer errors here, since we shouldn't auto clear
+ // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
+ // is the only one, then we'll clear.
+
+ if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
+ scope.errorMsg = "";
+ formCtrl.$setValidity('valPropertyMsg', true);
+ stopWatch();
+ }
+ }, true);
}
}
@@ -93,19 +95,7 @@ function valPropertyMsg(serverValidationManager) {
}
}
- //if there's any remaining errors in the server validation service then we should show them.
- var showValidation = serverValidationManager.items.length > 0;
- var hasError = false;
-
- //create properties on our custom scope so we can use it in our template
- scope.errorMsg = "";
-
- var unsubscribe = [];
-
- //listen for form validation changes.
- //The alternative is to add a watch to formCtrl.$invalid but that would lead to many more watches then
- // subscribing to this single watch.
- valFormManager.onValidationStatusChanged(function (evt, args) {
+ function checkValidationStatus() {
if (formCtrl.$invalid) {
//first we need to check if the valPropertyMsg validity is invalid
if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) {
@@ -130,10 +120,35 @@ function valPropertyMsg(serverValidationManager) {
hasError = false;
scope.errorMsg = "";
}
+ }
+
+ //if there's any remaining errors in the server validation service then we should show them.
+ var showValidation = serverValidationManager.items.length > 0;
+ if (!showValidation) {
+ //We can either get the form submitted status by the parent directive valFormManager (if we add a property to it)
+ //or we can just check upwards in the DOM for the css class (easier for now).
+ //The initial hidden state can't always be hidden because when we switch variants in the content editor we cannot
+ //reset the status.
+ showValidation = element.closest(".show-validation").length > 0;
+ }
+
+
+ var hasError = false;
+
+ //create properties on our custom scope so we can use it in our template
+ scope.errorMsg = "";
+
+ var unsubscribe = [];
+
+ //listen for form validation changes.
+ //The alternative is to add a watch to formCtrl.$invalid but that would lead to many more watches then
+ // subscribing to this single watch.
+ valFormManager.onValidationStatusChanged(function (evt, args) {
+ checkValidationStatus();
});
//listen for the forms saving event
- unsubscribe.push(scope.$on("formSubmitting", function(ev, args) {
+ unsubscribe.push(scope.$on("formSubmitting", function (ev, args) {
showValidation = true;
if (hasError && scope.errorMsg === "") {
scope.errorMsg = getErrorMsg();
@@ -145,7 +160,7 @@ function valPropertyMsg(serverValidationManager) {
}));
//listen for the forms saved event
- unsubscribe.push(scope.$on("formSubmitted", function(ev, args) {
+ unsubscribe.push(scope.$on("formSubmitted", function (ev, args) {
showValidation = false;
scope.errorMsg = "";
formCtrl.$setValidity('valPropertyMsg', true);
@@ -160,35 +175,32 @@ function valPropertyMsg(serverValidationManager) {
// indicate that a content property is invalid at the property level since developers may not actually implement
// the correct field validation in their property editors.
- if (scope.property) { //this can be null if no property was assigned
- serverValidationManager.subscribe(scope.property.alias, "", function (isValid, propertyErrors, allErrors) {
- hasError = !isValid;
- if (hasError) {
- //set the error message to the server message
- scope.errorMsg = propertyErrors[0].errorMsg;
- //flag that the current validator is invalid
- formCtrl.$setValidity('valPropertyMsg', false);
- startWatch();
- }
- else {
- scope.errorMsg = "";
- //flag that the current validator is valid
- formCtrl.$setValidity('valPropertyMsg', true);
- stopWatch();
- }
- });
+ if (scope.currentProperty) { //this can be null if no property was assigned
+ unsubscribe.push(serverValidationManager.subscribe(scope.currentProperty.alias,
+ currentCulture,
+ "",
+ function(isValid, propertyErrors, allErrors) {
+ hasError = !isValid;
+ if (hasError) {
+ //set the error message to the server message
+ scope.errorMsg = propertyErrors[0].errorMsg;
+ //flag that the current validator is invalid
+ formCtrl.$setValidity('valPropertyMsg', false);
+ startWatch();
+ }
+ else {
+ scope.errorMsg = "";
+ //flag that the current validator is valid
+ formCtrl.$setValidity('valPropertyMsg', true);
+ stopWatch();
+ }
+ }));
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
- stopWatch();
- serverValidationManager.unsubscribe(scope.property.alias, "");
- });
}
//when the scope is disposed we need to unsubscribe
scope.$on('$destroy', function () {
+ stopWatch();
for (var u in unsubscribe) {
unsubscribe[u]();
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js
index 53a1ea67b2..c1a2999017 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js
@@ -33,11 +33,9 @@ function valPropertyValidator(serverValidationManager) {
if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) {
throw new Error('val-property-validator directive must specify a function to call');
}
-
- var initResult = scope.valPropertyValidator();
-
+
// Validation method
- var validate = function (viewValue) {
+ function validate (viewValue) {
// Calls the validition method
var result = scope.valPropertyValidator();
if (!result.errorKey || result.isValid === undefined || !result.errorMsg) {
@@ -62,6 +60,9 @@ function valPropertyValidator(serverValidationManager) {
// Parsers are called as soon as the value in the form input is modified
modelCtrl.$parsers.push(validate);
+ //call on init
+ validate();
+
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js
index 6406583e77..123962c32c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js
@@ -14,8 +14,7 @@ function valRegex() {
var flags = "";
var regex;
- var eventBindings = [];
-
+
attrs.$observe("valRegexFlags", function (newVal) {
if (newVal) {
flags = newVal;
@@ -39,11 +38,15 @@ function valRegex() {
}
});
- eventBindings.push(scope.$watch('ngModel', function(newValue, oldValue){
- if(newValue && newValue !== oldValue) {
- patternValidator(newValue);
+ //An ngModel is supplied, assign a render function which is called when the model is changed
+ var oldRender = ctrl.$render;
+ ctrl.$render = function () {
+ patternValidator(ctrl.$viewValue);
+ //call any previously set render method
+ if (oldRender) {
+ oldRender();
}
- }));
+ };
var patternValidator = function (viewValue) {
if (regex) {
@@ -65,13 +68,6 @@ function valRegex() {
}
};
- scope.$on('$destroy', function(){
- // unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
- });
-
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js
index 1432a713c0..eb522fe783 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js
@@ -7,8 +7,9 @@
**/
function valServer(serverValidationManager) {
return {
- require: ['ngModel', '?^umbProperty'],
+ require: ['ngModel', '?^^umbProperty'],
restrict: "A",
+ scope: {},
link: function (scope, element, attr, ctrls) {
var modelCtrl = ctrls[0];
@@ -18,7 +19,20 @@ function valServer(serverValidationManager) {
return;
}
+ var currentProperty = umbPropCtrl.property;
+ var currentCulture = currentProperty.culture;
var watcher = null;
+ var unsubscribe = [];
+
+ //default to 'value' if nothing is set
+ var fieldName = "value";
+ if (attr.valServer) {
+ fieldName = scope.$eval(attr.valServer);
+ if (!fieldName) {
+ //eval returned nothing so just use the string
+ fieldName = attr.valServer;
+ }
+ }
//Need to watch the value model for it to change, previously we had subscribed to
//modelCtrl.$viewChangeListeners but this is not good enough if you have an editor that
@@ -40,6 +54,8 @@ function valServer(serverValidationManager) {
if (modelCtrl.$invalid) {
modelCtrl.$setValidity('valServer', true);
+ //clear the server validation entry
+ serverValidationManager.removePropertyError(currentProperty.alias, currentCulture, fieldName);
stopWatch();
}
}, true);
@@ -52,43 +68,33 @@ function valServer(serverValidationManager) {
watcher = null;
}
}
-
- var currentProperty = umbPropCtrl.property;
-
- //default to 'value' if nothing is set
- var fieldName = "value";
- if (attr.valServer) {
- fieldName = scope.$eval(attr.valServer);
- if (!fieldName) {
- //eval returned nothing so just use the string
- fieldName = attr.valServer;
- }
- }
//subscribe to the server validation changes
- serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) {
- if (!isValid) {
- modelCtrl.$setValidity('valServer', false);
- //assign an error msg property to the current validator
- modelCtrl.errorMsg = propertyErrors[0].errorMsg;
- startWatch();
- }
- else {
- modelCtrl.$setValidity('valServer', true);
- //reset the error message
- modelCtrl.errorMsg = "";
- stopWatch();
- }
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
+ unsubscribe.push(serverValidationManager.subscribe(currentProperty.alias,
+ currentCulture,
+ fieldName,
+ function(isValid, propertyErrors, allErrors) {
+ if (!isValid) {
+ modelCtrl.$setValidity('valServer', false);
+ //assign an error msg property to the current validator
+ modelCtrl.errorMsg = propertyErrors[0].errorMsg;
+ startWatch();
+ }
+ else {
+ modelCtrl.$setValidity('valServer', true);
+ //reset the error message
+ modelCtrl.errorMsg = "";
+ stopWatch();
+ }
+ }));
+
+ scope.$on('$destroy', function () {
stopWatch();
- serverValidationManager.unsubscribe(currentProperty.alias, fieldName);
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
});
}
};
}
-angular.module('umbraco.directives.validation').directive("valServer", valServer);
\ No newline at end of file
+angular.module('umbraco.directives.validation').directive("valServer", valServer);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js
index 29fd94ff5b..7f5427da8b 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js
@@ -10,10 +10,11 @@ function valServerField(serverValidationManager) {
return {
require: 'ngModel',
restrict: "A",
+ scope: {},
link: function (scope, element, attr, ngModel) {
var fieldName = null;
- var eventBindings = [];
+ var unsubscribe = [];
attr.$observe("valServerField", function (newVal) {
if (newVal && fieldName === null) {
@@ -24,7 +25,7 @@ function valServerField(serverValidationManager) {
// resubmitted. So once a field is changed that has a server error assigned to it
// we need to re-validate it for the server side validator so the user can resubmit
// the form. Of course normal client-side validators will continue to execute.
- eventBindings.push(scope.$watch(function() {
+ unsubscribe.push(scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue){
if (ngModel.$invalid) {
@@ -33,32 +34,28 @@ function valServerField(serverValidationManager) {
}));
//subscribe to the server validation changes
- serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
- if (!isValid) {
- ngModel.$setValidity('valServerField', false);
- //assign an error msg property to the current validator
- ngModel.errorMsg = fieldErrors[0].errorMsg;
- }
- else {
- ngModel.$setValidity('valServerField', true);
- //reset the error message
- ngModel.errorMsg = "";
- }
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
- serverValidationManager.unsubscribe(null, fieldName);
- });
+ unsubscribe.push(serverValidationManager.subscribe(null,
+ null,
+ fieldName,
+ function(isValid, fieldErrors, allErrors) {
+ if (!isValid) {
+ ngModel.$setValidity('valServerField', false);
+ //assign an error msg property to the current validator
+ ngModel.errorMsg = fieldErrors[0].errorMsg;
+ }
+ else {
+ ngModel.$setValidity('valServerField', true);
+ //reset the error message
+ ngModel.errorMsg = "";
+ }
+ }));
}
});
scope.$on('$destroy', function(){
// unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
+ for(var e in unsubscribe) {
+ unsubscribe[e]();
}
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
index ca571d3954..a65a08d17e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
@@ -18,7 +18,7 @@
}
var valFormManager = ctrl[1];
- scope.subView.hasError = false;
+ scope.model.hasError = false;
//listen for form validation changes
valFormManager.onValidationStatusChanged(function (evt, args) {
@@ -27,14 +27,14 @@
var subViewContent = el.find(".ng-invalid");
if (subViewContent.length > 0) {
- scope.subView.hasError = true;
+ scope.model.hasError = true;
} else {
- scope.subView.hasError = false;
+ scope.model.hasError = false;
}
}
else {
- scope.subView.hasError = false;
+ scope.model.hasError = false;
}
});
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 221e71eb8c..b636a0a51c 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
@@ -48,11 +48,14 @@
//exit/ignore
return $q.reject(rejection);
}
- var filtered = _.find(requestInterceptorFilter(), function (val) {
- return config.url.indexOf(val) > 0;
- });
- if (filtered) {
- return $q.reject(rejection);
+
+ if (config.url) {
+ var filtered = _.find(requestInterceptorFilter(), function (val) {
+ return config.url.indexOf(val) > 0;
+ });
+ if (filtered) {
+ return $q.reject(rejection);
+ }
}
//A 401 means that the user is not logged in
diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js
index a23fc86210..3b58c127a7 100644
--- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js
+++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js
@@ -52,6 +52,20 @@ angular.module('umbraco.mocks').
{ alias: "umbracoFile", label: "File", description:"Some file", view: "rte", value: "/media/1234/random.jpg" }
]
}
+ ],
+ apps: [
+ {
+ alias: "content",
+ name: "Content",
+ icon: "icon-document",
+ view: "views/media/apps/content/content.html"
+ },
+ {
+ alias: "info",
+ name: "Info",
+ icon: "icon-info",
+ view: "views/media/apps/info/info.html"
+ }
]
};
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 651c54bfeb..615a7aeec9 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
@@ -367,7 +367,7 @@ angular.module('umbraco.mocks').
"installer_databaseHeader": "Database configuration",
"installer_databaseInstall": " Press the install button to install the Umbraco %0% database ",
"installer_databaseInstallDone": "Umbraco %0% has now been copied to your database. Press Next to proceed.",
- "installer_databaseNotFound": "
Database not found! Please check that the information in the 'connection string' of the \"web.config\" file is correct.
To proceed, please edit the 'web.config' file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named 'UmbracoDbDSN' and save the file.
Database not found! Please check that the information in the 'connection string' of the \"web.config\" file is correct.
To proceed, please edit the 'web.config' file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named 'UmbracoDbDSN' and save the file.
",
"installer_databaseText": "To complete this step, you must know some information regarding your database server ('connection string'). Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.",
"installer_databaseUpgrade": "
Press the upgrade button to upgrade your database to Umbraco %0%
Don't worry - no content will be deleted and everything will continue working afterwards!
",
"installer_databaseUpgradeDone": "Your database has been upgraded to the final version %0%. Press Next to proceed. ",
@@ -395,7 +395,7 @@ angular.module('umbraco.mocks').
"installer_permissionsSettingUpPermissions": "Setting up folder permissions",
"installer_permissionsText": " Umbraco needs write/modify access to certain directories in order to store files like pictures and PDF's. It also stores temporary data (aka: cache) for enhancing the performance of your website. ",
"installer_runwayFromScratch": "I want to start from scratch",
- "installer_runwayFromScratchText": " Your website is completely empty at the moment, so that's perfect if you want to start from scratch and create your own document types and templates. (learn how) You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ",
+ "installer_runwayFromScratchText": " Your website is completely empty at the moment, so that's perfect if you want to start from scratch and create your own document types and templates. (learn how) You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ",
"installer_runwayHeader": "You've just set up a clean Umbraco platform. What do you want to do next?",
"installer_runwayInstalled": "Runway is installed",
"installer_runwayInstalledText": " You have the foundation in place. Select what modules you wish to install on top of it. This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules ",
@@ -432,7 +432,7 @@ angular.module('umbraco.mocks').
"login_greeting6": "Happy friendly Friday",
"login_greeting7": "Happy shiny Saturday",
"login_instruction": "Log in below:",
- "login_bottomText": "
",
"main_dashboard": "Dashboard",
"main_sections": "Sections",
"main_tree": "Content",
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 67b50dddcb..5fb6fe1625 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
@@ -157,7 +157,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to move content');
},
@@ -198,7 +198,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostCopy"),
- args),
+ args, { responseType: 'text' }),
'Failed to copy content');
},
@@ -330,26 +330,55 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the content item.
*
*/
- getById: function (id, culture) {
+ getById: function (id) {
return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "GetById",
- { id: id, culture: culture })),
- 'Failed to retrieve data for content id ' + id);
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetById",
+ { id: id })),
+ 'Failed to retrieve data for content id ' + id)
+ .then(function(result) {
+ return $q.when(umbDataFormatter.formatContentGetData(result));
+ });
},
getBlueprintById: function (id) {
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetBlueprintById",
+ [{ id: id }])),
+ 'Failed to retrieve data for content id ' + id)
+ .then(function(result) {
+ return $q.when(umbDataFormatter.formatContentGetData(result));
+ });
+ },
+
+ getNotifySettingsById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
- "GetBlueprintById",
- [{ id: id }])),
+ "GetNotificationOptions",
+ [{ contentId: id }])),
'Failed to retrieve data for content id ' + id);
},
+ setNotifySettingsById: function (id, options) {
+ if (!id) {
+ throw "contentId cannot be null";
+ }
+ return umbRequestHelper.resourcePromise(
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "PostNotificationOptions",
+ { contentId: id, notifyOptions: options })),
+ 'Failed to set notify settings for content id ' + id);
+ },
+
/**
* @ngdoc method
* @name umbraco.resources.contentResource#getByIds
@@ -379,12 +408,19 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
});
return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "GetByIds",
- idQuery)),
- 'Failed to retrieve data for content with multiple ids');
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetByIds",
+ idQuery)),
+ 'Failed to retrieve data for content with multiple ids')
+ .then(function (result) {
+ //each item needs to be re-formatted
+ _.each(result, function(r) {
+ umbDataFormatter.formatContentGetData(r)
+ });
+ return $q.when(result);
+ });
},
@@ -423,23 +459,29 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
getScaffold: function (parentId, alias) {
return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "GetEmpty",
- [{ contentTypeAlias: alias }, { parentId: parentId }])),
- 'Failed to retrieve data for empty content item type ' + alias);
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetEmpty",
+ [{ contentTypeAlias: alias }, { parentId: parentId }])),
+ 'Failed to retrieve data for empty content item type ' + alias)
+ .then(function(result) {
+ return $q.when(umbDataFormatter.formatContentGetData(result));
+ });
},
getBlueprintScaffold: function (parentId, blueprintId) {
return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "GetEmpty",
- [{ blueprintId: blueprintId }, { parentId: parentId }])),
- 'Failed to retrieve blueprint for id ' + blueprintId);
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetEmpty",
+ [{ blueprintId: blueprintId }, { parentId: parentId }])),
+ 'Failed to retrieve blueprint for id ' + blueprintId)
+ .then(function(result) {
+ return $q.when(umbDataFormatter.formatContentGetData(result));
+ });
},
/**
@@ -467,7 +509,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
- "GetNiceUrl", [{ id: id }])),
+ "GetNiceUrl", { id: id }),
+ { responseType: 'text' }),
'Failed to retrieve url for id:' + id);
},
@@ -556,38 +599,6 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
'Failed to retrieve children for content item ' + parentId);
},
- /**
- * @ngdoc method
- * @name umbraco.resources.contentResource#hasPermission
- * @methodOf umbraco.resources.contentResource
- *
- * @description
- * Returns true/false given a permission char to check against a nodeID
- * for the current user
- *
- * ##usage
- *
- * contentResource.hasPermission('p',1234)
- * .then(function() {
- * alert('You are allowed to publish this item');
- * });
- *
- *
- * @param {String} permission char representing the permission to check
- * @param {Int} id id of content item to delete
- * @returns {Promise} resourcePromise object.
- *
- */
- checkPermission: function (permission, id) {
- return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "HasPermission",
- [{ permissionToCheck: permission }, { nodeId: id }])),
- 'Failed to check permission for item ' + id);
- },
-
getDetailedPermissions: function (contentId) {
return umbRequestHelper.resourcePromise(
$http.get(
@@ -597,16 +608,6 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
'Failed to retrieve permissions for content item ' + contentId);
},
- getPermissions: function (nodeIds) {
- return umbRequestHelper.resourcePromise(
- $http.post(
- umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "GetPermissions"),
- nodeIds),
- 'Failed to get permissions');
- },
-
/**
* @ngdoc method
* @name umbraco.resources.contentResource#save
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
index cec9cf191e..56dff12985 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
@@ -266,7 +266,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to move content');
},
@@ -286,14 +286,14 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to copy content');
},
createContainer: function (parentId, name) {
return umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateContainer", { parentId: parentId, name: name })),
+ $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateContainer", { parentId: parentId, name: encodeURIComponent(name) })),
'Failed to create a folder under parent id ' + parentId);
},
@@ -333,6 +333,17 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca
notificationsService.error(value);
});
});
+ },
+
+ import: function (file) {
+ if (!file) {
+ throw "file cannot be null";
+ }
+
+ return umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "Import", { file: file })),
+ "Failed to import document type " + file
+ );
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js
index 32e9683dd8..e10837ceca 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js
@@ -10,6 +10,48 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) {
//the factory object returned
return {
+ getPermissions: function (nodeIds) {
+ return umbRequestHelper.resourcePromise(
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "currentUserApiBaseUrl",
+ "GetPermissions"),
+ nodeIds),
+ 'Failed to get permissions');
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.resources.currentUserResource#hasPermission
+ * @methodOf umbraco.resources.currentUserResource
+ *
+ * @description
+ * Returns true/false given a permission char to check against a nodeID
+ * for the current user
+ *
+ * ##usage
+ *
+ * contentResource.hasPermission('p',1234)
+ * .then(function() {
+ * alert('You are allowed to publish this item');
+ * });
+ *
+ *
+ * @param {String} permission char representing the permission to check
+ * @param {Int} id id of content item to delete
+ * @returns {Promise} resourcePromise object.
+ *
+ */
+ checkPermission: function (permission, id) {
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "currentUserApiBaseUrl",
+ "HasPermission",
+ [{ permissionToCheck: permission }, { nodeId: id }])),
+ 'Failed to check permission for item ' + id);
+ },
+
saveTourStatus: function (tourStatus) {
if (!tourStatus) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
index 7c18e91364..7c4daedd38 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
@@ -344,7 +344,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to move content');
},
@@ -355,7 +355,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"PostCreateContainer",
- { parentId: parentId, name: name })),
+ { parentId: parentId, name: encodeURIComponent(name) })),
'Failed to create a folder under parent id ' + parentId);
},
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 e3af5124f8..6647c6fb7f 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
@@ -46,7 +46,7 @@ function entityResource($q, $http, umbRequestHelper) {
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
- "GetSafeAlias", { value: value, camelCase: camelCase })),
+ "GetSafeAlias", { value: encodeURIComponent(value), camelCase: camelCase })),
'Failed to retrieve content type scaffold');
},
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 74c2718bad..5d2ac1e8b9 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
@@ -112,7 +112,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
{
parentId: args.parentId,
id: args.id
- }),
+ }, {responseType: 'text'}),
'Failed to move media');
},
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js
index a98398f3ab..8dc64e4cac 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js
@@ -213,7 +213,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to move content');
},
@@ -233,7 +233,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
{
parentId: args.parentId,
id: args.id
- }),
+ }, { responseType: 'text' }),
'Failed to copy content');
},
@@ -244,7 +244,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl",
"PostCreateContainer",
- { parentId: parentId, name: name })),
+ { parentId: parentId, name: encodeURIComponent(name) })),
'Failed to create a folder under parent id ' + parentId);
},
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/nestedcontent.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/nestedcontent.resource.js
index b3488fdff4..4f71c1d1b4 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/nestedcontent.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/nestedcontent.resource.js
@@ -7,6 +7,6 @@
$http.get(url),
'Failed to retrieve content types'
);
- },
+ }
};
});
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
index d04cfb1911..34dde7eb4b 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
@@ -9,6 +9,20 @@
function angularHelper($log, $q) {
return {
+ /**
+ * Method used to re-run the $parsers for a given ngModel
+ * @param {} scope
+ * @param {} ngModel
+ * @returns {}
+ */
+ revalidateNgModel: function (scope, ngModel) {
+ this.safeApply(scope, function() {
+ angular.forEach(ngModel.$parsers, function (parser) {
+ parser(ngModel.$viewValue);
+ });
+ });
+ },
+
/**
* Execute a list of promises sequentially. Unlike $q.all which executes all promises at once, this will execute them in sequence.
* @param {} promises
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js
index 2369af54b5..085ba52b7e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js
@@ -329,7 +329,7 @@ angular.module('umbraco.services').factory("editorState", function() {
},
set: function (value) {
throw "Use editorState.set to set the value of the current entity";
- },
+ }
});
return state;
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
index 8c1e42b0b6..16d6cc20c9 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
@@ -7,19 +7,25 @@
**/
function contentEditingHelper(fileManager, $q, $location, $routeParams, notificationsService, navigationService, localizationService, serverValidationManager, dialogService, formHelper, appState) {
- function isValidIdentifier(id){
+ function isValidIdentifier(id) {
+
//empty id <= 0
- if(angular.isNumber(id) && id > 0){
- return true;
+ if (angular.isNumber(id)) {
+ if (id === 0) {
+ return false;
+ }
+ if (id > 0) {
+ return true;
+ }
}
//empty guid
- if(id === "00000000-0000-0000-0000-000000000000"){
+ if (id === "00000000-0000-0000-0000-000000000000") {
return false;
}
//empty string / alias
- if(id === ""){
+ if (id === "") {
return false;
}
@@ -44,6 +50,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
if (!args.saveMethod) {
throw "args.saveMethod is not defined";
}
+ if (args.showNotifications === undefined) {
+ args.showNotifications = true;
+ }
var redirectOnSuccess = args.redirectOnSuccess !== undefined ? args.redirectOnSuccess : true;
var redirectOnFailure = args.redirectOnFailure !== undefined ? args.redirectOnFailure : true;
@@ -66,7 +75,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
scope: args.scope,
savedContent: data,
redirectOnSuccess: redirectOnSuccess,
- rebindCallback: function() {
+ rebindCallback: function () {
rebindCallback.apply(self, [args.content, data]);
}
});
@@ -76,13 +85,14 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
}, function (err) {
self.handleSaveError({
+ showNotifications: args.showNotifications,
redirectOnFailure: redirectOnFailure,
err: err,
- rebindCallback: function() {
+ rebindCallback: function () {
rebindCallback.apply(self, [args.content, err.data]);
}
});
-
+
args.scope.busy = false;
return $q.reject(err);
});
@@ -90,9 +100,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
else {
return $q.reject();
}
-
+
},
-
+
/** Used by the content editor and media editor to add an info tab to the tabs array (normally known as the properties tab) */
addInfoTab: function (tabs) {
@@ -165,7 +175,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
handler: args.methods.sendToPublish,
hotKey: "ctrl+p",
hotKeyWhenHidden: true,
- alias: "sendToPublish"
+ alias: "sendToPublish"
};
case "A":
//save
@@ -175,7 +185,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
handler: args.methods.save,
hotKey: "ctrl+s",
hotKeyWhenHidden: true,
- alias: "save"
+ alias: "save"
};
case "Z":
//unpublish
@@ -185,7 +195,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
handler: args.methods.unPublish,
hotKey: "ctrl+u",
hotKeyWhenHidden: true,
- alias: "unpublish"
+ alias: "unpublish"
};
default:
return null;
@@ -247,15 +257,15 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
// If we have a scheduled publish or unpublish date change the default button to
// "save" and update the label to "save and schedule
- if(args.content.releaseDate || args.content.removeDate) {
+ if (args.content.releaseDate || args.content.removeDate) {
// if save button is alread the default don't change it just update the label
if (buttons.defaultButton && buttons.defaultButton.letter === "A") {
buttons.defaultButton.labelKey = "buttons_saveAndSchedule";
return;
}
-
- if(buttons.defaultButton && buttons.subButtons && buttons.subButtons.length > 0) {
+
+ if (buttons.defaultButton && buttons.subButtons && buttons.subButtons.length > 0) {
// save a copy of the default so we can push it to the sub buttons later
var defaultButtonCopy = angular.copy(buttons.defaultButton);
var newSubButtons = [];
@@ -289,7 +299,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
* @function
*
* @description
- * Returns all propertes contained for the content item (since the normal model has properties contained inside of tabs)
+ * Returns all propertes contained for the tabbed content item
*/
getAllProps: function (content) {
var allProps = [];
@@ -313,66 +323,66 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
* @description
* Returns a letter array for buttons, with the primary one first based on content model, permissions and editor state
*/
- getAllowedActions : function(content, creating){
+ getAllowedActions: function (content, creating) {
- //This is the ideal button order but depends on circumstance, we'll use this array to create the button list
- // Publish, SendToPublish, Save
- var actionOrder = ["U", "H", "A"];
- var defaultActions;
- var actions = [];
+ //This is the ideal button order but depends on circumstance, we'll use this array to create the button list
+ // Publish, SendToPublish, Save
+ var actionOrder = ["U", "H", "A"];
+ var defaultActions;
+ var actions = [];
- //Create the first button (primary button)
- //We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item.
- if (!creating || _.contains(content.allowedActions, "C")) {
- for (var b in actionOrder) {
- if (_.contains(content.allowedActions, actionOrder[b])) {
- defaultAction = actionOrder[b];
- break;
- }
+ //Create the first button (primary button)
+ //We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item.
+ if (!creating || _.contains(content.allowedActions, "C")) {
+ for (var b in actionOrder) {
+ if (_.contains(content.allowedActions, actionOrder[b])) {
+ defaultAction = actionOrder[b];
+ break;
+ }
+ }
+ }
+
+ actions.push(defaultAction);
+
+ //Now we need to make the drop down button list, this is also slightly tricky because:
+ //We cannot have any buttons if there's no default button above.
+ //We cannot have the unpublish button (Z) when there's no publish permission.
+ //We cannot have the unpublish button (Z) when the item is not published.
+ if (defaultAction) {
+ //get the last index of the button order
+ var lastIndex = _.indexOf(actionOrder, defaultAction);
+
+ //add the remaining
+ for (var i = lastIndex + 1; i < actionOrder.length; i++) {
+ if (_.contains(content.allowedActions, actionOrder[i])) {
+ actions.push(actionOrder[i]);
}
}
- actions.push(defaultAction);
-
- //Now we need to make the drop down button list, this is also slightly tricky because:
- //We cannot have any buttons if there's no default button above.
- //We cannot have the unpublish button (Z) when there's no publish permission.
- //We cannot have the unpublish button (Z) when the item is not published.
- if (defaultAction) {
- //get the last index of the button order
- var lastIndex = _.indexOf(actionOrder, defaultAction);
-
- //add the remaining
- for (var i = lastIndex + 1; i < actionOrder.length; i++) {
- if (_.contains(content.allowedActions, actionOrder[i])) {
- actions.push(actionOrder[i]);
- }
- }
-
- //if we are not creating, then we should add unpublish too,
- // so long as it's already published and if the user has access to publish
- if (!creating) {
- if (content.publishDate && _.contains(content.allowedActions,"U")) {
- actions.push("Z");
- }
+ //if we are not creating, then we should add unpublish too,
+ // so long as it's already published and if the user has access to publish
+ if (!creating) {
+ if (content.publishDate && _.contains(content.allowedActions, "U")) {
+ actions.push("Z");
}
}
+ }
- return actions;
- },
+ return actions;
+ },
- /**
- * @ngdoc method
- * @name umbraco.services.contentEditingHelper#getButtonFromAction
- * @methodOf umbraco.services.contentEditingHelper
- * @function
- *
- * @description
- * Returns a button object to render a button for the tabbed editor
- * currently only returns built in system buttons for content and media actions
- * returns label, alias, action char and hot-key
- */
- getButtonFromAction : function(ch){
+ /**
+ * @ngdoc method
+ * @name umbraco.services.contentEditingHelper#getButtonFromAction
+ * @methodOf umbraco.services.contentEditingHelper
+ * @function
+ *
+ * @description
+ * Returns a button object to render a button for the tabbed editor
+ * currently only returns built in system buttons for content and media actions
+ * returns label, alias, action char and hot-key
+ */
+ getButtonFromAction: function (ch) {
switch (ch) {
case "U":
return {
@@ -407,7 +417,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return null;
}
- },
+ },
+
/**
* @ngdoc method
* @name umbraco.services.contentEditingHelper#reBindChangedProperties
@@ -415,26 +426,19 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
* @function
*
* @description
- * re-binds all changed property values to the origContent object from the savedContent object and returns an array of changed properties.
+ * Re-binds all changed property values to the origContent object from the savedContent object and returns an array of changed properties.
+ * This re-binds both normal object property values along with content property values and works for content, media and members.
+ * For variant content, this detects if the object contains the 'variants' property (i.e. for content) and re-binds all variant content properties.
+ * This returns the list of changed content properties (does not include standard object property changes).
*/
reBindChangedProperties: function (origContent, savedContent) {
- var changed = [];
-
- //get a list of properties since they are contained in tabs
- var allOrigProps = this.getAllProps(origContent);
- var allNewProps = this.getAllProps(savedContent);
-
- function getNewProp(alias) {
- return _.find(allNewProps, function (item) {
- return item.alias === alias;
- });
- }
+ //TODO: We should probably split out this logic to deal with media/members seperately to content
//a method to ignore built-in prop changes
- var shouldIgnore = function(propName) {
+ var shouldIgnore = function (propName) {
return _.some([
- "tabs",
+ "variants",
"notifications",
"ModelState",
"tabs",
@@ -450,13 +454,14 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
"removeDateMonth",
"removeDateDayNumber",
"removeDateDay",
- "removeDateTime",
+ "removeDateTime"
], function (i) {
return i === propName;
});
};
- //check for changed built-in properties of the content
- for (var o in origContent) {
+
+ //check for changed built-in properties of the content based on the server response object
+ for (var o in savedContent) {
//ignore the ones listed in the array
if (shouldIgnore(o)) {
@@ -468,24 +473,95 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
}
}
- //check for changed properties of the content
- for (var p in allOrigProps) {
- var newProp = getNewProp(allOrigProps[p].alias);
- if (newProp && !_.isEqual(allOrigProps[p].value, newProp.value)) {
+ //Now re-bind content properties. Since content has variants and media/members doesn't,
+ //we'll detect the variants property for content to distinguish if it's content vs media/members.
- //they have changed so set the origContent prop to the new one
- var origVal = allOrigProps[p].value;
- allOrigProps[p].value = newProp.value;
+ var isContent = false;
- //instead of having a property editor $watch their expression to check if it has
- // been updated, instead we'll check for the existence of a special method on their model
- // and just call it.
- if (angular.isFunction(allOrigProps[p].onValueChanged)) {
- //send the newVal + oldVal
- allOrigProps[p].onValueChanged(allOrigProps[p].value, origVal);
+ var origVariants = [];
+ var savedVariants = [];
+ if (origContent.variants) {
+ isContent = true;
+ //it's contnet so assign the variants as they exist
+ origVariants = origContent.variants;
+ savedVariants = savedContent.variants;
+ }
+ else {
+ //it's media/member, so just add the object as-is to the variants collection
+ origVariants.push(origContent);
+ savedVariants.push(savedContent);
+ }
+
+ var changed = [];
+
+ function getNewProp(alias, allNewProps) {
+ return _.find(allNewProps, function (item) {
+ return item.alias === alias;
+ });
+ }
+
+ //loop through each variant (i.e. tabbed content)
+ for (var j = 0; j < origVariants.length; j++) {
+
+ var origVariant = origVariants[j];
+ var savedVariant = savedVariants[j];
+
+ //special case for content, don't sync this variant if it wasn't tagged
+ //for saving in the first place
+ if (!origVariant.save) {
+ continue;
+ }
+
+ //if it's content (not media/members), then we need to sync the variant specific data
+ if (origContent.variants) {
+
+ //the variant property names we need to sync
+ var variantPropertiesSync = ["state"];
+
+ //loop through the properties returned on the server object
+ for (var b in savedVariant) {
+
+ var shouldCompare = _.some(variantPropertiesSync, function (e) {
+ return e === b;
+ });
+
+ //only compare the explicit ones or ones we don't ignore
+ if (shouldCompare || !shouldIgnore(b)) {
+ if (!_.isEqual(origVariant[b], savedVariant[b])) {
+ origVariant[b] = savedVariant[b];
+ }
+ }
+ }
+ }
+
+ //get a list of properties since they are contained in tabs
+ var allOrigProps = this.getAllProps(origVariant);
+ var allNewProps = this.getAllProps(savedVariant);
+
+ //check for changed properties of the content
+ for (var k = 0; k < allOrigProps.length; k++) {
+
+ var origProp = allOrigProps[k];
+ var alias = origProp.alias;
+ var newProp = getNewProp(alias, allNewProps);
+ if (newProp && !_.isEqual(origProp.value, newProp.value)) {
+
+ //they have changed so set the origContent prop to the new one
+ var origVal = origProp.value;
+
+ origProp.value = newProp.value;
+
+ //instead of having a property editor $watch their expression to check if it has
+ // been updated, instead we'll check for the existence of a special method on their model
+ // and just call it.
+ if (angular.isFunction(origProp.onValueChanged)) {
+ //send the newVal + oldVal
+ origProp.onValueChanged(origProp.value, origVal);
+ }
+
+ changed.push(origProp);
}
- changed.push(allOrigProps[p]);
}
}
@@ -521,6 +597,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
//wire up the server validation errs
formHelper.handleServerValidation(args.err.data.ModelState);
+ //add model state errors to notifications
+ if (args.showNotifications) {
+ for (var e in modelState) {
+ notificationsService.error("Validation", modelState[e][0]);
+ }
+ }
+
if (!args.redirectOnFailure || !this.redirectToCreatedContent(args.err.data.id, args.err.data.ModelState)) {
//we are not redirecting because this is not new content, it is existing content. In this case
// we need to detect what properties have changed and re-bind them with the server data. Then we need
@@ -530,7 +613,10 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
args.rebindCallback();
}
- serverValidationManager.executeAndClearAllSubscriptions();
+ //notify all validators (don't clear the server validations though since we need to maintain their state because of
+ // how the variant switcher works in content). server validation state is always cleared when an editor first loads
+ // and in theory when an editor is destroyed.
+ serverValidationManager.notify();
}
//indicates we've handled the server result
@@ -562,7 +648,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
// the default behaviour is to redirect on success. This adds option to prevent when false
args.redirectOnSuccess = args.redirectOnSuccess !== undefined ? args.redirectOnSuccess : true;
-
+
if (!args.redirectOnSuccess || !this.redirectToCreatedContent(args.redirectId ? args.redirectId : args.savedContent.id)) {
//we are not redirecting because this is not new content, it is existing content. In this case
@@ -587,9 +673,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
*/
redirectToCreatedContent: function (id, modelState) {
- //only continue if we are currently in create mode and not in infinite mode and if there is no 'Name' modelstate errors
- // since we need at least a name to create content.
- if ($routeParams.create && (isValidIdentifier(id) && (!modelState || !modelState["Name"]))) {
+ //only continue if we are currently in create mode and not in infinite mode and if the resulting ID is valid
+ if ($routeParams.create && (isValidIdentifier(id))) {
//need to change the location to not be in 'create' mode. Currently the route will be something like:
// /belle/#/content/edit/1234?doctype=newsArticle&create=true
@@ -600,7 +685,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
navigationService.clearSearch();
//change to new path
- $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
+ $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
//don't add a browser history for this
$location.replace();
return true;
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
index 0bbfe34fe1..453785f537 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
@@ -102,6 +102,82 @@
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#copy
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens a copy editor in infinite editing, the submit callback returns an array of selected items
+ * @param {String} editor.section The node entity type
+ * @param {String} editor.currentNode The current node id
+ * @param {Callback} editor.submit Saves, submits, and closes the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+
+ function copy(editor) {
+ editor.view = "views/common/infiniteeditors/copy/copy.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#move
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens a move editor in infinite editing.
+ * @param {String} editor.section The node entity type
+ * @param {String} editor.currentNode The current node id
+ * @param {Callback} editor.submit Saves, submits, and closes the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+
+ function move(editor) {
+ editor.view = "views/common/infiniteeditors/move/move.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#embed
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens an embed editor in infinite editing.
+ * @param {Callback} editor.submit Saves, submits, and closes the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+
+ function embed(editor) {
+ editor.view = "views/common/infiniteeditors/embed/embed.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#linkPicker
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens an embed editor in infinite editing.
+ * @param {Callback} editor.submit Saves, submits, and closes the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+
+ function linkPicker(editor) {
+ editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html";
+ editor.size = "small";
+ open(editor);
+ }
+
/**
* @ngdoc method
* @name umbraco.services.editorService#mediaEditor
@@ -232,6 +308,23 @@
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#templateEditor
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens the user group picker in infinite editing, the submit callback returns the saved template
+ * @param {String} editor.id The template id
+ * @param {Callback} editor.submit Submits the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+ function templateEditor(editor) {
+ editor.view = "views/templates/edit.html";
+ open(editor);
+ }
+
/**
* @ngdoc method
* @name umbraco.services.editorService#sectionPicker
@@ -249,6 +342,40 @@
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#insertField
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens the insert field editor in infinite editing, the submit callback returns the code snippet
+ * @param {Callback} editor.submit Submits the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+ function insertField(editor) {
+ editor.view = "views/common/infiniteeditors/insertfield/insertfield.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#templateSections
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens the template sections editor in infinite editing, the submit callback returns the type to insert
+ * @param {Callback} editor.submit Submits the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+ function templateSections(editor) {
+ editor.view = "views/common/infiniteeditors/templatesections/templatesections.html";
+ editor.size = "small";
+ open(editor);
+ }
+
/**
* @ngdoc method
* @name umbraco.services.editorService#sectionPicker
@@ -266,6 +393,63 @@
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#itemPicker
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens the section picker in infinite editing, the submit callback returns an array of the selected items
+ *
+ * @param {Array} editor.availableItems Array of available items.
+ * @param {Array} editor.selectedItems Array of selected items. When passed in the selected items will be filtered from the available items.
+ * @param {Boolean} editor.filter Set to false to hide the filter.
+ * @param {Callback} editor.submit Submits the editor.
+ * @param {Callback} editor.close Closes the editor.
+ * @returns {Object} editor object
+ */
+ function itemPicker(editor) {
+ editor.view = "views/common/infiniteeditors/itempicker/itempicker.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#macroPicker
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens a macro picker in infinite editing, the submit callback returns an array of the selected items
+ *
+ * @param {Callback} editor.submit Submits the editor.
+ * @param {Callback} editor.close Closes the editor.
+ * @returns {Object} editor object
+ */
+ function macroPicker(editor) {
+ editor.view = "views/common/infiniteeditors/macropicker/macropicker.html";
+ editor.size = "small";
+ open(editor);
+ }
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#macroPicker
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens a member group picker in infinite editing.
+ *
+ * @param {Callback} editor.submit Submits the editor.
+ * @param {Callback} editor.close Closes the editor.
+ * @returns {Object} editor object
+ */
+ function memberGroupPicker(editor) {
+ editor.view = "views/common/infiniteeditors/membergrouppicker/membergrouppicker.html";
+ editor.size = "small";
+ open(editor);
+ }
+
var service = {
getEditors: getEditors,
open: open,
@@ -273,6 +457,10 @@
mediaEditor: mediaEditor,
contentEditor: contentEditor,
contentPicker: contentPicker,
+ copy: copy,
+ move: move,
+ embed: embed,
+ linkPicker: linkPicker,
mediaPicker: mediaPicker,
iconPicker: iconPicker,
documentTypeEditor: documentTypeEditor,
@@ -282,8 +470,14 @@
nodePermissions: nodePermissions,
insertCodeSnippet: insertCodeSnippet,
userGroupPicker: userGroupPicker,
+ templateEditor: templateEditor,
sectionPicker: sectionPicker,
- userPicker: userPicker
+ insertField: insertField,
+ templateSections: templateSections,
+ userPicker: userPicker,
+ itemPicker: itemPicker,
+ macroPicker: macroPicker,
+ memberGroupPicker: memberGroupPicker
};
return service;
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js
index 5983f82e0f..6d319ad90a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js
@@ -23,14 +23,33 @@ function fileManager() {
* Attaches files to the current manager for the current editor for a particular property, if an empty array is set
* for the files collection that effectively clears the files for the specified editor.
*/
- setFiles: function(propertyAlias, files) {
- //this will clear the files for the current property and then add the new ones for the current property
+ setFiles: function (args) {
+
+ //propertyAlias, files
+ if (!angular.isString(args.propertyAlias)) {
+ throw "args.propertyAlias must be a non empty string";
+ }
+ if (!angular.isObject(args.files)) {
+ throw "args.files must be an object";
+ }
+
+ //normalize to null
+ if (!args.culture) {
+ args.culture = null;
+ }
+
+ var metaData = [];
+ if (angular.isArray(args.metaData)) {
+ metaData = args.metaData;
+ }
+
+ //this will clear the files for the current property/culture and then add the new ones for the current property
fileCollection = _.reject(fileCollection, function (item) {
- return item.alias === propertyAlias;
+ return item.alias === args.propertyAlias && (!args.culture || args.culture === item.culture);
});
- for (var i = 0; i < files.length; i++) {
+ for (var i = 0; i < args.files.length; i++) {
//save the file object to the files collection
- fileCollection.push({ alias: propertyAlias, file: files[i] });
+ fileCollection.push({ alias: args.propertyAlias, file: args.files[i], culture: args.culture, metaData: metaData });
}
},
@@ -62,4 +81,4 @@ function fileManager() {
};
}
-angular.module('umbraco.services').factory('fileManager', fileManager);
\ No newline at end of file
+angular.module('umbraco.services').factory('fileManager', fileManager);
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
index ac90a0d333..c019258a02 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
@@ -116,7 +116,7 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
this.handleServerValidation(err.data.ModelState);
//execute all server validation events and subscribers
- serverValidationManager.executeAndClearAllSubscriptions();
+ serverValidationManager.notifyAndClearAllSubscriptions();
}
else {
dialogService.ysodDialog(err);
@@ -145,15 +145,15 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
// that each property is a User Developer property editor.
// The way that Content Type Editor ModelState is created is simply based on the ASP.Net validation data-annotations
// system.
- // So, to do this (since we need to support backwards compat), we need to hack a little bit. For Content Properties,
- // which are user defined, we know that they will exist with a prefixed ModelState of "_Properties.", so if we detect
- // this, then we know it's a Property.
+ // So, to do this there's some special ModelState syntax we need to know about.
+ // For Content Properties, which are user defined, we know that they will exist with a prefixed
+ // ModelState of "_Properties.", so if we detect this, then we know it's for a content Property.
//the alias in model state can be in dot notation which indicates
// * the first part is the content property alias
// * the second part is the field to which the valiation msg is associated with
- //There will always be at least 2 parts for properties since all model errors for properties are prefixed with "Properties"
- //If it is not prefixed with "Properties" that means the error is for a field of the object directly.
+ //There will always be at least 3 parts for content properties since all model errors for properties are prefixed with "_Properties"
+ //If it is not prefixed with "_Properties" that means the error is for a field of the object directly.
var parts = e.split(".");
@@ -163,14 +163,23 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
var propertyAlias = parts[1];
- //if it contains 2 '.' then we will wire it up to a property's field
+ var culture = null;
if (parts.length > 2) {
+ culture = parts[2];
+ //special check in case the string is formatted this way
+ if (culture === "null") {
+ culture = null;
+ }
+ }
+
+ //if it contains 3 '.' then we will wire it up to a property's html field
+ if (parts.length > 3) {
//add an error with a reference to the field for which the validation belongs too
- serverValidationManager.addPropertyError(propertyAlias, parts[2], modelState[e][0]);
+ serverValidationManager.addPropertyError(propertyAlias, culture, parts[3], modelState[e][0]);
}
else {
- //add a generic error for the property, no reference to a specific field
- serverValidationManager.addPropertyError(propertyAlias, "", modelState[e][0]);
+ //add a generic error for the property, no reference to a specific html field
+ serverValidationManager.addPropertyError(propertyAlias, culture, "", modelState[e][0]);
}
}
@@ -181,9 +190,6 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
serverValidationManager.addFieldError(e, modelState[e][0]);
}
- //add to notifications
- notificationsService.error("Validation", modelState[e][0]);
-
}
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/help.service.js b/src/Umbraco.Web.UI.Client/src/common/services/help.service.js
index 1de992d9db..ff8d487bb7 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/help.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/help.service.js
@@ -3,7 +3,7 @@ angular.module('umbraco.services')
var helpTopics = {};
var defaultUrl = "https://our.umbraco.com/rss/help";
- var tvUrl = "http://umbraco.tv/feeds/help";
+ var tvUrl = "https://umbraco.tv/feeds/help";
function getCachedHelp(url){
if(helpTopics[url]){
@@ -85,4 +85,4 @@ angular.module('umbraco.services')
return service;
- });
\ No newline at end of file
+ });
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 42b739fe61..6862b11565 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
@@ -497,7 +497,7 @@
});
//we need to use 'apply' to call intersection with an array of arrays,
- //see: http://stackoverflow.com/a/16229480/694494
+ //see: https://stackoverflow.com/a/16229480/694494
var intersectPermissions = _.intersection.apply(_, arr);
return {
@@ -506,7 +506,7 @@
canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D
canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M
canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U
- canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
+ canUnpublish: _.contains(intersectPermissions, 'U') //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
index 636437e387..5db9e9c77e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
@@ -4,7 +4,7 @@
* @description A helper object used for dealing with media items
**/
function mediaHelper(umbRequestHelper) {
-
+
//container of fileresolvers
var _mediaFileResolvers = {};
@@ -45,7 +45,7 @@ function mediaHelper(umbRequestHelper) {
//this performs a simple check to see if we have a media file as value
//it doesnt catch everything, but better then nothing
- if (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) {
+ if (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) {
return true;
}
@@ -75,7 +75,7 @@ function mediaHelper(umbRequestHelper) {
return "";
},
-
+
/**
* @ngdoc function
* @name umbraco.services.mediaHelper#getImagePropertyValue
@@ -125,7 +125,7 @@ function mediaHelper(umbRequestHelper) {
return "";
},
- registerFileResolver: function(propertyEditorAlias, func){
+ registerFileResolver: function (propertyEditorAlias, func) {
_mediaFileResolvers[propertyEditorAlias] = func;
},
@@ -141,8 +141,8 @@ function mediaHelper(umbRequestHelper) {
* @param {object} mediaEntity A media Entity returned from the entityResource
* @param {boolean} thumbnail Whether to return the thumbnail url or normal url
*/
- resolveFileFromEntity : function(mediaEntity, thumbnail) {
-
+ resolveFileFromEntity: function (mediaEntity, thumbnail) {
+
if (!angular.isObject(mediaEntity.metaData)) {
throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData";
}
@@ -179,67 +179,67 @@ function mediaHelper(umbRequestHelper) {
* @param {boolean} thumbnail Whether to return the thumbnail url or normal url
*/
/*jshint loopfunc: true */
- resolveFile : function(mediaItem, thumbnail){
-
- function iterateProps(props){
+ resolveFile: function (mediaItem, thumbnail) {
+
+ function iterateProps(props) {
var res = null;
- for(var resolver in _mediaFileResolvers) {
- var property = _.find(props, function(prop){ return prop.editor === resolver; });
- if(property){
+ for (var resolver in _mediaFileResolvers) {
+ var property = _.find(props, function (prop) { return prop.editor === resolver; });
+ if (property) {
res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail);
break;
}
}
- return res;
+ return res;
}
//we either have properties raw on the object, or spread out on tabs
var result = "";
- if(mediaItem.properties){
+ if (mediaItem.properties) {
result = iterateProps(mediaItem.properties);
- }else if(mediaItem.tabs){
- for(var tab in mediaItem.tabs) {
- if(mediaItem.tabs[tab].properties){
+ } else if (mediaItem.tabs) {
+ for (var tab in mediaItem.tabs) {
+ if (mediaItem.tabs[tab].properties) {
result = iterateProps(mediaItem.tabs[tab].properties);
- if(result){
+ if (result) {
break;
}
}
}
}
- return result;
+ return result;
},
/*jshint loopfunc: true */
- hasFilePropertyType : function(mediaItem){
- function iterateProps(props){
- var res = false;
- for(var resolver in _mediaFileResolvers) {
- var property = _.find(props, function(prop){ return prop.editor === resolver; });
- if(property){
- res = true;
- break;
- }
- }
- return res;
- }
+ hasFilePropertyType: function (mediaItem) {
+ function iterateProps(props) {
+ var res = false;
+ for (var resolver in _mediaFileResolvers) {
+ var property = _.find(props, function (prop) { return prop.editor === resolver; });
+ if (property) {
+ res = true;
+ break;
+ }
+ }
+ return res;
+ }
- //we either have properties raw on the object, or spread out on tabs
- var result = false;
- if(mediaItem.properties){
- result = iterateProps(mediaItem.properties);
- }else if(mediaItem.tabs){
- for(var tab in mediaItem.tabs) {
- if(mediaItem.tabs[tab].properties){
- result = iterateProps(mediaItem.tabs[tab].properties);
- if(result){
- break;
- }
- }
- }
- }
- return result;
+ //we either have properties raw on the object, or spread out on tabs
+ var result = false;
+ if (mediaItem.properties) {
+ result = iterateProps(mediaItem.properties);
+ } else if (mediaItem.tabs) {
+ for (var tab in mediaItem.tabs) {
+ if (mediaItem.tabs[tab].properties) {
+ result = iterateProps(mediaItem.tabs[tab].properties);
+ if (result) {
+ break;
+ }
+ }
+ }
+ }
+ return result;
},
/**
@@ -307,10 +307,7 @@ function mediaHelper(umbRequestHelper) {
var thumbnailUrl = umbRequestHelper.getApiUrl(
"imagesApiBaseUrl",
"GetBigThumbnail",
- [{ originalImagePath: imagePath }]);
-
- //var ext = imagePath.substr(imagePath.lastIndexOf('.'));
- //return imagePath.substr(0, imagePath.lastIndexOf('.')) + "_big-thumb" + ".jpg";
+ [{ originalImagePath: imagePath }]) + '&rnd=' + Math.random();
return thumbnailUrl;
},
@@ -331,7 +328,7 @@ function mediaHelper(umbRequestHelper) {
if (!imagePath) {
return false;
}
-
+
var lowered = imagePath.toLowerCase();
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
@@ -348,22 +345,26 @@ function mediaHelper(umbRequestHelper) {
*
* @param {string} file types, ex: jpg,png,tiff
*/
- formatFileTypes: function(fileTypes) {
+ formatFileTypes: function (fileTypes) {
- var fileTypesArray = fileTypes.split(',');
- var newFileTypesArray = [];
+ var fileTypesArray = fileTypes.split(',');
+ var newFileTypesArray = [];
- for (var i = 0; i < fileTypesArray.length; i++) {
- var fileType = fileTypesArray[i];
+ for (var i = 0; i < fileTypesArray.length; i++) {
+ var fileType = fileTypesArray[i].trim();
- if (fileType.indexOf(".") !== 0) {
- fileType = ".".concat(fileType);
- }
+ if (!fileType) {
+ continue;
+ }
- newFileTypesArray.push(fileType);
- }
+ if (fileType.indexOf(".") !== 0) {
+ fileType = ".".concat(fileType);
+ }
- return newFileTypesArray.join(",");
+ newFileTypesArray.push(fileType);
+ }
+
+ return newFileTypesArray.join(",");
},
@@ -378,7 +379,7 @@ function mediaHelper(umbRequestHelper) {
*
* @param {string} filePath File path, ex /media/1234/my-image.jpg
*/
- getFileExtension: function(filePath) {
+ getFileExtension: function (filePath) {
if (!filePath) {
return false;
@@ -388,6 +389,6 @@ function mediaHelper(umbRequestHelper) {
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
return ext;
}
-
+
};
-}angular.module('umbraco.services').factory('mediaHelper', mediaHelper);
+} angular.module('umbraco.services').factory('mediaHelper', mediaHelper);
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
index 5cc0b9bb1e..13940b5124 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
@@ -28,6 +28,8 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
navReadyPromise.resolve(mainTreeApi);
});
+ //A list of query strings defined that when changed will not cause a reload of the route
+ var nonRoutingQueryStrings = ["mculture", "cculture"];
//used to track the current dialog object
var currentDialog = null;
@@ -91,8 +93,85 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
}
}
+ /**
+ * Converts a string request path to a dictionary of route params
+ * @param {any} requestPath
+ */
+ function pathToRouteParts(requestPath) {
+ if (!angular.isString(requestPath)) {
+ throw "The value for requestPath is not a string";
+ }
+ var pathAndQuery = requestPath.split("#")[1];
+ if (pathAndQuery) {
+ if (pathAndQuery.indexOf("%253") || pathAndQuery.indexOf("%252")) {
+ pathAndQuery = decodeURIComponent(pathAndQuery);
+ }
+ var pathParts = pathAndQuery.split("?");
+ var path = pathParts[0];
+ var qry = pathParts.length === 1 ? "" : pathParts[1];
+ var qryParts = qry.split("&");
+ var result = {
+ path: path
+ };
+ for (var i = 0; i < qryParts.length; i++) {
+ var keyVal = qryParts[i].split("=");
+ if (keyVal.length == 2) {
+ result[keyVal[0]] = keyVal[1];
+ }
+ }
+ return result;
+ }
+ }
+
var service = {
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#isRouteChangingNavigation
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Detects if the route param differences will cause a navigation change or if the route param differences are
+ * only tracking state changes.
+ * This is used for routing operations where reloadOnSearch is false and when detecting form dirty changes when navigating to a different page.
+ * @param {object} currUrlParams Either a string path or a dictionary of route parameters
+ * @param {object} nextUrlParams Either a string path or a dictionary of route parameters
+ */
+ isRouteChangingNavigation: function (currUrlParams, nextUrlParams) {
+
+ if (angular.isString(currUrlParams)) {
+ currUrlParams = pathToRouteParts(currUrlParams);
+ }
+
+ if (angular.isString(nextUrlParams)) {
+ nextUrlParams = pathToRouteParts(nextUrlParams);
+ }
+
+ var allowRoute = true;
+
+ //The only time that we want to not route is if only any of the nonRoutingQueryStrings have changed/added.
+ //If any of the other parts have changed we do not cancel
+ var currRoutingKeys = _.difference(_.keys(currUrlParams), nonRoutingQueryStrings);
+ var nextRoutingKeys = _.difference(_.keys(nextUrlParams), nonRoutingQueryStrings);
+ var diff1 = _.difference(currRoutingKeys, nextRoutingKeys);
+ var diff2 = _.difference(nextRoutingKeys, currRoutingKeys);
+
+ //if the routing parameter keys are the same, we'll compare their values to see if any have changed and if so then the routing will be allowed.
+ if (diff1.length === 0 && diff2.length === 0) {
+ var partsChanged = 0;
+ _.each(currRoutingKeys, function (k) {
+ if (currUrlParams[k] != nextUrlParams[k]) {
+ partsChanged++;
+ }
+ });
+ if (partsChanged === 0) {
+ allowRoute = false; //nothing except our query strings changed, so don't continue routing
+ }
+ }
+
+ return allowRoute;
+ },
+
/**
* @ngdoc method
* @name umbraco.services.navigationService#waitForNavReady
@@ -523,7 +602,7 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
//These will show up on the dialog controller's $scope under dialogOptions
currentNode: args.node,
- currentAction: args.action,
+ currentAction: args.action
});
//save the currently assigned dialog so it can be removed before a new one is created
@@ -544,7 +623,7 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
setMode("default");
if(showMenu){
- this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") });
+ this.showMenu({ skipDefault: true, node: appState.getMenuState("currentNode") });
}
},
/**
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js
index d7e34d0f1c..11e54c0fdb 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js
@@ -28,11 +28,11 @@ function serverValidationManager($timeout) {
//find errors for this field name
return _.filter(self.items, function (item) {
- return (item.propertyAlias === null && item.fieldName === fieldName);
+ return (item.propertyAlias === null && item.culture === null && item.fieldName === fieldName);
});
}
- function getPropertyErrors(self, propertyAlias, fieldName) {
+ function getPropertyErrors(self, propertyAlias, culture, fieldName) {
if (!angular.isString(propertyAlias)) {
throw "propertyAlias must be a string";
}
@@ -42,15 +42,34 @@ function serverValidationManager($timeout) {
//find all errors for this property
return _.filter(self.items, function (item) {
- return (item.propertyAlias === propertyAlias && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
+ return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
});
}
+ function notifyCallbacks(self) {
+ for (var cb in callbacks) {
+ if (callbacks[cb].propertyAlias === null) {
+ //its a field error callback
+ var fieldErrors = getFieldErrors(self, callbacks[cb].fieldName);
+ if (fieldErrors.length > 0) {
+ executeCallback(self, fieldErrors, callbacks[cb].callback);
+ }
+ }
+ else {
+ //its a property error
+ var propErrors = getPropertyErrors(self, callbacks[cb].propertyAlias, callbacks[cb].culture, callbacks[cb].fieldName);
+ if (propErrors.length > 0) {
+ executeCallback(self, propErrors, callbacks[cb].callback);
+ }
+ }
+ }
+ }
+
return {
/**
* @ngdoc function
- * @name umbraco.services.serverValidationManager#subscribe
+ * @name notifyAndClearAllSubscriptions
* @methodOf umbraco.services.serverValidationManager
* @function
*
@@ -63,28 +82,12 @@ function serverValidationManager($timeout) {
* so that any persisted validation errors are re-bound to their controls. Once they are re-binded this then clears the validation
* colleciton so that if another route change occurs, the previously persisted validation errors are not re-bound to the new item.
*/
- executeAndClearAllSubscriptions: function() {
+ notifyAndClearAllSubscriptions: function() {
var self = this;
$timeout(function () {
-
- for (var cb in callbacks) {
- if (callbacks[cb].propertyAlias === null) {
- //its a field error callback
- var fieldErrors = getFieldErrors(self, callbacks[cb].fieldName);
- if (fieldErrors.length > 0) {
- executeCallback(self, fieldErrors, callbacks[cb].callback);
- }
- }
- else {
- //its a property error
- var propErrors = getPropertyErrors(self, callbacks[cb].propertyAlias, callbacks[cb].fieldName);
- if (propErrors.length > 0) {
- executeCallback(self, propErrors, callbacks[cb].callback);
- }
- }
- }
+ notifyCallbacks(self);
//now that they are all executed, we're gonna clear all of the errors we have
self.clear();
});
@@ -92,11 +95,29 @@ function serverValidationManager($timeout) {
/**
* @ngdoc function
- * @name umbraco.services.serverValidationManager#subscribe
+ * @name notify
* @methodOf umbraco.services.serverValidationManager
* @function
*
* @description
+ * This method isn't used very often but can be used if all subscriptions need to be notified again. This can be
+ * handy if a view needs to be reloaded/rebuild like when switching variants in the content editor.
+ */
+ notify: function() {
+ var self = this;
+
+ $timeout(function () {
+ notifyCallbacks(self);
+ });
+ },
+
+ /**
+ * @ngdoc function
+ * @name subscribe
+ * @methodOf umbraco.services.serverValidationManager
+ * @function
+ * @returns {} a method to unsubscribe this callback
+ * @description
* Adds a callback method that is executed whenever validation changes for the field name + property specified.
* This is generally used for server side validation in order to match up a server side validation error with
* a particular field, otherwise we can only pinpoint that there is an error for a content property, not the
@@ -104,52 +125,77 @@ function serverValidationManager($timeout) {
* field alias to listen for.
* If propertyAlias is null, then this subscription is for a field property (not a user defined property).
*/
- subscribe: function (propertyAlias, fieldName, callback) {
+ subscribe: function (propertyAlias, culture, fieldName, callback) {
if (!callback) {
return;
}
-
+
+ var id = String.CreateGuid();
+
if (propertyAlias === null) {
- //don't add it if it already exists
- var exists1 = _.find(callbacks, function (item) {
- return item.propertyAlias === null && item.fieldName === fieldName;
+ callbacks.push({
+ propertyAlias: null,
+ culture: null,
+ fieldName: fieldName,
+ callback: callback,
+ id: id
});
- if (!exists1) {
- callbacks.push({ propertyAlias: null, fieldName: fieldName, callback: callback });
- }
}
else if (propertyAlias !== undefined) {
- //don't add it if it already exists
- var exists2 = _.find(callbacks, function (item) {
- return item.propertyAlias === propertyAlias && item.fieldName === fieldName;
- });
- if (!exists2) {
- callbacks.push({ propertyAlias: propertyAlias, fieldName: fieldName, callback: callback });
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
}
+ callbacks.push({
+ propertyAlias: propertyAlias,
+ culture: culture, fieldName: fieldName,
+ callback: callback,
+ id: id
+ });
}
+
+ function unsubscribeId() {
+ //remove all callbacks for the content field
+ callbacks = _.reject(callbacks, function (item) {
+ return item.id === id;
+ });
+ }
+
+ //return a function to unsubscribe this subscription by uniqueId
+ return unsubscribeId;
},
-
- unsubscribe: function (propertyAlias, fieldName) {
+
+ /**
+ * Removes all callbacks registered for the propertyALias, culture and fieldName combination
+ * @param {} propertyAlias
+ * @param {} culture
+ * @param {} fieldName
+ * @returns {}
+ */
+ unsubscribe: function (propertyAlias, culture, fieldName) {
if (propertyAlias === null) {
//remove all callbacks for the content field
callbacks = _.reject(callbacks, function (item) {
- return item.propertyAlias === null && item.fieldName === fieldName;
+ return item.propertyAlias === null && item.culture === null && item.fieldName === fieldName;
});
}
else if (propertyAlias !== undefined) {
-
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
//remove all callbacks for the content property
callbacks = _.reject(callbacks, function (item) {
- return item.propertyAlias === propertyAlias &&
+ return item.propertyAlias === propertyAlias && item.culture === culture &&
(item.fieldName === fieldName ||
((item.fieldName === undefined || item.fieldName === "") && (fieldName === undefined || fieldName === "")));
});
}
-
-
},
@@ -164,10 +210,16 @@ function serverValidationManager($timeout) {
* This will always return any callbacks registered for just the property (i.e. field name is empty) and for ones with an
* explicit field name set.
*/
- getPropertyCallbacks: function (propertyAlias, fieldName) {
+ getPropertyCallbacks: function (propertyAlias, culture, fieldName) {
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
var found = _.filter(callbacks, function (item) {
//returns any callback that have been registered directly against the field and for only the property
- return (item.propertyAlias === propertyAlias && (item.fieldName === fieldName || (item.fieldName === undefined || item.fieldName === "")));
+ return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (item.fieldName === undefined || item.fieldName === "")));
});
return found;
},
@@ -184,7 +236,7 @@ function serverValidationManager($timeout) {
getFieldCallbacks: function (fieldName) {
var found = _.filter(callbacks, function (item) {
//returns any callback that have been registered directly against the field
- return (item.propertyAlias === null && item.fieldName === fieldName);
+ return (item.propertyAlias === null && item.culture === null && item.fieldName === fieldName);
});
return found;
},
@@ -207,6 +259,7 @@ function serverValidationManager($timeout) {
if (!this.hasFieldError(fieldName)) {
this.items.push({
propertyAlias: null,
+ culture: null,
fieldName: fieldName,
errorMsg: errorMsg
});
@@ -231,24 +284,30 @@ function serverValidationManager($timeout) {
* @description
* Adds an error message for the content property
*/
- addPropertyError: function (propertyAlias, fieldName, errorMsg) {
+ addPropertyError: function (propertyAlias, culture, fieldName, errorMsg) {
if (!propertyAlias) {
return;
}
-
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
//only add the item if it doesn't exist
- if (!this.hasPropertyError(propertyAlias, fieldName)) {
+ if (!this.hasPropertyError(propertyAlias, culture, fieldName)) {
this.items.push({
propertyAlias: propertyAlias,
+ culture: culture,
fieldName: fieldName,
errorMsg: errorMsg
});
}
//find all errors for this item
- var errorsForCallback = getPropertyErrors(this, propertyAlias, fieldName);
+ var errorsForCallback = getPropertyErrors(this, propertyAlias, culture, fieldName);
//we should now call all of the call backs registered for this error
- var cbs = this.getPropertyCallbacks(propertyAlias, fieldName);
+ var cbs = this.getPropertyCallbacks(propertyAlias, culture, fieldName);
//call each callback for this error
for (var cb in cbs) {
executeCallback(this, errorsForCallback, cbs[cb].callback);
@@ -264,14 +323,20 @@ function serverValidationManager($timeout) {
* @description
* Removes an error message for the content property
*/
- removePropertyError: function (propertyAlias, fieldName) {
+ removePropertyError: function (propertyAlias, culture, fieldName) {
if (!propertyAlias) {
return;
}
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
//remove the item
this.items = _.reject(this.items, function (item) {
- return (item.propertyAlias === propertyAlias && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
+ return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
});
},
@@ -316,10 +381,16 @@ function serverValidationManager($timeout) {
* @description
* Gets the error message for the content property
*/
- getPropertyError: function (propertyAlias, fieldName) {
+ getPropertyError: function (propertyAlias, culture, fieldName) {
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
var err = _.find(this.items, function (item) {
//return true if the property alias matches and if an empty field name is specified or the field name matches
- return (item.propertyAlias === propertyAlias && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
+ return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
});
return err;
},
@@ -336,7 +407,7 @@ function serverValidationManager($timeout) {
getFieldError: function (fieldName) {
var err = _.find(this.items, function (item) {
//return true if the property alias matches and if an empty field name is specified or the field name matches
- return (item.propertyAlias === null && item.fieldName === fieldName);
+ return (item.propertyAlias === null && item.culture === null && item.fieldName === fieldName);
});
return err;
},
@@ -348,12 +419,18 @@ function serverValidationManager($timeout) {
* @function
*
* @description
- * Checks if the content property + field name combo has an error
+ * Checks if the content property + culture + field name combo has an error
*/
- hasPropertyError: function (propertyAlias, fieldName) {
+ hasPropertyError: function (propertyAlias, culture, fieldName) {
+
+ //normalize culture to null
+ if (!culture) {
+ culture = null;
+ }
+
var err = _.find(this.items, function (item) {
//return true if the property alias matches and if an empty field name is specified or the field name matches
- return (item.propertyAlias === propertyAlias && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
+ return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
});
return err ? true : false;
},
@@ -370,7 +447,7 @@ function serverValidationManager($timeout) {
hasFieldError: function (fieldName) {
var err = _.find(this.items, function (item) {
//return true if the property alias matches and if an empty field name is specified or the field name matches
- return (item.propertyAlias === null && item.fieldName === fieldName);
+ return (item.propertyAlias === null && item.culture === null && item.fieldName === fieldName);
});
return err ? true : false;
},
@@ -380,4 +457,4 @@ function serverValidationManager($timeout) {
};
}
-angular.module('umbraco.services').factory('serverValidationManager', serverValidationManager);
\ No newline at end of file
+angular.module('umbraco.services').factory('serverValidationManager', serverValidationManager);
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
index 18d37ea5b2..b18cb73eae 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
@@ -7,7 +7,34 @@
* @description A helper object used to format/transform JSON Umbraco data, mostly used for persisting data to the server
**/
function umbDataFormatter() {
-
+
+ /**
+ * maps the display properties to a property collection for persisting/POSTing
+ * @param {any} tabs
+ */
+ function getContentProperties(tabs) {
+
+ var properties = [];
+
+ _.each(tabs, function (tab) {
+
+ _.each(tab.properties, function (prop) {
+
+ //don't include the custom generic tab properties
+ //don't include a property that is marked readonly
+ if (!prop.alias.startsWith("_umb_") && !prop.readonly) {
+ properties.push({
+ id: prop.id,
+ alias: prop.alias,
+ value: prop.value
+ });
+ }
+ });
+ });
+
+ return properties;
+ }
+
return {
formatChangePasswordModel: function(model) {
@@ -289,10 +316,10 @@
// by looking at the key
switch (foundAlias[0]) {
case "umbracoMemberLockedOut":
- saveModel.isLockedOut = prop.value ? (prop.value.toString() === "1" ? true : false) : false;
+ saveModel.isLockedOut = Object.toBoolean(prop.value);
break;
case "umbracoMemberApproved":
- saveModel.isApproved = prop.value ? (prop.value.toString() === "1" ? true : false) : true;
+ saveModel.isApproved = Object.toBoolean(prop.value);
break;
case "umbracoMemberComments":
saveModel.comments = prop.value;
@@ -313,7 +340,7 @@
// we don't want to post all of the data as it is unecessary.
var saveModel = {
id: displayModel.id,
- properties: [],
+ properties: getContentProperties(displayModel.tabs),
name: displayModel.name,
contentTypeAlias: displayModel.contentTypeAlias,
parentId: displayModel.parentId,
@@ -321,50 +348,31 @@
action: action
};
- _.each(displayModel.tabs, function (tab) {
-
- _.each(tab.properties, function (prop) {
-
- //don't include the custom generic tab properties
- //don't include a property that is marked readonly
- if (!prop.alias.startsWith("_umb_") && !prop.readonly) {
- saveModel.properties.push({
- id: prop.id,
- alias: prop.alias,
- value: prop.value
- });
- }
- });
- });
-
return saveModel;
},
/** formats the display model used to display the content to the model used to save the content */
formatContentPostData: function (displayModel, action) {
- //this is basically the same as for media but we need to explicitly add some extra properties
- var saveModel = this.formatMediaPostData(displayModel, action);
-
- //get the selected variant and build the additional published variants
- saveModel.publishVariations = [];
-
- //if there's any variants than we need to set the language and include the variants to publish
- if (displayModel.variants.length > 0) {
- _.each(displayModel.variants,
- function (d) {
- //set the selected variant if this is current
- if (d.current === true) {
- saveModel.culture = d.language.culture;
- }
- if (d.publish === true) {
- saveModel.publishVariations.push({
- culture: d.language.culture,
- segment: d.segment
- });
- }
- });
- }
+ //NOTE: the display model inherits from the save model so we can in theory just post up the display model but
+ // we don't want to post all of the data as it is unecessary.
+ var saveModel = {
+ id: displayModel.id,
+ name: displayModel.name,
+ contentTypeAlias: displayModel.contentTypeAlias,
+ parentId: displayModel.parentId,
+ //set the action on the save model
+ action: action,
+ variants: _.map(displayModel.variants, function(v) {
+ return {
+ name: v.name,
+ properties: getContentProperties(v.tabs),
+ culture: v.language ? v.language.culture : null,
+ publish: v.publish,
+ save: v.save
+ };
+ })
+ };
var propExpireDate = displayModel.removeDate;
var propReleaseDate = displayModel.releaseDate;
@@ -375,6 +383,52 @@
saveModel.templateAlias = propTemplate ? propTemplate : null;
return saveModel;
+ },
+
+ /**
+ * This formats the server GET response for a content display item
+ * @param {} displayModel
+ * @returns {}
+ */
+ formatContentGetData: function(displayModel) {
+
+ //We need to check for invariant properties among the variant variants.
+ //When we detect this, we want to make sure that the property object instance is the
+ //same reference object between all variants instead of a copy (which it will be when
+ //return from the JSON structure).
+
+ if (displayModel.variants && displayModel.variants.length > 1) {
+
+ var invariantProperties = [];
+
+ //collect all invariant properties on the first first variant
+ var firstVariant = displayModel.variants[0];
+ _.each(firstVariant.tabs, function(tab, tabIndex) {
+ _.each(tab.properties, function (property, propIndex) {
+ //in theory if there's more than 1 variant, that means they would all have a language
+ //but we'll do our safety checks anyways here
+ if (firstVariant.language && !property.culture) {
+ invariantProperties.push({
+ tabIndex: tabIndex,
+ propIndex: propIndex,
+ property: property
+ });
+ }
+ });
+ });
+
+
+ //now assign this same invariant property instance to the same index of the other variants property array
+ for (var j = 1; j < displayModel.variants.length; j++) {
+ var variant = displayModel.variants[j];
+
+ _.each(invariantProperties, function (invProp) {
+ variant.tabs[invProp.tabIndex].properties[invProp.propIndex] = invProp.property;
+ });
+ }
+ }
+
+ return displayModel;
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
index 9eeadf7154..228c885529 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
@@ -152,6 +152,13 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
}, function (response) {
+ if (!response.status && response.message && response.stack) {
+ //this is a JS/angular error that we should deal with
+ return $q.reject({
+ errorMsg: response.message
+ })
+ }
+
//invoke the callback
var result = callbacks.error.apply(this, [response.data, response.status, response.headers, response.config]);
@@ -221,7 +228,12 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
for (var f in args.files) {
//each item has a property alias and the file object, we'll ensure that the alias is suffixed to the key
// so we know which property it belongs to on the server side
- formData.append("file_" + args.files[f].alias, args.files[f].file);
+ var fileKey = "file_" + args.files[f].alias + "_" + (args.files[f].culture ? args.files[f].culture : "");
+
+ if (angular.isArray(args.files[f].metaData) && args.files[f].metaData.length > 0) {
+ fileKey += ("_" + args.files[f].metaData.join("_"));
+ }
+ formData.append(fileKey, args.files[f].file);
}
}).then(function (response) {
//success callback
@@ -236,6 +248,8 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
formHelper.showNotifications(response.data);
+ //TODO: Do we need to pass the result through umbDataFormatter.formatContentGetData? Right now things work so not sure but we should check
+
//the data returned is the up-to-date data so the UI will refresh
return $q.resolve(response.data);
}, function (response) {
diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js
index 6e444cbf84..50e5c21203 100644
--- a/src/Umbraco.Web.UI.Client/src/init.js
+++ b/src/Umbraco.Web.UI.Client/src/init.js
@@ -43,8 +43,7 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH
}
var currentRouteParams = null;
- var globalQueryStrings = ["mculture"];
-
+
/** execute code on each successful route */
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
@@ -126,28 +125,7 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH
//check if the location being changed is only the mculture query string, if so, cancel the routing since this is just
//used as a global persistent query string that does not change routes.
- var currUrlParts = currentRouteParams;
- var nextUrlParts = next.params;
-
- var allowRoute = true;
-
- //the only time that we want to cancel is if any of the globalQueryStrings have changed
- //in which case the number of parts need to be equal before comparing values
- if (_.keys(currUrlParts).length == _.keys(nextUrlParts).length) {
- var partsChanged = 0;
- _.each(currUrlParts, function (value, key) {
- if (globalQueryStrings.indexOf(key) === -1) {
- if (value.toLowerCase() !== nextUrlParts[key].toLowerCase()) {
- partsChanged++;
- }
- }
- });
- if (partsChanged === 0) {
- allowRoute = false; //nothing except our query strings chagned, so don't continue routing
- }
- }
-
- if (allowRoute) {
+ if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) {
//continue the route
$route.reload();
}
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html
index ff0ec56620..5fb9d9bda8 100644
--- a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html
+++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html
@@ -25,21 +25,21 @@
-
-
-
+
+
- At least {{installer.current.model.minCharLength}} characters long
-
-
- At least {{installer.current.model.minNonAlphaNumericLength}} symbol{{installer.current.model.minNonAlphaNumericLength > 1 ? 's' : ''}}
-
-
+ At least {{installer.current.model.minCharLength}} characters long
+
+
+ At least {{installer.current.model.minNonAlphaNumericLength}} symbol{{installer.current.model.minNonAlphaNumericLength > 1 ? 's' : ''}}
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
similarity index 98%
rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js
rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
index 058f71bfcf..aa63f2d6d6 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
@@ -26,6 +26,12 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi
}
};
+ $scope.close = function() {
+ if($scope.model.close) {
+ $scope.model.close();
+ }
+ }
+
/** changes the view to edit the params of the selected macro */
/** if there is pnly one macro, and it has parameters - editor can skip selecting the Macro **/
function editParams(insertIfNoParameters) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html
new file mode 100644
index 0000000000..a9551138e1
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html
@@ -0,0 +1,91 @@
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/recyclebin.html b/src/Umbraco.Web.UI.Client/src/views/content/recyclebin.html
index 3dd5e846a8..2981097656 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/recyclebin.html
+++ b/src/Umbraco.Web.UI.Client/src/views/content/recyclebin.html
@@ -1,19 +1,20 @@
-
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/sort.html b/src/Umbraco.Web.UI.Client/src/views/content/sort.html
new file mode 100644
index 0000000000..fd3eae1e7a
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/content/sort.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html
index 1a83a5e329..5b42657ddd 100644
--- a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html
+++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html
@@ -1,9 +1,9 @@
Hours of Umbraco training videos are only a click away
-
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
Hours of Umbraco training videos are only a click away
-
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco, then visit umbraco.tv for even more Umbraco videos.
+
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco, then visit umbraco.tv for even more Umbraco videos.
- This application requires Adobe® AIR™ to be installed for Mac OS or Windows.
+ This application requires Adobe® AIR™ to be installed for Mac OS or Windows.
Hours of Umbraco training videos are only a click away
-
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+
Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
index 30c2b493da..7a1a825e46 100644
--- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
@@ -290,8 +290,7 @@
/* ---------- SAVE ---------- */
function save() {
- //return the saveInternal method but catch rejections since this is the upper most caller
- return saveInternal().catch(angular.noop);
+ saveInternal();
}
/** This internal save method performs the actual saving and returns a promise, not to be bound to any buttons but used by other bound methods */
@@ -305,6 +304,9 @@
// reformat allowed content types to array if id's
vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes);
+ //if this is a new item and it's creating a template, ensure that the template alias is synced correctly
+ syncTemplateAlias(vm.contentType);
+
return contentEditingHelper.contentEditorPerformSave({
saveMethod: contentTypeResource.save,
scope: $scope,
@@ -411,6 +413,24 @@
vm.contentType = contentType;
}
+ /** Syncs the template alias for new doc types before saving if a template is to be created */
+ function syncTemplateAlias(contentType) {
+ if (!noTemplate && contentType.id === 0) {
+ //sync default template that had the placeholder flag
+ if (contentType.defaultTemplate !== null && contentType.defaultTemplate.placeholder) {
+ contentType.defaultTemplate.name = contentType.name;
+ contentType.defaultTemplate.alias = contentType.alias;
+ }
+ //sync allowed templates that had the placeholder flag
+ angular.forEach(contentType.allowedTemplates, function (allowedTemplate) {
+ if (allowedTemplate.placeholder) {
+ allowedTemplate.name = contentType.name;
+ allowedTemplate.alias = contentType.alias;
+ }
+ });
+ }
+ }
+
function convertLegacyIcons(contentType) {
// make array to store contentType icon
var contentTypeArray = [];
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js
new file mode 100644
index 0000000000..b7e09db84f
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js
@@ -0,0 +1,71 @@
+angular.module("umbraco")
+ .controller("Umbraco.Editors.DocumentTypes.ImportController",
+ function ($scope, contentTypeResource, navigationService, Upload, umbRequestHelper) {
+ var vm = this;
+ vm.serverErrorMessage = "";
+ vm.state = "upload";
+ vm.model = {};
+ vm.uploadStatus = "";
+
+ $scope.handleFiles = function (files, event) {
+ if (files && files.length > 0) {
+ $scope.upload(files[0]);
+ }
+ };
+
+ $scope.upload = function (file) {
+ Upload.upload({
+ url: umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "Upload"),
+ fields: {},
+ file: file
+ }).success(function (data, status, headers, config) {
+ if (data.notifications && data.notifications.length > 0) {
+
+ // set error status on file
+ vm.uploadStatus = "error";
+
+ // Throw message back to user with the cause of the error
+ vm.serverErrorMessage = data.notifications[0].message;
+ } else {
+
+ // set done status on file
+ vm.uploadStatus = "done";
+ vm.model = data;
+ vm.state = "confirm";
+ }
+ }).error(function (evt, status, headers, config) {
+
+ // set status done
+ $scope.uploadStatus = "error";
+
+ // If file not found, server will return a 404 and display this message
+ if (status === 404) {
+ $scope.serverErrorMessage = "File not found";
+ }
+ else if (status == 400) {
+ //it's a validation error
+ $scope.serverErrorMessage = evt.message;
+ }
+ else {
+ //it's an unhandled error
+ //if the service returns a detailed error
+ if (evt.InnerException) {
+ $scope.serverErrorMessage = evt.InnerException.ExceptionMessage;
+
+ //Check if its the common "too large file" exception
+ if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
+ $scope.serverErrorMessage = "File too large to upload";
+ }
+
+ } else if (evt.Message) {
+ $scope.serverErrorMessage = evt.Message;
+ }
+ }
+ });
+ };
+
+ $scope.import = function () {
+ contentTypeResource.import(vm.model.tempFileName);
+ vm.state = "done";
+ }
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html
new file mode 100644
index 0000000000..db6c19da98
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html
@@ -0,0 +1,53 @@
+
+
+
+
+ To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click 'Import' (you'll be asked for confirmation on the next screen)
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/info/info.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/info/info.html
index a5bb9a4929..84b7327b8a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/apps/info/info.html
+++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/info/info.html
@@ -1,4 +1,4 @@
+ ng-if="content"
+ node="content">
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js
deleted file mode 100644
index 7ad5724e75..0000000000
--- a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js
+++ /dev/null
@@ -1,23 +0,0 @@
-(function () {
- "use strict";
-
- function MediaAppListViewController($scope) {
-
- var vm = this;
-
- vm.listViewGroup = {};
-
- function onInit() {
- angular.forEach($scope.model.tabs, function(group){
- if(group.alias === "Contents") {
- vm.listViewGroup = group;
- }
- });
- }
-
- onInit();
-
- }
-
- angular.module("umbraco").controller("Umbraco.Editors.Media.Apps.ListViewController", MediaAppListViewController);
-})();
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html
index e463680ae6..6c558301c7 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html
+++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html
@@ -1,8 +1,7 @@
-
+
-
+
-
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/edit.html b/src/Umbraco.Web.UI.Client/src/views/media/edit.html
index 3de3758af2..a9afde36ac 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/edit.html
+++ b/src/Umbraco.Web.UI.Client/src/views/media/edit.html
@@ -1,81 +1,74 @@
-
+
-
-
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js
index 8c171af2f0..688b44ae64 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js
@@ -116,25 +116,6 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
"view": "views/media/apps/listview/listview.html"
};
- $scope.content.apps = [];
-
- if($scope.content.contentTypeAlias === "Folder") {
- // add list view app
- $scope.content.apps.push(listview);
-
- // remove the list view tab
- angular.forEach($scope.content.tabs, function(tab, index){
- if(tab.alias === "Contents") {
- tab.hide = true;
- }
- });
-
- } else {
- $scope.content.apps.push(contentApp);
- }
-
- $scope.content.apps.push(infoApp);
-
// set first app to active
$scope.content.apps[0].active = true;
@@ -220,7 +201,7 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
// route but there might be server validation errors in the collection which we need to display
// after the redirect, so we will bind all subscriptions which will show the server validation errors
// if there are any and then clear them so the collection no longer persists them.
- serverValidationManager.executeAndClearAllSubscriptions();
+ serverValidationManager.notifyAndClearAllSubscriptions();
if(!infiniteMode) {
syncTreeNode($scope.content, data.path, true);
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.recyclebin.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.recyclebin.controller.js
index 4e54bd832a..00e09bd34d 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/media.recyclebin.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/media/media.recyclebin.controller.js
@@ -22,18 +22,9 @@ function MediaRecycleBinController($scope, $routeParams, mediaResource, navigati
$routeParams.id = "-21";
mediaResource.getRecycleBin().then(function (result) {
- //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
- // single property, so we'll extract that property (list view) and use it's data.
- var listproperty = result.tabs[0].properties[0];
-
- _.each(listproperty.config, function (val, key) {
- $scope.model.config[key] = val;
- });
- $scope.listViewPath = 'views/propertyeditors/listview/listview.html';
+ $scope.content = result;
});
- $scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
-
// sync tree node
navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false });
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.sort.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.sort.controller.js
new file mode 100644
index 0000000000..e7d3cc2dfb
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/media/media.sort.controller.js
@@ -0,0 +1,82 @@
+(function () {
+ "use strict";
+
+ function MediaSortController($scope, $filter, mediaResource, navigationService) {
+
+ var vm = this;
+ var parentId = $scope.currentNode.parentId ? $scope.currentNode.parentId : "-1";
+ var id = $scope.currentNode.id;
+
+ vm.loading = false;
+ vm.children = [];
+ vm.saveButtonState = "init";
+ vm.sortOrder = {};
+ vm.sortableOptions = {
+ distance: 10,
+ tolerance: "pointer",
+ opacity: 0.7,
+ scroll: true,
+ cursor: "move",
+ helper: fixSortableHelper,
+ update: function() {
+ // clear the sort order when drag and drop is used
+ vm.sortOrder.column = "";
+ vm.sortOrder.reverse = false;
+ }
+ };
+
+ vm.save = save;
+ vm.sort = sort;
+
+ function onInit() {
+ vm.loading = true;
+ mediaResource.getChildren(id)
+ .then(function(data){
+ vm.children = data.items;
+ vm.loading = false;
+ });
+ }
+
+ function save() {
+ vm.saveButtonState = "busy";
+
+ var args = {
+ parentId: parentId,
+ sortedIds: _.map(vm.children, function(child){ return child.id; })
+ };
+
+ mediaResource.sort(args)
+ .then(function(){
+ navigationService.syncTree({ tree: "media", path: $scope.currentNode.path, forceReload: true, activate: false });
+ vm.saveButtonState = "success";
+ }, function(error) {
+ vm.error = error;
+ vm.saveButtonState = "error";
+ });
+ }
+
+ function fixSortableHelper(e, ui) {
+ // keep the correct width of each table cell when sorting
+ ui.children().each(function () {
+ $(this).width($(this).width());
+ });
+ return ui;
+ }
+
+ function sort(column) {
+ // reverse if it is already ordered by that column
+ if(vm.sortOrder.column === column) {
+ vm.sortOrder.reverse = !vm.sortOrder.reverse
+ } else {
+ vm.sortOrder.column = column;
+ vm.sortOrder.reverse = false;
+ }
+ vm.children = $filter('orderBy')(vm.children, vm.sortOrder.column, vm.sortOrder.reverse);
+ }
+
+ onInit();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.Media.SortController", MediaSortController);
+})();
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/recyclebin.html b/src/Umbraco.Web.UI.Client/src/views/media/recyclebin.html
index 16649f214a..cd4ad6d07e 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/recyclebin.html
+++ b/src/Umbraco.Web.UI.Client/src/views/media/recyclebin.html
@@ -1,16 +1,20 @@
-
-
-
-
-
-
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.controller.js
index e397722f7d..9ce7e18f06 100644
--- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.controller.js
@@ -4,7 +4,7 @@
function updateToggleValue() {
$scope.toggleValue = false;
- if ($scope.model && $scope.model.value && ($scope.model.value.toString() === "1" || angular.lowercase($scope.model.value) === "true")) {
+ if ($scope.model && Object.toBoolean($scope.model.value)) {
$scope.toggleValue = true;
}
}
@@ -16,7 +16,7 @@
updateToggleValue();
$scope.toggle = function(){
- if($scope.model.value === 1 || $scope.model.value === "1"){
+ if (Object.toBoolean($scope.model.value)) {
$scope.model.value = "0";
updateToggleValue();
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js
index 729d899439..4c8c5f845a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js
@@ -5,11 +5,11 @@ function booleanEditorController($scope, $rootScope, assetsService) {
value: false
};
- if ($scope.model.config && $scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
+ if ($scope.model.config && $scope.model.config.default && Object.toBoolean($scope.model.config.default) && $scope.model && !$scope.model.value) {
$scope.renderModel.value = true;
}
- if ($scope.model && $scope.model.value && ($scope.model.value.toString() === "1" || $scope.model.value.toString().toLowerCase() === "true")) {
+ if ($scope.model && $scope.model.value && Object.toBoolean($scope.model.value)) {
$scope.renderModel.value = true;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js
index f58655937d..9ef1b69aad 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js
@@ -1,64 +1,91 @@
angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListController",
- function($scope) {
-
- if (angular.isObject($scope.model.config.items)) {
-
- //now we need to format the items in the dictionary because we always want to have an array
- var newItems = [];
- var vals = _.values($scope.model.config.items);
- var keys = _.keys($scope.model.config.items);
- for (var i = 0; i < vals.length; i++) {
- newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value });
+ function ($scope) {
+
+ function init() {
+
+ //we can't really do anything if the config isn't an object
+ if (angular.isObject($scope.model.config.items)) {
+
+ //now we need to format the items in the dictionary because we always want to have an array
+ var configItems = [];
+ var vals = _.values($scope.model.config.items);
+ var keys = _.keys($scope.model.config.items);
+ for (var i = 0; i < vals.length; i++) {
+ configItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value });
+ }
+
+ //ensure the items are sorted by the provided sort order
+ configItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
+
+ if ($scope.model.value === null || $scope.model.value === undefined) {
+ $scope.model.value = [];
+ }
+
+ updateViewModel(configItems);
+
+ //watch the model.value in case it changes so that we can keep our view model in sync
+ $scope.$watchCollection("model.value",
+ function (newVal) {
+ updateViewModel(configItems);
+ });
}
-
- //ensure the items are sorted by the provided sort order
- newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
-
- //re-assign
- $scope.model.config.items = newItems;
-
+
}
- function setupViewModel() {
+ function updateViewModel(configItems) {
+
+ //check if it's already in sync
+
+ //get the checked vals from the view model
+ var selectedVals = _.map(_.filter($scope.selectedItems,
+ function(f) {
+ return f.checked;
+ }),
+ function(m) {
+ return m.key;
+ });
+ //get all of the same values between the arrays
+ var same = _.intersection($scope.model.value, selectedVals);
+ //if the lengths are the same as the value, then we are in sync, just exit
+ if (same.length == $scope.model.value.length === selectedVals.length) {
+ return;
+ }
+
$scope.selectedItems = [];
- //now we need to check if the value is null/undefined, if it is we need to set it to "" so that any value that is set
- // to "" gets selected by default
- if ($scope.model.value === null || $scope.model.value === undefined) {
- $scope.model.value = [];
- }
-
- for (var i = 0; i < $scope.model.config.items.length; i++) {
- var isChecked = _.contains($scope.model.value, $scope.model.config.items[i].id);
+ for (var i = 0; i < configItems.length; i++) {
+ var isChecked = _.contains($scope.model.value, configItems[i].id);
$scope.selectedItems.push({
checked: isChecked,
- key: $scope.model.config.items[i].id,
- val: $scope.model.config.items[i].value
+ key: configItems[i].id,
+ val: configItems[i].value
});
}
-
}
- setupViewModel();
-
+ function changed(item) {
+ var index = _.findIndex($scope.model.value,
+ function (v) {
+ return v === item.key;
+ });
- //update the model when the items checked changes
- $scope.$watch("selectedItems", function(newVal, oldVal) {
-
- $scope.model.value = [];
- for (var x = 0; x < $scope.selectedItems.length; x++) {
- if ($scope.selectedItems[x].checked) {
- $scope.model.value.push($scope.selectedItems[x].key);
+ if (item.checked) {
+ //if it doesn't exist in the model, then add it
+ if (index < 0) {
+ $scope.model.value.push(item.key);
}
}
+ else {
+ //if it exists in the model, then remove it
+ if (index >= 0) {
+ $scope.model.value.splice(index, 1);
+ }
+ }
+ }
- }, true);
-
- //here we declare a special method which will be called whenever the value has changed from the server
- //this is instead of doing a watch on the model.value = faster
- $scope.model.onValueChanged = function (newVal, oldVal) {
- //update the display val again if it has changed from the server
- setupViewModel();
- };
+ $scope.selectedItems = [];
+ $scope.changed = changed;
+
+ init();
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
index 0e528fbe80..b287b0b58e 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
@@ -1,11 +1,12 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
index ef660f02b1..47d670e68a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
@@ -1,4 +1,4 @@
-function listViewController($rootScope, $scope, $routeParams, $injector, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService, navigationService, treeService) {
+function listViewController($scope, $routeParams, $injector, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $timeout, mediaResource, listViewHelper, navigationService, editorService) {
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
// that isn't created yet, if we continue this will use the parent id in the route params which isn't what
@@ -76,7 +76,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
"canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D
"canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M
"canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U
- "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
+ "canUnpublish": _.contains(currentUserPermissions, 'U') //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
};
}
}
@@ -99,9 +99,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
canUnpublish: true
};
- $scope.$watch(function () {
- return $scope.selection.length;
- }, function (newVal, oldVal) {
+ $scope.$watch("selection.length", function (newVal, oldVal) {
if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) {
@@ -126,7 +124,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
});
if (missingLookup.length > 0) {
- contentResource.getPermissions(missingLookup).then(function (p) {
+ currentUserResource.getPermissions(missingLookup).then(function (p) {
$scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions);
});
}
@@ -431,27 +429,20 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
};
$scope.move = function() {
- $scope.moveDialog = {};
- $scope.moveDialog.section = $scope.entityType;
- $scope.moveDialog.currentNode = $scope.contentId;
- $scope.moveDialog.view = "move";
- $scope.moveDialog.show = true;
-
- $scope.moveDialog.submit = function(model) {
-
- if (model.target) {
- performMove(model.target);
+ var move = {
+ section: $scope.entityType,
+ currentNode: $scope.contentId,
+ submit: function(model) {
+ if (model.target) {
+ performMove(model.target);
+ }
+ editorService.close();
+ },
+ close: function() {
+ editorService.close();
}
-
- $scope.moveDialog.show = false;
- $scope.moveDialog = null;
- };
-
- $scope.moveDialog.close = function(oldModel) {
- $scope.moveDialog.show = false;
- $scope.moveDialog = null;
- };
-
+ }
+ editorService.move(move);
};
@@ -500,28 +491,22 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
});
}
- $scope.copy = function () {
- $scope.copyDialog = {};
- $scope.copyDialog.section = $scope.entityType;
- $scope.copyDialog.currentNode = $scope.contentId;
- $scope.copyDialog.view = "copy";
- $scope.copyDialog.show = true;
-
- $scope.copyDialog.submit = function (model) {
- if (model.target) {
- performCopy(model.target, model.relateToOriginal);
- }
-
- $scope.copyDialog.show = false;
- $scope.copyDialog = null;
- };
-
- $scope.copyDialog.close = function (oldModel) {
- $scope.copyDialog.show = false;
- $scope.copyDialog = null;
- };
-
- };
+ $scope.copy = function () {
+ var copyEditor = {
+ section: $scope.entityType,
+ currentNode: $scope.contentId,
+ submit: function(model) {
+ if (model.target) {
+ performCopy(model.target, model.relateToOriginal);
+ }
+ editorService.close();
+ },
+ close: function() {
+ editorService.close();
+ }
+ };
+ editorService.copy(copyEditor);
+ };
function performCopy(target, relateToOriginal) {
applySelected(
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html
index 3df37f6306..e05067ee42 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html
@@ -174,20 +174,6 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js
index c0a469a554..e5e3d98f6f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js
@@ -10,17 +10,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
$scope.allowEditMedia = false;
$scope.allowAddMedia = false;
- userService.getCurrentUser().then(function(userData) {
- if (!$scope.model.config.startNodeId) {
- $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
- $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
- }
- // only allow users to add and edit media if they have access to the media section
- var hasAccessToMedia = userData.allowedSections.indexOf("media") !== -1;
- $scope.allowEditMedia = hasAccessToMedia;
- $scope.allowAddMedia = hasAccessToMedia;
- });
-
function setupViewModel() {
$scope.images = [];
$scope.ids = [];
@@ -37,7 +26,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// might require it's use. Therefore we need to use the metaData property to get at the thumbnail
// value.
- entityResource.getByIds(ids, "Media").then(function(medias) {
+ entityResource.getByIds(ids, "Media").then(function (medias) {
// The service only returns item results for ids that exist (deleted items are silently ignored).
// This results in the picked items value to be set to contain only ids of picked items that could actually be found.
@@ -47,11 +36,11 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// on whether it is simply resaved or not.
// This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders
// when there is no match for a selected id. This will ensure that the values being set on save, are the same as before.
-
+
medias = _.map(ids,
- function(id) {
+ function (id) {
var found = _.find(medias,
- function(m) {
+ function (m) {
// We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and
// it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString()
// compares and be completely sure it works.
@@ -72,7 +61,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
});
_.each(medias,
- function(media, i) {
+ function (media, i) {
// if there is no thumbnail, try getting one if the media is not a placeholder item
if (!media.thumbnail && media.id && media.metaData) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
@@ -87,95 +76,22 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
}
});
- $scope.sync();
+ sync();
});
}
}
- setupViewModel();
-
- $scope.remove = function(index) {
- $scope.images.splice(index, 1);
- $scope.ids.splice(index, 1);
- $scope.sync();
+ function sync() {
+ $scope.model.value = $scope.ids.join();
};
- $scope.editItem = function(item) {
- var mediaEditor = {
- id: item.id,
- submit: function(model) {
- editorService.close();
- // update the selected media item to match the saved media item
- // the media picker is using media entities so we get the
- // entity so we easily can format it for use in the media grid
- if(model && model.mediaNode) {
- entityResource.getById(model.mediaNode.id, "media")
- .then(function (mediaEntity) {
- // if an image is selecting more than once
- // we need to update all the media items
- angular.forEach($scope.images, function(image){
- if(image.id === model.mediaNode.id) {
- angular.extend(image, mediaEntity);
- image.thumbnail = mediaHelper.resolveFileFromEntity(image, true);
- }
- });
- });
- }
- },
- close: function(model) {
- editorService.close();
- }
- };
- editorService.mediaEditor(mediaEditor);
- };
-
- $scope.add = function() {
- var mediaPicker = {
- startNodeId: $scope.model.config.startNodeId,
- startNodeIsVirtual: $scope.model.config.startNodeIsVirtual,
- multiPicker: multiPicker,
- onlyImages: onlyImages,
- disableFolderSelect: disableFolderSelect,
- submit: function(model) {
-
- editorService.close();
-
- _.each(model.selectedImages, function(media, i) {
- // if there is no thumbnail, try getting one if the media is not a placeholder item
- if (!media.thumbnail && media.id && media.metaData) {
- media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
- }
-
- $scope.images.push(media);
-
- if ($scope.model.config.idType === "udi") {
- $scope.ids.push(media.udi);
- }
- else {
- $scope.ids.push(media.id);
- }
-
- });
- $scope.sync();
- reloadUpdatedMediaItems(model.updatedMediaNodes);
- },
- close: function(model) {
- editorService.close();
- reloadUpdatedMediaItems(model.updatedMediaNodes);
- }
- }
-
- editorService.mediaPicker(mediaPicker);
-
- };
-
- function reloadUpdatedMediaItems(updatedMediaNodes) {
+ function reloadUpdatedMediaItems(updatedMediaNodes) {
// because the images can be edited through the media picker we need to
// reload. We only reload the images that is already picked but has been updated.
// We have to get the entities from the server because the media
// can be edited without being selected
- _.each($scope.images, function(image, i) {
- if(updatedMediaNodes.indexOf(image.udi) !== -1) {
+ _.each($scope.images, function (image, i) {
+ if (updatedMediaNodes.indexOf(image.udi) !== -1) {
image.loading = true;
entityResource.getById(image.udi, "media")
.then(function (mediaEntity) {
@@ -185,31 +101,131 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
});
}
})
- }
+ }
- $scope.sortableOptions = {
- disabled: !$scope.isMultiPicker,
- items: "li:not(.add-wrapper)",
- cancel: ".unsortable",
- update: function(e, ui) {
- var r = [];
- // TODO: Instead of doing this with a half second delay would be better to use a watch like we do in the
- // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the
- // watch do all the rest.
- $timeout(function(){
- angular.forEach($scope.images, function(value, key) {
+ function init() {
+
+ userService.getCurrentUser().then(function (userData) {
+ if (!$scope.model.config.startNodeId) {
+ $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
+ $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
+ }
+ // only allow users to add and edit media if they have access to the media section
+ var hasAccessToMedia = userData.allowedSections.indexOf("media") !== -1;
+ $scope.allowEditMedia = hasAccessToMedia;
+ $scope.allowAddMedia = hasAccessToMedia;
+
+ setupViewModel();
+
+ //When the model value changes sync the view model
+ $scope.$watch("model.value",
+ function (newVal, oldVal) {
+ if (newVal !== oldVal) {
+ setupViewModel();
+ }
+ });
+ });
+
+ }
+
+ $scope.remove = function (index) {
+ $scope.images.splice(index, 1);
+ $scope.ids.splice(index, 1);
+ sync();
+ };
+
+ $scope.editItem = function (item) {
+ var mediaEditor = {
+ id: item.id,
+ submit: function (model) {
+ editorService.close();
+ // update the selected media item to match the saved media item
+ // the media picker is using media entities so we get the
+ // entity so we easily can format it for use in the media grid
+ if (model && model.mediaNode) {
+ entityResource.getById(model.mediaNode.id, "media")
+ .then(function (mediaEntity) {
+ // if an image is selecting more than once
+ // we need to update all the media items
+ angular.forEach($scope.images, function (image) {
+ if (image.id === model.mediaNode.id) {
+ angular.extend(image, mediaEntity);
+ image.thumbnail = mediaHelper.resolveFileFromEntity(image, true);
+ }
+ });
+ });
+ }
+ },
+ close: function (model) {
+ editorService.close();
+ }
+ };
+ editorService.mediaEditor(mediaEditor);
+ };
+
+ $scope.add = function () {
+ var mediaPicker = {
+ startNodeId: $scope.model.config.startNodeId,
+ startNodeIsVirtual: $scope.model.config.startNodeIsVirtual,
+ multiPicker: multiPicker,
+ onlyImages: onlyImages,
+ disableFolderSelect: disableFolderSelect,
+
+ allowMediaEdit: true,
+ submit: function(model) {
+
+ editorService.close();
+
+ _.each(model.selectedImages, function (media, i) {
+ // if there is no thumbnail, try getting one if the media is not a placeholder item
+ if (!media.thumbnail && media.id && media.metaData) {
+ media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
+ }
+
+ $scope.images.push(media);
+
+ if ($scope.model.config.idType === "udi") {
+ $scope.ids.push(media.udi);
+ }
+ else {
+ $scope.ids.push(media.id);
+ }
+
+ });
+ sync();
+ reloadUpdatedMediaItems(model.updatedMediaNodes);
+ },
+ close: function (model) {
+ editorService.close();
+ reloadUpdatedMediaItems(model.updatedMediaNodes);
+ }
+ }
+
+ editorService.mediaPicker(mediaPicker);
+
+ };
+
+
+
+ $scope.sortableOptions = {
+ disabled: !$scope.isMultiPicker,
+ items: "li:not(.add-wrapper)",
+ cancel: ".unsortable",
+ update: function (e, ui) {
+ var r = [];
+ // TODO: Instead of doing this with a half second delay would be better to use a watch like we do in the
+ // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the
+ // watch do all the rest.
+ $timeout(function () {
+ angular.forEach($scope.images, function (value, key) {
r.push($scope.model.config.idType === "udi" ? value.udi : value.id);
});
$scope.ids = r;
- $scope.sync();
+ sync();
}, 500, false);
}
};
- $scope.sync = function() {
- $scope.model.value = $scope.ids.join();
- };
-
$scope.showAdd = function () {
if (!multiPicker) {
if ($scope.model.value && $scope.model.value !== "") {
@@ -219,10 +235,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
return true;
};
- //here we declare a special method which will be called whenever the value has changed from the server
- //this is instead of doing a watch on the model.value = faster
- $scope.model.onValueChanged = function (newVal, oldVal) {
- //update the display val again if it has changed from the server
- setupViewModel();
- };
+ init();
+
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html
index 48858a507b..3658f828f0 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html
@@ -42,12 +42,4 @@
Tab:
Select the tab who's properties should be displayed. If left blank, the first tab on the doc type will be used.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
index 46eab104e1..83076b54a0 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
@@ -1,11 +1,11 @@