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: " 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.

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

    ", + "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.

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

    ", "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": "

    © 2001 - %0%
    Umbraco.org

    ", + "login_bottomText": "

    © 2001 - %0%
    Umbraco.org

    ", "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/less/application/animations.less b/src/Umbraco.Web.UI.Client/src/less/application/animations.less index 53793b5616..0f2dcb7a7e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/application/animations.less +++ b/src/Umbraco.Web.UI.Client/src/less/application/animations.less @@ -1,8 +1,8 @@ // Animations // ------------------------- -//Animate.css - http://daneden.me/animate -//Licensed under the MIT license - http://opensource.org/licenses/MIT +//Animate.css - https://daneden.github.io/animate.css/ +//Licensed under the MIT license - https://opensource.org/licenses/MIT //Copyright (c) 2013 Daniel Eden .animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);-ms-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);-ms-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);-ms-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);-ms-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);-ms-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);-ms-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);-ms-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);-ms-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);-ms-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);-ms-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);-ms-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);-ms-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;-ms-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);-ms-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);-ms-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);-ms-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);-ms-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);-ms-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);-ms-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);-ms-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);-ms-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);-ms-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);-ms-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);-ms-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);-ms-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);-ms-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);-ms-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);-ms-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);-ms-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);-ms-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);-ms-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);-ms-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);-ms-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);-ms-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);-ms-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);-ms-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);-ms-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);-ms-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);-ms-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);-ms-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);-ms-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);-ms-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);-ms-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);-ms-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);-ms-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);-ms-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);-ms-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);-ms-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);-ms-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);-ms-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);-ms-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);-ms-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;-ms-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);-ms-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;-ms-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp} diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index b6fbba6949..b2f2f99dd5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -75,7 +75,6 @@ @import "listview.less"; @import "gridview.less"; @import "footer.less"; -@import "dragdrop.less"; @import "dashboards.less"; @import "forms/umb-validation-label.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index 2156f75d00..73f059b4ee 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -1,10 +1,20 @@ .umb-toggle { - display: flex; - align-items: center; + display: flex; + align-items: center; + background: none; + border: none; + padding: 0; + + &:focus { + outline: 0; + } } .umb-toggle__handler { - display: inline-block; + position: absolute; + top: 0; + left: 0; + display: block; width: 24px; height: 24px; background-color: @white; @@ -62,4 +72,4 @@ .umb-toggle__icon--right { right: 7px; color: @gray-5; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less b/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less index a4c3a89c99..fadf9c7940 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less @@ -1,6 +1,5 @@ .check_circle { display: flex; - opacity: 0; width: 20px; height: 20px; margin: 0 auto; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index e7564ca2ca..72c1ea5209 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -64,11 +64,9 @@ .umb-editor-header { background: @white; - // flex: 1 1 60px; position: absolute; padding: 0 20px; - z-index: 1; - // box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.16); + z-index: @zindexFixedNavbar; border-bottom: 1px solid @gray-9; width: 100%; box-sizing: border-box; @@ -266,3 +264,14 @@ a.umb-variant-switcher__toggle { margin-right: auto; padding-right: 10px; } + +/* Confirm */ +.umb-editor-confirm { + background-color: @white; + padding: 20px; + position: absolute; + left: 0; + bottom: 0; + z-index: 10; + box-shadow: 0 -3px 12px 0px rgba(0,0,0,0.16); +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less index c6009bce7b..26347c8501 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less @@ -1,5 +1,4 @@ .umb-expansion-panel { - //border: 1px solid @gray-8; background: @white; border-radius: 3px; margin-bottom: 16px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less index a307e5c585..5363a8db9b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less @@ -1,5 +1,10 @@ .umb-prevalues-multivalues { width: 400px; + max-width: 100%; + + .umb-overlay & { + width: 500px; + } } .umb-prevalues-multivalues__left { @@ -27,7 +32,7 @@ .umb-prevalues-multivalues__add button { margin: 0 6px 0 0; - float: right + margin-left: auto; } .umb-prevalues-multivalues__listitem { @@ -46,6 +51,7 @@ .umb-prevalues-multivalues__listitem a { cursor: pointer; + margin-left: auto; } .umb-prevalues-multivalues__listitem input { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less index e27bd0d654..c7905879d8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less @@ -18,12 +18,6 @@ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); } - &.active { - .check_circle { - opacity: 1; - } - } - &.umb-color-box--m { width: 40px; height: 40px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less index c5f9cdde88..407cfaf6a7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less @@ -50,3 +50,34 @@ font-size: 12px; line-height: 1em; } + +.umb-sub-views-nav-item__more { + margin-bottom: 10px; +} + +.umb-sub-views-nav-item__more i { + height: 5px; + width: 5px; + border-radius: 50%; + background: @gray-3; + display: inline-block; + margin: 0 5px 0 0; +} + +.umb-sub-views-nav-item__more i:last-of-type { + margin-right: 0; +} + +// make dots green the an item is active +.umb-sub-views-nav-item.is-active .umb-sub-views-nav-item__more i { + background-color: @turquoise-d1; +} + +.umb-sub-views-nav__dropdown.umb-sub-views-nav__dropdown { + left: auto; + right: 0; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + min-width: auto; + margin-top: 10px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 7dfcc493a8..a4c744bf7f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -4,6 +4,7 @@ // ------------------------- .umb-grid IFRAME { overflow: hidden; + width: 100%; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index e2a8c8fc81..d1ad8ee8ff 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -1,16 +1,13 @@ -.nested-content -{ +.umb-nested-content { text-align: center; } -.nested-content--not-supported -{ +.umb-nested-content--not-supported { opacity: 0.3; pointer-events: none; } -.nested-content-overlay -{ +.umb-nested-content-overlay { position: absolute; top: 0; left: 0; @@ -19,47 +16,39 @@ z-index: 1000; } -.nested-content__item -{ +.umb-nested-content__item { position: relative; text-align: left; border-top: solid 1px transparent; - background: white; - - + background: @white; } -.nested-content__item--active:not(.nested-content__item--single) -{ - background: #f8f8f8; +.umb-nested-content__item--active:not(.umb-nested-content__item--single) { + background: @gray-10; } -.nested-content__item.ui-sortable-placeholder -{ - background: #f8f8f8; - border: 1px dashed #d9d9d9; +.umb-nested-content__item.ui-sortable-placeholder { + background: @gray-10; + border: 1px dashed @gray-8; visibility: visible !important; height: 55px; margin-top: -1px; } -.nested-content__item--single > .nested-content__content -{ +.umb-nested-content__item--single > .umb-nested-content__content { border: 0; } -.nested-content__item--single > .nested-content__content > .umb-pane -{ +.umb-nested-content__item--single > .umb-nested-content__content > .umb-pane { margin: 0; } -.nested-content__header-bar -{ +.umb-nested-content__header-bar { padding: 15px 20px; - border-bottom: 1px dashed #e0e0e0; + border-bottom: 1px dashed @gray-8; text-align: right; cursor: pointer; - background-color: white; + background-color: @white; -moz-user-select: none; -khtml-user-select: none; @@ -67,24 +56,21 @@ -o-user-select: none; } -.nested-content__heading { +.umb-nested-content__heading { line-height: 20px; position: relative; - &.-with-icon - { + &.-with-icon { padding-left: 20px; } - i - { - color: #999; /* same icon color as the icons in the item type picker */ + i { + color: @gray-2; position: absolute; left: 0; } - .nested-content__item-name - { + .umb-nested-content__item-name { max-height: 20px; text-align: left; white-space: nowrap; @@ -94,22 +80,16 @@ } } -.nested-content__icons -{ +.umb-nested-content__icons { opacity: 0; - transition: opacity .15s ease-in-out; - -moz-transition: opacity .15s ease-in-out; - -webkit-transition: opacity .15s ease-in-out; - position: absolute; right: 0px; top: 2px; - background-color: white; + background-color: @white; padding: 5px; - &:before - { + &:before { content: ' '; position: absolute; display: block; @@ -117,126 +97,118 @@ left: -30px; top: 0; bottom: 0; - background: -webkit-linear-gradient(90deg, rgba(255,255,255,0), white); - background: -moz-linear-gradient(90deg, rgba(255,255,255,0), white); background: linear-gradient(90deg, rgba(255,255,255,0), white); } } -.nested-content__header-bar:hover .nested-content__icons, -.nested-content__item--active > .nested-content__header-bar .nested-content__icons -{ +.umb-nested-content__header-bar:hover .umb-nested-content__icons, +.umb-nested-content__item--active > .umb-nested-content__header-bar .umb-nested-content__icons { opacity: 1; } -.nested-content__icon, -.nested-content__icon.nested-content__icon--disabled:hover -{ +.umb-nested-content__icon, +.umb-nested-content__icon.umb-nested-content__icon--disabled:hover { display: inline-block; padding: 4px 6px; margin: 2px; cursor: pointer; - background: #fff; - border: 1px solid #b6b6b6; + background: @white; + border: 1px solid @gray-7; border-radius: 200px; text-decoration: none !important; } -.nested-content__icon:hover, -.nested-content__icon--active +.umb-nested-content__icon:hover, +.umb-nested-content__icon--active { - color: white; - background: #2e8aea; - border-color: #2e8aea; + color: @white; + background: @turquoise-d1; + border-color: @turquoise-d1; text-decoration: none; } -.nested-content__icon .icon, -.nested-content__icon.nested-content__icon--disabled:hover .icon -{ +.umb-nested-content__icon .icon, +.umb-nested-content__icon.umb-nested-content__icon--disabled:hover .icon { display: block; font-size: 16px !important; - color: #5f5f5f; + color: @gray-3; } -.nested-content__icon:hover .icon, -.nested-content__icon--active .icon -{ - color: white; +.umb-nested-content__icon:hover .icon, +.umb-nested-content__icon--active .icon { + color: @white; } -.nested-content__icon--disabled -{ +.umb-nested-content__icon--disabled { opacity: 0.3; } -.nested-content__footer-bar -{ +.umb-nested-content__footer-bar { text-align: center; padding-top: 20px; } -.nested-content__content -{ - border-bottom: 1px dashed #e0e0e0; +.umb-nested-content__content { + border-bottom: 1px dashed @gray-8; } -.nested-content__content .umb-control-group { +.umb-nested-content__content .umb-control-group { padding-bottom: 0; } -.nested-content__item.ui-sortable-helper .nested-content__content -{ +.umb-nested-content__item.ui-sortable-helper .umb-nested-content__content { display: none !important; } -.nested-content__help-text -{ +.umb-nested-content__help-text { display: inline-block; padding: 10px 20px 10px 20px; clear: both; font-size: 14px; - color: #555; - background: #f8f8f8; + color: @gray-3; + background: @gray-10; border-radius: 15px; } -.nested-content__doctypepicker table input, .nested-content__doctypepicker table select { +.umb-nested-content__doctypepicker table input, +.umb-nested-content__doctypepicker table select { width: 100%; padding-right: 0; } -.nested-content__doctypepicker table td.icon-navigation, .nested-content__doctypepicker i.nested-content__help-icon { +.umb-nested-content__doctypepicker table td.icon-navigation, +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon { vertical-align: middle; - color: #CCC; + color: @gray-7; } -.nested-content__doctypepicker table td.icon-navigation:hover, .nested-content__doctypepicker i.nested-content__help-icon:hover { - color: #343434; +.umb-nested-content__doctypepicker table td.icon-navigation:hover, +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon:hover { + color: @gray-2; } -.nested-content__doctypepicker i.nested-content__help-icon { +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon { margin-left: 10px; } -.form-horizontal .nested-content--narrow .controls-row +.form-horizontal .umb-nested-content--narrow .controls-row { margin-left: 40% !important; } -.form-horizontal .nested-content--narrow .controls-row .umb-textstring, -.form-horizontal .nested-content--narrow .controls-row .umb-textarea +.form-horizontal .umb-nested-content--narrow .controls-row .umb-textstring, +.form-horizontal .umb-nested-content--narrow .controls-row .umb-textarea { width: 95%; } -.form-horizontal .nested-content--narrow .controls-row .umb-dropdown { +.form-horizontal .umb-nested-content--narrow .controls-row .umb-dropdown { width: 99%; } -.usky-grid.nested-content__node-type-picker .cell-tools-menu { +.usky-grid.umb-nested-content__node-type-picker .cell-tools-menu { position: relative; transform: translate(-50%, -25%); } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 3d1015f972..07bc32d79a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -72,6 +72,7 @@ font-size: 13px; font-weight: bold; color: @gray-5; + cursor: pointer; } .umb-node-preview__action:hover { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less index 54116e9093..c041f3f4ea 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less @@ -20,6 +20,7 @@ .umb-permission__content { display: flex; flex-direction: column; + justify-content: center; flex: 1 1 auto; cursor: pointer; } @@ -27,4 +28,4 @@ .umb-permission__description { font-size: 13px; color: @gray-5; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/dragdrop.less b/src/Umbraco.Web.UI.Client/src/less/dragdrop.less deleted file mode 100644 index 328ab27e5b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/dragdrop.less +++ /dev/null @@ -1,37 +0,0 @@ -body.dragging, body.dragging * { - cursor: move !important; -} - -li.dragged { - position: absolute; - opacity: 0.5; - z-index: 2000; -} - -.umb-sort li{ - display: block; - margin: 5px; - padding: 5px; - border: 1px solid @gray-7; - background: @gray-10; -} - -.umb-sort .placeholder { - position: relative; - margin: 0; - padding: 0; - border: none; -} - -.umb-sort .placeholder:before { - position: absolute; - content: ""; - width: 0; - height: 0; - margin-top: -5px; - left: -5px; - top: -4px; - border: 5px solid transparent; - border-left-color: @red; - border-right: none; -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 4df1bd9b8d..6940987290 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -496,21 +496,6 @@ input[type="checkbox"][readonly] { border-color: @red-l1 !important; } -//disable the glowing border for the umb-content-name -.show-validation .umb-headline-editor-wrapper input:focus:invalid, -.show-validation .umb-headline-editor-wrapper textarea:focus:invalid, -.show-validation .umb-headline-editor-wrapper select:focus:invalid { - border:none; - color:inherit; - border-color:inherit; - @shadow:inherit; - .box-shadow(@shadow); -} - -.ng-invalid > .umb-headline-editor-wrapper h1{ - border-bottom: 1px dashed @red; color: @red; cursor: pointer; -}; - // FORM ACTIONS // ------------ diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 68a703201a..def6d114ff 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -80,18 +80,6 @@ background-color: @white; } -.umb-headline-editor-wrapper { - position: relative; -} - -.umb-headline-editor-wrapper .help-inline { - right: 0px; - top: 25px; - position: absolute; - font-size: 10px; - color: @red; -} - .umb-panel-header p { margin: 0px 20px; } @@ -100,36 +88,6 @@ opacity: 0.6; } -.umb-headline-editor-wrapper input { - background: none; - border: none; - margin: -6px 0 0 0; - padding: 0 0 2px 0; - border-radius: 0; - line-height: normal; - border: 1px solid transparent; - color: @black; - letter-spacing: -0.01em; -} - -.umb-headline-editor-wrapper input.ng-invalid { - color: @red; -} - -.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder, -.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder, -.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder { - color: @red; - line-height: 22px; -} - -/* -.umb-panel-header i { - font-size: 13px; - vertical-align: middle; -} -*/ - .umb-panel-header-meta { height: 50px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less index 57edbd7266..c7d156d44c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/properties.less +++ b/src/Umbraco.Web.UI.Client/src/less/properties.less @@ -70,7 +70,6 @@ margin: 0 0 0 14px; background-color: @gray-8; position: absolute; - z-index: 0; } .history-item { @@ -78,7 +77,6 @@ align-items: center; margin-bottom: 24px; position: relative; - z-index: 1; } .history-item__avatar { diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index fd9296259f..2b716b9b9f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -139,12 +139,6 @@ ul.color-picker li { border: 2px solid transparent; width: 60px; - &.active { - .check_circle { - opacity: 1; - } - } - .thumbnail{ min-width: auto; width: inherit; @@ -180,13 +174,13 @@ ul.color-picker li { div.color-picker-prediv { display: inline-flex; align-items: center; + max-width: 80%; pre { - display: inline; + display: inline-flex; font-family: monospace; margin-right: 10px; margin-left: 10px; - width: 50%; white-space: nowrap; overflow: hidden; margin-bottom: 0; @@ -194,21 +188,28 @@ ul.color-picker li { padding-top: 7px; padding-bottom: 7px; background: #f7f7f7; + flex: 0 0 auto; } span { margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } } input[type="text"] { - min-width: 40%; - width: 320px; - display: inline-block; + display: flex; + flex: 1 1 100px; margin-top: 1px; + margin-right: 15px; + min-width: auto; + width: auto; } .sp-replacer { + display: inline-flex; margin-right: 18px; } @@ -569,7 +570,7 @@ ul.color-picker li { .imagecropper .umb-cropper__container { position: relative; margin-bottom: 10px; - max-width: 100%; + max-width: 100%; border: 1px solid @gray-10; @media (min-width: 769px) { @@ -582,7 +583,7 @@ ul.color-picker li { top: 3px; right: 3px; cursor: pointer; - z-index: 1; + z-index: @zindexCropperOverlay + 1; } .umb-close-cropper:hover { @@ -656,28 +657,8 @@ ul.color-picker li { line-height: 120px } -.umb-upload-drop-zone{ - margin-bottom:5px; -} - -.umb-upload-drop-zone .info, .umb-upload-button-big{ - display: block; - padding: 20px; - opacity: 1; - border: 1px dashed @gray-8; - background: none; - text-align: center; - font-size: 14px; - color: @gray-8; -} - .umb-upload-button-big:hover{color: @gray-8;} -.umb-upload-drop-zone .info i.icon, .umb-upload-button-big i.icon{ - font-size: 55px; - line-height: 70px -} - .umb-upload-button-big {display: block} .umb-upload-button-big input { left: 0; @@ -687,123 +668,6 @@ ul.color-picker li { } - -// -// Photo folder styling -// -------------------------------------------------- - -.umb-photo-folder .picrow{ - overflow-y: hidden; - position: relative; -} - - - -.umb-photo-folder .picrow div, .umb-photo-preview{ - margin: 0px; - padding: 0px; - border: none; - display: inline-block; - vertical-align: top; - position: relative; -} - - - -.umb-photo-folder .picrow div a:first-child { - width:100%; - height:100%; -} - -.umb-photo-folder .picrow div.umb-photo { - width:100%; - height:100%; - background-color: @gray-10; -} - -.umb-photo-folder a:hover{text-decoration: none} -.umb-photo-folder .umb-non-thumbnail{ - text-align: center; - vertical-align: middle; - font-size: 12px; - background: @gray-10; - color: @black; - text-decoration: none; -} - -.umb-photo-folder .selector-overlay{ - display: none; -} - -//this is a temp hack, to provide selectors in the dialog: -.umb-photo-folder .pic:hover .selector-overlay { - position: absolute; - bottom: 0px; - left: 0px; - right: 0px; - padding: 5px; - background: @black; - z-index: 100; - display: block; - text-align: center; - color: @white; - opacity: 0.4; - text-decoration:none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.umb-photo-folder .umb-non-thumbnail i{ - color: @gray-8; - font-size: 50px; - line-height: 60px; - display: block; - margin: auto; - /*vertically aligns */ - position: relative; - top: 50%; - transform: translateY(-50%); -} - -.umb-photo-folder .umb-non-thumbnail span{ - position: absolute; - display: block; - margin: auto; - width: 100%; - top: 20px; -} - -.umb-photo-folder .selected{ - position: relative; -} - -.umb-photo-folder .selected:before{ - content: "\e165"; - font-family: Icomoon; - - position: absolute; - bottom: 10px; - right: 10px; - - font-size: 24px; - color: @black; - opacity: 0.5; - background: @white; - - line-height: 36px; - text-align: center; - -moz-border-radius: 15px; - border-radius: 15px; - - height: 32px; - width: 32px; - overflow: hidden; - display: block; - z-index: 100; -} - - // // File upload // -------------------------------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/less/tables.less b/src/Umbraco.Web.UI.Client/src/less/tables.less index 5f1acfaf5d..fa8a44ec47 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tables.less +++ b/src/Umbraco.Web.UI.Client/src/less/tables.less @@ -66,11 +66,12 @@ table { // CONDENSED TABLE W/ HALF PADDING // ------------------------------- -.table-condensed { - th, - td { - padding: 4px 5px; - } +.table-condensed tbody td { + padding: 5px 10px; +} + +.table-condensed thead th { + padding: 10px; } @@ -249,3 +250,8 @@ table th[class*="span"], background-color: darken(@infoBackground, 5%); } } + +// table sortable +.table-sortable tbody tr { + cursor: move; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 35481af343..c7d2e8d6ed 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -126,7 +126,7 @@ .color-green, .color-green i{color: @green-d1 !important;} .color-yellow, .color-yellow i{color: @yellowIcon !important;} -/* Colors based on http://zavoloklom.github.io/material-design-color-palette/colors.html */ +/* Colors based on https://zavoloklom.github.io/material-design-color-palette/colors.html */ .btn-color-black {background-color: @black;} .color-black i { color: @black;} diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 8b5ca87470..2d8ad46371 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -180,7 +180,7 @@ app.config(function ($routeProvider) { .when('/:section/:tree/:method?/:id', { //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method. template: "
    ", - //This controller will execute for this route, then we replace the template dynamnically based on the current tree. + //This controller will execute for this route, then we replace the template dynamically based on the current tree. controller: function ($scope, $route, $routeParams, treeService) { if (!$routeParams.tree || !$routeParams.method) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js index d18277737a..fdd438e9cf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js @@ -181,7 +181,7 @@ modalClass: "login-overlay", animation: "slide", show: true, - callback: callback, + callback: callback }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html index 46acddddb4..0e777a5b9f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html @@ -96,7 +96,7 @@
    - +
    Visit umbraco.tv
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js new file mode 100644 index 0000000000..54d5da29a8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js @@ -0,0 +1,70 @@ + (function() { + "use strict"; + + function CompositionsController($scope,$location) { + + var vm = this; + var oldModel = null; + + vm.showConfirmSubmit = false; + + vm.isSelected = isSelected; + vm.openContentType = openContentType; + vm.submit = submit; + vm.close = close; + + function onInit() { + + /* make a copy of the init model so it is possible to roll + back the changes on cancel */ + oldModel = angular.copy($scope.model); + + if(!$scope.model.title) { + $scope.model.title = "Compositions"; + } + + } + + function isSelected(alias) { + if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { + return true; + } + } + + function openContentType(contentType, section) { + var url = (section === "documentType" ? "/settings/documenttypes/edit/" : "/settings/mediaTypes/edit/") + contentType.id; + $location.path(url); + } + + function submit() { + if ($scope.model && $scope.model.submit) { + + // check if any compositions has been removed + vm.compositionRemoved = false; + for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { + var oldComposition = oldModel.compositeContentTypes[i]; + if(_.contains($scope.model.compositeContentTypes, oldComposition) === false) { + vm.compositionRemoved = true; + } + } + + /* submit the form if there havne't been removed any composition + or the confirm checkbox has been checked */ + if(!vm.compositionRemoved || vm.allowSubmit) { + $scope.model.submit($scope.model); + } + } + } + + function close() { + if ($scope.model && $scope.model.close) { + $scope.model.close(oldModel); + } + } + + onInit(); + } + + angular.module("umbraco").controller("Umbraco.Editors.CompositionsController", CompositionsController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html new file mode 100644 index 0000000000..5d7a5420db --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html @@ -0,0 +1,123 @@ +
    + + + +
    + + + + + + + + +
    + +
    + +
    + +
    + + + + + + + + + +
    + +
      +
    • + +
      + +
      + + + +
    • +
    + + + + +
    +
    Warning
    +

    Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?

    + +
    + + + + + + + + + + + + +
    + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js similarity index 84% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js index 5de36dfd44..060e17a55e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js @@ -1,13 +1,15 @@ (function() { "use strict"; - function CopyOverlay($scope, localizationService, eventsService, entityHelper) { + function CopyController($scope, localizationService, eventsService, entityHelper) { var vm = this; vm.hideSearch = hideSearch; vm.selectResult = selectResult; vm.onSearchResults = onSearchResults; + vm.submit = submit; + vm.close = close; var dialogOptions = $scope.model; var searchText = "Search..."; @@ -105,10 +107,22 @@ $scope.miniListView = node; } - onInit(); + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay); + angular.module("umbraco").controller("Umbraco.Editors.CopyController", CopyController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html new file mode 100644 index 0000000000..18a35394ec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html @@ -0,0 +1,94 @@ +
    + + + +
    + + + + + + + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + + +
    + +
    + +
    +
    +
    +
    + + + + + + + + + + +
    + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js new file mode 100644 index 0000000000..fb66552731 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js @@ -0,0 +1,125 @@ +(function() { + "use strict"; + + function EmbedController($scope, $http, $sce, umbRequestHelper, localizationService) { + + var vm = this; + var origWidth = 500; + var origHeight = 300; + + vm.trustedPreview = null; + + $scope.model.embed = { + url: "", + width: 360, + height: 240, + constrain: true, + preview: "", + success: false, + info: "", + supportsDimensions: "" + }; + + vm.showPreview = showPreview; + vm.changeSize = changeSize; + vm.submit = submit; + vm.close = close; + + function onInit() { + if(!$scope.model.title) { + localizationService.localize("general_embed").then(function(value){ + $scope.model.title = value; + }); + } + } + + function showPreview() { + + if ($scope.model.embed.url) { + $scope.model.embed.show = true; + $scope.model.embed.preview = "
    "; + $scope.model.embed.info = ""; + $scope.model.embed.success = false; + + + $http({ + method: 'GET', + url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), + params: { + url: $scope.model.embed.url, + width: $scope.model.embed.width, + height: $scope.model.embed.height + } + }).then(function(response) { + + $scope.model.embed.preview = ""; + + switch (response.data.Status) { + case 0: + //not supported + $scope.model.embed.info = "Not supported"; + break; + case 1: + //error + $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; + break; + case 2: + $scope.model.embed.preview = response.data.Markup; + vm.trustedPreview = $sce.trustAsHtml(response.data.Markup); + $scope.model.embed.supportsDimensions = response.data.SupportsDimensions; + $scope.model.embed.success = true; + break; + } + }, function() { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; + }); + + } else { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Please enter a URL"; + } + } + + function changeSize(type) { + + var width, height; + + if ($scope.model.embed.constrain) { + width = parseInt($scope.model.embed.width, 10); + height = parseInt($scope.model.embed.height, 10); + if (type == 'width') { + origHeight = Math.round((width / origWidth) * height); + $scope.model.embed.height = origHeight; + } else { + origWidth = Math.round((height / origHeight) * width); + $scope.model.embed.width = origWidth; + } + } + if ($scope.model.embed.url !== "") { + showPreview(); + } + + } + + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.EmbedController", EmbedController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html new file mode 100644 index 0000000000..f6a641f2af --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html @@ -0,0 +1,73 @@ +
    +
    + + + + + + + + + + + + + + + + + +

    +
    +
    + +
    + + + + + + + + + + + +
    + +
    +
    +
    + + + + + + + + + + +
    + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js index f428e64e3e..05c76beeae 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js @@ -13,6 +13,28 @@ function IconPickerController($scope, iconHelper, localizationService) { vm.selectIcon = selectIcon; vm.close = close; + vm.colors = [ + { name: "Black", value: "color-black" }, + { name: "Blue Grey", value: "color-blue-grey" }, + { name: "Grey", value: "color-grey" }, + { name: "Brown", value: "color-brown" }, + { name: "Blue", value: "color-blue" }, + { name: "Light Blue", value: "color-light-blue" }, + { name: "Indigo", value: "color-indigo" }, + { name: "Purple", value: "color-purple" }, + { name: "Deep Purple", value: "color-deep-purple" }, + { name: "Cyan", value: "color-cyan" }, + { name: "Green", value: "color-green" }, + { name: "Light Green", value: "color-light-green" }, + { name: "Lime", value: "color-lime" }, + { name: "Yellow", value: "color-yellow" }, + { name: "Amber", value: "color-amber" }, + { name: "Orange", value: "color-orange" }, + { name: "Deep Orange", value: "color-deep-orange" }, + { name: "Red", value: "color-red" }, + { name: "Pink", value: "color-pink" } + ]; + function onInit() { vm.loading = true; @@ -24,6 +46,12 @@ function IconPickerController($scope, iconHelper, localizationService) { vm.loading = false; }); + // set a default color if nothing is passed in + vm.color = $scope.model.color ? $scope.model.color : vm.colors[0].value; + + // if an icon is passed in - preselect it + vm.icon = $scope.model.icon ? $scope.model.icon : undefined; + } function setTitle() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html index 320e0cee8a..8a7358116a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html @@ -32,35 +32,19 @@
    - + +
    -
      -
    • - +
        +
      • +
      • diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.controller.js index 90d3e945a4..526d076048 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.controller.js @@ -28,52 +28,39 @@ } function openMacroPicker() { - - vm.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: {}, - show: true, submit: function(model) { - $scope.model.insert = { "type": "macro", "macroParams": model.macroParams, "selectedMacro": model.selectedMacro }; - $scope.model.submit($scope.model); - - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - + editorService.close(); + }, + close: function() { + editorService.close(); } }; - + editorService.macroPicker(macroPicker); } function openPageFieldOverlay() { - vm.pageFieldOverlay = { - submitButtonLabel: "Insert", - closeButtonlabel: "Cancel", - view: "insertfield", - show: true, + var insertFieldEditor = { submit: function(model) { - $scope.model.insert = { "type": "umbracoField", "umbracoField": model.umbracoField }; - $scope.model.submit($scope.model); - - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + editorService.close(); }, close: function (model) { - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + editorService.close(); } }; + editorService.insertField(insertFieldEditor); } function openDictionaryItemOverlay() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html index 2cc5cb2e5c..58b422ceb2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html @@ -54,15 +54,5 @@ - - - - - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.controller.js similarity index 90% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.controller.js index ebe2feabb9..db3e16f8b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function InsertFieldController($scope, $http, contentTypeResource, localizationService) { + function InsertFieldController($scope, contentTypeResource, localizationService) { var vm = this; @@ -29,6 +29,8 @@ vm.setCasingOption = setCasingOption; vm.setEncodingOption = setEncodingOption; vm.generateOutputSample = generateOutputSample; + vm.submit = submit; + vm.close = close; function onInit() { @@ -139,10 +141,21 @@ } - onInit(); + function submit(model) { + if($scope.model.submit) { + $scope.model.submit(model); + } + } + function close() { + if($scope.model.close) { + $scope.model.close(); + } + } + + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.InsertFieldController", InsertFieldController); + angular.module("umbraco").controller("Umbraco.Editors.InsertFieldController", InsertFieldController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html new file mode 100644 index 0000000000..56bd498fd1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html @@ -0,0 +1,223 @@ +
    + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
    + +
    + + +
    + + +
    + + Add fallback field + +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    + + +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    Format and encoding
    + + +
    +
    +
    +
    + +
    + Date only + Date and time + +
    +
    +
    + + +
    +
    +
    +
    + +
    + Uppercase + Lowercase +
    +
    +
    + + +
    +
    +
    +
    + +
    + HTML + URL +
    +
    +
    + +
    Modify output
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    + + +
    +
    +
    + +
    {{ vm.generateOutputSample() }}
    +
    +
    +
    + +
    +
    +
    + + + + + + + + + + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.controller.js new file mode 100644 index 0000000000..094e108d8f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.controller.js @@ -0,0 +1,47 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.ItemPickerController + * @function + * + * @description + * The controller for a reusable editor to pick items + */ + +function ItemPickerController($scope, localizationService) { + + var vm = this; + + vm.selectItem = selectItem; + vm.submit = submit; + vm.close = close; + + function onInit() { + if (!$scope.model.title) { + localizationService.localize("defaultdialogs_selectItem").then(function(value){ + $scope.model.title = value; + }); + } + } + + function selectItem(item) { + $scope.model.selectedItem = item; + submit($scope.model); + }; + + function submit(model) { + if($scope.model.submit) { + $scope.model.submit(model); + } + } + + function close() { + if($scope.model.close) { + $scope.model.close(); + } + } + + onInit(); + +} + +angular.module("umbraco").controller("Umbraco.Editors.ItemPicker", ItemPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.html new file mode 100644 index 0000000000..9963807772 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/itempicker/itempicker.html @@ -0,0 +1,54 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js similarity index 84% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js index 720edc2114..36640eae75 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js @@ -1,17 +1,24 @@ //used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", - function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService, tinyMceService) { +angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", + function ($scope, eventsService, entityResource, contentResource, mediaHelper, userService, localizationService, tinyMceService, editorService) { + + var vm = this; var dialogOptions = $scope.model; - var anchorPattern = //gi; - var searchText = "Search..."; + + vm.submit = submit; + vm.close = close; + localizationService.localize("general_search").then(function (value) { searchText = value + "..."; }); if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectLink"); + localizationService.localize("defaultdialogs_selectLink") + .then(function(value) { + $scope.model.title = value; + }); } $scope.dialogTreeApi = {}; @@ -83,7 +90,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", } else { contentResource.getById(args.node.id).then(function (resp) { $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); - $scope.model.target.url = resp.urls[0]; + $scope.model.target.url = resp.urls[0].text; }); } @@ -101,11 +108,9 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.switchToMediaPicker = function () { userService.getCurrentUser().then(function (userData) { - $scope.mediaPickerOverlay = { - view: "mediapicker", + var mediaPicker = { startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], startNodeIsVirtual: userData.startMediaIds.length !== 1, - show: true, submit: function (model) { var media = model.selectedImages[0]; @@ -115,12 +120,13 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.model.target.name = media.name; $scope.model.target.url = mediaHelper.resolveFile(media); - debugger; - - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.mediaPicker(mediaPicker); }); }; @@ -167,4 +173,16 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.miniListView = node; } + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html new file mode 100644 index 0000000000..dd258f6c75 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html @@ -0,0 +1,137 @@ + 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 @@ +
    + + + + + + + + + +
    + +
    + + + + + + + + There are no macros available to insert + + +
    + +
    + +
    {{model.selectedMacro.name}}
    + +
      +
    • + + + + + + + +
    • +
    + + + There are no parameters for this macro + + +
    +
    +
    +
    +
    + + + + + + + + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 1d34482358..298c90c822 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService, editorService) { + function($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { localizationService.localize("defaultdialogs_selectMedia") @@ -20,6 +20,7 @@ angular.module("umbraco") $scope.cropSize = dialogOptions.cropSize; $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); $scope.lockedFolder = true; + $scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false; var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); @@ -50,7 +51,7 @@ angular.module("umbraco") pageSize: 100, totalItems: 0, totalPages: 0, - filter: '', + filter: '' }; //preload selected item @@ -435,4 +436,4 @@ angular.module("umbraco") onInit(); - }); \ No newline at end of file + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index f17a08dd47..5a858accbb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -78,14 +78,14 @@ hide-dropzone="{{!activeDrag && images.length > 0 || searchOptions.filter }}" compact="{{ images.length > 0 }}"> - + - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.controller.js similarity index 77% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.controller.js index 83567ee1fa..4fab2a753a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.controller.js @@ -1,10 +1,15 @@ //used for the member picker dialog -angular.module("umbraco").controller("Umbraco.Overlays.MemberGroupPickerController", - function($scope, eventsService, entityResource, searchService, $log, localizationService) { +angular.module("umbraco").controller("Umbraco.Editors.MemberGroupPickerController", + function($scope, eventsService, localizationService) { + + var vm = this; $scope.dialogTreeApi = {}; $scope.multiPicker = $scope.model.multiPicker; + vm.submit = submit; + vm.close = close; + function activate() { if(!$scope.model.title) { @@ -59,6 +64,18 @@ angular.module("umbraco").controller("Umbraco.Overlays.MemberGroupPickerControll $scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler); }; + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + activate(); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html new file mode 100644 index 0000000000..13af3e4108 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html @@ -0,0 +1,49 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js similarity index 85% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js index bccf7487bb..c2a66dddf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js @@ -1,13 +1,15 @@ (function() { "use strict"; - function MoveOverlay($scope, localizationService, eventsService, entityHelper) { + function MoveController($scope, localizationService, entityHelper) { var vm = this; vm.hideSearch = hideSearch; vm.selectResult = selectResult; vm.onSearchResults = onSearchResults; + vm.submit = submit; + vm.close = close; var dialogOptions = $scope.model; var searchText = "Search..."; @@ -105,10 +107,22 @@ $scope.miniListView = node; } + function submit() { + if ($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if ($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay); + angular.module("umbraco").controller("Umbraco.Editors.MoveController", MoveController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html new file mode 100644 index 0000000000..03d7e84342 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html @@ -0,0 +1,84 @@ +
    + + + + + + + + + + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + + + +
    +
    +
    + + + + + + + + + + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js index 1366eb10e4..7239fd22e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js @@ -35,8 +35,8 @@ vm.showSensitiveData = user.userGroups.indexOf("sensitiveData") != -1; }); - //make the default the same as the content type - if (!$scope.model.property.id) { + //make the default the same as the content type + if (!$scope.model.property.dataTypeId) { $scope.model.property.allowCultureVariant = $scope.model.contentTypeAllowCultureVariant; } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js index fc273ab089..1beac99928 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js @@ -58,7 +58,7 @@ sort: { property: { alias: "", - name: "", + name: "" }, direction: "ascending", //This is the value for sorting sent to server translation: { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.controller.js similarity index 52% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.controller.js index d8cc93c1b8..36d7c0f4ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.controller.js @@ -1,35 +1,47 @@ (function () { "use strict"; - function TemplateSectionsOverlayController($scope) { + function TemplateSectionsController($scope, formHelper) { var vm = this; + vm.select = select; + vm.submit = submit; + vm.close = close; + $scope.model.mandatoryRenderSection = false; if(!$scope.model.title) { $scope.model.title = "Sections"; } - vm.select = select; - function onInit() { - if($scope.model.hasMaster) { $scope.model.insertType = 'addSection'; } else { $scope.model.insertType = 'renderBody'; } - } function select(type) { $scope.model.insertType = type; } + function submit(model) { + if (formHelper.submitForm({scope: $scope})) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model.close) { + $scope.model.close(); + } + } + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.TemplateSectionsOverlay", TemplateSectionsOverlayController); + angular.module("umbraco").controller("Umbraco.Editors.TemplateSectionsController", TemplateSectionsController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html new file mode 100644 index 0000000000..d6e3996287 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html @@ -0,0 +1,105 @@ +
    + +
    + + + + + + + + + + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + + + + + +
    + +
    + + +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    + + + + + +
    +
    + +
    + +
    +
    + +
    + + + + + + + + + + +
    + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js deleted file mode 100644 index 7e768336a6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js +++ /dev/null @@ -1,25 +0,0 @@ - (function() { - "use strict"; - - function CompositionsOverlay($scope,$location) { - - var vm = this; - - vm.isSelected = isSelected; - vm.openContentType = openContentType; - - function isSelected(alias) { - if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { - return true; - } - } - function openContentType(contentType, section) { - - var url = (section === "documentType" ? "/settings/documenttypes/edit/" : "/settings/mediaTypes/edit/") + contentType.id; - $location.path(url); - } - } - - angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html deleted file mode 100644 index f2d8902f9d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html +++ /dev/null @@ -1,58 +0,0 @@ -
    - -
    - -
    - -
    - -
    - - - - - - - - -
      -
    • - -
      - -
      - - - -
    • -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html deleted file mode 100644 index 16d00c8689..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html +++ /dev/null @@ -1,55 +0,0 @@ -
    - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - -
    - - -
    - -
    - - -
    - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js deleted file mode 100644 index c64430d9fe..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js +++ /dev/null @@ -1,108 +0,0 @@ -(function() { - "use strict"; - - function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) { - - var vm = this; - var origWidth = 500; - var origHeight = 300; - - $scope.model.embed = { - url: "", - width: 360, - height: 240, - constrain: true, - preview: "", - success: false, - info: "", - supportsDimensions: "" - }; - - vm.showPreview = showPreview; - vm.changeSize = changeSize; - - function onInit() { - if(!$scope.model.title) { - localizationService.localize("general_embed").then(function(value){ - $scope.model.title = value; - }); - } - } - - function showPreview() { - - if ($scope.model.embed.url) { - $scope.model.embed.show = true; - $scope.model.embed.preview = "
    "; - $scope.model.embed.info = ""; - $scope.model.embed.success = false; - - $http({ - method: 'GET', - url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), - params: { - url: $scope.model.embed.url, - width: $scope.model.embed.width, - height: $scope.model.embed.height - } - }) - .success(function(data) { - - $scope.model.embed.preview = ""; - - switch (data.Status) { - case 0: - //not supported - $scope.model.embed.info = "Not supported"; - break; - case 1: - //error - $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; - break; - case 2: - $scope.model.embed.preview = data.Markup; - $scope.model.embed.supportsDimensions = data.SupportsDimensions; - $scope.model.embed.success = true; - break; - } - }) - .error(function() { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; - }); - } else { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Please enter a URL"; - } - } - - function changeSize(type) { - - var width, height; - - if ($scope.model.embed.constrain) { - width = parseInt($scope.model.embed.width, 10); - height = parseInt($scope.model.embed.height, 10); - if (type == 'width') { - origHeight = Math.round((width / origWidth) * height); - $scope.model.embed.height = origHeight; - } else { - origWidth = Math.round((height / origHeight) * width); - $scope.model.embed.width = origWidth; - } - } - if ($scope.model.embed.url !== "") { - showPreview(); - } - - } - - onInit(); - - } - - angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html deleted file mode 100644 index 401acca2c5..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - - - - - - - -

    -
    -
    - -
    - - - - - - - - - - - -
    - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js deleted file mode 100644 index 3c36505fcb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ -function IconPickerOverlay($scope, iconHelper, localizationService) { - - $scope.loading = true; - $scope.model.hideSubmitButton = false; - - $scope.colors = [ - { name: "Black", value: "color-black" }, - { name: "Blue Grey", value: "color-blue-grey" }, - { name: "Grey", value: "color-grey" }, - { name: "Brown", value: "color-brown" }, - { name: "Blue", value: "color-blue" }, - { name: "Light Blue", value: "color-light-blue" }, - { name: "Indigo", value: "color-indigo" }, - { name: "Purple", value: "color-purple" }, - { name: "Deep Purple", value: "color-deep-purple" }, - { name: "Cyan", value: "color-cyan" }, - { name: "Green", value: "color-green" }, - { name: "Light Green", value: "color-light-green" }, - { name: "Lime", value: "color-lime" }, - { name: "Yellow", value: "color-yellow" }, - { name: "Amber", value: "color-amber" }, - { name: "Orange", value: "color-orange" }, - { name: "Deep Orange", value: "color-deep-orange" }, - { name: "Red", value: "color-red" }, - { name: "Pink", value: "color-pink" } - ]; - - if (!$scope.color) { - // Set default selected color to black - $scope.color = $scope.colors[0].value; - }; - - if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectIcon"); - }; - - if ($scope.model.color) { - $scope.color = $scope.model.color; - }; - - if ($scope.model.icon) { - $scope.icon = $scope.model.icon; - }; - - iconHelper.getIcons().then(function (icons) { - $scope.icons = icons; - $scope.loading = false; - }); - - $scope.selectIcon = function (icon, color) { - $scope.model.icon = icon; - $scope.model.color = color; - $scope.submitForm($scope.model); - }; - - var unsubscribe = $scope.$on("formSubmitting", - function () { - if ($scope.color) { - $scope.model.color = $scope.color; - } - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on("$destroy", - function () { - unsubscribe(); - }); -} - -angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html deleted file mode 100644 index 0098463cb4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html +++ /dev/null @@ -1,40 +0,0 @@ - - -
    - -
    - -
    - -
    - -
    - - - -
    - -
    - - - No icons were found. - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html deleted file mode 100644 index 3218b44dd8..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html +++ /dev/null @@ -1,188 +0,0 @@ -
    - - -
    -
    - -
    - -
    -
    -
    - -
    - - -
    - - -
    - - Add fallback field - -
    -
    - -
    - -
    -
    -
    -
    -
    - - -
    - - -
    -
    - -
    - -
    -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - -
    Format and encoding
    - - -
    -
    -
    -
    - -
    - Date only - Date and time - -
    -
    -
    - - -
    -
    -
    -
    - -
    - Uppercase - Lowercase -
    -
    -
    - - -
    -
    -
    -
    - -
    - HTML - URL -
    -
    -
    - -
    Modify output
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    {{ vm.generateOutputSample() }}
    -
    -
    -
    - -
    - - diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html deleted file mode 100644 index 5dd1275014..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html +++ /dev/null @@ -1,100 +0,0 @@ -
    - - - - - - - - - - - - - - - - - - - - - -
    -
    - Link to page -
    - -
    - - - -
    - - - - -
    - - -
    -
    - - - - -
    - -
    -
    - Link to media -
    - - Select media - -
    - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html deleted file mode 100644 index 8f16bc40d4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html +++ /dev/null @@ -1,63 +0,0 @@ -
    - -
    - - -
    - - - - - - - - There are no macros available to insert - - -
    - -
    - -
    {{model.selectedMacro.name}}
    - -
      -
    • - - - - - - - -
    • -
    - - - There are no parameters for this macro - - -
    -
    - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js deleted file mode 100644 index d2d2178861..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ /dev/null @@ -1,400 +0,0 @@ -//used for the media picker dialog -angular.module("umbraco") - .controller("Umbraco.Overlays.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) { - - if (!$scope.model.title) { - localizationService.localize("defaultdialogs_selectMedia").then(function(value){ - $scope.model.title = value; - }); - } - - var dialogOptions = $scope.model; - - $scope.disableFolderSelect = dialogOptions.disableFolderSelect; - $scope.onlyImages = dialogOptions.onlyImages; - $scope.showDetails = dialogOptions.showDetails; - $scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false; - $scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1; - $scope.cropSize = dialogOptions.cropSize; - $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); - $scope.lockedFolder = true; - - var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; - var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); - if ($scope.onlyImages) { - $scope.acceptedFileTypes = mediaHelper.formatFileTypes(umbracoSettings.imageFileTypes); - } else { - // Use whitelist of allowed file types if provided - if (allowedUploadFiles !== '') { - $scope.acceptedFileTypes = allowedUploadFiles; - } else { - // If no whitelist, we pass in a blacklist by adding ! to the file extensions, allowing everything EXCEPT for disallowedUploadFiles - $scope.acceptedFileTypes = !mediaHelper.formatFileTypes(umbracoSettings.disallowedUploadFiles); - } - } - - $scope.maxFileSize = umbracoSettings.maxFileSize + "KB"; - - $scope.model.selectedImages = []; - - $scope.acceptedMediatypes = []; - mediaTypeHelper.getAllowedImagetypes($scope.startNodeId) - .then(function(types) { - $scope.acceptedMediatypes = types; - }); - - $scope.searchOptions = { - pageNumber: 1, - pageSize: 100, - totalItems: 0, - totalPages: 0, - filter: '', - }; - - //preload selected item - $scope.target = undefined; - if (dialogOptions.currentTarget) { - $scope.target = dialogOptions.currentTarget; - } - - function onInit() { - if ($scope.startNodeId !== -1) { - entityResource.getById($scope.startNodeId, "media") - .then(function (ent) { - $scope.startNodeId = ent.id; - run(); - }); - } else { - run(); - } - } - - function run() { - //default root item - if (!$scope.target) { - if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { - entityResource.getById($scope.lastOpenedNode, "media") - .then(ensureWithinStartNode, gotoStartNode); - } else { - gotoStartNode(); - } - } else { - //if a target is specified, go look it up - generally this target will just contain ids not the actual full - //media object so we need to look it up - var id = $scope.target.udi ? $scope.target.udi : $scope.target.id - var altText = $scope.target.altText; - if (id) { - mediaResource.getById(id) - .then(function (node) { - $scope.target = node; - if (ensureWithinStartNode(node)) { - selectImage(node); - $scope.target.url = mediaHelper.resolveFile(node); - $scope.target.altText = altText; - $scope.openDetailsDialog(); - } - }, - gotoStartNode); - } else { - gotoStartNode(); - } - } - } - - $scope.upload = function(v) { - angular.element(".umb-file-dropzone-directive .file-select").click(); - }; - - $scope.dragLeave = function(el, event) { - $scope.activeDrag = false; - }; - - $scope.dragEnter = function(el, event) { - $scope.activeDrag = true; - }; - - $scope.submitFolder = function() { - if ($scope.newFolderName) { - $scope.creatingFolder = true; - mediaResource - .addFolder($scope.newFolderName, $scope.currentFolder.id) - .then(function(data) { - //we've added a new folder so lets clear the tree cache for that specific item - treeService.clearCache({ - cacheKey: "__media", //this is the main media tree cache key - childrenOf: data.parentId //clear the children of the parent - }); - $scope.creatingFolder = false; - $scope.gotoFolder(data); - $scope.showFolderInput = false; - $scope.newFolderName = ""; - }); - } else { - $scope.showFolderInput = false; - } - }; - - $scope.enterSubmitFolder = function(event) { - if (event.keyCode === 13) { - $scope.submitFolder(); - event.stopPropagation(); - } - }; - - $scope.gotoFolder = function(folder) { - if (!$scope.multiPicker) { - deselectAllImages($scope.model.selectedImages); - } - - if (!folder) { - folder = { id: -1, name: "Media", icon: "icon-folder" }; - } - - if (folder.id > 0) { - entityResource.getAncestors(folder.id, "media") - .then(function(anc) { - $scope.path = _.filter(anc, - function(f) { - return f.path.indexOf($scope.startNodeId) !== -1; - }); - }); - - } else { - $scope.path = []; - } - - mediaTypeHelper.getAllowedImagetypes(folder.id) - .then(function (types) { - $scope.acceptedMediatypes = types; - }); - - $scope.lockedFolder = folder.id === -1 && $scope.model.startNodeIsVirtual; - - $scope.currentFolder = folder; - localStorageService.set("umbLastOpenedMediaNodeId", folder.id); - return getChildren(folder.id); - }; - - $scope.clickHandler = function(image, event, index) { - if (image.isFolder) { - if ($scope.disableFolderSelect) { - $scope.gotoFolder(image); - } else { - eventsService.emit("dialogs.mediaPicker.select", image); - selectImage(image); - } - } else { - eventsService.emit("dialogs.mediaPicker.select", image); - if ($scope.showDetails) { - - $scope.target = image; - - // handle both entity and full media object - if (image.image) { - $scope.target.url = image.image; - } else { - $scope.target.url = mediaHelper.resolveFile(image); - } - - $scope.openDetailsDialog(); - } else { - selectImage(image); - } - } - }; - - $scope.clickItemName = function(item) { - if (item.isFolder) { - $scope.gotoFolder(item); - } - }; - - function selectImage(image) { - if (image.selected) { - for (var i = 0; $scope.model.selectedImages.length > i; i++) { - var imageInSelection = $scope.model.selectedImages[i]; - if (image.key === imageInSelection.key) { - image.selected = false; - $scope.model.selectedImages.splice(i, 1); - } - } - } else { - if (!$scope.multiPicker) { - deselectAllImages($scope.model.selectedImages); - } - image.selected = true; - $scope.model.selectedImages.push(image); - } - } - - function deselectAllImages(images) { - for (var i = 0; i < images.length; i++) { - var image = images[i]; - image.selected = false; - } - images.length = 0; - } - - $scope.onUploadComplete = function(files) { - $scope.gotoFolder($scope.currentFolder).then(function() { - if (files.length === 1 && $scope.model.selectedImages.length === 0) { - selectImage($scope.images[$scope.images.length - 1]); - } - }); - }; - - $scope.onFilesQueue = function() { - $scope.activeDrag = false; - }; - - function ensureWithinStartNode(node) { - // make sure that last opened node is on the same path as start node - var nodePath = node.path.split(","); - - if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) { - $scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" }); - return true; - } else { - $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); - return false; - } - } - - function gotoStartNode(err) { - $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); - } - - $scope.openDetailsDialog = function() { - - $scope.mediaPickerDetailsOverlay = {}; - $scope.mediaPickerDetailsOverlay.show = true; - - $scope.mediaPickerDetailsOverlay.submit = function(model) { - $scope.model.selectedImages.push($scope.target); - $scope.model.submit($scope.model); - - $scope.mediaPickerDetailsOverlay.show = false; - $scope.mediaPickerDetailsOverlay = null; - }; - - $scope.mediaPickerDetailsOverlay.close = function(oldModel) { - $scope.mediaPickerDetailsOverlay.show = false; - $scope.mediaPickerDetailsOverlay = null; - }; - }; - - var debounceSearchMedia = _.debounce(function() { - $scope.$apply(function() { - if ($scope.searchOptions.filter) { - searchMedia(); - } else { - // reset pagination - $scope.searchOptions = { - pageNumber: 1, - pageSize: 100, - totalItems: 0, - totalPages: 0, - filter: '' - }; - getChildren($scope.currentFolder.id); - } - }); - }, 500); - - $scope.changeSearch = function() { - $scope.loading = true; - debounceSearchMedia(); - }; - - $scope.changePagination = function(pageNumber) { - $scope.loading = true; - $scope.searchOptions.pageNumber = pageNumber; - searchMedia(); - }; - - function searchMedia() { - $scope.loading = true; - entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions) - .then(function(data) { - // update image data to work with image grid - angular.forEach(data.items, - function(mediaItem) { - // set thumbnail and src - mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true); - mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false); - // set properties to match a media object - mediaItem.properties = []; - if (mediaItem.metaData) { - if (mediaItem.metaData.umbracoWidth && mediaItem.metaData.umbracoHeight) { - mediaItem.properties.push({ - alias: "umbracoWidth", - value: mediaItem.metaData.umbracoWidth.Value - }); - mediaItem.properties.push({ - alias: "umbracoHeight", - value: mediaItem.metaData.umbracoHeight.Value - }); - } - if (mediaItem.metaData.umbracoFile) { - mediaItem.properties.push({ - alias: "umbracoFile", - editor: mediaItem.metaData.umbracoFile.PropertyEditorAlias, - value: mediaItem.metaData.umbracoFile.Value - }); - } - } - }); - // update images - $scope.images = data.items ? data.items : []; - // update pagination - if (data.pageNumber > 0) - $scope.searchOptions.pageNumber = data.pageNumber; - if (data.pageSize > 0) - $scope.searchOptions.pageSize = data.pageSize; - $scope.searchOptions.totalItems = data.totalItems; - $scope.searchOptions.totalPages = data.totalPages; - // set already selected images to selected - preSelectImages(); - $scope.loading = false; - }); - } - - function getChildren(id) { - $scope.loading = true; - return mediaResource.getChildren(id) - .then(function(data) { - $scope.searchOptions.filter = ""; - $scope.images = data.items ? data.items : []; - // set already selected images to selected - preSelectImages(); - $scope.loading = false; - }); - } - - function preSelectImages() { - for (var folderImageIndex = 0; folderImageIndex < $scope.images.length; folderImageIndex++) { - var folderImage = $scope.images[folderImageIndex]; - var imageIsSelected = false; - - if ($scope.model && angular.isArray($scope.model.selectedImages)) { - for (var selectedImageIndex = 0; - selectedImageIndex < $scope.model.selectedImages.length; - selectedImageIndex++) { - var selectedImage = $scope.model.selectedImages[selectedImageIndex]; - - if (folderImage.key === selectedImage.key) { - imageIsSelected = true; - } - } - } - - if (imageIsSelected) { - folderImage.selected = true; - } - } - } - - onInit(); - - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html deleted file mode 100644 index 482485c4b2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ /dev/null @@ -1,150 +0,0 @@ -
    - -
    - -
    - - - - - - -
    - - -
    - -
    - -
    - -
    -
    - - - - - - - -
    - - -
    - - - - - -
    - - - -
    - -
    - - -
    - -
    - -
    - Preview -
    - - - - -
    - -
    - -
    - - -
    - -
    - - -
    - - -
    - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html deleted file mode 100644 index ab481d5f0d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html +++ /dev/null @@ -1,13 +0,0 @@ -
    - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html deleted file mode 100644 index 30afe56d4b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html +++ /dev/null @@ -1,44 +0,0 @@ -
    -
    -
    - - -
    - -
    - - -
    - -
    - - -
    -
    - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html deleted file mode 100644 index b97057302e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html +++ /dev/null @@ -1,64 +0,0 @@ -
    - - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    - - - - - -
    - -
    - - -
    - -
    -
    -
    - -
    - -
    - -
    -
    -
    - -
    - -
    -
    - - - - - -
    -
    - -
    -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html index e850bf22b8..fc62485521 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html @@ -1,4 +1,4 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html index c5cecb2190..41ddd04584 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html @@ -8,38 +8,14 @@
    - - - - -
    - - - - - - - -
    - + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index ac38761010..faa3be7c16 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -169,9 +169,12 @@ - - {{publishStatus.label}} - +
    + {{status.culture}}: + + {{status.label}} + +
    @@ -180,12 +183,12 @@ + style="min-width: 100%; margin-bottom: 0;" + icon="node.icon" + name="node.contentTypeName" + alias="documentType.alias" + allow-open="allowChangeDocumentType" + on-open="openDocumentType(documentType)"> @@ -197,7 +200,7 @@ ng-change="updateTemplate(node.template)"> - + Open diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html new file mode 100644 index 0000000000..921f595142 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -0,0 +1,18 @@ +
    + +
    + +
    +
    {{ group.label }}
    +   +
    + +
    + + + +
    + +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html new file mode 100644 index 0000000000..f0c1132005 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html @@ -0,0 +1,34 @@ +
    + + + +
    + + + + + + + +
    +
    + +
    +
    +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-state.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-state.html new file mode 100644 index 0000000000..3a41f0623d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-state.html @@ -0,0 +1,6 @@ +
    +
    +
    +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html index b3978ca754..9e675b15ba 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html @@ -1,5 +1,10 @@ 
    - + + +
    + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 9d259d2c70..0914346189 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -1,5 +1,5 @@ 
    - +
    @@ -12,7 +12,7 @@
    + title="{{$parent.icon}}">
    @@ -20,36 +20,33 @@
    -
    - +
    @@ -100,17 +98,15 @@
    - +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html index df64055d1e..3166992e8a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html @@ -1,14 +1,45 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html new file mode 100644 index 0000000000..068ba1a1b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html @@ -0,0 +1,10 @@ +
    + +
    +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html index 76d3104924..4e4a7229cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html @@ -3,12 +3,12 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html index 21500e7c84..8ea809ef8b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html @@ -1,9 +1,10 @@
    -
    + +
    - + -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index 24bc1de6ed..a6f2096002 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -2,7 +2,7 @@
    - +
    @@ -19,4 +19,4 @@
    -
    \ No newline at end of file +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html index 5e6fe6d662..b2acbb8774 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html @@ -6,7 +6,7 @@ ng-click="load(node)">  - {{::node.name}} + {{node.name}} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html index eae299e579..d3105d52a1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html @@ -1,7 +1,7 @@ 
    - diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 098f36cf20..4aa0072b6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -299,12 +299,4 @@ - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index c55d1e9212..6e8b70f4f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -2,7 +2,7 @@
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html index ca7a30f152..eb94b349d2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -13,9 +13,9 @@
    - Edit - Open - Remove + Edit + Open + Remove
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html new file mode 100644 index 0000000000..f205dffa57 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html @@ -0,0 +1,54 @@ +
    + + +
    + +

    Click to upload

    + +
    + +
    +
    + +
    + +
    +
    +
    + {{file.fileName}} + + {{file.fileName}} + +
    +
    +
    + +
    + + + + .{{file.extension}} + + .{{file.extension}} + + + +
    {{file.fileName}}
    +
    + +
    + + + +
    +
    +
    +
    +
    + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js index 3bc2e231bb..7d3f10fab4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js @@ -1,18 +1,35 @@ (function () { "use strict"; - function ContentAppContentController($scope) { + function ContentAppContentController($scope, $timeout, serverValidationManager) { var vm = this; + vm.loading = true; function onInit() { - angular.forEach($scope.model.tabs, function(group){ - group.open = true; - }); + vm.content = $scope.model.viewModel; + serverValidationManager.notify(); + vm.loading = false; } onInit(); + //if this variant has a culture/language assigned, then we need to watch it since it will change + //if the language drop down changes and we need to re-init + if ($scope.model.viewModel.language) { + $scope.$watch(function () { + return $scope.model.viewModel.language.culture; + }, function (newVal, oldVal) { + if (newVal !== oldVal) { + vm.loading = true; + + //TODO: Can we minimize the flicker? + $timeout(function () { + onInit(); + }, 100); + } + }); + } } angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ContentController", ContentAppContentController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html index 95676a0bcd..d391f2cd95 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html @@ -1,18 +1,8 @@
    -
    - -
    -
    {{ group.label }}
    -   -
    - -
    - - - -
    - -
    - + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html index 197aeb35b9..c40486012c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html @@ -1,4 +1,4 @@ - \ No newline at end of file + ng-if="content" + node="content"> + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js deleted file mode 100644 index d21715854d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js +++ /dev/null @@ -1,23 +0,0 @@ -(function () { - "use strict"; - - function ContentAppListViewController($scope) { - - var vm = this; - - vm.listViewGroup = {}; - - function onInit() { - angular.forEach($scope.model.tabs, function(group){ - if(group.alias === "umbContainerView") { - vm.listViewGroup = group; - } - }); - } - - onInit(); - - } - - angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ListViewController", ContentAppListViewController); -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html index 74d690c671..a19d7ea4d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html @@ -1,6 +1,6 @@ -
    +
    - + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html new file mode 100644 index 0000000000..0283459483 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html @@ -0,0 +1,24 @@ +
    +
    + +

    UI magic to be done...

    +
    +
    + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js new file mode 100644 index 0000000000..6f72543cfc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js @@ -0,0 +1,17 @@ +(function () { + "use strict"; + + function AssignDomainController($scope) { + + var vm = this; + vm.closeDialog = closeDialog; + + function closeDialog() { + $scope.nav.hideDialog(); + } + + } + + angular.module("umbraco").controller("Umbraco.Editors.Content.AssignDomainController", AssignDomainController); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 6e2865046d..356e056533 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -38,6 +38,7 @@ function ContentDeleteController($scope, $timeout, contentResource, treeService, //ensure the recycle bin has child nodes now var recycleBin = treeService.getDescendantNode(rootNode, -20); if (recycleBin) { + //TODO: This seems to return a rejection and we end up with "Possibly unhanded rejection" treeService.syncTree({ node: recycleBin, path: treeService.getPath(recycleBin), forceReload: true }); } }, 500); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index d3086a5b34..bb7f198a36 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -6,7 +6,7 @@ * @description * The controller for the content editor */ -function ContentEditController($scope, $routeParams, contentResource) { +function ContentEditController($scope, $rootScope, $routeParams, contentResource) { var infiniteMode = $scope.model && $scope.model.infiniteMode; @@ -23,7 +23,15 @@ function ContentEditController($scope, $routeParams, contentResource) { $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty; $scope.page = $routeParams.page; $scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create; - $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; //load the default culture selected in the main tree if any + //load the default culture selected in the main tree if any + $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + + //Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered. + //This is so we can listen to changes on the cculture parameter since that will not cause a route change + // and then we can pass in the updated culture to the editor + $scope.$on('$routeUpdate', function (event, next) { + $scope.culture = next.params.cculture; + }); } angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.notify.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.notify.controller.js new file mode 100644 index 0000000000..4c446b11e0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.notify.controller.js @@ -0,0 +1,48 @@ +(function () { + function CreateNotifyController( + $scope, + contentResource, + navigationService, + angularHelper) { + var vm = this; + var currentForm; + vm.notifyOptions = []; + vm.save = save; + vm.cancel = cancel; + vm.message = { + name: $scope.currentNode.name + };; + function onInit() { + vm.loading = true; + contentResource.getNotifySettingsById($scope.currentNode.id).then(function (options) { + currentForm = angularHelper.getCurrentForm($scope); + vm.loading = false; + vm.notifyOptions = options; + }); + } + function cancel() { + navigationService.hideMenu(); + }; + function save(notifyOptions) { + vm.saveState = "busy"; + vm.saveError = false; + vm.saveSuccces = false; + var selectedString = []; + angular.forEach(notifyOptions, function (option) { + if (option.checked === true && option.notifyCode) { + selectedString.push(option.notifyCode); + } + }) + + contentResource.setNotifySettingsById($scope.currentNode.id, selectedString).then(function () { + vm.saveState = "success"; + vm.saveSuccces = true; + }, function (error) { + vm.saveState = "error"; + vm.saveError = error; + }); + } + onInit(); + } + angular.module("umbraco").controller("Umbraco.Editors.Content.CreateNotifyController", CreateNotifyController); +}()); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.recyclebin.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.recyclebin.controller.js index 4a8c50db39..ab3f5cc04a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.recyclebin.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.recyclebin.controller.js @@ -22,18 +22,9 @@ function ContentRecycleBinController($scope, $routeParams, contentResource, navi $routeParams.id = "-20"; contentResource.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: "content", path: ["-1", $routeParams.id], forceReload: false }); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js new file mode 100644 index 0000000000..4581b78c8c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js @@ -0,0 +1,82 @@ +(function () { + "use strict"; + + function ContentSortController($scope, $filter, contentResource, 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; + contentResource.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; }) + }; + + contentResource.sort(args) + .then(function(){ + navigationService.syncTree({ tree: "content", 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.Content.SortController", ContentSortController); +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/notify.html b/src/Umbraco.Web.UI.Client/src/views/content/notify.html new file mode 100644 index 0000000000..ec9b3d2b0d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/notify.html @@ -0,0 +1,44 @@ +
    + +
    +
    +
    +
    +
    {{vm.saveError.errorMsg}}
    +
    {{vm.saveError.data.message}}
    +
    +
    +
    +
    + {{currentNode.name}} +
    +
    +
    +
    +
    Set your notification for {{ currentNode.name }}
    + + + + +
    +
    + +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js new file mode 100644 index 0000000000..6d9c8303b4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js @@ -0,0 +1,122 @@ +(function () { + "use strict"; + + function PublishController($scope, localizationService) { + + var vm = this; + vm.loading = true; + vm.hasPristineVariants = false; + + vm.changeSelection = changeSelection; + vm.dirtyVariantFilter = dirtyVariantFilter; + vm.pristineVariantFilter = pristineVariantFilter; + + /** Returns true if publishing is possible based on if there are un-published mandatory languages */ + function canPublish() { + var selected = []; + for (var i = 0; i < vm.variants.length; i++) { + var variant = vm.variants[i]; + + //if this variant will show up in the publish-able list + var publishable = dirtyVariantFilter(variant); + + if ((variant.language.isMandatory && (variant.state === "NotCreated" || variant.state === "Draft")) + && (!publishable || !variant.publish)) { + //if a mandatory variant isn't published and it's not publishable or not selected to be published + //then we cannot publish anything + + //TODO: Show a message when this occurs + return false; + } + + if (variant.publish) { + selected.push(variant.publish); + } + } + return selected.length > 0; + } + + function changeSelection(variant) { + $scope.model.disableSubmitButton = !canPublish(); + //need to set the Save state to true if publish is true + variant.save = variant.publish; + } + + function dirtyVariantFilter(variant) { + //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's + // * the active one + // * it's editor is in a $dirty state + // * it has pending saves + // * it is unpublished + // * it is in NotCreated state + return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges" || variant.state === "NotCreated"); + } + + function pristineVariantFilter(variant) { + return !(dirtyVariantFilter(variant)); + } + + function onInit() { + + vm.variants = $scope.model.variants; + + if (!$scope.model.title) { + localizationService.localize("content_readyToPublish").then(function (value) { + $scope.model.title = value; + }); + } + + vm.hasPristineVariants = false; + + _.each(vm.variants, + function (variant) { + variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : ""); + variant.htmlId = "_content_variant_" + variant.compositeId; + + //check for pristine variants + if (!vm.hasPristineVariants) { + vm.hasPristineVariants = pristineVariantFilter(variant); + } + }); + + if (vm.variants.length !== 0) { + //now sort it so that the current one is at the top + vm.variants = _.sortBy(vm.variants, function (v) { + return v.active ? 0 : 1; + }); + + var active = _.find(vm.variants, function (v) { + return v.active; + }); + + if (active) { + //ensure that the current one is selected + active.publish = true; + active.save = true; + } + + $scope.model.disableSubmitButton = !canPublish(); + + } else { + //disable Publish button if we have nothing to publish, should not happen + $scope.model.disableSubmitButton = true; + } + + vm.loading = false; + + } + + onInit(); + + //when this dialog is closed, reset all 'publish' flags + $scope.$on('$destroy', function () { + for (var i = 0; i < vm.variants.length; i++) { + vm.variants[i].publish = false; + vm.variants[i].save = false; + } + }); + } + + angular.module("umbraco").controller("Umbraco.Overlays.PublishController", PublishController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html similarity index 62% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html rename to src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html index 9e95efe2cf..46ad4e7299 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html @@ -18,27 +18,29 @@ type="checkbox" ng-model="variant.publish" ng-change="vm.changeSelection(variant)" - ng-disabled="variant.current === true" + ng-disabled="variant.active === true" style="margin-right: 8px;" val-server-field="{{variant.htmlId}}" />
    -
    -
    -
    {{ variant.state }}
    + +
    + +
    {{publishVariantSelectorForm.publishVariantSelector.errorMsg}}
    - +
    - +
    -
    +
    @@ -48,8 +50,11 @@
    -
    {{ variant.language.name }}
    -
    {{ variant.state }}
    +
    + {{ variant.language.name }} + * +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js similarity index 52% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js rename to src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js index ba8d4f1712..a99da13811 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js @@ -1,45 +1,29 @@ (function () { "use strict"; - function PublishController($scope, localizationService) { + function SaveContentController($scope, localizationService) { var vm = this; - vm.variants = $scope.model.variants; - vm.changeSelection = changeSelection; vm.loading = true; - vm.dirtyVariantFilter = dirtyVariantFilter; - vm.pristineVariantFilter = pristineVariantFilter; vm.hasPristineVariants = false; - //watch this model, if it's reset, then re init - $scope.$watch(function () { - return $scope.model.variants; - }, - function (newVal, oldVal) { - vm.variants = newVal; - if (oldVal && oldVal.length) { - //re-bind the selections - for (var i = 0; i < oldVal.length; i++) { - var found = _.find(vm.variants, function (v) { - return v.language.id === oldVal[i].language.id; - }); - if (found) { - found.publish = oldVal[i].publish; - } - } - } - onInit(); - }); - + vm.changeSelection = changeSelection; + vm.dirtyVariantFilter = dirtyVariantFilter; + vm.pristineVariantFilter = pristineVariantFilter; + function changeSelection(variant) { var firstSelected = _.find(vm.variants, function (v) { - return v.publish; + return v.save; }); $scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected } function dirtyVariantFilter(variant) { - return (variant.current || variant.isEdited === true || (variant.isEdited === false && variant.state === "Unpublished")); + //determine a variant is 'dirty' (meaning it will show up as save-able) if it's + // * the active one + // * it's editor is in a $dirty state + // * it is in NotCreated state + return (variant.active || variant.isDirty); } function pristineVariantFilter(variant) { @@ -48,8 +32,10 @@ function onInit() { + vm.variants = $scope.model.variants; + if(!$scope.model.title) { - localizationService.localize("content_readyToPublish").then(function(value){ + localizationService.localize("content_readyToSave").then(function(value){ $scope.model.title = value; }); } @@ -59,7 +45,7 @@ _.each(vm.variants, function (variant) { variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : ""); - variant.htmlId = "publish_variant_" + variant.compositeId; + variant.htmlId = "_content_variant_" + variant.compositeId; //check for pristine variants if (!vm.hasPristineVariants) { @@ -70,19 +56,37 @@ if (vm.variants.length !== 0) { //now sort it so that the current one is at the top vm.variants = _.sortBy(vm.variants, function (v) { - return v.current ? 0 : 1; + return v.active ? 0 : 1; }); - //ensure that the current one is selected - vm.variants[0].publish = true; + + var active = _.find(vm.variants, function (v) { + return v.active; + }); + + if (active) { + //ensure that the current one is selected + active.save = true; + } + } else { - //disable Publish button if we have nothing to publish + //disable save button if we have nothing to save $scope.model.disableSubmitButton = true; } vm.loading = false; } + + onInit(); + + //when this dialog is closed, reset all 'save' flags + $scope.$on('$destroy', function () { + for (var i = 0; i < vm.variants.length; i++) { + vm.variants[i].save = false; + } + }); + } - angular.module("umbraco").controller("Umbraco.Overlays.PublishController", PublishController); + angular.module("umbraco").controller("Umbraco.Overlays.SaveContentController", SaveContentController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html new file mode 100644 index 0000000000..0515bd878e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html @@ -0,0 +1,62 @@ +
    + +
    +

    +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    +
    {{saveVariantSelectorForm.saveVariantSelector.errorMsg}}
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +

    +
    + +
    +
    +
    + {{ variant.language.name }} + * +
    + +
    +
    +
    + +
    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

    To get you started:

      diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html index d8d3d127eb..5b4bc988c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html @@ -5,10 +5,10 @@

      Find out more:

      diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html index 02ec1e6dd8..680f7cc889 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html @@ -1,7 +1,7 @@

      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.

      -
      diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/desktopmediauploader.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/desktopmediauploader.html index 59a50f44fb..10609bf497 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/desktopmediauploader.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/desktopmediauploader.html @@ -8,7 +8,7 @@

      Download Desktop Media Uploader now.

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

      diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardintro.html index 0af91b4544..b8c28bbd39 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardintro.html @@ -10,4 +10,4 @@
    • Drag the files and folders you wish to upload directly into the Desktop Media Uploader application
    • Click Upload to start uploading
    -

    For a more thorough guide on how to use the Desktop Media Uploader, checkout this video.

    +

    For a more thorough guide on how to use the Desktop Media Uploader, checkout this video.

    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html index 08e7fb3c41..f04aff24c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html @@ -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

    To get you started:

      @@ -15,4 +15,4 @@
  • - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html index 8037e00f58..10e9983e05 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html @@ -7,9 +7,9 @@
  • Read more about working with the Items in Settings in the Documentation section of Our Umbraco
  • Download the Editors Manual for details on working with the Umbraco UI
  • Ask a question in the Community Forum
  • -
  • Watch our tutorial videos (some are free, some require a subscription)
  • -
  • Find out about our productivity boosting tools and commercial support
  • -
  • Find out about real-life training and certification opportunities
  • +
  • Watch our tutorial videos (some are free, some require a subscription)
  • +
  • Find out about our productivity boosting tools and commercial support
  • +
  • Find out about real-life training and certification opportunities
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 85ec8461f2..7ac3f9952f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -28,7 +28,7 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig label: preVals[i].label, view: preVals[i].view, value: preVals[i].value, - config: preVals[i].config, + config: preVals[i].config }); } } @@ -98,7 +98,7 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig // 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(); navigationService.syncTree({ tree: "datatypes", path: data.path }).then(function (syncArgs) { $scope.page.menu.currentNode = syncArgs.node; diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.edit.controller.js index 1b9d5c3f4c..8c1e7edb73 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.edit.controller.js @@ -6,7 +6,8 @@ * @description * The controller for editing dictionary items */ -function DictionaryEditController($scope, $routeParams, dictionaryResource, treeService, navigationService, appState, editorState, contentEditingHelper, formHelper, notificationsService, localizationService) { +function DictionaryEditController($scope, $routeParams, $location, dictionaryResource, navigationService, appState, editorState, contentEditingHelper, formHelper, notificationsService, localizationService) { + var vm = this; //setup scope vars @@ -18,6 +19,10 @@ function DictionaryEditController($scope, $routeParams, dictionaryResource, tree vm.page.menu.currentSection = appState.getSectionState("currentSection"); vm.page.menu.currentNode = null; vm.description = ""; + vm.showBackButton = true; + + vm.save = saveDictionary; + vm.back = back; function loadDictionary() { @@ -26,9 +31,7 @@ function DictionaryEditController($scope, $routeParams, dictionaryResource, tree //we are editing so get the content item from the server dictionaryResource.getById($routeParams.id) .then(function (data) { - bindDictionary(data); - vm.page.loading = false; }); } @@ -100,7 +103,9 @@ function DictionaryEditController($scope, $routeParams, dictionaryResource, tree } } - vm.save = saveDictionary; + function back() { + $location.path("settings/dictionary/list"); + } $scope.$watch("vm.content.name", function (newVal, oldVal) { //when the value changes, we need to set the name dirty diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js index 35739b3db7..83daa6b243 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js @@ -18,9 +18,10 @@ function DictionaryListController($scope, $location, dictionaryResource, localiz dictionaryResource.getList() .then(function (data) { - vm.items = data; - + angular.forEach(vm.items, function(item){ + item.style = { "paddingLeft": item.level * 10 }; + }); vm.loading = false; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html b/src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html index c016d37eca..b14517a005 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html @@ -12,14 +12,20 @@ name-locked="vm.page.nameLocked" hide-icon="true" hide-description="true" - hide-alias="true"> + hide-alias="true" + on-back="vm.back()" + show-back-button="vm.showBackButton"> -

    - - - + + +

    + + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html index 82b088df49..db0314639c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html @@ -11,36 +11,27 @@ name-locked="true"> -
    -
    -
    -
    -
    - Name -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    -
    -
    -
    + + + + + + + + + + + + + + +
    Name{{column.displayName}}
    + {{item.name}} + + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-content-name.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-content-name.html deleted file mode 100644 index 46ad6e5eff..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-content-name.html +++ /dev/null @@ -1,19 +0,0 @@ - -
    - - - - Required - {{contentNameForm.name.errorMsg}} - -
    -
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-header.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-header.html deleted file mode 100644 index c65b373795..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-header.html +++ /dev/null @@ -1,14 +0,0 @@ -
    -
    - -
    - - -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-item-sorter.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-item-sorter.html deleted file mode 100644 index 569a678dfb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-item-sorter.html +++ /dev/null @@ -1,38 +0,0 @@ -
    - -
    -
    -

    Sort children of {{viewModel.name}}

    - - - - - - - - - - - - - - - -
    NameLast UpdateIndex
    {{item.column1}}{{item.column2}}{{item.column3}}
    - -
    -
    - - -
    -
    -
    -
    - {{model.successMsg}} -
    - -
    - -
    -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-login.html deleted file mode 100644 index 3bf8332ad3..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-login.html +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    -

    Happy {{today}}!, log in below

    -
    - -
    -
    - -
    - -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-optionsmenu.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-optionsmenu.html deleted file mode 100644 index f834dfb93b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-optionsmenu.html +++ /dev/null @@ -1,22 +0,0 @@ -
    - - - - Actions - - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-photo-folder.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-photo-folder.html deleted file mode 100644 index 67e0ecbf66..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-photo-folder.html +++ /dev/null @@ -1,28 +0,0 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-tab-view.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-tab-view.html deleted file mode 100644 index 93b520cb79..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-tab-view.html +++ /dev/null @@ -1,5 +0,0 @@ -
    -
    - -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-upload-dropzone.html b/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-upload-dropzone.html deleted file mode 100644 index 20a8f9d741..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/_obsolete/umb-upload-dropzone.html +++ /dev/null @@ -1,8 +0,0 @@ -
    -
    - -

    - Drop your files here... -

    -
    -
    \ 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) + +

    + +
    + + + + + or + + + Cancel + +
    + +
    Please choose a .udt file to import
    + + + +
    +
    + + Name: + + {{vm.model.name}} +
    + + Alias: + + {{vm.model.alias}} +
    +
    + +
    +
    + {{vm.model.name}} has been imported! +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index a5c446dfb5..42d14fa5dc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -31,7 +31,7 @@ var labelKeys = [ "treeHeaders_languages", "general_mandatory", - "general_default", + "general_default" "languages_fallsbackToLabel" ]; diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js index 3349bfbcba..6dcf1211c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js @@ -6,7 +6,7 @@ var vm = this; function onInit() { - angular.forEach($scope.model.tabs, function(group){ + angular.forEach($scope.content.tabs, function(group){ group.open = true; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html index a865e2ff5d..c0e2c2ae33 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html @@ -1,9 +1,9 @@
    -
    +
    {{ group.label }}
    -   +  
    @@ -14,4 +14,4 @@
    -
    \ No newline at end of file +
    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/media/sort.html b/src/Umbraco.Web.UI.Client/src/views/media/sort.html new file mode 100644 index 0000000000..14bf92ec42 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/media/sort.html @@ -0,0 +1,88 @@ +
    + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js index 14b3a56c9b..c8aba53cf5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js @@ -76,17 +76,20 @@ vm.page.navigation = [ { "name": vm.labels.design, + "alias": "design", "icon": "icon-document-dashed-line", "view": "views/mediatypes/views/design/design.html", "active": true }, { "name": vm.labels.listview, + "alias": "listView", "icon": "icon-list", "view": "views/mediatypes/views/listview/listview.html" }, { "name": vm.labels.permissions, + "alias": "permissions", "icon": "icon-keychain", "view": "views/mediatypes/views/permissions/permissions.html" } diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.controller.js index e424d58929..1d865f2fa0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.controller.js @@ -1,7 +1,7 @@ (function() { 'use strict'; - function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService) { + function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService, overlayService) { /* ----------- SCOPE VARIABLES ----------- */ @@ -43,20 +43,23 @@ } function addChild($event) { - vm.childNodeSelectorOverlay = { + var childNodeSelectorOverlay = { view: "itempicker", title: childNodeSelectorOverlayTitle, availableItems: vm.mediaTypes, selectedItems: vm.selectedChildren, + position: "target", event: $event, - show: true, submit: function(model) { vm.selectedChildren.push(model.selectedItem); $scope.model.allowedContentTypes.push(model.selectedItem.id); - vm.childNodeSelectorOverlay.show = false; - vm.childNodeSelectorOverlay = null; + overlayService.close(); + }, + close: function() { + overlayService.close(); } }; + overlayService.open(childNodeSelectorOverlay); } function removeChild(selectedChild, index) { diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html index 9fa2efe9fc..caa4c4dc11 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html @@ -2,51 +2,34 @@
    -
    - - + +
    -
    -
    -
    -
    - - - - - - - + selected-children="vm.selectedChildren" + available-children="vm.mediaTypes" + parent-name="model.name" + parent-icon="model.icon" + parent-id="model.id" + on-add="vm.addChild" + on-remove="vm.removeChild"> +
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/member/list.html b/src/Umbraco.Web.UI.Client/src/views/member/list.html index ef2fc716a1..a61614e8f5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/list.html @@ -4,33 +4,26 @@
    - + - - + + - + +
    +
    + +
    +
    +
    - - - - - - - - - - -
    - -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/member/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/member/listview/listview.html new file mode 100644 index 0000000000..6c558301c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/listview/listview.html @@ -0,0 +1,7 @@ +
    + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js index c592fa28d3..78daeceab4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -117,7 +117,7 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS // 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(); $scope.page.loading = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js index c58a5573a9..84b58cac94 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js @@ -154,50 +154,34 @@ } function openMacroOverlay() { - - vm.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: {}, - show: true, submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); insert(macroObject.syntax); - - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - + editorService.close(); }, - close: function(oldModel) { - // close the dialog - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - // focus editor + close: function() { + editorService.close(); vm.editor.focus(); } }; + editorService.macroPicker(macroPicker); } function openPageFieldOverlay() { - vm.pageFieldOverlay = { - submitButtonLabel: "Insert", - closeButtonlabel: "Cancel", - view: "insertfield", - show: true, + var insertFieldEditor = { submit: function (model) { insert(model.umbracoField); - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + editorService.close(); }, - close: function (model) { - // close the dialog - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; - // focus editor - vm.editor.focus(); + close: function () { + editorService.close(); + vm.editor.focus(); } }; + editorService.insertField(insertFieldEditor); } diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html index 810b1cb8ae..e28b76d785 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html @@ -76,18 +76,4 @@
    - - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index d5a17c83c7..64ca0331af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -161,50 +161,33 @@ function openMacroOverlay() { - - vm.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: {}, - show: true, submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); insert(macroObject.syntax); - - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - + editorService.close(); }, - close: function(oldModel) { - // close the dialog - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - // focus editor + close: function() { + editorService.close(); vm.editor.focus(); } }; + editorService.macroPicker(macroPicker); } - function openPageFieldOverlay() { - vm.pageFieldOverlay = { - submitButtonLabel: "Insert", - closeButtonlabel: "Cancel", - view: "insertfield", - show: true, + var insertFieldEditor = { submit: function (model) { insert(model.umbracoField); - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + editorService.close(); }, - close: function (model) { - // close the dialog - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; - // focus editor + close: function () { + editorService.close(); vm.editor.focus(); } }; + editorService.insertField(insertFieldEditor); } @@ -372,7 +355,7 @@ }); }, readOnly: true - }, + } ]); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html index 4a16ad4ebf..a222234e80 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html @@ -84,18 +84,4 @@ - - - - - -
    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/colorpicker/colorpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html index 1cb61f4168..f726800696 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html @@ -5,9 +5,9 @@
      -
    • +
    • -
      +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html index 7d88fb068f..a70ecabf35 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html @@ -1,4 +1,4 @@ -
      +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 78ce1f29a5..5a8396776e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -12,7 +12,7 @@ * @param {any} $location * @param {any} localizationService */ -function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, localizationService, editorService) { +function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, localizationService, editorService, $q) { var unsubscribe; @@ -30,23 +30,9 @@ function contentPickerController($scope, entityResource, editorState, iconHelper return str.replace(rgxtrim, ''); } - function startWatch() { - //We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required - // because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable - // occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs. - // In their source code there is no event so we need to just subscribe to our model changes here. - //This also makes it easier to manage models, we update one and the rest will just work. - $scope.$watch(function () { - //return the joined Ids as a string to watch - return _.map($scope.renderModel, function (i) { - return $scope.model.config.idType === "udi" ? i.udi : i.id; - }).join(); - }, function (newVal) { - var currIds = _.map($scope.renderModel, function (i) { - return $scope.model.config.idType === "udi" ? i.udi : i.id; - }); - $scope.model.value = trim(currIds.join(), ","); - + /** Performs validation based on the renderModel data */ + function validate() { + if ($scope.contentPickerForm) { //Validate! if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) { $scope.contentPickerForm.minCount.$setValidity("minCount", false); @@ -61,15 +47,32 @@ function contentPickerController($scope, entityResource, editorState, iconHelper else { $scope.contentPickerForm.maxCount.$setValidity("maxCount", true); } + } + } - setSortingState($scope.renderModel); + function startWatch() { + + //due to the way angular-sortable works, it needs to update a model, we don't want it to update renderModel since renderModel + //is updated based on changes to model.value so if we bound angular-sortable to that and put a watch on it we'd end up in a + //infinite loop. Instead we have a custom array model for angular-sortable and we'll watch that which we'll use to sync the model.value + //which in turn will sync the renderModel. + $scope.$watchCollection("sortableModel", function(newVal, oldVal) { + $scope.model.value = newVal.join(); + }); + //if the underlying model changes, update the view model, this ensures that the view is always consistent with the underlying + //model if it changes (i.e. based on server updates, or if used in split view, etc...) + $scope.$watch("model.value", function(newVal, oldVal) { + if (newVal !== oldVal) { + syncRenderModel(); + } }); } $scope.renderModel = []; - - $scope.dialogEditor = editorState && editorState.current && editorState.current.isDialogEditor === true; + $scope.sortableModel = []; + + $scope.dialogEditor = editorState && editorState.current && editorState.current.isDialogEditor === true; //the default pre-values var defaultConfig = { @@ -78,11 +81,11 @@ function contentPickerController($scope, entityResource, editorState, iconHelper showEditButton: false, showPathOnHover: false, maxNumber: 1, - minNumber : 0, + minNumber: 0, startNode: { query: "", type: "content", - id: $scope.model.config.startNodeId ? $scope.model.config.startNodeId : -1 // get start node for simple Content Picker + id: $scope.model.config.startNodeId ? $scope.model.config.startNodeId : -1 // get start node for simple Content Picker } }; @@ -107,12 +110,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper $scope.model.config.showOpenButton = Object.toBoolean($scope.model.config.showOpenButton); $scope.model.config.showEditButton = Object.toBoolean($scope.model.config.showEditButton); $scope.model.config.showPathOnHover = Object.toBoolean($scope.model.config.showPathOnHover); - + var entityType = $scope.model.config.startNode.type === "member" ? "Member" : $scope.model.config.startNode.type === "media" - ? "Media" - : "Document"; + ? "Media" + : "Document"; $scope.allowOpenButton = entityType === "Document"; $scope.allowEditButton = entityType === "Document"; $scope.allowRemoveButton = true; @@ -145,7 +148,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //since most of the pre-value config's are used in the dialog options (i.e. maxNumber, minNumber, etc...) we'll merge the // pre-value config on to the dialog options angular.extend(dialogOptions, $scope.model.config); - + //We need to manually handle the filter for members here since the tree displayed is different and only contains // searchable list views if (entityType === "Member") { @@ -153,7 +156,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper dialogOptions.filterCssClass = "not-allowed"; var currFilter = dialogOptions.filter; //now change the filter to be a method - dialogOptions.filter = function(i) { + dialogOptions.filter = function (i) { //filter out the list view nodes if (i.metaData.isContainer) { return true; @@ -175,7 +178,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") { //if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query dialogOptions.startNodeId = -1; - } else if ($scope.model.config.startNode.query) { + } + else if ($scope.model.config.startNode.query) { //if we have a query for the startnode, we will use that. var rootId = $routeParams.id; entityResource.getByQuery($scope.model.config.startNode.query, rootId, "Document").then(function (ent) { @@ -209,8 +213,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }; $scope.remove = function (index) { - $scope.renderModel.splice(index, 1); - angularHelper.getCurrentForm($scope).$setDirty(); + var currIds = $scope.model.value ? $scope.model.value.split(',') : []; + if (currIds.length > 0) { + currIds.splice(index, 1); + angularHelper.getCurrentForm($scope).$setDirty(); + $scope.model.value = currIds.join(); + } }; $scope.showNode = function (index) { @@ -229,36 +237,35 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } $scope.add = function (item) { - var currIds = _.map($scope.renderModel, function (i) { - return $scope.model.config.idType === "udi" ? i.udi : i.id; - }); + var currIds = $scope.model.value ? $scope.model.value.split(',') : []; var itemId = $scope.model.config.idType === "udi" ? item.udi : item.id; if (currIds.indexOf(itemId) < 0) { - setEntityUrl(item); + currIds.push(itemId); + $scope.model.value = currIds.join(); } }; $scope.clear = function () { - $scope.renderModel = []; + $scope.model.value = null; }; - $scope.openContentEditor = function(node) { + $scope.openContentEditor = function (node)  { var contentEditor = { id: node.id, - submit: function(model) { + submit: function (model) { // update the node node.name = model.contentNode.name; node.published = model.contentNode.hasPublishedVersion; - if(entityType !== "Member") { - entityResource.getUrl(model.contentNode.id, entityType).then(function(data){ + if (entityType !== "Member") { + entityResource.getUrl(model.contentNode.id, entityType).then(function (data) { node.url = data; }); } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; @@ -267,48 +274,95 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //when the scope is destroyed we need to unsubscribe $scope.$on('$destroy', function () { - if(unsubscribe) { + if (unsubscribe) { unsubscribe(); } }); - - var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - //load current data if anything selected - if (modelIds.length > 0) { - entityResource.getByIds(modelIds, entityType).then(function(data) { + /** Syncs the renderModel based on the actual model.value and returns a promise */ + function syncRenderModel() { + + var valueIds = $scope.model.value ? $scope.model.value.split(',') : []; + + //sync the sortable model + $scope.sortableModel = valueIds; + + //load current data if anything selected + if (valueIds.length > 0) { + + //need to determine which items we already have loaded + var renderModelIds = _.map($scope.renderModel, function(d) { + return $scope.model.config.idType === "udi" ? d.udi : d.id; + }); + + //get the ids that no longer exist + var toRemove = _.difference(renderModelIds, valueIds); + + //remove the ones that no longer exist + for (var j = 0; j < toRemove.length; j++) { + var index = renderModelIds.indexOf(toRemove[j]); + $scope.renderModel.splice(index, 1); + } + + //get the ids that we need to lookup entities for + var missingIds = _.difference(valueIds, renderModelIds); + + if (missingIds.length > 0) { + return entityResource.getByIds(missingIds, entityType).then(function(data) { + + _.each(valueIds, + function(id, i) { + var entity = _.find(data, function(d) { + return $scope.model.config.idType === "udi" ? (d.udi == id) : (d.id == id); + }); + + if (entity) { + addSelectedItem(entity); + } - _.each(modelIds, - function(id, i) { - var entity = _.find(data, - function(d) { - return $scope.model.config.idType === "udi" ? (d.udi == id) : (d.id == id); }); - if (entity) { - setEntityUrl(entity); - } - + validate(); + setSortingState($scope.renderModel); + return $q.when(true); }); + } + else { + //if there's nothing missing, make sure it's sorted correctly + + var current = $scope.renderModel; + $scope.renderModel = []; + for (var k = 0; k < valueIds.length; k++) { + var id = valueIds[k]; + var found = _.find(current, function(d) { + return $scope.model.config.idType === "udi" ? (d.udi == id) : (d.id == id); + }); + if (found) { + $scope.renderModel.push(found); + } + } + + validate(); + setSortingState($scope.renderModel); + return $q.when(true); + } + } + else { + $scope.renderModel = []; + validate(); + setSortingState($scope.renderModel); + return $q.when(true); + } - //everything is loaded, start the watch on the model - startWatch(); - subscribe(); - }); - } - else { - //everything is loaded, start the watch on the model - startWatch(); - subscribe(); } function setEntityUrl(entity) { // get url for content and media items - if(entityType !== "Member") { - entityResource.getUrl(entity.id, entityType).then(function(data){ + if (entityType !== "Member") { + entityResource.getUrl(entity.id, entityType).then(function (data) { // update url - angular.forEach($scope.renderModel, function(item){ + angular.forEach($scope.renderModel, function (item) { if (item.id === entity.id) { if (entity.trashed) { item.url = localizationService.dictionary.general_recycleBin; @@ -320,17 +374,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); } - // add the selected item to the renderModel - // if it needs to show a url the item will get - // updated when the url comes back from server - addSelectedItem(entity); - } function addSelectedItem(item) { // set icon - if(item.icon) { + if (item.icon) { item.icon = iconHelper.convertFromLegacyIcon(item.icon); } @@ -349,7 +398,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } } - $scope.renderModel.push({ + $scope.renderModel.push({ "name": item.name, "id": item.id, "udi": item.udi, @@ -361,17 +410,28 @@ function contentPickerController($scope, entityResource, editorState, iconHelper // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); + setEntityUrl(item); } function setSortingState(items) { // disable sorting if the list only consist of one item - if(items.length > 1) { + if (items.length > 1) { $scope.sortableOptions.disabled = false; } else { $scope.sortableOptions.disabled = true; } } + function init() { + syncRenderModel().then(function () { + //everything is loaded, start the watch on the model + startWatch(); + subscribe(); + }); + } + + init(); + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 0a41119a48..2f1303077c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -5,8 +5,8 @@ -
      - + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js index 9e2b796a93..dead3da6ec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js @@ -32,11 +32,11 @@ function entityPicker($scope, entityResource) { } else { //if it's multiple, change the value to an array - if ($scope.model.config.multiple === "1") { + if (Object.toBoolean($scope.model.config.multiple)) { if (_.isString($scope.model.value)) { $scope.model.value = $scope.model.value.split(','); } } } } -angular.module('umbraco').controller("Umbraco.PropertyEditors.EntityPickerController", entityPicker); \ No newline at end of file +angular.module('umbraco').controller("Umbraco.PropertyEditors.EntityPickerController", entityPicker); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js index 58f438c194..17959b9950 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js @@ -1,180 +1,70 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.FileUploadController - * @function - * - * @description - * The controller for the file upload property editor. It is important to note that the $scope.model.value - * doesn't necessarily depict what is saved for this property editor. $scope.model.value can be empty when we - * are submitting files because in that case, we are adding files to the fileManager which is what gets peristed - * on the server. However, when we are clearing files, we are setting $scope.model.value to "{clearFiles: true}" - * to indicate on the server that we are removing files for this property. We will keep the $scope.model.value to - * be the name of the file selected (if it is a newly selected file) or keep it to be it's original value, this allows - * for the editors to check if the value has changed and to re-bind the property if that is true. - * -*/ -function fileUploadController($scope, $element, $compile, imageHelper, fileManager, umbRequestHelper, mediaHelper) { +(function () { + 'use strict'; - /** 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($scope.model.alias, []); - //clear the current files - $scope.files = []; + /** + * @ngdoc controller + * @name Umbraco.Editors.FileUploadController + * @function + * + * @description + * The controller for the file upload property editor. + * + */ + function fileUploadController($scope, fileManager) { + + $scope.fileChanged = onFileChanged; + //declare a special method which will be called whenever the value has changed from the server + $scope.model.onValueChanged = onValueChanged; - if($scope.propertyForm) { - if ($scope.propertyForm.fileCount) { - //this is required to re-validate - $scope.propertyForm.fileCount.$setViewValue($scope.files.length); - } + /** + * Called when the file selection value changes + * @param {any} value + */ + function onFileChanged(value) { + $scope.model.value = value; } - } - - /** this method is used to initialize the data and to re-initialize it if the server value is changed */ - function initialize(index) { - - clearFiles(); - - if (!index) { - index = 1; - } - - //this is used in order to tell the umb-single-file-upload directive to - //rebuild the html input control (and thus clearing the selected file) since - //that is the only way to manipulate the html for the file input control. - $scope.rebuildInput = { - index: index - }; - //clear the current files - $scope.files = []; - //store the original value so we can restore it if the user clears and then cancels clearing. - $scope.originalValue = $scope.model.value; - - //create the property to show the list of files currently saved - if ($scope.model.value != "" && $scope.model.value != undefined) { - - var images = $scope.model.value.split(","); - - $scope.persistedFiles = _.map(images, function (item) { - return { file: item, isImage: imageHelper.detectIfImageByExtension(item) }; + /** + * called whenever the value has changed from the server + * @param {any} newVal + * @param {any} oldVal + */ + function onValueChanged(newVal, oldVal) { + //clear current uploaded files + fileManager.setFiles({ + propertyAlias: $scope.model.alias, + culture: $scope.model.culture, + files: [] }); } - else { - $scope.persistedFiles = []; - } + + }; - _.each($scope.persistedFiles, function (file) { + angular.module("umbraco") + .controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController) + .run(function (mediaHelper, umbRequestHelper, assetsService) { + if (mediaHelper && mediaHelper.registerFileResolver) { - var thumbnailUrl = umbRequestHelper.getApiUrl( - "imagesApiBaseUrl", - "GetBigThumbnail", - [{ originalImagePath: file.file }]); - - var extension = file.file.substring(file.file.lastIndexOf(".") + 1, file.file.length); - - file.thumbnail = thumbnailUrl + '&rnd=' + Math.random(); - file.extension = extension.toLowerCase(); - }); - - $scope.clearFiles = false; - } - - initialize(); - - // Method required by the valPropertyValidator directive (returns true if the property editor has at least one file selected) - $scope.validateMandatory = function () { - return { - isValid: !$scope.model.validation.mandatory || ((($scope.persistedFiles != null && $scope.persistedFiles.length > 0) || ($scope.files != null && $scope.files.length > 0)) && !$scope.clearFiles), - errorMsg: "Value cannot be empty", - errorKey: "required" - }; - } - - //listen for clear files changes to set our model to be sent up to the server - $scope.$watch("clearFiles", function (isCleared) { - if (isCleared == true) { - $scope.model.value = { clearFiles: true }; - clearFiles(); - } - else { - //reset to original value - $scope.model.value = $scope.originalValue; - //this is required to re-validate - if($scope.propertyForm) { - $scope.propertyForm.fileCount.$setViewValue($scope.files.length); - } - } - }); - - //listen for when a file is selected - $scope.$on("filesSelected", function (event, args) { - $scope.$apply(function () { - //set the files collection - fileManager.setFiles($scope.model.alias, args.files); - //clear the current files - $scope.files = []; - var newVal = ""; - for (var i = 0; i < args.files.length; i++) { - //save the file object to the scope's files collection - $scope.files.push({ alias: $scope.model.alias, file: args.files[i] }); - newVal += args.files[i].name + ","; - } - - //this is required to re-validate - $scope.propertyForm.fileCount.$setViewValue($scope.files.length); - - //set clear files to false, this will reset the model too - $scope.clearFiles = false; - //set the model value to be the concatenation of files selected. Please see the notes - // in the description of this controller, it states that this value isn't actually used for persistence, - // but we need to set it so that the editor and the server can detect that it's been changed, and it is used for validation. - $scope.model.value = { selectedFiles: newVal.trimEnd(",") }; - - //need to explicity setDirty here as file upload field can't track dirty & we can't use the fileCount (hidden field/model) - $scope.propertyForm.$setDirty(); - }); - }); - - //listen for when the model value has changed - $scope.$watch("model.value", function (newVal, oldVal) { - //cannot just check for !newVal because it might be an empty string which we - //want to look for. - if (newVal !== null && newVal !== undefined && newVal !== oldVal) { - // here we need to check if the value change needs to trigger an update in the UI. - // if the value is only changed in the controller and not in the server values, we do not - // want to trigger an update yet. - // we can however no longer rely on checking values in the controller vs. values from the server - // to determine whether to update or not, since you could potentially be uploading a file with - // the exact same name - in that case we need to reinitialize to show the newly uploaded file. - if (newVal.clearFiles !== true && !newVal.selectedFiles) { - initialize($scope.rebuildInput.index + 1); - } - } - }); -}; -angular.module("umbraco") - .controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController) - .run(function(mediaHelper, umbRequestHelper, assetsService){ - if (mediaHelper && mediaHelper.registerFileResolver) { - - //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource - // they contain different data structures so if we need to query against it we need to be aware of this. - mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){ - if (thumbnail) { - if (mediaHelper.detectIfImageByExtension(property.value)) { - //get default big thumbnail from image processor - var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first"; - return thumbnailUrl; + //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource + // they contain different data structures so if we need to query against it we need to be aware of this. + mediaHelper.registerFileResolver("Umbraco.UploadField", function (property, entity, thumbnail) { + if (thumbnail) { + if (mediaHelper.detectIfImageByExtension(property.value)) { + //get default big thumbnail from image processor + var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first"; + return thumbnailUrl; + } + else { + return null; + } } else { - return null; + return property.value; } - } - else { - return property.value; - } - }); + }); - } - }); + } + }); + + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html index 9db24c237b..8f97456c60 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html @@ -1,38 +1,7 @@ -
      - - - -
      -
      - -
      -
        -
      • {{file.file.name}}
      • -
      -
      - - - +
      + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js index dfc9cfdcef..beb8edab20 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js @@ -1,28 +1,33 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.EmbedController", - function ($scope, $rootScope, $timeout) { + function ($scope, $timeout, $sce, editorService) { + + function onInit() { + $scope.trustedValue = null; + $scope.trustedValue = $sce.trustAsHtml($scope.control.value); + + if(!$scope.control.value) { + $timeout(function(){ + if($scope.control.$initializing){ + $scope.setEmbed(); + } + }, 200); + } + } $scope.setEmbed = function(){ - $scope.embedDialog = {}; - $scope.embedDialog.view = "embed"; - $scope.embedDialog.show = true; - - $scope.embedDialog.submit = function(model) { - $scope.control.value = model.embed.preview; - $scope.embedDialog.show = false; - $scope.embedDialog = null; + var embed = { + submit: function(model) { + $scope.control.value = model.embed.preview; + $scope.trustedValue = $sce.trustAsHtml(model.embed.preview); + editorService.close(); + }, + close: function() { + editorService.close(); + } }; + editorService.embed(embed); + }; - $scope.embedDialog.close = function(oldModel) { - $scope.embedDialog.show = false; - $scope.embedDialog = null; - }; - - }; - - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setEmbed(); - } - }, 200); + onInit(); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html index 00daa48644..87109e1eb9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html @@ -1,17 +1,10 @@
      -
      +
      Click to embed
      -
      - - - +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js index d536d9174a..5c1d729364 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js @@ -1,6 +1,6 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MacroController", - function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) { + function ($scope, $timeout, editorService, macroResource, macroService, $routeParams) { $scope.title = "Click to insert macro"; @@ -14,31 +14,24 @@ angular.module("umbraco") } }; - $scope.macroPickerOverlay = {}; - $scope.macroPickerOverlay.view = "macropicker"; - $scope.macroPickerOverlay.dialogData = dialogData; - $scope.macroPickerOverlay.show = true; + var macroPicker = { + dialogData: dialogData, + submit: function(model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); - $scope.macroPickerOverlay.submit = function(model) { - - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); - - $scope.control.value = { + $scope.control.value = { macroAlias: macroObject.macroAlias, macroParamsDictionary: macroObject.macroParamsDictionary - }; - - $scope.setPreview($scope.control.value ); - - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - - $scope.macroPickerOverlay.close = function(oldModel) { - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - + }; + + $scope.setPreview($scope.control.value ); + editorService.close(); + }, + close: function() { + editorService.close(); + } + } + editorService.macroPicker(macroPicker); }; $scope.setPreview = function(macro){ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.html index 4aad38a24e..cc581698fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.html @@ -12,12 +12,5 @@
      - - - - +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index 21f9534848..c240d6b37d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -1,6 +1,6 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MediaController", - function ($scope, $rootScope, $timeout, userService) { + function ($scope, $timeout, userService, editorService) { if (!$scope.model.config.startNodeId) { userService.getCurrentUser().then(function (userData) { @@ -9,38 +9,44 @@ angular.module("umbraco") }); } - $scope.setImage = function(){ - $scope.mediaPickerOverlay = {}; - $scope.mediaPickerOverlay.view = "mediapicker"; - $scope.mediaPickerOverlay.startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; - $scope.mediaPickerOverlay.startNodeIsVirtual = $scope.mediaPickerOverlay.startNodeId ? $scope.model.config.startNodeIsVirtual : undefined; - $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined; - $scope.mediaPickerOverlay.showDetails = true; - $scope.mediaPickerOverlay.disableFolderSelect = true; - $scope.mediaPickerOverlay.onlyImages = true; - $scope.mediaPickerOverlay.show = true; - - $scope.mediaPickerOverlay.submit = function(model) { - var selectedImage = model.selectedImages[0]; - - $scope.control.value = { - focalPoint: selectedImage.focalPoint, - id: selectedImage.id, - udi: selectedImage.udi, - image: selectedImage.image, - altText: selectedImage.altText - }; - + function onInit() { + if($scope.control.value){ $scope.setUrl(); + } + } - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; - }; + $scope.setImage = function(){ + var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; + var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined; - $scope.mediaPickerOverlay.close = function(oldModel) { - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; - }; + var mediaPicker = { + startNodeId: startNodeId, + startNodeIsVirtual: startNodeIsVirtual, + cropSize: $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined, + showDetails: true, + disableFolderSelect: true, + onlyImages: true, + submit: function(model) { + var selectedImage = model.selectedImages[0]; + + $scope.control.value = { + focalPoint: selectedImage.focalPoint, + id: selectedImage.id, + udi: selectedImage.udi, + image: selectedImage.image, + altText: selectedImage.altText + }; + + $scope.setUrl(); + + editorService.close(); + }, + close: function() { + editorService.close(); + } + } + + editorService.mediaPicker(mediaPicker); }; $scope.setUrl = function(){ @@ -68,11 +74,6 @@ angular.module("umbraco") } }; - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setImage(); - }else if($scope.control.value){ - $scope.setUrl(); - } - }, 200); + onInit(); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html index dd35757397..7ffb26d831 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html @@ -14,11 +14,4 @@
      - - -
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js index 397438d5a0..91cdfe6b9a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js @@ -1,7 +1,7 @@ (function() { "use strict"; - function GridRichTextEditorController($scope, tinyMceService, macroService, editorState) { + function GridRichTextEditorController($scope, tinyMceService, macroService, editorState, editorService) { var vm = this; @@ -11,64 +11,65 @@ vm.openEmbed = openEmbed; function openLinkPicker(editor, currentTarget, anchorElement) { - - vm.linkPickerOverlay = { - view: "linkpicker", + var linkPicker = { currentTarget: currentTarget, anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)), - show: true, submit: function(model) { tinyMceService.insertLinkInEditor(editor, model.target, anchorElement); - vm.linkPickerOverlay.show = false; - vm.linkPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.linkPicker(linkPicker); } function openMediaPicker(editor, currentTarget, userData) { - vm.mediaPickerOverlay = { + var mediaPicker = { currentTarget: currentTarget, onlyImages: true, showDetails: true, startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], - view: "mediapicker", - show: true, submit: function(model) { tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]); - vm.mediaPickerOverlay.show = false; - vm.mediaPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.mediaPicker(mediaPicker); } function openEmbed(editor) { - vm.embedOverlay = { - view: "embed", - show: true, + var embed = { submit: function(model) { tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview); - vm.embedOverlay.show = false; - vm.embedOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.embed(embed); } function openMacroPicker(editor, dialogData) { - vm.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: dialogData, - show: true, submit: function(model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); tinyMceService.insertMacroInEditor(editor, macroObject, $scope); - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.macroPicker(macroPicker); } - - } angular.module("umbraco").controller("Umbraco.PropertyEditors.Grid.RichTextEditorController", GridRichTextEditorController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html index 7bceed6a60..cee261829f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html @@ -11,32 +11,4 @@ on-macro-picker-click="vm.openMacroPicker">
      - - - - - - - - - - - -
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.controller.js index 5403ce4aad..7978ca3150 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.controller.js @@ -28,7 +28,7 @@ angular.module("umbraco") name: "1 column layout", sections: [ { - grid: 12, + grid: 12 } ] }, @@ -36,7 +36,7 @@ angular.module("umbraco") name: "2 column layout", sections: [ { - grid: 4, + grid: 4 }, { grid: 8 diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index 749d3b4faa..04bfe98284 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -1,111 +1,168 @@ -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it angular.module('umbraco') .controller("Umbraco.PropertyEditors.ImageCropperController", function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager, angularHelper) { var config = angular.copy($scope.model.config); - $scope.imageIsLoaded = false; - //move previously saved value to the editor - if ($scope.model.value) { - //backwards compat with the old file upload (incase some-one swaps them..) - if (angular.isString($scope.model.value)) { - config.src = $scope.model.value; - $scope.model.value = config; - } else if ($scope.model.value.crops) { - //sync any config changes with the editor and drop outdated crops - _.each($scope.model.value.crops, function (saved) { - var configured = _.find(config.crops, function (item) { return item.alias === saved.alias }); + $scope.filesSelected = onFileSelected; + $scope.filesChanged = onFilesChanged; + $scope.fileUploaderInit = onFileUploaderInit; + $scope.crop = crop; + $scope.done = done; + $scope.clear = clear; + $scope.focalPointChanged = focalPointChanged; + //declare a special method which will be called whenever the value has changed from the server + $scope.model.onValueChanged = onValueChanged; - if (configured && configured.height === saved.height && configured.width === saved.width) { - configured.coordinates = saved.coordinates; - } - }); - $scope.model.value.crops = config.crops; + /** + * Called when the umgImageGravity component updates the focal point value + * @param {any} left + * @param {any} top + */ + function focalPointChanged(left, top) { + //update the model focalpoint value + $scope.model.value.focalPoint = { + left: left, + top: top + }; - //restore focalpoint if missing - if (!$scope.model.value.focalPoint) { - $scope.model.value.focalPoint = { left: 0.5, top: 0.5 }; - } - } - - $scope.imageSrc = $scope.model.value.src; + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); } + /** + * Used to assign a new model value + * @param {any} src + */ + function setModelValueWithSrc(src) { + if (!$scope.model.value || !$scope.model.value.src) { + //we are copying to not overwrite the original config + $scope.model.value = angular.extend(angular.copy($scope.model.config), { src: src }); + } + } - //crop a specific crop - $scope.crop = function (crop) { - $scope.currentCrop = crop; - $scope.currentPoint = undefined; - }; - - //done cropping - $scope.done = function () { - $scope.currentCrop = undefined; - $scope.currentPoint = undefined; - }; - - //crop a specific crop - $scope.clear = function (crop) { + /** + * called whenever the value has changed from the server + * @param {any} newVal + * @param {any} oldVal + */ + function onValueChanged(newVal, oldVal) { //clear current uploaded files - fileManager.setFiles($scope.model.alias, []); + fileManager.setFiles({ + propertyAlias: $scope.model.alias, + culture: $scope.model.culture, + files: [] + }); + } + + /** + * Called when the a new file is selected + * @param {any} value + */ + function onFileSelected(value, files) { + setModelValueWithSrc(value); + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); + } + + /** + * Called when the file collection changes + * @param {any} value + * @param {any} files + */ + function onFilesChanged(files) { + if (files && files[0]) { + $scope.imageSrc = files[0].fileSrc; + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); + } + } + + /** + * Called when the file uploader initializes + * @param {any} value + */ + function onFileUploaderInit(value, files) { + //move previously saved value to the editor + if ($scope.model.value) { + //backwards compat with the old file upload (incase some-one swaps them..) + if (angular.isString($scope.model.value)) { + setModelValueWithSrc($scope.model.value); + } + else if ($scope.model.value.crops) { + //sync any config changes with the editor and drop outdated crops + _.each($scope.model.value.crops, function (saved) { + var configured = _.find(config.crops, function (item) { return item.alias === saved.alias }); + + if (configured && configured.height === saved.height && configured.width === saved.width) { + configured.coordinates = saved.coordinates; + } + }); + $scope.model.value.crops = config.crops; + + //restore focalpoint if missing + if (!$scope.model.value.focalPoint) { + $scope.model.value.focalPoint = { left: 0.5, top: 0.5 }; + } + } + + //if there are already files in the client assigned then set the src + if (files && files[0]) { + $scope.imageSrc = files[0].fileSrc; + } + else { + $scope.imageSrc = $scope.model.value.src; + } + + } + } + + /** + * crop a specific crop + * @param {any} crop + */ + function crop(crop) { + $scope.currentCrop = crop; + $scope.currentPoint = null; + + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); + }; + + /** done cropping */ + function done() { + $scope.currentCrop = null; + $scope.currentPoint = null; + + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); + }; + + /** + * crop a specific crop + * @param {any} crop + */ + function clear(crop) { + //clear current uploaded files + fileManager.setFiles({ + propertyAlias: $scope.model.alias, + culture: $scope.model.culture, + files: [] + }); //clear the ui - $scope.imageSrc = undefined; + $scope.imageSrc = null; if ($scope.model.value) { - delete $scope.model.value; + $scope.model.value = null; } - // set form to dirty to tricker discard changes dialog - var currForm = angularHelper.getCurrentForm($scope); - currForm.$setDirty(); + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); }; - - //show previews - $scope.togglePreviews = function () { - if ($scope.showPreviews) { - $scope.showPreviews = false; - $scope.tempShowPreviews = false; - } else { - $scope.showPreviews = true; - } - }; - - $scope.imageLoaded = function() { - $scope.imageIsLoaded = true; - }; - - //on image selected, update the cropper - $scope.$on("filesSelected", function (ev, args) { - $scope.model.value = config; - - if (args.files && args.files[0]) { - - fileManager.setFiles($scope.model.alias, args.files); - - var reader = new FileReader(); - reader.onload = function (e) { - - $scope.$apply(function () { - $scope.imageSrc = e.target.result; - }); - - }; - - reader.readAsDataURL(args.files[0]); - } - }); - - - //here we declare a special method which will be called whenever the value has changed from the server - $scope.model.onValueChanged = function (newVal, oldVal) { - //clear current uploaded files - fileManager.setFiles($scope.model.alias, []); - }; - + var unsubscribe = $scope.$on("formSubmitting", function () { - $scope.done(); + $scope.currentCrop = null; + $scope.currentPoint = null; }); $scope.$on('$destroy', function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index 64a2b97ef4..bc84f1b64f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -1,72 +1,70 @@
      + - - -

      Click to upload

      - -
      + +
      -
      +
      +
      + -
      +
      + + +
      -
      -
      - - - - - - - Reset -
      + +
      + + + Remove file +
      + +
        +
      • + + + + +
        + {{value.alias}} + {{value.width}}px x {{value.height}}px +
        +
      • +
      +
      -
      - - - Remove file -
      + -
        -
      • + - - - -
        - {{value.alias}} - {{value.width}}px x {{value.height}}px -
        -
      • -
      - - -
      - -
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.controller.js index abe2b41bd1..9ba8822843 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.controller.js @@ -9,7 +9,7 @@ (function() { "use strict"; - function ListViewLayoutsPreValsController($scope) { + function ListViewLayoutsPreValsController($scope, editorService) { var vm = this; vm.focusLayoutName = false; @@ -63,21 +63,22 @@ } function openIconPicker(layout) { - vm.iconPickerDialog = { - view: "iconpicker", - show: true, - submit: function(model) { - if (model.color) { - layout.icon = model.icon + " " + model.color; - } else { - layout.icon = model.icon; - } - vm.focusLayoutName = true; - vm.iconPickerDialog.show = false; - vm.iconPickerDialog = null; - } - }; - } + var iconPicker = { + submit: function(model) { + if (model.color) { + layout.icon = model.icon + " " + model.color; + } else { + layout.icon = model.icon; + } + vm.focusLayoutName = true; + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.iconPicker(iconPicker); + } activate(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html index c71f3306cd..f8ad0bb3f4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html @@ -47,11 +47,4 @@
      - - -
      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 @@ - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js index 640eb5fa02..b66c46f66d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js @@ -1,6 +1,6 @@ //this controller simply tells the dialogs service to open a memberPicker window //with a specified callback, this callback will receive an object with a selection on it -function memberGroupPicker($scope, dialogService){ +function memberGroupPicker($scope, editorService){ function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -18,34 +18,25 @@ function memberGroupPicker($scope, dialogService){ } $scope.openMemberGroupPicker = function() { - - $scope.memberGroupPicker = {}; - $scope.memberGroupPicker.multiPicker = true; - $scope.memberGroupPicker.view = "memberGroupPicker"; - $scope.memberGroupPicker.show = true; - - $scope.memberGroupPicker.submit = function(model) { - - if(model.selectedMemberGroups) { - _.each(model.selectedMemberGroups, function (item, i) { - $scope.add(item); - }); - } - - if(model.selectedMemberGroup) { - $scope.clear(); - $scope.add(model.selectedMemberGroup); - } - - $scope.memberGroupPicker.show = false; - $scope.memberGroupPicker = null; - }; - - $scope.memberGroupPicker.close = function(oldModel) { - $scope.memberGroupPicker.show = false; - $scope.memberGroupPicker = null; - }; - + var memberGroupPicker = { + multiPicker: true, + submit: function(model) { + if(model.selectedMemberGroups) { + _.each(model.selectedMemberGroups, function (item, i) { + $scope.add(item); + }); + } + if(model.selectedMemberGroup) { + $scope.clear(); + $scope.add(model.selectedMemberGroup); + } + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.memberGroupPicker(memberGroupPicker); }; $scope.remove =function(index){ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html index 0f2a9e58d6..afd0c358a6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html @@ -18,11 +18,4 @@ Add - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index efafb9066d..0a44a9fcaa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -229,10 +229,10 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop $scope.sortableOptions = { axis: 'y', cursor: "move", - handle: ".nested-content__icon--move", + handle: ".umb-nested-content__icon--move", start: function (ev, ui) { // Yea, yea, we shouldn't modify the dom, sue me - $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { + $("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { tinymce.execCommand('mceRemoveEditor', false, $(this).attr('id')); $(this).css("visibility", "hidden"); }); @@ -244,7 +244,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop $scope.setDirty(); }, stop: function (ev, ui) { - $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { + $("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { tinymce.execCommand('mceAddEditor', true, $(this).attr('id')); $(this).css("visibility", "visible"); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html index 4b43df9c93..0617331682 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html @@ -1,4 +1,4 @@ -
    +
    @@ -45,11 +45,11 @@ Add - +
    -
    +

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

    - + -
    +

    {{property.notSupportedMessage}}

    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html index 03dc7d47ab..9a5d75ae92 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html @@ -1,48 +1,48 @@ -
    + ng-class="{'umb-nested-content--narrow':!wideMode, 'umb-nested-content--wide':wideMode}"> -
    +
    -
    +
    -
    +
    -
    +
    - -
    +
    -
    +
    -
    "); - // Build a placeholder cell that spans all the cells in the row: http://stackoverflow.com/questions/25845310/jquery-ui-sortable-and-table-cell-size + // Build a placeholder cell that spans all the cells in the row: https://stackoverflow.com/questions/25845310/jquery-ui-sortable-and-table-cell-size var cellCount = 0; $('td, th', ui.helper).each(function () { // For each td or th try and get it's colspan attribute, and add that or 1 to the total diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index a9d1cd1234..07dffea75e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -1,6 +1,6 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.RTEController", - function ($rootScope, $scope, $q, $locale, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService, editorState) { + function ($scope, $q, $locale, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService, editorState, editorService) { $scope.isLoading = true; @@ -14,9 +14,14 @@ angular.module("umbraco") var alreadyDirty = false; function syncContent(editor){ editor.save(); + + //stop watching before we update the value + stopWatch(); angularHelper.safeApply($scope, function () { $scope.model.value = editor.getContent(); }); + //re-watch the value + startWatch(editor); if (!alreadyDirty) { //make the form dirty manually so that the track changes works, setting our model doesn't trigger @@ -27,6 +32,33 @@ angular.module("umbraco") } } + var unwatch = null; + + /** + * Starts a watch on the model value so that we can update TinyMCE if the model changes behind the scenes or from the server + * @param {any} editor + */ + function startWatch(editor) { + unwatch = $scope.$watch("model.value", function (newVal, oldVal) { + if (newVal !== oldVal) { + //update the display val again if it has changed from the server; + //uses an empty string in the editor when the value is null + editor.setContent(newVal || "", { format: 'raw' }); + + //we need to manually fire this event since it is only ever fired based on loading from the DOM, this + // is required for our plugins listening to this event to execute + editor.fire('LoadContent', null); + } + }); + } + + /** Stops the watch on model.value which is done anytime we are manually updating the model.value */ + function stopWatch() { + if (unwatch) { + unwatch(); + } + } + tinyMceService.configuration().then(function (tinyMceConfig) { //config value from general tinymce.config file @@ -275,72 +307,72 @@ angular.module("umbraco") }); tinyMceService.createLinkPicker(editor, $scope, function(currentTarget, anchorElement) { - $scope.linkPickerOverlay = { - view: "linkpicker", + var linkPicker = { currentTarget: currentTarget, - anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)), - show: true, + anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)), submit: function(model) { tinyMceService.insertLinkInEditor(editor, model.target, anchorElement); - $scope.linkPickerOverlay.show = false; - $scope.linkPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.linkPicker(linkPicker); }); //Create the insert media plugin tinyMceService.createMediaPicker(editor, $scope, function(currentTarget, userData){ - - $scope.mediaPickerOverlay = { + var mediaPicker = { currentTarget: currentTarget, onlyImages: true, showDetails: true, disableFolderSelect: true, startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], startNodeIsVirtual: userData.startMediaIds.length !== 1, - view: "mediapicker", - show: true, submit: function(model) { tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]); - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; - + editorService.mediaPicker(mediaPicker); }); //Create the embedded plugin tinyMceService.createInsertEmbeddedMedia(editor, $scope, function() { - - $scope.embedOverlay = { - view: "embed", - show: true, - submit: function(model) { - tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview); - $scope.embedOverlay.show = false; - $scope.embedOverlay = null; - } - }; - + var embed = { + submit: function(model) { + tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview); + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.embed(embed); }); //Create the insert macro plugin tinyMceService.createInsertMacro(editor, $scope, function(dialogData) { - - $scope.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: dialogData, - show: true, submit: function(model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); tinyMceService.insertMacroInEditor(editor, macroObject, $scope); - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; - + editorService.macroPicker(macroPicker); }); + + startWatch(editor); }; /** Loads in the editor */ @@ -354,25 +386,11 @@ angular.module("umbraco") $scope.isLoading = false; - }, 200, false); + }, 200); } - - - loadTinyMce(); - - //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; - //uses an empty string in the editor when the value is null - tinyMceEditor.setContent(newVal || "", { format: 'raw' }); - //we need to manually fire this event since it is only ever fired based on loading from the DOM, this - // is required for our plugins listening to this event to execute - tinyMceEditor.fire('LoadContent', null); - }; - + //listen for formSubmitting event (the result is callback used to remove the event subscription) var unsubscribe = $scope.$on("formSubmitting", function () { //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer @@ -389,6 +407,7 @@ angular.module("umbraco") tinyMceEditor.destroy() } }); + }); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html index 5842384006..7aa2a14b26 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html @@ -1,35 +1,9 @@
    Loading...
    - - - - - - - - - - - - - - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index 53c4719f90..588e2d2b03 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -1,117 +1,144 @@ function sliderController($scope, $log, $element, assetsService, angularHelper) { - //configure some defaults - if (!$scope.model.config.orientation) { - $scope.model.config.orientation = "horizontal"; - } - if (!$scope.model.config.enableRange) { - $scope.model.config.enableRange = false; - } - else { - $scope.model.config.enableRange = $scope.model.config.enableRange === "1" ? true : false; - } + var sliderRef = null; - if (!$scope.model.config.initVal1) { - $scope.model.config.initVal1 = 0; - } - else { - $scope.model.config.initVal1 = parseFloat($scope.model.config.initVal1); - } - if (!$scope.model.config.initVal2) { - $scope.model.config.initVal2 = 0; - } - else { - $scope.model.config.initVal2 = parseFloat($scope.model.config.initVal2); - } - if (!$scope.model.config.minVal) { - $scope.model.config.minVal = 0; - } - else { - $scope.model.config.minVal = parseFloat($scope.model.config.minVal); - } - if (!$scope.model.config.maxVal) { - $scope.model.config.maxVal = 100; - } - else { - $scope.model.config.maxVal = parseFloat($scope.model.config.maxVal); - } - if (!$scope.model.config.step) { - $scope.model.config.step = 1; - } - else { - $scope.model.config.step = parseFloat($scope.model.config.step); - } + /** configure some defaults on init */ + function configureDefaults() { + + if (!$scope.model.config.orientation) { + $scope.model.config.orientation = "horizontal"; + } + if (!$scope.model.config.enableRange) { + $scope.model.config.enableRange = false; + } + else { + $scope.model.config.enableRange = Object.toBoolean($scope.model.config.enableRange); + } - if (!$scope.model.config.handle) { - $scope.model.config.handle = "round"; - } + if (!$scope.model.config.initVal1) { + $scope.model.config.initVal1 = 0; + } + else { + $scope.model.config.initVal1 = parseFloat($scope.model.config.initVal1); + } + if (!$scope.model.config.initVal2) { + $scope.model.config.initVal2 = 0; + } + else { + $scope.model.config.initVal2 = parseFloat($scope.model.config.initVal2); + } + if (!$scope.model.config.minVal) { + $scope.model.config.minVal = 0; + } + else { + $scope.model.config.minVal = parseFloat($scope.model.config.minVal); + } + if (!$scope.model.config.maxVal) { + $scope.model.config.maxVal = 100; + } + else { + $scope.model.config.maxVal = parseFloat($scope.model.config.maxVal); + } + if (!$scope.model.config.step) { + $scope.model.config.step = 1; + } + else { + $scope.model.config.step = parseFloat($scope.model.config.step); + } - if (!$scope.model.config.reversed) { - $scope.model.config.reversed = false; - } - else { - $scope.model.config.reversed = $scope.model.config.reversed === "1" ? true : false; - } + if (!$scope.model.config.handle) { + $scope.model.config.handle = "round"; + } - if (!$scope.model.config.tooltip) { - $scope.model.config.tooltip = "show"; - } + if (!$scope.model.config.reversed) { + $scope.model.config.reversed = false; + } + else { + $scope.model.config.reversed = Object.toBoolean($scope.model.config.reversed); + } - if (!$scope.model.config.tooltipSplit) { - $scope.model.config.tooltipSplit = false; - } - else { - $scope.model.config.tooltipSplit = $scope.model.config.tooltipSplit === "1" ? true : false; - } + if (!$scope.model.config.tooltip) { + $scope.model.config.tooltip = "show"; + } - if ($scope.model.config.tooltipFormat) { - $scope.model.config.formatter = function (value) { - if (angular.isArray(value) && $scope.model.config.enableRange) { - return $scope.model.config.tooltipFormat.replace("{0}", value[0]).replace("{1}", value[1]); - } else { - return $scope.model.config.tooltipFormat.replace("{0}", value); + if (!$scope.model.config.tooltipSplit) { + $scope.model.config.tooltipSplit = false; + } + else { + $scope.model.config.tooltipSplit = Object.toBoolean($scope.model.config.tooltipSplit); + } + + if ($scope.model.config.tooltipFormat) { + $scope.model.config.formatter = function (value) { + if (angular.isArray(value) && $scope.model.config.enableRange) { + return $scope.model.config.tooltipFormat.replace("{0}", value[0]).replace("{1}", value[1]); + } else { + return $scope.model.config.tooltipFormat.replace("{0}", value); + } } } + + if (!$scope.model.config.ticks) { + $scope.model.config.ticks = []; + } + else if (angular.isString($scope.model.config.ticks)) { + // returns comma-separated string to an array, e.g. [0, 100, 200, 300, 400] + $scope.model.config.ticks = _.map($scope.model.config.ticks.split(','), function (item) { + return parseInt(item.trim()); + }); + } + + if (!$scope.model.config.ticksPositions) { + $scope.model.config.ticksPositions = []; + } + else if (angular.isString($scope.model.config.ticksPositions)) { + // returns comma-separated string to an array, e.g. [0, 30, 60, 70, 90, 100] + $scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) { + return parseInt(item.trim()); + }); + } + + if (!$scope.model.config.ticksLabels) { + $scope.model.config.ticksLabels = []; + } + else if (angular.isString($scope.model.config.ticksLabels)) { + // returns comma-separated string to an array, e.g. ['$0', '$100', '$200', '$300', '$400'] + $scope.model.config.ticksLabels = _.map($scope.model.config.ticksLabels.split(','), function (item) { + return item.trim(); + }); + } + + if (!$scope.model.config.ticksSnapBounds) { + $scope.model.config.ticksSnapBounds = 0; + } + else { + $scope.model.config.ticksSnapBounds = parseFloat($scope.model.config.ticksSnapBounds); + } } - if (!$scope.model.config.ticks) { - $scope.model.config.ticks = []; - } - else { - // returns comma-separated string to an array, e.g. [0, 100, 200, 300, 400] - $scope.model.config.ticks = _.map($scope.model.config.ticks.split(','), function (item) { - return parseInt(item.trim()); - }); + function getValueForSlider(val) { + + if (!angular.isArray(val)) { + val = val.toString().split(","); + } + var val1 = val[0]; + var val2 = val.length > 1 ? val[1] : null; + + //configure the model value based on if range is enabled or not + if ($scope.model.config.enableRange == true) { + var i1 = parseFloat(val1); + var i2 = parseFloat(val2); + return [ + isNaN(i1) ? $scope.model.config.minVal : (i1 >= $scope.model.config.minVal ? i1 : $scope.model.config.minVal), + isNaN(i2) ? $scope.model.config.maxVal : (i2 >= i1 ? (i2 <= $scope.model.config.maxVal ? i2 : $scope.model.config.maxVal) : $scope.model.config.maxVal) + ]; + } + else { + return parseFloat(val1); + } } - if (!$scope.model.config.ticksPositions) { - $scope.model.config.ticksPositions = []; - } - else { - // returns comma-separated string to an array, e.g. [0, 30, 60, 70, 90, 100] - $scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) { - return parseInt(item.trim()); - }); - } - - if (!$scope.model.config.ticksLabels) { - $scope.model.config.ticksLabels = []; - } - else { - // returns comma-separated string to an array, e.g. ['$0', '$100', '$200', '$300', '$400'] - $scope.model.config.ticksLabels = _.map($scope.model.config.ticksLabels.split(','), function (item) { - return item.trim(); - }); - } - - if (!$scope.model.config.ticksSnapBounds) { - $scope.model.config.ticksSnapBounds = 0; - } - else { - $scope.model.config.ticksSnapBounds = parseFloat($scope.model.config.ticksSnapBounds); - } - - /** This creates the slider with the model values - it's called on startup and if the model value changes */ + /** This creates the slider with the model values - it's called on startup and returns a reference to the slider object */ function createSlider() { //the value that we'll give the slider - if it's a range, we store our value as a comma separated val but this slider expects an array @@ -122,35 +149,23 @@ //If no value saved yet - then use default value //If it contains a single value - then also create a new array value if (!$scope.model.value || $scope.model.value.indexOf(",") == -1) { - var i1 = parseFloat($scope.model.config.initVal1); - var i2 = parseFloat($scope.model.config.initVal2); - sliderVal = [ - isNaN(i1) ? $scope.model.config.minVal : (i1 >= $scope.model.config.minVal ? i1 : $scope.model.config.minVal), - isNaN(i2) ? $scope.model.config.maxVal : (i2 >= i1 ? (i2 <= $scope.model.config.maxVal ? i2 : $scope.model.config.maxVal) : $scope.model.config.maxVal) - ]; + sliderVal = getValueForSlider([$scope.model.config.initVal1, $scope.model.config.initVal2]); } else { //this will mean it's a delimited value stored in the db, convert it to an array - sliderVal = _.map($scope.model.value.split(','), function (item) { - return parseFloat(item); - }); + sliderVal = getValueForSlider($scope.model.value.split(',')); } } else { //If no value saved yet - then use default value if ($scope.model.value) { - sliderVal = parseFloat($scope.model.value); + sliderVal = getValueForSlider($scope.model.value); } else { - sliderVal = $scope.model.config.initVal1; + sliderVal = getValueForSlider($scope.model.config.initVal1); } } - // Initialise model value if not set - if (!$scope.model.value) { - setModelValueFromSlider(sliderVal); - } - //initiate slider, add event handler and get the instance reference (stored in data) var slider = $element.find('.slider-item').bootstrapSlider({ max: $scope.model.config.maxVal, @@ -172,46 +187,64 @@ range: $scope.model.config.enableRange, //set the slider val - we cannot do this with data- attributes when using ranges value: sliderVal - }).on('slideStop', function (e) { - var value = e.value; - angularHelper.safeApply($scope, function () { - setModelValueFromSlider(value); - }); - }).data('slider'); - } - - /** Called on start-up when no model value has been applied and on change of the slider via the UI - updates - the model with the currently selected slider value(s) **/ - function setModelValueFromSlider(sliderVal) { - //Get the value from the slider and format it correctly, if it is a range we want a comma delimited value - if ($scope.model.config.enableRange == true) { - $scope.model.value = sliderVal.join(","); - } - else { - $scope.model.value = sliderVal.toString(); - } - } - - //tell the assetsService to load the bootstrap slider - //libs from the plugin folder - assetsService - .loadJs("lib/slider/js/bootstrap-slider.js") - .then(function () { - - createSlider(); - - //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) { - if (newVal != oldVal) { - createSlider(); - } - }; - }); - //load the separate css for the editor to avoid it blocking our js loading - assetsService.loadCss("lib/slider/bootstrap-slider.css", $scope); - assetsService.loadCss("lib/slider/bootstrap-slider-custom.css", $scope); + slider.on('slideStop', function (e) { + var value = e.value; + angularHelper.safeApply($scope, function () { + $scope.model.value = getModelValueFromSlider(value); + }); + }).data('slider'); + + return slider; + } + + function getModelValueFromSlider(sliderVal) { + //Get the value from the slider and format it correctly, if it is a range we want a comma delimited value + if ($scope.model.config.enableRange == true) { + return sliderVal.join(","); + } + else { + return sliderVal.toString(); + } + } + + function init() { + + configureDefaults(); + + //tell the assetsService to load the bootstrap slider + //libs from the plugin folder + assetsService + .loadJs("lib/slider/js/bootstrap-slider.js") + .then(function () { + + var slider = createSlider(); + + // Initialize model value if not set + if (!$scope.model.value) { + var sliderVal = slider.bootstrapSlider('getValue'); + $scope.model.value = getModelValueFromSlider(sliderVal); + } + + //watch for the model value being changed and update the slider value when it does + $scope.$watch("model.value", function (newVal, oldVal) { + if (newVal != oldVal) { + var sliderVal = getModelValueFromSlider(slider.bootstrapSlider('getValue')); + if (newVal !== sliderVal) { + slider.bootstrapSlider('setValue', getValueForSlider(newVal)); + } + } + }); + + }); + + //load the separate css for the editor to avoid it blocking our js loading + assetsService.loadCss("lib/slider/bootstrap-slider.css", $scope); + assetsService.loadCss("lib/slider/bootstrap-slider-custom.css", $scope); + } + + init(); + } angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html index e7d9a06af4..638ecead4b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html @@ -1,5 +1,5 @@ 
    - + -
    \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js index 12482f562b..94d74ada7b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js @@ -79,7 +79,7 @@ angular.module("umbraco") addTag($scope.tagToAdd); $scope.tagToAdd = ""; //this clears the value stored in typeahead so it doesn't try to add the text again - // http://issues.umbraco.org/issue/U4-4947 + // https://issues.umbraco.org/issue/U4-4947 $typeahead.typeahead('val', ''); }; @@ -183,7 +183,7 @@ angular.module("umbraco") tagsHound.get(query, function (suggestions) { cb(removeCurrentTagsFromSuggestions(suggestions)); }); - }, + } }).bind("typeahead:selected", function (obj, datum, name) { angularHelper.safeApply($scope, function () { addTag(datum["value"]); diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js index 5c1a24611f..62c5ace83a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js @@ -159,7 +159,7 @@ }); }, readOnly: true - }, + } ]); // initial cursor placement diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 39ae1dd583..2d7df19424 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -5,6 +5,9 @@ var vm = this; var oldMasterTemplateAlias = null; + var infiniteMode = $scope.model && $scope.model.infiniteMode; + var id = infiniteMode ? $scope.model.id : $routeParams.id; + var create = infiniteMode ? $scope.model.create : $routeParams.create; vm.page = {}; vm.page.loading = true; @@ -83,24 +86,25 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - if (!suppressNotification) { - localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function(data){ - var header = data[0]; - var message = data[1]; - notificationsService.success(header, message); - }); - } - + if (!suppressNotification) { + localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function(data){ + var header = data[0]; + var message = data[1]; + notificationsService.success(header, message); + }); + } vm.page.saveButtonState = "success"; vm.template = saved; //sync state - editorState.set(vm.template); + if(!infiniteMode) { + editorState.set(vm.template); + } // sync tree // if master template alias has changed move the node to it's new location - if(oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { + if(!infiniteMode && oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { // When creating a new template the id is -1. Make sure We don't remove the root node. if (vm.page.menu.currentNode.id !== "-1") { @@ -119,15 +123,21 @@ } else { // normal tree sync - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); + if(!infiniteMode) { + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } } // clear $dirty state on form setFormState("pristine"); + if(infiniteMode) { + submit(); + } + }, function (err) { @@ -154,18 +164,14 @@ vm.templates = templates; }); - if($routeParams.create){ - - templateResource.getScaffold(($routeParams.id)).then(function (template) { + if(create) { + templateResource.getScaffold((id)).then(function (template) { vm.ready(template); }); - - }else{ - - templateResource.getById($routeParams.id).then(function(template){ + } else { + templateResource.getById(id).then(function(template){ vm.ready(template); }); - } }; @@ -176,7 +182,7 @@ vm.template = template; // if this is a new template, bind to the blur event on the name - if ($routeParams.create) { + if (create) { $timeout(function() { var nameField = angular.element(document.querySelector('[data-element="editor-name-field"]')); if (nameField) { @@ -191,10 +197,12 @@ //sync state - editorState.set(vm.template); - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); + if(!infiniteMode) { + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } // save state of master template to use for comparison when syncing the tree on save oldMasterTemplateAlias = angular.copy(template.masterTemplateAlias); @@ -305,14 +313,14 @@ }); }, readOnly: true - }, + } ]); // initial cursor placement // Keep cursor in name field if we are create a new template // else set the cursor at the bottom of the code editor - if(!$routeParams.create) { + if(!create) { $timeout(function(){ vm.editor.navigateFileEnd(); vm.editor.focus(); @@ -341,6 +349,8 @@ vm.getMasterTemplateName = getMasterTemplateName; vm.removeMasterTemplate = removeMasterTemplate; vm.closeShortcuts = closeShortcuts; + vm.submit = submit; + vm.close = close; function openInsertOverlay() { var insertOverlay = { @@ -381,50 +391,33 @@ } function openMacroOverlay() { - - vm.macroPickerOverlay = { - view: "macropicker", + var macroPicker = { dialogData: {}, - show: true, submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); insert(macroObject.syntax); - - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - + editorService.close(); }, - close: function(oldModel) { - // close the dialog - vm.macroPickerOverlay.show = false; - vm.macroPickerOverlay = null; - // focus editor + close: function() { + editorService.close(); vm.editor.focus(); } }; + editorService.macroPicker(macroPicker); } - function openPageFieldOverlay() { - vm.pageFieldOverlay = { - submitButtonLabel: "Insert", - closeButtonlabel: "Cancel", - view: "insertfield", - show: true, + var insertFieldEditor = { submit: function (model) { insert(model.umbracoField); - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + editorService.close(); }, - close: function (model) { - // close the dialog - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; - // focus editor - vm.editor.focus(); + close: function () { + editorService.close(); + vm.editor.focus(); } }; + editorService.insertField(insertFieldEditor); } @@ -517,12 +510,8 @@ function openSectionsOverlay() { - - vm.sectionsOverlay = { - view: "templatesections", + var templateSections = { isMaster: vm.template.isMasterTemplate, - submitButtonLabel: "Insert", - show: true, submit: function(model) { if (model.insertType === 'renderBody') { @@ -540,18 +529,15 @@ wrap(code); } - vm.sectionsOverlay.show = false; - vm.sectionsOverlay = null; + editorService.close(); }, close: function(model) { - // close dialog - vm.sectionsOverlay.show = false; - vm.sectionsOverlay = null; - // focus editor + editorService.close(); vm.editor.focus(); } } + editorService.templateSections(templateSections); } function openMasterTemplateOverlay() { @@ -572,16 +558,11 @@ localizationService.localize("template_mastertemplate").then(function(value){ var title = value; - - vm.masterTemplateOverlay = { - view: "itempicker", + var masterTemplate = { title: title, availableItems: availableMasterTemplates, - show: true, submit: function(model) { - var template = model.selectedItem; - if (template && template.alias) { vm.template.masterTemplateAlias = template.alias; setLayout(template.alias + ".cshtml"); @@ -589,18 +570,16 @@ vm.template.masterTemplateAlias = null; setLayout(null); } - - vm.masterTemplateOverlay.show = false; - vm.masterTemplateOverlay = null; + editorService.close(); }, close: function(oldModel) { // close dialog - vm.masterTemplateOverlay.show = false; - vm.masterTemplateOverlay = null; + editorService.close(); // focus editor vm.editor.focus(); } }; + editorService.itemPicker(masterTemplate); }); } @@ -715,6 +694,19 @@ function closeShortcuts() { vm.showKeyboardShortcut = false; } + + function submit() { + if($scope.model.submit) { + $scope.model.template = vm.template; + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model.close) { + $scope.model.close(); + } + } vm.init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 0b1b574e70..5576d2713f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -106,16 +106,24 @@ + + + - - + + @@ -124,33 +132,4 @@ - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js index abe287ed88..4d25419175 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js @@ -47,7 +47,8 @@ $location.search("subview", "users") }, "view": !usersUri || usersUri === "users" ? "views/users/views/users/users.html" : null, - "active": !usersUri || usersUri === "users" + "active": !usersUri || usersUri === "users", + "alias": "users" }, { "name": vm.page.labels.groups, @@ -56,7 +57,8 @@ $location.search("subview", "groups") }, "view": usersUri === "groups" ? "views/users/views/groups/groups.html" : null, - "active": usersUri === "groups" + "active": usersUri === "groups", + "alias": "userGroups" } ]; }); diff --git a/src/Umbraco.Web.UI.Client/test/lib/angular/angular-mocks.js b/src/Umbraco.Web.UI.Client/test/lib/angular/angular-mocks.js index da92ff3363..b4a3fb9ed6 100644 --- a/src/Umbraco.Web.UI.Client/test/lib/angular/angular-mocks.js +++ b/src/Umbraco.Web.UI.Client/test/lib/angular/angular-mocks.js @@ -1420,13 +1420,13 @@ function MockXhr() { var header = this.$$respHeaders[name]; if (header) return header; - name = angular.lowercase(name); + name = name.toLowerCase(); header = this.$$respHeaders[name]; if (header) return header; header = undefined; angular.forEach(this.$$respHeaders, function (headerVal, headerName) { - if (!header && angular.lowercase(headerName) == name) header = headerVal; + if (!header && headerName.toLowerCase() == name) header = headerVal; }); return header; }; @@ -1883,4 +1883,4 @@ window.jstestdriver && (function (window) { } } }; -})(window); \ No newline at end of file +})(window); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js index c79b2ed72b..484cbeb0dc 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js @@ -54,14 +54,6 @@ describe('edit media controller tests', function () { it('it should have added an info app', function () { expect(scope.content.apps[1].alias).toBe("info"); }); - - it('all other tabs than the info tab should have a properties collection', function () { - $(scope.content.tabs).each(function (i, tab) { - if (tab.id !== -1 && tab.alias !== '_umb_infoTab') { - expect(tab.properties.length).toBeGreaterThan(0); - } - }); - }); - + }); }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/content-editing-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/content-editing-helper.spec.js index 5937b7f2fb..f3d2f8d759 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/content-editing-helper.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/content-editing-helper.spec.js @@ -123,7 +123,8 @@ describe('contentEditingHelper tests', function () { var allProps = contentEditingHelper.getAllProps(content); //act - formHelper.handleServerValidation({ "_Properties.bodyText.value": ["Required"] }); + //note the null, that's because culture is null + formHelper.handleServerValidation({ "_Properties.bodyText.null.value": ["Required"] }); //assert expect(serverValidationManager.items.length).toBe(1); @@ -143,7 +144,8 @@ describe('contentEditingHelper tests', function () { { "Name": ["Required"], "UpdateDate": ["Invalid date"], - "_Properties.bodyText.value": ["Required field"], + //note the null, that's because culture is null + "_Properties.bodyText.null.value": ["Required field"], "_Properties.textarea": ["Invalid format"] }); @@ -191,13 +193,13 @@ describe('contentEditingHelper tests', function () { expect(result).toBe(true); }); - it('does not redirect when creating content with an invalid name', function () { + it('does not redirect when creating content with an invalid id', function () { //arrange $routeParams.create = true; //act - var result = contentEditingHelper.redirectToCreatedContent(1234, {Name: ["Required"]}); + var result = contentEditingHelper.redirectToCreatedContent(0, {Name: ["Required"]}); //assert expect(result).toBe(false); @@ -212,6 +214,7 @@ describe('contentEditingHelper tests', function () { //arrange var origContent = mocksUtils.getMockContent(1234); + origContent.save = true; origContent.tabs[0].properties[0].value = { complex1: "origValue1a", complex2: "origValue1b" }; origContent.tabs[1].properties[0].value = "origValue2"; origContent.tabs[1].properties[1].value = "origValue3"; @@ -226,6 +229,7 @@ describe('contentEditingHelper tests', function () { //act var changed = contentEditingHelper.reBindChangedProperties(origContent, newContent); + //assert expect(changed.length).toBe(2); expect(changed[0].alias).toBe("grid"); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/file-manager.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/file-manager.spec.js index e86119cf5f..e5751341bc 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/file-manager.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/file-manager.spec.js @@ -10,24 +10,39 @@ describe('file manager tests', function () { describe('file management', function () { it('adding a file adds to the collection', function () { - fileManager.setFiles('testProp', ["testFile"]); + fileManager.setFiles({ + propertyAlias: 'testProp', + files: ["testFile"] + }); expect(fileManager.getFiles().length).toBe(1); }); it('adding a file with the same property id replaces the existing one', function () { - fileManager.setFiles('testProp', ["testFile"]); - fileManager.setFiles('testProp', ["testFile2"]); + fileManager.setFiles({ + propertyAlias: 'testProp', + files: ["testFile"] + }); + fileManager.setFiles({ + propertyAlias: 'testProp', + files: ["testFile2"] + }); expect(fileManager.getFiles().length).toBe(1); expect(fileManager.getFiles()[0].file).toBe("testFile2"); }); it('clears all files', function () { - fileManager.setFiles('testProp1', ["testFile"]); - fileManager.setFiles('testProp2', ["testFile"]); + fileManager.setFiles({ + propertyAlias: 'testProp', + files: ["testFile"] + }); + fileManager.setFiles({ + propertyAlias: 'testProp2', + files: ["testFile"] + }); expect(fileManager.getFiles().length).toBe(2); fileManager.clearFiles(); expect(fileManager.getFiles().length).toBe(0); }); }); -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/media-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/media-helper.spec.js new file mode 100644 index 0000000000..15f8eb87d1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/media-helper.spec.js @@ -0,0 +1,39 @@ +describe('mediaHelper service tests', function () { + var mediaHelper; + + beforeEach(module('umbraco.services')); + + beforeEach(inject(function ($injector) { + mediaHelper = $injector.get('mediaHelper'); + })); + + describe('formatFileTypes', function () { + + it('will ignore any empty strings passed as arguments', function () { + + var result = mediaHelper.formatFileTypes(""); + expect(result.length).toBe(0); + + result = mediaHelper.formatFileTypes(" "); + expect(result.length).toBe(0); + + result = mediaHelper.formatFileTypes(" "); + expect(result.length).toBe(0); + + result = mediaHelper.formatFileTypes(" , "); + expect(result.length).toBe(0); + + result = mediaHelper.formatFileTypes(" , ,,"); + expect(result.length).toBe(0); + + }); + + it('includes prefixed dot when formatting', function () { + + var result = mediaHelper.formatFileTypes(".jpg, .png, gif"); + expect(result).toBe(".jpg,.png,.gif"); + + }); + + }); +}); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/navigation-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/navigation-service.spec.js new file mode 100644 index 0000000000..b09d867d8d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/navigation-service.spec.js @@ -0,0 +1,50 @@ +describe('navigation services tests', function () { + var navigationService; + + beforeEach(module('umbraco.services')); + beforeEach(module('ngRoute')); + + beforeEach(inject(function ($injector) { + navigationService = $injector.get('navigationService'); + })); + + describe('determine if route change causes navigation', function () { + + it('navigates when parameters added', function () { + var currParams = { section: "content", id: 123 }; + var nextParams = { section: "content", id: 123, create: true }; + var result = navigationService.isRouteChangingNavigation(currParams, nextParams); + expect(result).toBe(true); + }); + + it('navigates when parameters removed', function () { + var currParams = { section: "content", id: 123, create: true }; + var nextParams = { section: "content", id: 123 }; + var result = navigationService.isRouteChangingNavigation(currParams, nextParams); + expect(result).toBe(true); + }); + + it('does not navigate when non routing parameters added', function () { + var currParams = { section: "content", id: 123 }; + var nextParams = { section: "content", id: 123, mculture: "abc", cculture: "xyz" }; + var result = navigationService.isRouteChangingNavigation(currParams, nextParams); + expect(result).toBe(false); + }); + + it('does not navigate when non routing parameters changed', function () { + var currParams = { section: "content", id: 123, mculture: "abc" }; + var nextParams = { section: "content", id: 123, mculture: "ooo", cculture: "xyz" }; + var result = navigationService.isRouteChangingNavigation(currParams, nextParams); + expect(result).toBe(false); + }); + + it('does not navigate when non routing parameters removed', function () { + var currParams = { section: "content", id: 123, mculture: "abc", cculture: "xyz" }; + var nextParams = { section: "content", id: 123, cculture: "xyz" }; + var result = navigationService.isRouteChangingNavigation(currParams, nextParams); + expect(result).toBe(false); + }); + + }); + +}); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js index 2740bd7317..7510b766a8 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js @@ -60,12 +60,12 @@ it('can retrieve property validation errors for a sub field', function () { //arrange - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Another value 2"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2"); //act - var err1 = serverValidationManager.getPropertyError("myProperty", "value1"); - var err2 = serverValidationManager.getPropertyError("myProperty", "value2"); + var err1 = serverValidationManager.getPropertyError("myProperty", null, "value1"); + var err2 = serverValidationManager.getPropertyError("myProperty", null, "value2"); //assert expect(err1).not.toBeUndefined(); @@ -82,8 +82,8 @@ it('can add a property errors with multiple sub fields and it the first will be retreived with only the property alias', function () { //arrange - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Another value 2"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2"); //act var err = serverValidationManager.getPropertyError("myProperty"); @@ -98,10 +98,10 @@ it('will return null for a non-existing property error', function () { //arrage - serverValidationManager.addPropertyError("myProperty", "value", "Required"); + serverValidationManager.addPropertyError("myProperty", null, "value", "Required"); //act - var err = serverValidationManager.getPropertyError("DoesntExist", "value"); + var err = serverValidationManager.getPropertyError("DoesntExist", null, "value"); //assert expect(err).toBeUndefined(); @@ -111,15 +111,15 @@ it('detects if a property error exists', function () { //arrange - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Another value 2"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2"); //act var err1 = serverValidationManager.hasPropertyError("myProperty"); - var err2 = serverValidationManager.hasPropertyError("myProperty", "value1"); - var err3 = serverValidationManager.hasPropertyError("myProperty", "value2"); + var err2 = serverValidationManager.hasPropertyError("myProperty", null, "value1"); + var err3 = serverValidationManager.hasPropertyError("myProperty", null, "value2"); var err4 = serverValidationManager.hasPropertyError("notFound"); - var err5 = serverValidationManager.hasPropertyError("myProperty", "notFound"); + var err5 = serverValidationManager.hasPropertyError("myProperty", null, "notFound"); //assert expect(err1).toBe(true); @@ -133,30 +133,30 @@ it('can remove a property error with a sub field specified', function () { //arrage - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Another value 2"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2"); //act - serverValidationManager.removePropertyError("myProperty", "value1"); + serverValidationManager.removePropertyError("myProperty", null, "value1"); //assert - expect(serverValidationManager.hasPropertyError("myProperty", "value1")).toBe(false); - expect(serverValidationManager.hasPropertyError("myProperty", "value2")).toBe(true); + expect(serverValidationManager.hasPropertyError("myProperty", null, "value1")).toBe(false); + expect(serverValidationManager.hasPropertyError("myProperty", null, "value2")).toBe(true); }); it('can remove a property error and all sub field errors by specifying only the property', function () { //arrage - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Another value 2"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2"); //act serverValidationManager.removePropertyError("myProperty"); //assert - expect(serverValidationManager.hasPropertyError("myProperty", "value1")).toBe(false); - expect(serverValidationManager.hasPropertyError("myProperty", "value2")).toBe(false); + expect(serverValidationManager.hasPropertyError("myProperty", null, "value1")).toBe(false); + expect(serverValidationManager.hasPropertyError("myProperty", null, "value2")).toBe(false); }); @@ -168,7 +168,7 @@ var args; //arrange - serverValidationManager.subscribe(null, "Name", function (isValid, propertyErrors, allErrors) { + serverValidationManager.subscribe(null, null, "Name", function (isValid, propertyErrors, allErrors) { args = { isValid: isValid, propertyErrors: propertyErrors, @@ -178,7 +178,7 @@ //act serverValidationManager.addFieldError("Name", "Required"); - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); //assert expect(args).not.toBeUndefined(); @@ -195,8 +195,8 @@ }; var cb2 = function () { }; - serverValidationManager.subscribe(null, "Name", cb1); - serverValidationManager.subscribe(null, "Title", cb2); + serverValidationManager.subscribe(null, null, "Name", cb1); + serverValidationManager.subscribe(null, null, "Title", cb2); //act serverValidationManager.addFieldError("Name", "Required"); @@ -224,7 +224,7 @@ var numCalled = 0; //arrange - serverValidationManager.subscribe("myProperty", "value1", function (isValid, propertyErrors, allErrors) { + serverValidationManager.subscribe("myProperty", null, "value1", function (isValid, propertyErrors, allErrors) { args1 = { isValid: isValid, propertyErrors: propertyErrors, @@ -232,7 +232,7 @@ }; }); - serverValidationManager.subscribe("myProperty", "", function (isValid, propertyErrors, allErrors) { + serverValidationManager.subscribe("myProperty", null, "", function (isValid, propertyErrors, allErrors) { numCalled++; args2 = { isValid: isValid, @@ -242,9 +242,9 @@ }); //act - serverValidationManager.addPropertyError("myProperty", "value1", "Some value 1"); - serverValidationManager.addPropertyError("myProperty", "value2", "Some value 2"); - serverValidationManager.addPropertyError("myProperty", "", "Some value 3"); + serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1"); + serverValidationManager.addPropertyError("myProperty", null, "value2", "Some value 2"); + serverValidationManager.addPropertyError("myProperty", null, "", "Some value 3"); //assert expect(args1).not.toBeUndefined(); @@ -272,4 +272,4 @@ }); -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/umb-data-formatter-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/umb-data-formatter-service.spec.js new file mode 100644 index 0000000000..7c307a96e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/umb-data-formatter-service.spec.js @@ -0,0 +1,88 @@ +describe('umbDataFormatter service tests', function () { + var umbDataFormatter; + + beforeEach(module('umbraco.services')); + + beforeEach(inject(function ($injector) { + umbDataFormatter = $injector.get('umbDataFormatter'); + })); + + describe('formatting GET content data', function () { + + it('will set the same invariant property instance reference between all variants', function () { + + var model = { + variants: [ + { + language: { culture: "en-US" }, + tabs: [ + { + properties: [ + { alias: "test1", culture: null, value: "test1" }, + { alias: "test2", culture: "en-US", value: "test2" } + ] + }, + { + properties: [ + { alias: "test3", culture: "en-US", value: "test3" }, + { alias: "test4", culture: null, value: "test4" } + ] + } + ] + + }, + { + language: { culture: "es-ES" }, + tabs: [ + { + properties: [ + { alias: "test1", culture: null, value: "test5" }, + { alias: "test2", culture: "en-US", value: "test6" } + ] + }, + { + properties: [ + { alias: "test3", culture: "en-US", value: "test7" }, + { alias: "test4", culture: null, value: "test8" } + ] + } + ] + }, + { + language: { culture: "fr-FR" }, + tabs: [ + { + properties: [ + { alias: "test1", culture: null, value: "test9" }, + { alias: "test2", culture: "en-US", value: "test10" } + ] + }, + { + properties: [ + { alias: "test3", culture: "en-US", value: "test11" }, + { alias: "test4", culture: null, value: "test12" } + ] + } + ] + } + ] + }; + var result = umbDataFormatter.formatContentGetData(model); + + //make sure the same property reference exists for property 0 and 3 for each variant + for (var i = 1; i < result.variants.length; i++) { + expect(result.variants[0].tabs[0].properties[0]).toBe(result.variants[i].tabs[0].properties[0]); + expect(result.variants[0].tabs[1].properties[3]).toBe(result.variants[i].tabs[1].properties[3]); + } + + //test that changing a property value in one variant is definitely updating the same object reference and therefor + //is done on all variants. + result.variants[0].tabs[0].properties[0].value = "hello"; + for (var i = 1; i < result.variants.length; i++) { + expect(result.variants[0].tabs[0].properties[0].value).toBe("hello"); + } + + }); + + }); +}); diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index db6d974fc2..499a98554a 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -180,14 +180,6 @@ ChangeDocType.aspx - - sort.aspx - ASPXCodeBehind - - - sort.aspx - ASPXCodeBehind - default.Master ASPXCodeBehind @@ -348,31 +340,8 @@ ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - - - @@ -385,31 +354,17 @@ - - - - - - - - - - - - - - Designer @@ -500,7 +455,6 @@ - @@ -512,27 +466,12 @@ Form - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index d9606d8af7..dd17f9d6f5 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -10,9 +10,9 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validate/jquery.validate.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); var success = TempData["ProfileUpdateSuccess"] != null; } @@ -63,4 +63,4 @@ } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml index 5dfdfb36bd..c3fcf04ba3 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml @@ -8,12 +8,12 @@ @{ var loginModel = new LoginModel(); - + Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validate/jquery.validate.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); } @* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ @@ -38,4 +38,4 @@ -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 7c7c9262d0..d9d2c11448 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -10,12 +10,12 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); + Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); var logoutModel = new PostRedirectModel(); - + @* Here you can specify a redirect URL for after logging out, by default umbraco will simply redirect to the current page. Example to redirect to the home page: @@ -40,4 +40,4 @@ @Html.HiddenFor(m => logoutModel.RedirectUrl) } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index a3fb34d4bf..51c0cc0443 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -34,9 +34,9 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validate/jquery.validate.min.js"); + Html.RequiresJs("/Umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); var success = TempData["FormSuccess"] != null; } diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml index cd2e64cb44..1f26d628e4 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml @@ -1,19 +1,11 @@ -@using System.Collections -@using System.Net.Http -@using System.Web.Mvc.Html -@using Umbraco.Core +@using Umbraco.Core @using ClientDependency.Core @using ClientDependency.Core.Mvc -@using Microsoft.Owin.Security -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq @using Umbraco.Core.IO @using Umbraco.Web -@using Umbraco.Web.Editors -@using umbraco @using Umbraco.Core.Configuration -@inherits System.Web.Mvc.WebViewPage +@inherits WebViewPage @{ var isDebug = false; @@ -51,18 +43,25 @@ new BasicPath("Umbraco", IOHelper.ResolveUrl(SystemDirectories.Umbraco)), new BasicPath("UmbracoClient", IOHelper.ResolveUrl(SystemDirectories.UmbracoClient))) - + +
    - -
    + + - + @@ -90,45 +87,42 @@ - +
    - + - + - + @Html.BareMinimumServerVariablesScript(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath }), Model.Features, UmbracoConfig.For.GlobalSettings()) - - - @*And finally we can load in our angular app*@ - - + + @if (isDebug) { diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 4620ff3314..dce1986868 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -424,6 +424,7 @@ Navnet '%0%' eksisterer allerede. ]]> + Ordbog Indtast dit brugernavn diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index e29d3c45eb..cf810fbd24 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2038,9 +2038,9 @@ To manage your website, simply open the Umbraco back office and start adding con Validation - Validate as email + Validate as an email address Validate as a number - Validate as a Url + Validate as a URL ...or enter a custom validation Field is mandatory Enter a regular expression diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 91da005a40..26338f5a43 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -128,7 +128,7 @@ Save Save and close - Save and publish + Publish Publish… Save and schedule Save and send for approval @@ -231,8 +231,7 @@ This document is published but its url cannot be routed Publish Published - Published (pending changes) - Unpublished (pending changes) + Published (pending changes)> Publication Status Publish at Unpublish at @@ -245,7 +244,8 @@ Alternative text (optional) Type Unpublish - Unpublished + Draft + Not created Last edited Date/time this document was edited Remove file(s) @@ -264,8 +264,11 @@ This value is hidden. If you need access to view this value please contact your website administrator. This value is hidden. What languages would you like to publish? - Published Languages. + What languages would you like to save? + Published Languages + Unmodified Languages Ready to Publish? + Ready to Save? Create a new Content Template from '%0%' @@ -1368,6 +1371,7 @@ To manage your website, simply open the Umbraco back office and start adding con Sorting complete. Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items + This node has no child nodes to sort Validation @@ -1459,7 +1463,6 @@ To manage your website, simply open the Umbraco back office and start adding con Invitation has been re-sent to %0% Cannot publish the document since the required '%0%' is not published Validation failed for language '%0%' - Unexpected validation failed for language '%0%' Document type was exported to file An error occurred while exporting the document type @@ -2055,9 +2058,9 @@ To manage your website, simply open the Umbraco back office and start adding con Validation - Validate as email + Validate as an email address Validate as a number - Validate as a Url + Validate as a URL ...or enter a custom validation Field is mandatory Enter a regular expression @@ -2068,6 +2071,9 @@ To manage your website, simply open the Umbraco back office and start adding con Invalid date Not a number Invalid email + Value cannot be null + Value cannot be empty + Value is invalid, it does not match the correct pattern @@ -1468,9 +1430,6 @@ - - ASPXCodeBehind - @@ -1487,17 +1446,10 @@ ASPXCodeBehind - - - ASPXCodeBehind - ASPXCodeBehind - - ASPXCodeBehind - @@ -1551,6 +1503,9 @@ umbraco_org_umbraco_update_CheckForUpgrade + + + - - - - - - -
    - - - - - - - - - - - - - -
    <%=Services.TextService.Localize("assignDomain/domain") %><%=Services.TextService.Localize("assignDomain/language") %> -
    - -
    - - - - <%=Services.TextService.Localize("assignDomain/domainHelp") %> - - - - - - -
    - - - - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs deleted file mode 100644 index db04f26bcf..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Text; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.UI.Pages; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.Editors; -using Umbraco.Web.WebServices; -using Umbraco.Web._Legacy.Actions; - - -namespace umbraco.dialogs -{ - public partial class AssignDomain2 : UmbracoEnsuredPage - { - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - var nodeId = GetNodeId(); - CheckPathAndPermissions(nodeId, UmbracoObjectTypes.Document, ActionAssignDomain.Instance); - } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - var nodeId = GetNodeId(); - var node = Services.ContentService.GetById(nodeId); - - if (node == null) - { - feedback.Text = Services.TextService.Localize("assignDomain/invalidNode"); - pane_language.Visible = false; - pane_domains.Visible = false; - p_buttons.Visible = false; - return; - } - - pane_language.Title = Services.TextService.Localize("assignDomain/setLanguage"); - pane_domains.Title = Services.TextService.Localize("assignDomain/setDomains"); - prop_language.Text = Services.TextService.Localize("assignDomain/language"); - - var nodeDomains = Services.DomainService.GetAssignedDomains(nodeId, true).ToArray(); - var wildcard = nodeDomains.FirstOrDefault(d => d.IsWildcard); - - var sb = new StringBuilder(); - sb.Append("languages: ["); - var i = 0; - foreach (var language in Current.Services.LocalizationService.GetAllLanguages()) - sb.AppendFormat("{0}{{ \"Id\": {1}, \"Code\": \"{2}\" }}", (i++ == 0 ? "" : ","), language.Id, language.IsoCode); - sb.Append("]\r\n"); - - sb.AppendFormat(",language: {0}", wildcard == null ? "undefined" : wildcard.LanguageId.ToString()); - - sb.Append(",domains: ["); - i = 0; - foreach (var domain in nodeDomains.Where(d => d.IsWildcard == false)) - sb.AppendFormat("{0}{{ \"Name\": \"{1}\", \"Lang\": \"{2}\" }}", (i++ == 0 ? "" :","), domain.DomainName, domain.LanguageId); - sb.Append("]\r\n"); - - data.Text = sb.ToString(); - } - - protected int GetNodeId() - { - int nodeId; - if (int.TryParse(Request.QueryString["id"], out nodeId) == false) - nodeId = -1; - return nodeId; - } - - protected string GetRestServicePath() - { - const string action = "ListDomains"; - var path = Url.GetUmbracoApiService(action); - return path.TrimEnd(action).EnsureEndsWith('/'); - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.designer.cs deleted file mode 100644 index af45317d12..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.designer.cs +++ /dev/null @@ -1,69 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.dialogs { - - - public partial class AssignDomain2 { - - /// - /// data control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal data; - - /// - /// feedback control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Feedback feedback; - - /// - /// pane_language control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_language; - - /// - /// prop_language control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel prop_language; - - /// - /// pane_domains control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_domains; - - /// - /// p_buttons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl p_buttons; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx deleted file mode 100644 index 44280f6710..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx +++ /dev/null @@ -1,49 +0,0 @@ -<%@ Page MasterPageFile="../masterpages/umbracoDialog.Master" Language="c#" Codebehind="importDocumenttype.aspx.cs" AutoEventWireup="false" - Inherits="umbraco.presentation.umbraco.dialogs.importDocumentType" %> - - - - - - - - - - -
    - -

    - - <%=Services.TextService.Localize("importDocumentTypeHelp")%> - -

    - -

    - -

    - - - <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -
    - - - - - <%=Services.TextService.Localize("name")%> - : - -
    - - <%=Services.TextService.Localize("alias")%> - : - -
    -
    - -
    - - - has been imported! - -
    -
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs deleted file mode 100644 index 38f061e079..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Linq; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using System.Xml; -using System.Xml.Linq; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.presentation.umbraco.dialogs -{ - /// - /// Summary description for importDocumentType. - /// - public class importDocumentType : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - public importDocumentType() - { - - CurrentApp = Constants.Applications.Settings.ToString(); - - } - protected Literal FeedBackMessage; - protected Literal jsShowWindow; - protected Panel Wizard; - protected HtmlTable Table1; - protected HtmlInputHidden tempFile; - protected HtmlInputFile documentTypeFile; - protected Button submit; - protected Panel Confirm; - protected Literal dtName; - protected Literal dtAlias; - protected Button import; - protected Literal dtNameConfirm; - protected Panel done; - private string tempFileName = ""; - - private void Page_Load(object sender, EventArgs e) - { - if (!IsPostBack) - { - submit.Text = Services.TextService.Localize("import"); - import.Text = Services.TextService.Localize("import"); - } - } - - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.submit.Click += new System.EventHandler(this.submit_Click); - this.import.Click += new System.EventHandler(this.import_Click); - this.Load += new System.EventHandler(this.Page_Load); - - } - #endregion - - private void import_Click(object sender, EventArgs e) - { - var xd = new XmlDocument(); - xd.XmlResolver = null; - xd.Load(tempFile.Value); - - var userId = Security.GetUserId(); - - var element = XElement.Parse(xd.InnerXml); - var importContentTypes = Current.Services.PackagingService.ImportContentTypes(element, userId); - var contentType = importContentTypes.FirstOrDefault(); - if (contentType != null) - dtNameConfirm.Text = contentType.Name; - - // Try to clean up the temporary file. - try - { - System.IO.File.Delete(tempFile.Value); - } - catch(Exception ex) - { - Current.Logger.Error(typeof(importDocumentType), "Error cleaning up temporary udt file in App_Data: " + ex.Message, ex); - } - - Wizard.Visible = false; - Confirm.Visible = false; - done.Visible = true; - } - - private void submit_Click(object sender, EventArgs e) - { - tempFileName = "justDelete_" + Guid.NewGuid().ToString() + ".udt"; - var fileName = IOHelper.MapPath(SystemDirectories.Data + "/" + tempFileName); - tempFile.Value = fileName; - - documentTypeFile.PostedFile.SaveAs(fileName); - - var xd = new XmlDocument(); - xd.XmlResolver = null; - xd.Load(fileName); - dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value; - dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value; - - Wizard.Visible = false; - done.Visible = false; - Confirm.Visible = true; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx deleted file mode 100644 index 77c4841d7d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx +++ /dev/null @@ -1,18 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="notifications.aspx.cs" AutoEventWireup="True" - Inherits="umbraco.dialogs.notifications" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - -
    - - - - - -
    - -
    \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs deleted file mode 100644 index ef3cc5f63b..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Collections; -using System.Globalization; -using System.Linq; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Core.Models.Entities; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.dialogs -{ - /// - /// Summary description for cruds. - /// - public partial class notifications : UmbracoEnsuredPage - { - private ArrayList actions = new ArrayList(); - private IUmbracoEntity node; - - public notifications() - { - CurrentApp = Constants.Applications.Content.ToString(); - - } - - protected void Page_Load(object sender, EventArgs e) - { - Button1.Text = Services.TextService.Localize("update"); - pane_form.Text = Services.TextService.Localize("notifications/editNotifications", new[] { Server.HtmlEncode(node.Name) }); - } - - #region Web Form Designer generated code - - protected override void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - - node = Services.EntityService.Get(int.Parse(Request.GetItemAsString("id"))); - - var actionList = Current.Actions; - - foreach (var a in actionList) - { - if (a.ShowInNotifier) - { - - CheckBox c = new CheckBox(); - c.ID = a.Letter.ToString(CultureInfo.InvariantCulture); - - var notifications = Services.NotificationService.GetUserNotifications(Security.CurrentUser, node.Path); - if (notifications.Any(x => x.Action == a.Letter.ToString())) - c.Checked = true; - - var pp = new Umbraco.Web._Legacy.Controls.PropertyPanel(); - pp.CssClass = "inline"; - pp.Text = Services.TextService.Localize("actions", a.Alias); - pp.Controls.Add(c); - - pane_form.Controls.Add(pp); - - actions.Add(c); - - } - } - - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - - protected void Button1_Click(object sender, EventArgs e) - { - string notifications = ""; - - // First off - load all users - foreach (CheckBox c in actions) - { - // Update the user with the new permission - if (c.Checked) - notifications += c.ID; - } - - Current.Services.NotificationService.SetNotifications(Security.CurrentUser, node, notifications.ToCharArray().Select(x => x.ToString()).ToArray()); - - var feedback = new Umbraco.Web._Legacy.Controls.Feedback(); - feedback.Text = Services.TextService.Localize("notifications") + " " + Services.TextService.Localize("ok") + "

    " + Services.TextService.Localize("closeThisWindow") + ""; - feedback.type = Umbraco.Web._Legacy.Controls.Feedback.feedbacktype.success; - - pane_form.Controls.Clear(); - pane_form.Controls.Add(feedback); - - //pane_form.Visible = false; - pl_buttons.Visible = false; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.designer.cs deleted file mode 100644 index 861f6bbbfb..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.dialogs { - - - public partial class notifications { - - ///

    - /// pane_form control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_form; - - /// - /// pl_buttons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl pl_buttons; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs deleted file mode 100644 index f85114aaec..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using System.Web.UI; -using System.Collections.Generic; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Models; -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.cms.presentation -{ - /// - /// Summary description for sort. - /// - public partial class sort : UmbracoEnsuredPage - { - /// - /// The Parent Id being sorted - /// - protected int? ParentIdAsInt { get; private set; } - protected string ParentIdAsString { get; private set; } - - private readonly List _nodes = new List(); - - protected bool HideDateColumn - { - set { ViewState["HideDateColumn"] = value; } - get { return ViewState["HideDateColumn"] == null ? false : (bool) ViewState["HideDateColumn"]; } - } - - protected override void OnInit(EventArgs e) - { - CurrentApp = Request.GetItemAsString("app"); - - ParentIdAsString = Request.GetItemAsString("ID"); - int parentId; - if (int.TryParse(ParentIdAsString, out parentId)) - { - ParentIdAsInt = parentId; - - if (CurrentApp == Constants.Applications.Content || CurrentApp == Constants.Applications.Media) - { - CheckPathAndPermissions( - ParentIdAsInt.Value, - CurrentApp == Constants.Applications.Content ? UmbracoObjectTypes.Document : UmbracoObjectTypes.Media, - ActionSort.Instance); - } - } - - base.OnInit(e); - } - - protected void Page_Load(object sender, EventArgs e) - { - sortDone.Text = Services.TextService.Localize("sort/sortDone"); - } - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/nodesorter.asmx")); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/legacyAjaxCalls.asmx")); - - var app = Request.GetItemAsString("app"); - - var icon = "../images/umbraco/doc.gif"; - - if (ParentIdAsInt.HasValue) - { - if (app == Constants.Applications.Media) - { - icon = "../images/umbraco/mediaPhoto.gif"; - var mediaService = Current.Services.MediaService; - - if (ParentIdAsInt.Value == -1) - { - foreach (var child in mediaService.GetRootMedia().ToList().OrderBy(x => x.SortOrder)) - _nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon)); - } - else - { - var children = mediaService.GetChildren(ParentIdAsInt.Value); - foreach (var child in children.OrderBy(x => x.SortOrder)) - _nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon)); - } - } - - if (app == Constants.Applications.Content) - { - var contentService = Current.Services.ContentService; - - if (ParentIdAsInt.Value == -1) - { - foreach (var child in contentService.GetRootContent().ToList().OrderBy(x => x.SortOrder)) - _nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon)); - } - else - { - var children = contentService.GetChildren(ParentIdAsInt.Value); - foreach (var child in children) - _nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon)); - } - } - - bindNodesToList(string.Empty); - } - else - { - // hack for stylesheet, used to sort stylesheet properties - if (app == Constants.Applications.Settings) - { - icon = "../images/umbraco/settingCss.gif"; - - HideDateColumn = true; - - var stylesheetName = ParentIdAsString; - if (stylesheetName.IsNullOrWhiteSpace())throw new NullReferenceException("No Id passed in to editor"); - var stylesheet = Services.FileService.GetStylesheetByName(stylesheetName.EnsureEndsWith(".css")); - if (stylesheet == null) throw new InvalidOperationException("No stylesheet found by name " + stylesheetName); - - var sort = 0; - foreach (var child in stylesheet.Properties) - { - _nodes.Add(CreateNode(child.Name, sort, child.Name, DateTime.Now, icon)); - sort++; - } - - bindNodesToList(string.Empty); - } - } - } - - public void bindNodesToList(string sortBy) - { - if (string.IsNullOrEmpty(sortBy) == false) - { - switch (sortBy) - { - case "nodeName": - _nodes.Sort(new nodeNameCompare()); - break; - case "createDate": - _nodes.Sort(new createDateCompare()); - break; - } - } - - foreach (var n in _nodes) - lt_nodes.Text += string.Format( - "{1}{2} {3}{4}", - n.id, n.Name, n.createDate.ToShortDateString(), n.createDate.ToShortTimeString(), n.sortOrder, HideDateColumn ? "none" : "table-cell"); - } - - private static SortableNode CreateNode(string id, int sortOrder, string name, DateTime createDateTime, string icon) - { - var node = new SortableNode - { - id = id, - sortOrder = sortOrder, - Name = name, - icon = icon, - createDate = createDateTime - }; - return node; - } - - public class SortableNode - { - public string id; - public int sortOrder; - public string Name; - public string icon; - public DateTime createDate; - } - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// JsInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; - - /// - /// prog1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.ProgressBar prog1; - - /// - /// sortDone control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal sortDone; - - /// - /// sortPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane sortPane; - - /// - /// lt_nodes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_nodes; - - } - - public class nodeNameCompare : IComparer - { - - #region IComparer Members - - public int Compare(sort.SortableNode x, sort.SortableNode y) - { - var returnValue = String.Compare(x.Name, y.Name, StringComparison.Ordinal); - - return returnValue; - } - - #endregion - } - - public class createDateCompare : IComparer - { - - #region IComparer Members - - public int Compare(sort.SortableNode x, sort.SortableNode y) - { - var returnValue = x.createDate.CompareTo(y.createDate); - - return returnValue; - } - - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx deleted file mode 100644 index 740b069006..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx +++ /dev/null @@ -1,72 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoPage.Master"Codebehind="viewAuditTrail.aspx.cs" AutoEventWireup="True" - Inherits="umbraco.presentation.umbraco.dialogs.viewAuditTrail" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - -
    - - - - - - <%=Services.TextService.Localize("action")%>   - - - - <%# FormatAction(DataBinder.Eval(Container.DataItem, "LogType", "{0}")) %> - - - - - - <%=Services.TextService.Localize("user")%> - - - - <%--TODO: N+1 !!!! and no null checks--%> - <%# Services.UserService.GetUserById(int.Parse(DataBinder.Eval(Container.DataItem, "UserId", "{0}"))).Name%> - - - - - - <%=Services.TextService.Localize("date")%> - - - - <%# DataBinder.Eval(Container.DataItem, "Timestamp", "{0:D} {0:T}") %> - - - - - - <%=Services.TextService.Localize("comment")%> - - - - <%# DataBinder.Eval(Container.DataItem, "Comment", "{0}") %> - - - - -
    -
    - -
    - - - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs deleted file mode 100644 index f80fa5d064..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Web; -using System.Web.SessionState; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.Actions; -using Action = Umbraco.Web._Legacy.Actions.Action; - -namespace umbraco.presentation.umbraco.dialogs -{ - /// - /// Summary description for viewAuditTrail. - /// - public partial class viewAuditTrail : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - public viewAuditTrail() - { - CurrentApp = Constants.Applications.Content.ToString(); - - } - - protected void Page_Load(object sender, System.EventArgs e) - { - // Put user code to initialize the page here - //nodeName.Text = new cms.businesslogic.CMSNode(int.Parse(helper.Request("nodeID"))).Text; - auditLog.DataSource = Services.AuditService.GetLogs(int.Parse(Request["nodeID"])); - auditLog.DataBind(); - auditLog.BorderWidth = 0; - auditLog.BorderStyle = BorderStyle.None; - } - - public string FormatAction(string action) - { - action = action.ToLower(); - if (action == "new") - action = "create"; - var actions = Current.Actions; - foreach (var a in actions) - { - return Services.TextService.Localize(action); - } - return action; - } - - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.designer.cs deleted file mode 100644 index fa69f92d4e..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class viewAuditTrail { - - /// - /// auditLog control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DataGrid auditLog; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx deleted file mode 100644 index 0c98313e1e..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx +++ /dev/null @@ -1,16 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" Codebehind="DictionaryItemList.aspx.cs" - Inherits="umbraco.presentation.settings.DictionaryItemList" MasterPageFile="../masterpages/umbracoPage.Master" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - -
    -
    -
    - -
    \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.cs deleted file mode 100644 index a80e25840f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Web.Composing; - -namespace umbraco.presentation.settings { - - [WebformsPageTreeAuthorize(Constants.Trees.Dictionary)] - public partial class DictionaryItemList : Umbraco.Web.UI.Pages.UmbracoEnsuredPage { - - - private readonly ILanguage[] _languages = Current.Services.LocalizationService.GetAllLanguages().ToArray(); - - - protected void Page_Load(object sender, EventArgs e) { - - - string header = "Key"; - foreach (var lang in _languages) { - header += "" + lang.CultureName + ""; - } - header += ""; - - lt_table.Text = header; - - lt_table.Text += ""; - - ProcessKeys(Services.LocalizationService.GetRootDictionaryItems(), 0); - - lt_table.Text += ""; - - } - - private void ProcessKeys(IEnumerable dictionaryItems, int level) { - - string style = "style='padding-left: " + level * 10 + "px;'"; - - foreach (var di in dictionaryItems) { - lt_table.Text += "" + di.ItemKey + ""; - - foreach (var lang in _languages) { - lt_table.Text += ""; - - var trans = di.Translations.FirstOrDefault(x => x.LanguageId == lang.Id); - - if (trans == null || string.IsNullOrEmpty(trans.Value)) - lt_table.Text += ""; - else - lt_table.Text += ""; - - lt_table.Text += ""; - } - lt_table.Text += ""; - - var children = Services.LocalizationService.GetDictionaryItemChildren(di.Key); - if (children.Any()) - ProcessKeys(children, (level+1)); - } - - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.designer.cs deleted file mode 100644 index 638daa75b9..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/DictionaryItemList.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.settings { - - - public partial class DictionaryItemList { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.UmbracoPanel Panel1; - - /// - /// pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane1; - - /// - /// lt_table control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_table; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx deleted file mode 100644 index 320a9dcd18..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx +++ /dev/null @@ -1,15 +0,0 @@ -<%@ Register Namespace="umbraco" TagPrefix="umb" Assembly="Umbraco.Web" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoPage.Master" ValidateRequest="false" - CodeBehind="EditDictionaryItem.aspx.cs" AutoEventWireup="True" Inherits="umbraco.settings.EditDictionaryItem" %> - - - - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs deleted file mode 100644 index 76ee437bb3..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Linq; -using System.Web.UI; -using System.Web.UI.WebControls; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI; - -namespace umbraco.settings -{ - /// - /// Summary description for EditDictionaryItem. - /// - [WebformsPageTreeAuthorize(Constants.Trees.Dictionary)] - public partial class EditDictionaryItem : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - protected LiteralControl keyTxt = new LiteralControl(); - protected Umbraco.Web._Legacy.Controls.TabView tbv = new Umbraco.Web._Legacy.Controls.TabView(); - private System.Collections.ArrayList languageFields = new System.Collections.ArrayList(); - private IDictionaryItem currentItem; - protected TextBox boxChangeKey; - protected Label labelChangeKey; - protected Literal txt; - - protected void Page_Load(object sender, System.EventArgs e) - { - currentItem = Services.LocalizationService.GetDictionaryItemById(int.Parse(Request.QueryString["id"])); - - // Put user code to initialize the page here - Panel1.hasMenu = true; - Panel1.Text = Services.TextService.Localize("editdictionary") + ": " + currentItem.ItemKey; - - var save = Panel1.Menu.NewButton(); - save.Text = Services.TextService.Localize("save"); - save.Click += save_Click; - save.ToolTip = Services.TextService.Localize("save"); - save.ID = "save"; - save.ButtonType = Umbraco.Web._Legacy.Controls.MenuButtonType.Primary; - - var p = new Umbraco.Web._Legacy.Controls.Pane(); - - boxChangeKey = new TextBox - { - ID = "changeKey-" + currentItem.Id, - CssClass = "umbEditorTextField", - Text = currentItem.ItemKey - }; - - labelChangeKey = new Label - { - ID = "changeKeyLabel", - CssClass = "text-error" - }; - - p.addProperty(new Literal - { - Text = "

    " + Services.TextService.Localize("dictionaryItem/changeKey") + "

    " - }); - p.addProperty(boxChangeKey); - p.addProperty(labelChangeKey); - - - txt = new Literal(); - txt.Text = "

    " + Services.TextService.Localize("dictionaryItem/description", new[] { currentItem.ItemKey }) + "


    "; - p.addProperty(txt); - - foreach (var l in Current.Services.LocalizationService.GetAllLanguages()) - { - - TextBox languageBox = new TextBox(); - languageBox.TextMode = TextBoxMode.MultiLine; - languageBox.ID = l.Id.ToString(); - languageBox.CssClass = "umbEditorTextFieldMultiple"; - - if (!IsPostBack) - { - languageBox.Text = currentItem.GetTranslatedValue(l.Id); - } - - languageFields.Add(languageBox); - p.addProperty(l.CultureName, languageBox); - - } - - - if (!IsPostBack) - { - var path = BuildPath(currentItem); - ClientTools - .SyncTree(path, false); - } - - Panel1.Controls.Add(p); - } - - private string BuildPath(IDictionaryItem current) - { - var parentPath = current.ParentId.HasValue == false ? "" : BuildPath(current) + ","; - return parentPath + current.Id; - } - - void save_Click(object sender, EventArgs e) - { - labelChangeKey.Text = ""; // reset error text - var newKey = boxChangeKey.Text; - var save = true; - if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.ItemKey) - { - if (Services.LocalizationService.DictionaryItemExists(newKey)) - { - // reject - labelChangeKey.Text = Services.TextService.Localize("dictionaryItem/changeKeyError", newKey); - boxChangeKey.Text = currentItem.ItemKey; // reset key - save = false; - } - else - { - // update key - currentItem.ItemKey = newKey; - - // update title - Panel1.title.InnerHtml = Services.TextService.Localize("editdictionary") + ": " + newKey; - - // sync the content tree - var path = BuildPath(currentItem); - ClientTools.SyncTree(path, true); - } - } - - if (save) - { - foreach (TextBox t in languageFields) - { - //check for null but allow empty string! - // http://issues.umbraco.org/issue/U4-1931 - if (t.Text != null) - { - Services.LocalizationService.AddOrUpdateDictionaryValue( - currentItem, - Services.LocalizationService.GetLanguageById(int.Parse(t.ID)), - t.Text); - } - } - - Services.LocalizationService.Save(currentItem); - ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, Services.TextService.Localize("speechBubbles/dictionaryItemSaved"), ""); - } - - txt.Text = "

    " + Services.TextService.Localize("dictionaryItem/description", currentItem.ItemKey) + "


    "; - } - - #region Web Form Designer generated code - - override protected void OnInit(EventArgs e) - { - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.designer.cs deleted file mode 100644 index db9c54c1d0..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.settings { - - - public partial class EditDictionaryItem { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.UmbracoPanel Panel1; - } -} diff --git a/src/umbraco.sln b/src/umbraco.sln index e70f4b60d4..0bcddf1d3e 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2849E9D4 ..\build\build-bootstrap.ps1 = ..\build\build-bootstrap.ps1 ..\build\build.ps1 = ..\build\build.ps1 ..\NuGet.Config = ..\NuGet.Config - ..\build\RevertToCleanInstall.bat = ..\build\RevertToCleanInstall.bat - ..\build\RevertToEmptyInstall.bat = ..\build\RevertToEmptyInstall.bat SolutionInfo.cs = SolutionInfo.cs EndProjectSection EndProject